Sensor Widget JS Not Working in Safari (Desktop, Mobile)

Hi Everyone! I’m adding the widget that can be copied directly into HTML code for an outside site to show sensor information. Here’s the link I’ve copied using the “Get This Widget” option on the Real-Time Map view.

"

Loading PurpleAir Widget…

"

The issue is that this does not load in safari - both desktop and mobile - in the most recent versions of both. The copied widget functions as expected in Chrome and Firefox. The console error is as follows and affects any sensor’s widget: “[Error] TypeError: null is not an object (evaluating ‘t[1]’) (anonymous function) (pa.widget.js:1:39258)”.

Is this issue or error familiar to anyone in this community? I have not been able to fully understand the problem based on my only research so far, and would greatly appreciate any support available!

1 Like

I have same issue. Works fine in Chrome on an iMac

1 Like

Same exact issue here. I sent PurpleAir Support an email, they responded with request for info, I replied and haven’t heard back. Hopefully they’re working on it.

1 Like

Same issue for me as well – this used to work, but recently stopped working in Safari on both MacOS and iOS. Works for me in Chrome. I’ve logged a bug with Apple. It may be that Apple tightened security for embedded Javascript and PurpleAir needs to do something differently, or it may be a bug in a recent Safari or iOS+macOS.

On May 16, PurpleAir responded to me when I made direct contact with them:

"We have been able to replicate this issue and have seen other users contact us about this issue. The widget currently does not function with Safari version 16.4.

We notified the development team about this and will update the community as we work on a solution."

That’s all I’ve heard so far. The problem is still not resolved. My bug report to Apple, filed on May 13, 2023, has had no reply. It’s listed as “open.”

1 Like

Same Mac & iOS issue here, notified PA in March, in May they acknowledged shared issue … looking forward to fix!
Oh, also, on iOS16.5 the widget seems to NOT work in ANY browser.

1 Like

Now Safari 17.0 (MacOS 12.6.8), still not working.
Also attempts to log onto https://develop.purpleair.com to create API key do not progress …

1 Like

Thanks for reaching out to them. This haunted me for a while since it stopped randomly working for me one day and I just primarily use it on my iPad. However, I’m mainly a Windows desktop computer user and my MacOS machine is corporate managed and unable to be upgraded (yet). It baffled me since it seemed to work just fine on that older MacOS desktop in Safari (I just figured it was a bug in my code).

Finally had a chance to test on a newer MacOS machine with Safari 16.4 when I saw the error console. And it’s true: Looks like the error is buried somewhere in the widget itself. Odd that it works everywhere else except the latest versions of Safari (both mobile and desktop).

Since I usually have a Mac or Apple device going, I decided to make a crude & quick php webpage for grabbing the current 2.5 micron ratings. This works on alley Apple stuff, plus Firefox etc. As noted on the first comment, for use on WAN, be sure to set up port forwarding so local port 80 of LANIP can be accessed at WANIP. Here is the code –

<?php 
// Replace WANIP & WANPORT on line 23 with YOUR numbers, 
// or replace WANIP:WANPORT with LANIP ...
$UpdInt = 15 ; 			//Update interval - seconds
?>

<!DOCTYPE html> 
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >
<meta name="robots" content="noindex,nofollow" > 
<title>Air Quality</title>
<style> #main { display: flex; justify-content: center; } </style>
</head>
<body>
<div style='text-align:center'>
<span STYLE= 'font-size:18pt; font-family:\"Century Schoolbook\"; color:black; text-shadow: 0.05em 0.05em 0.1em black' >
Air Quality </span> <BR>
<BR>
<?php echo '<meta http-equiv="refresh" content="'.$UpdInt.'">' ; ?> 
<span STYLE= 'font-size:14pt; font-family:\"Century Schoolbook\"' >

<?php echo "local PurpleAir data ... " ; 

$sensordata = file_get_contents('http://WANIP:WANPORT/json?live=false') ;

echo " analyzing ... " . PHP_EOL ; 

$find1 = 'pm2.5_aqi' ;

$xyz1 = substr ($sensordata, strpos($sensordata, $find1, 400 )+10, 7 ) ;
preg_match( "/\d+/", $xyz1 , $PM25b ) ;

$xyz2 = substr ($sensordata, strpos($sensordata, $find1, 700 )+10, 7 ) ;
preg_match( "/\d+/", $xyz2 , $PM25 ) ;

$PM25avg = 0.5 * ( $PM25[0] + $PM25b[0] ) ;
echo "PM2.5: &nbsp;" . $PM25b[0] . ", &nbsp;" . $PM25[0] . "; &nbsp; avg = " . $PM25avg . PHP_EOL . PHP_EOL ;

//Version: AQ2 2023-08-15 A. Cellier <br/>
?>

<BR>
<BR> 
</div>
<div style='text-align:center'>
<span STYLE= 'font-size:10pt; font-family:\"Century Schoolbook\"' >
<a href='index.html' target='_self' > <img src= 'home.gif' width='60' height='15' alt='home' > </a> <br/>
Version: AirQuality.php 2023-08-23<br/>
</span> 
<span STYLE= 'font-size:10pt; font-family:\"Century Schoolbook\"' >
<?php echo "$Version" . PHP_EOL . "Auto-updates every $UpdInt seconds. " . PHP_EOL ; ?>
<div>
<script>	
	var m = "Page Last Updated on " + document.lastModified + " PST"; var p = m.length-0;
	document.write(m.substring(p, 0)); 
	document.writeln('</span>'); 
</script> 
</span>
</div>
</body>
</html>
1 Like

Good work. I’m a PHP geek myself. My use case is to have the page hosted on my public site (so it’s visible without having to be on the local network). I realized if you watch the network tab, you can see a hit to a URL like this one (note that I just made up the DEVICE_ names):

https://www.purpleair.com/widget.json?show={DEVICE_ID}&key={DEVICE_KEY}

To get these, go to map.purpleair.com, find your device, then click “Get this widget”. In there you’ll see the values you need, e.g. something like this:

<div id='PurpleAirWidget_{DEVICE_ID}_module_AQI_conversion_C0_average_10_layer_standard'>Loading PurpleAir Widget...</div>
<script src='https://www.purpleair.com/pa.widget.js?key={DEVICE_KEY}&module=AQI&conversion=C0&average=10&layer=standard&container=PurpleAirWidget_{DEVICE_ID}_module_AQI_conversion_C0_average_10_layer_standard'></script>

From there, you’ve got your publicly available personalized JSON key which you can use to build a client-side widget of your own. Just note that you’ll need to compute the actual AQI. I haven’t taken the time yet to reverse engineer the existing widget to see how it does it, but it wouldn’t take much time at all to figure out, I’m certain!

The URL to fetch the JSON is a good start.

Unfortunately making use of it is not as simple as writing a client side JavaScript widget. Because the request goes to a different domain than the one you’re presumably going to display the widget on (e.g. your own mydomain.xyz vs purpleair.com where the widget.json is served from), the browser will block the request. The same-origin browser policy comes into play here.

You’d need to write your own server that generates the HTML/JavaScript combination, which makes the request to purpleair.com in the backend. A lot more involved than just copy/pasting the HTML.

Just to clarify @jollyturns, are you referring to the one I pasted above or the one referencing an internal WAN IP? If it’s regarding the https://www.purpleair.com/widget.json endpoint, then it should be perfectly fine regarding CORS issues (unless there’s a particular CSP rule applied on the local domain, likely under the dev’s control anyway). That domain (at least that widget.json endpoint) is emitting the necessary Access-Control-Allow-Origin: * headers and works just fine across domains. In fact, it’s necessarily built that way, since it was created explicitly for use in embedding in people’s websites (it exposes both regular json and the jsonp callback format).

I happen to know this because I’ve already built my own widget which reverse engineered the API for my own personal site for my own personal sensor (just for fun) :grimacing:. It’s incomplete so I didn’t want to share it for that reason, but probably more importantly, also because the API wasn’t built for that purpose (I figured sharing it could cause issues if they decided to change it, I didn’t want to ruin it for everyone by forcing PurpleAir’s hand there). Let’s just say it’s not locked down; just extremely obtuse, lol. Probably intentionally so.

Anyway: Turns out PurpleAir does provide access to a great API which is a fair bit more straight forward than utilizing the widget.json and it is effectively* free; the only minor issue there is that if you wanted to keep it entirely client side (without a server side script of some sort to proxy it for you) then you’ll expose your keys. Here’s their pricing info: API Pricing

* Disclaimer: The reason I say “effectively free” (at least at first) for personal use is because they start you off with 1,000,000 “points” when you first sign up. So if you’re querying/caching your own sensor properly then it’s probably fine to use it for a fairly long time. Very hand wavy there… but I’d prefer the public API for other reasons though (noted above); not just because of the artificial expiration on accessing my own sensor data due to this cap (or requirement to pay, which is reasonable for a public service, to be fair). However, that said – they are working on it (see lower in that thread).

After writing that and commenting on the API pricing thread (since I have concerns there) it makes me wonder if maybe there’s an opportunity for an open source project that synchronizes the simpler http://[LAN_IP]/json API endpoint with a cloud service bucket (e.g. GCP Cloud Storage or Amazon S3) which is then trivial to hit publicly with whatever you build behind the scenes.

Would be even cooler if you could set it up via installing/augmenting the software on the sensor instead of having to setup a separate computer/server on the LAN to ping it (e.g. like a Raspberry PI or docker container running somewhere).

:thinking:

Edit: Again note that my goal is simply to get the data public in a low cost, long term and resilient fashion that doesn’t necessarily rely to heavily on PurpleAir themselves (which could be brittle over the long term)

Ah, my bad, I didn’t check that. OK, good to know.

Personally I’d use the widget.json endpoint in a simple JS widget, just to avoid having to register with PurpleAir for a developer account and so on. Especially if other people would want to use it.

I have the same issue. When will Purple Air resolve it? It has been in this state for a long time already.

"We have been able to replicate this issue and have seen other users contact us about this issue. The widget currently does not function with Safari version 16.4.

Also waiting on a fix for this… works on iOS 15 Mobile Safari, doesn’t load on iOS 16.

I’ve enabled Safari developer remote inspector access but I don’t see any errors in the JS console.

Is anyone listening? Pretty basic stuff to be broken for so long, not great.

The widget is working again on Safari! I just got this email from PurpleAir support:

“Our technical team has deployed a fix for the widget, which should now work on Safari. Please let us know if you encounter any further issues.”

1 Like

@kylwalsh, @Dave_Robinson, @Kevin_Widener, @n6ac, @patricknelson, @jollyturns, @Tj_Mm, @skyepn

As @wilmot13 mentioned, we’ve deployed a fix and the widget should now work on Safari. Please let us know if you still encounter any issues.

HOORAY !!!
Thank you.
@n6ac

2 Likes

Wonderful news and confirmed on my end, too! Thank you, Purple Air team, for making time to fix this widget in safari.

Thanks also to everyone who has replied and mused on this thread in the meantime!

2 Likes