Dreading the trip to the mall (it turned out fine)

One of the malls in San Diego is University Towne Center. It has always been my least favorite mall because the traffic around it is horrendous. I hadn’t been there in years even prior to the pandemic. A few years ago, I had read that the whole mall was under construction and they added a major parking garage (there was a small parking structure in addition to lots of surface parking before) which made me want to go there even less. In addition, they started charging for parking. Last week when I was looking to get a MacBook Air, I didn’t want to have it shipped as it would have arrived when we weren’t home and the other close Apple Store didn’t have it, so I decided to brave the mall and goto UTC. I read that the first two hours of parking were free, so that was good.

I drove to UTC using a sightly less traveled route and was kind of lost as I approached it as there is still a lot of construction for the trolley extension. Traffic was light (it was a weekday around lunchtime), so it wasn’t bad getting there. I turned into the parking garage and was overwhelmed at its size; the entrance was very wide open and spanned two stories. I got my ticket and followed some signs to Nordstrom since I really had no idea where the Apple Store was located. As I proceeded, I saw signs that indicated how many empty spaces down each section. In the past I’ve thought that they were kind of made up. Once I started going down an aisle, I realized that the numbers were completely accurate; overhead down the center of each aisle was a small sign that had a red or green indicator on each side showing me which spot was occupied. This made finding a space super easy. I parked, recorded the section I was in on my ticket as I had no idea where I was going, followed the painted walkways to the stores, and then went up 2 flights of stairs.

The digital map said to use at your own discretion, so I pulled out my phone and looked for the Apple Store. I had managed to park relatively close, so it was a short walk. I stood in line outside for less than a minute, showed my barcode, had my temperature checked (yeah, I know this is a waste) and then was told to wait on one of the black dots near the window inside the store. Someone brought out my computer, checked my ID and I was on my way.

Getting back to my car was easy and exiting the garage was another painless activity. When I got to the gate to leave, I tried to stuff the ticket in the machine, but it wouldn’t go and the gate opened. I think it read the barcode and opened without me having to put it in. This particular exit was on a street that had less traffic, so I was on my way. I think my total time from leaving my house to returning was less than hour with about 25 minutes of that being driving time.

The experience was so pleasant, I might consider going back to the mall in the future!

Another stab at fixing the Vizio SB36512-F6 Soundbar

About a year and a half ago, I wrote about fixing the SB36512-F6 soundbar as sometimes we just couldn’t hear anything from it. My "fix" seemed to work for awhile, but over the last few months, the problems have been worse. We would start a show using Channels or Netflix (primarily) and got no sound. Lately the volume buttons wouldn’t work (through HDMI-CEC) which is also frustrating. Through a combination of powering down the TV, changing the input on the soundbar, and powering off the outlet for the devices, we were able to get sound again.

I’ve been so frustrated with this that I’ve been on the verge of purchasing another soundbar, but waiting for a Costco sale so that I can take advantage of their return policy when it performs poorly. Today I decided to do another web search to see if others encountered the same problem. Unfortunately my original post was one of the hits! On the positive side, other posts indicated that Dolby Atmos was pretty poor on these types of soundbars which gave me an idea.

In order to get Dolby Atmos on the soundbar, I plugged the Apple TV into the soundbar’s HDMI port and then the TV into the soundbar’s HDMI ARC port. If I was willing to forego Atmos, I had more options for connecting the soundbar. Since I like HDMI-CEC for controlling all my devices, I decided to plug the Apple TV directly into my TV and then plugged the soundbar into the TV using HDMI-ARC.

Initial tests show that the setup works as expected with HDMI-CEC working for controlling volume and the power to the devices (I use power loosely as the devices are always in some type of standby mode). Will this fix my problems? I sure hope so or it is back to the drawing board on how to deal with this frustration.

It still baffles me that getting devices that adhere to standards working together is such a crapshoot. I have no idea how the average person gets any type of technology or electronic device to work.

Apple Watch 6 – My Take

When the first Apple Watch came out, I was immediately hooked. I wrote that it did everything I needed it to do. The next few generations of watches didn’t add a huge amount of value to me, so I skipped them. When the Apple Watch 4 came out, I decided to upgrade because it was waterproof, was significantly faster and could run the latest watchOS. I skipped the following year even though the always on display looked like a great feature.

This year with the pandemic, I was unable to goto the pool (last year I swam a lot and used my watch all the time being thankful it was waterproof) so I went back to running. When I run, I take my iPhone and put it in an armband carrier. This has worked well for years, but really started getting on me as I was running on an almost daily basis. When the Apple Watch 6 came out, I was torn. I didn’t need a new watch, but the blood oxygen sensor was interesting, the always on display was something that I didn’t know I really wanted (I’ve been shaking my arm for the last 5 years to get the screen to light up!) and the faster processor would make the watch more usable.

After looking at my usage pattern and selling a bunch of old stuff on eBay to cover the cost, I decided to get the GPS+Cellular version of the Apple Watch 6. Yes, I was going to have to pay an additional $10+fees per month for it, but I’d be freed a little bit from my phone tether.

I can’t say enough good things about this watch; the always on display is absolutely amazing and I can stop shaking my wrist all the time! Running (and walking) without carrying my phone is very pleasant allowing me to still be connected if need be, but not having a phone in my pocket (walks) or attached to my arm (runs). Apple is making huge strides with the processing speed of these watches with each generation. This watch is instantly responsive and Siri on it works very well.

With watchOS 7 comes sleep tracking and I’ve been wearing my watch almost 24/7 because of it. While I didn’t know exactly what my sleep pattern was, I could kind of tell when I didn’t sleep well. My watch now tells me (for the most part) if I had trouble sleeping. This, of course, isn’t exact as it is based on arm movement, but it is much better than not knowing.

Wearing my watch 24/7 causes an interesting problem with battery life. The cellular connection on my watch drains the watch faster than without it, so on days that I run or walk, I have to be aware that I have to charge my watch. In order to make it through the day, I am typically charging my watch in the morning and depending on the day, in the late evening. Some people would say that this is inconvenient, but just dropping it on the charging stand isn’t a big deal.

If I’m as happy with my Apple Watch 6 as I was with my last 2 Apple Watches, it will be another good purchase.

Toyota RAV4 Prime – The vehicle I wanted 4 years ago

When I was shopping for a car over 4 years ago, I had a few requirements including CarPlay and being a plugin hybrid. The plugin hybrid would have been perfect for my needs, but had to have around 40-50 miles of electric range.

Unfortunately I was unable to get a plugin hybrid, but I wouldn’t budge on having CarPlay. At the time, I had a Toyota Highlander and would have jumped at another Toyota, but Toyota dragged their heals on putting CarPlay in their vehicles, so I settled on the Subaru Impreza and was reasonably happy with it. However, as time went on, there were a few things that started getting on my nerves about it. The first was that it was too low to the ground. This, of course, was entirely my fault for making the decision to get a sportier car. The second was some low speed shifting issues that the dealer said were normal, Third was the local dealer; one of the service people was rude and lied to me. There are limited choices for Subaru dealers in close proximity to where I live. The fourth and most annoying problem was the infotainment system and CarPlay. I found that CarPlay wouldn’t always start and required me to reboot the system. I wasn’t the only person that had problems with the system as Subaru settled a class action lawsuit for the issues with it; I walked away with $350 which seems pretty significant for an issue that didn’t physically affect me. On various forums, people have argued that the infotainment system problems aren’t a big deal and that people don’t buy cars just for the infotainment system. Well, I’m not most people! CarPlay and the infotainment system were probably the biggest reasons I went with a Subaru instead of another Toyota.

In August, I wrote about looking for a new car. As much as I wanted to keep my Subaru for as long as I kept my Highlander (14 years), the annoyances I listed above especially the infotainment system, pushed me to long for a new car. My driving habits back when I was looking for the Impreza were pretty similar to before the pandemic whereby a plugin hybrid with 40-50 mile range would be ideal for me. I wouldn’t have range anxiety and could go on longer trips without having to plan charging stops. The RAV4 Prime fit the bill and I searched and searched for months to get one at a reasonable price. Most dealers wanted significantly above MSRP and those that didn’t had no idea when they would be getting what I wanted. However, I didn’t let that deter me.

On Black Friday, I got an alert from cars.com that I had setup and quickly sent an email to the dealer that was 90 miles away. Based on my communications with other dealers, I wasn’t holding my breath, but when I flat out refused his first price, he came down to a reasonable number. I made all the arrangements, packed the family in the car and made the drive up to the dealer. As one last middle finger to me, when I started the Impreza I could not get any volume out of the infotainment system. Luckily my wife was able to futz with it and get sound out of it again!

I’ve now had the vehicle for 5 weeks and driven it in a mix of electric and hybrid modes and like most new car owners, I’m generally pleased with it. Driving electric is amazing as it is quiet and the acceleration is unbelievable, so much so that the Toyota app has told me that I have aggressive acceleration as I’m not even aware that I’m pressing the pedal that hard!

Four years ago, I wanted a compact SUV (similar to my Highlander), plugin hybrid, and CarPlay. I finally have all that in my RAV4 Prime; I would not have thought twice about buying this vehicle if it was out back then.

Time, of course, will tell if I made the right decision and if there are any major problems with it.

Two Years of Daily Meditation

For the last 2 years, I’ve made an effort to spend about 10 minutes a day meditating. I’ve been using the Calm app listening and following the "daily calm". I tried other meditation apps, but have settled on Calm because I usually get a new guided meditation every day (there have been a number of repeats over the course of 2 years). Repetition in other apps was boring and made it very hard to keep meditating every day.

While 10 minutes a day is not a lot for those that have done meditation for a long time, it is a good start for me and I’ve managed to take the few minutes a day out of my schedule to just sit. As someone that finds it hard to sit still and do nothing, I consider this a huge accomplishment.

My day is not complete without my daily Calm and I hope that doing this is starting to change me for the better. Is it working? Sometimes.

Think Globally, Act Locally

Think Globally, Act Locally

More than 30 years ago, I took a tour of the Boston Globe as part of my high school graphic arts class. During the tour, they showed us the printing presses that were idle and hadn’t been cleaned yet after the prior night’s run. I asked what happened to all the paper still in the presses. I was told that they just threw away all the paper. Looking back on this answer, I believe it may have been incorrect, but it started me thinking about how much we waste (paper in particular). I decided to start a paper recycling program as my Eagle Scout project at my high school as a way to help reduce waste.

I became very involved in recycling efforts and joined my town’s recycling committee. My interest in recycling was probably the start of me thinking about the slogan "think globally, act locally". This slogan, of course, wasn’t new at the time, but was very on point as my recycling program wasn’t going to change the world, but it was a small part that I could to do help and if there were other like minded people, we collectively could do great things.

Recycling of paper products is now common place and people don’t think about it much. While I definitely didn’t make this happen globally, I’d like to think that I helped educate thousands of people through my efforts.

In my adult life, I’ve tried to keep following the act locally mantra by volunteering and by donating money. I know I’m not going to change the lives of millions of people and my name probably won’t be known by many people, but that doesn’t matter as long as I can make an impact on a few.

Now more than ever, I think the "think globally, act locally" slogan is important. While we as individuals can’t make COVID-19 go away, small acts such as wearing masks, staying home, and physically distancing can make a huge difference if we all followed the guidelines. When safe vaccines become available, getting one is another way to make a difference. It isn’t necessarily about protecting you from COVID-19, but also about protecting others. There are certain people that can’t get vaccines and we, as a society, need to do our best to protect them.

Some of the guidelines that we’re being asked to follow may be inconvenient, but we’re all in this together. I don’t think there are any excuses to not following the guidelines; some people are selfish and believe it is all about them and other people are not science literate to understand why we have been asked to do certain things. I am fortunate that I can financially ride this out; if everyone had followed the guidelines 6-9 months ago, we may not be where we are today with the increasing numbers and with so many people hit financially.

I urge people to do their part now and when we get through this pandemic about acting locally so that we can all help make the world a better place.

A look at UL/ETL Certification and Home Automation

Ever since I can remember, my dad has told me that any piece of electrical equipment that has a cord or is hardwired should be Underwriters Laboratories listed. UL listing means that a sample of the product has undergone testing and meets certain criteria for the type of device it is. Does this mean that items that lack the UL listing aren’t safe? No, it could just mean that the company didn’t spend the money to get the product tested.

Most things you buy with a cord are UL listed, so it really isn’t a concern. However, with more and more products coming direct from overseas and being developed faster and cheaper, it is becoming more common to see mass market products lacking UL listing.

Taking a slight detour here, UL has been the predominant testing lab/certification and the one that most people recognize. However, recently more products bear the ETL mark. Edison Testing Laboratory was started by none other than Thomas Edison and performs similar functions to UL. I’ve done a bit of research and the only real difference is which service a company choses to use. ETL tests to the same standard as UL. I suspect there could be a cost or time to test difference between the two.

I’ve been very cautious about things I buy direct from overseas and even some things I buy here that aren’t from mainstream manufacturers. While a UL or ETL mark doesn’t guarantee that something is going to be safe, it gives me some reassurance that an independent lab has looked at the product.

When I went to purchase a 3D printer, the Creality Ender 3 Pro was advertised as having a UL listed power supply (older ones didn’t). This got me curious about what that meant. Components of products can be UL listed without the entire product being listed; for whatever reason the whole device didn’t go through certification. These components bear a different mark called a "listed component".

So while the power supply on the printer is UL listed, the printer itself is not. I’m not overly concerned about this as the power supply is the piece that connects to 120 V and outputs low voltage. This, of course, doesn’t mean that the printer couldn’t catch fire (I’ve seen reports of this).

For my son’s Eagle Scout project, he’s building something (I’ll post about it when it is complete) that plugs into the wall and is controlled by a low voltage circuit. We found an example of what he wants to do on GitHub where the author posted a list of components. One of the components is a solid state relay that takes line voltage (120V) on one side and is controlled by low voltage on the other. While relays are quite common, mixing low and line voltage can be dangerous. According to the National Electric Code any time low voltage and line voltage are placed in the same electrical box, there must be a plastic separator between the two. When we remodeled our house, I had to find electrical boxes that had separators as I put low voltage audio controls in every room next to the bank of light switches.

Looking at this relay, I didn’t see a UL component mark on it and even if I did, I’d be concerned about putting it in a box that had line voltage and low voltage. So in my effort to help my son (adults can help on projects and he’s asked me to handle the electrical part and some of the electronics due to my knowledge of the field), I started researching parts that are UL or ETL certified so that we didn’t have to worry about mixing line voltage and low voltage. The pieces we’re looking at are widely used in IoT devices and consist of an outlet that can be controlled by a low voltage circuit.

I’ve looked and looked and have found some standalone outlets that are listed. I came across a blog post talking about the Sonoff smart plugs that are UL listed. Excellent, I may have found what I needed. I purchased 2 of the plugs and received them last week. They didn’t have an ETL logo on them, but did have a UL logo on them. Unfortunately looking up the product in the UL database didn’t come up with anything, but it did appear in the ETL database. When I asked Sonoff about this, they said that the S31 was ETL certified and not UL listed. That’s kind of odd and the discrepancy is enough for me to send them back. I did, however, find another outlet TGWF115PQM by Top Greener that is UL listed (and is in the UL database). In addition, this plug was easily flashed with the Tasmota firmware that works with Home Assistant and I believe it will work for my son’s project.

Many of the major brands of IoT devices, including the Belkin plugs don’t list UL or ETL certification on their web pages. If they are listed, I think that would be an important fact to mention on their site. If you browse Amazon for smart plugs, you’ll see some say CE or FCC certification; this is NOT the same as UL or ETL certification. Even if they say ETL or UL listed, I’d strongly encourage people to lookup the products and verify the certifications at UL or ETL.

If you’re in the market for IoT devices, look carefully at the certification on the devices and would not order anything direct from overseas that is line voltage (I ordered a small computer last year direct and the external power supply was not UL listed so I just swapped it with another one that I had just to be on the safe side).

Stay tuned for more on the project and the journey.

Ryobi 18V ONE+ HP Compact Brushless Drill and Impact Driver Review

I’ve written a number of times about how much I like my Ryobi tools. This summer one of my small 1.5 Ah batteries died, so I looked at getting new ones. While many people prefer the larger batteries that last longer, my projects typically are short and don’t require a lot of power. The small batteries are lighter and make the tools easier to use. Home Depot (the exclusive seller of Ryobi products), has a version of 1.5 Ah batteries that don’t have a fuel gauge on them; to me, this makes the batteries pretty useless. There are a number of sellers on eBay selling the 1.5 Ah batteries with the gauges for about $30 per battery.

I saw that Ryobi released new HP brushless tools including a drill/driver kit with 2 1.5 Ah batteries that have fuel gauges! The cost, however, is $180 meaning the driver and drill are about $120. As I already have a perfectly good drill and driver, it was hard to justify.

For the holidays, I saw that Home Depot discounted the set by $40 bringing the cost to a bit more reasonable $140. I still didn’t need the set, but the new tools are lighter than my current ones and potentially more powerful than the ones that I bought many, many years ago. I couldn’t help myself and hit the buy button!

The tools arrived last week and the batteries are smaller and slightly lighter than my existing 1.5 Ah battery and have a sleeker design. The new impact driver weighs 842 g (without battery) and the drill 952 g vs 1192 g and 1226 g respectively for my old set (I have an even older drill that weighs 1420 g). The battery is 410 g vs 446 g for the 1.5 Ah battery. The weight differences are huge!


Left is the oldest drill/driver; middle was the one I bought a few years ago and the one on the right is the latest brushless drill/driver

Old and new impact drivers
Left is old impact driver; right is new impact driver.

I’ve used both tools briefly and they perform well. Are they better than the older tools? Maybe marginally, but it is hard for me to tell. I’ve seen a video where the person compared the new brushless drill/driver with the old brushless driver and found it less powerful (he had some odd results depending on the type of battery he used). Am I going to notice? I will definitely notice the weight difference, but probably not the performance.


  • Brushless motors
  • Lighter than what I already had


  • May not provide better performance (depending on what you already have)

If you have existing tools that work, it is really hard to justify these new HP brushless drill/driver. Having extra batteries is always good and the lighter ones are well worth it for me. If the tools last as long as my old ones, then this will be an excellent purchase.

I was impressed enough with the set that I purchased the combo set for my dad. He has been using a 12V DeWalt drill for ages that is starting to fail. He likes the lightweight tools and I hope that he can get as excited for the Ryobi tools as I am.

Debugging Firebase Analytics on iOS

Once again I’ve been instructed to add analytics into an app. Anyone that has ever instrumented analytics knows that it is a royal pain as analytics requires various pieces of information in all kinds of places in apps. While there are ways to mitigate the number of places that code has to be modified, someone still needs to verify that the required events are fired and the necessary parameters are included.

Like many analytics systems, Firebase batches events and sends them off to the server every so often making it hard to see in real time what events are being fired. In order to facilitate analytics verification, Firebase has a method to enable near real time events. Unfortunately this requires access to the source as the parameter Google says to add is a runtime parameter (-FIRDebugEnabled). I was also told to look at an article about debugging, but clearly the author never actually tried the method described on iOS as it references the runtime parameter which is NOT available in TestFlight builds.

Just to make sure I wasn’t crazy that there was no documented way to enable debugging in a TestFlight build, I asked a friend to review the documentation. He confirmed what I thought, but pointed out the following:

This behavior persists until you explicitly disable Debug mode by specifying the following command line argument:


Immediately after he pointed this out, I got an idea that the runtime flag stored a parameter in the UserDefaults for the app. I ran my app in the simulator using the enabled runtime flag (and another flag) and then used ControlRoom to inspect the plist containing my user defaults. I then ran it again with the disabled flag and compared the results. I found the following 2 entries when the debugging was enabled:


In order to test my theory that these 2 preferences would turn on Firebase debugging, I added a hidden preference to the app to toggle it and ran the app.

UserDefaults.standard.set(true, forKey: "/google/firebase/debug_mode")
UserDefaults.standard.set(true, forKey: "/google/measurement/debug_mode")

Once I toggled the preference, I looked at the Firebase console and didn’t notice anything. Based on the fact that the Google used runtime parameters, I thought that maybe the values were only checked at startup. So I killed the app and re-ran it. Low and behold, data started showing up in the Firebase console! I fixed up my hidden debugging setting to call exit(0) after toggling the preference, built the app and tested on a device not connected to the debugger. It worked perfectly!

I suspect that Google wanted to keep this a runtime parameter so that it wasn’t accidentally turned on in the wild and cause extra load on the servers. However, it really makes it hard to debug the analytics without access to the source code.

Just after writing this article, I searched the web again for a way to do this and found a Stack Overflow answer that sets the runtime flags when the app is started. The answers are interesting, but should also have a mechanism to turn off the debugging! To be more complete, the answers should be more like:

    #if DEBUG || STAGING
    if UserDefaults.standard.bool(forKey: "firebaseDebug") == true {
        var args = ProcessInfo.processInfo.arguments
        ProcessInfo.processInfo.setValue(args, forKey: "arguments")
    } else {
        var args = ProcessInfo.processInfo.arguments
        ProcessInfo.processInfo.setValue(args, forKey: "arguments")

Then in some hidden debug screen, add a flag to turn on firebaseDebug UserDefault.

Building an Air Quality Sensor

With the recent fires in California, I’ve been concerned about the air quality as it tells me if I should run, if we should go on our daily walk and if we should leave our doors open like we usually do. The EPA uses air quality sensors around the country to collect data and displays them on the AirNow website. These sensors are very expensive and therefore not placed everywhere. The air quality, of course, can differ depending on where you live and the closest EPA sensor to where I live is about 10 miles away. The EPA has started including data from low cost sensors made by a company called PurpleAir in their fire and smoke map.

With someone’s PurpleAir sensor about 0.5 miles from me, I can get a reasonable view of local air quality. Being the tinkerer that I am, I decided to look into the PurpleAir outdoor air sensor. At $279, it was a little out of my “curiosity price range”. After a little research, I was able to determine what parts are in the PurpleAir sensor. It consists of 2 Plantower PM5003 laser particulate sensors, a BME 280 temperature/pressure/humidity sensor running on an ESP8266 board.

I’ve been experimenting with the NodeMCU microcontroller which is based on the ESP8266, so I was already familiar with parts of the setup. I already have an indoor temperature sensor running on a NodeMCU, so adding a second device shouldn’t be that difficult. On my Home Assistant instance, I’m running the ESPHome add on which makes the ESP modules available to Home Assistant. ESPHome has support for lots of devices including the PM5003 and the BME280 which simplifies the software part of the setup.

In addition to purchasing the PM5003, BME 280 and a Wemos Mini d1 compatible board (ESP8266), I purchased a PVC cap to mount it. Total parts cost was about $45. I followed parts of an online tutorial for wiring things up which meant soldering the PMS5003 power to the 5V on the board, the ground on the sensor to the board and the TX line to D4 on the board. For the BME 280, power went to 3V, SDA to D2 and SCL to D1.

Wired Board

After wiring up the board, I used my trusty Ryobi Hot Glue Gun to glue the pieces into the PVC housing.

Mounted components

I then configured ESPHome. The ESPHome configuration is below:

  rx_pin: D4
  baud_rate: 9600

  - platform: pmsx003
    type: PMSX003
      name: "Particulate Matter <1.0µm Concentration"
        - throttle: 30s
      name: "Particulate Matter <2.5µm Concentration"
        - throttle: 30s
      name: "Particulate Matter <10.0µm Concentration"
        - throttle: 30s

  - platform: bme280
    address: 0x76
    i2c_id: bus_a
      name: "Outside Temperature"
      oversampling: 16x
      accuracy_decimals: 1
      name: "Outside Pressure"
      accuracy_decimals: 1
      name: "Outside Humidity"
      accuracy_decimals: 1
    update_interval: 30s

  - platform: dht
    pin: D5
      name: "Outside Temperature Alt"
      name: "Outside Humidity Alt"
    update_interval: 30s

  sda: D2
  scl: D1
  scan: True
  id: bus_a

In addition to the BME 280 sensor, I added a second temperature/humidity sensor, the DHT22 so that I can compare results as the BME 280 apparently doesn’t have accurate results as the component heats up itself. (I added the DHT22 after the pictures were taken.)

With the sensor setup in ESPHome, the next part was getting the readings converted into an air quality index (AQI). There are various calculations and corrections used to calculate the index. I stuck to a simple calculation that I found in Jason Snell‘s Scriptable widget that works with PurpleAir data.

I’m a big fan of Node-RED and used that to periodically take the data from the sensors and generate an AQI. In Node-RED, I have it poll the sensor once a minute and then calculate the AQI and then update the Home Assistant sensor

[{"id":"90c83f52.90ab4","type":"poll-state","z":"3c8c01a5.14121e","name":"2.5um","server":"d83da4b3.5bea38","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"60","updateIntervalUnits":"seconds","outputinitially":true,"outputonchanged":false,"entity_id":"sensor.particulate_matter_2_5um_concentration","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":110,"y":1380,"wires":[["46dfb46e.8c564c"]]},{"id":"46dfb46e.8c564c","type":"change","z":"3c8c01a5.14121e","name":"Set Payload","rules":[{"t":"set","p":"particulate","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":1380,"wires":[["54903d05.bb2a04"]]},{"id":"54903d05.bb2a04","type":"function","z":"3c8c01a5.14121e","name":"","func":"function calcAQI(Cp, Ih, Il, BPh, BPl) {\n    var a = (Ih - Il);\n    var b = (BPh - BPl);\n    var c = (Cp - BPl);\n    return Math.round((a/b) * c + Il);\n}\n      \nfunction getAQIDescription(aqi) {\n\tif (aqi >= 401) {\n\t  return 'Hazardous';\n\t} else if (aqi >= 301) {\n\t  return 'Hazardous';\n\t} else if (aqi >= 201) {\n\t  return 'Very Unhealthy';\n\t} else if (aqi >= 151) {\n\t  return 'Unhealthy';\n\t} else if (aqi >= 101) {\n\t  return 'Unhealthy for Sensitive Groups';\n\t} else if (aqi >= 51) {\n\t  return 'Moderate';\n\t} else if (aqi >= 0) {\n\t  return 'Good';\n\t} else {\n\t  return undefined;\n\t}\n }\n\nfunction getAQIMessage(aqi) {\n\tif (aqi >= 401) {\n\t  return '>401: Health alert: everyone may experience more serious health effects';\n\t} else if (aqi >= 301) {\n\t  return '301-400: Health alert: everyone may experience more serious health effects';\n\t} else if (aqi >= 201) {\n\t  return '201-300: Health warnings of emergency conditions. The entire population is more likely to be affected. ';\n\t} else if (aqi >= 151) {\n\t  return '151-200: Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects.';\n\t} else if (aqi >= 101) {\n\t  return '101-150: Members of sensitive groups may experience health effects. The general public is not likely to be affected.';\n\t} else if (aqi >= 51) {\n\t  return '51-100: Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution.';\n\t} else if (aqi >= 0) {\n\t  return '0-50: Air quality is considered satisfactory, and air pollution poses little or no risk';\n\t} else {\n\t  return undefined;\n\t}\n }\n\n\n\nvar pm = msg.particulate;\nvar aqi;\n\nif (isNaN(pm)) aqi = \"-\"; \nif (pm === undefined) aqi = \"-\";\nif (pm < 0) aqi = pm; \nif (pm > 1000) aqi = \"-\"; \n        /*      \n              Good                              0 - 50         0.0 - 15.0         0.0 – 12.0\n        Moderate                        51 - 100           >15.0 - 40        12.1 – 35.4\n        Unhealthy for Sensitive Groups   101 – 150     >40 – 65          35.5 – 55.4\n        Unhealthy                                 151 – 200         > 65 – 150       55.5 – 150.4\n        Very Unhealthy                    201 – 300 > 150 – 250     150.5 – 250.4\n        Hazardous                                 301 – 400         > 250 – 350     250.5 – 350.4\n        Hazardous                                 401 – 500         > 350 – 500     350.5 – 500\n        */\n\nvar particulateSize;\nvar sensorName;\nvar sensorFriendlyName;\nif (msg.topic.includes('2_5')) { \n    particulateSize = \"2.5\";\n    sensorName = \"aqi_pm_25\";\n    sensorFriendlyName = \"EPA PM 2.5 AQI\";\n\n    if (aqi === undefined) {\n\t\tif (pm > 350.5) {\n\t\t\taqi = calcAQI(pm, 500, 401, 500, 350.5);\n\t\t} else if (pm > 250.5) {\n\t\t\taqi = calcAQI(pm, 400, 301, 350.4, 250.5);\n\t\t} else if (pm > 150.5) {\n\t\t\taqi = calcAQI(pm, 300, 201, 250.4, 150.5);\n\t\t} else if (pm > 55.5) {\n\t\t\taqi = calcAQI(pm, 200, 151, 150.4, 55.5);\n\t\t} else if (pm > 35.5) {\n\t\t\taqi = calcAQI(pm, 150, 101, 55.4, 35.5);\n\t\t} else if (pm > 12.1) {\n\t\t\taqi = calcAQI(pm, 100, 51, 35.4, 12.1);\n\t\t} else if (pm >= 0) {\n\t\t\taqi = calcAQI(pm, 50, 0, 12, 0);\n\t\t} else {\n\t\t\taqi = undefined;\n\t\t}\n\t}\n} else {\n    particulateSize = \"10.0\";\n    sensorName = \"aqi_pm_10\";\n    sensorFriendlyName = \"EPA PM 10 AQI\";\n    if (aqi === undefined) {\n\t\tif (pm > 425) {\n\t\t\taqi = calcAQI(pm, 500, 301, 604, 425);\n\t\t} else if (pm > 355) {\n\t\t\taqi = calcAQI(pm, 300, 201, 424, 355);\n\t\t} else if (pm > 255) {\n\t\t\taqi = calcAQI(pm, 200, 151, 354, 255);\n\t\t} else if (pm > 155) {\n\t\t\taqi = calcAQI(pm, 150, 101, 254, 155);\n\t\t} else if (pm > 55) {\n\t\t\taqi = calcAQI(pm, 100, 51, 154, 55);\n\t\t} else if (pm >= 0) {\n\t\t\taqi = calcAQI(pm, 50, 0, 54, 0);\n\t\t} else {\n\t\t\taqi = undefined;\n\t\t}\n    }\n}\nmsg.payload = {\"aqi\": aqi, \"description\": getAQIDescription(aqi), \"message\": getAQIMessage(aqi), \"particulate\" : particulateSize, \"sensor_name\": sensorName, \"friendly_name\": sensorFriendlyName};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":560,"y":1380,"wires":[["ba491750.52e4f8"]]},{"id":"ba491750.52e4f8","type":"ha-api","z":"3c8c01a5.14121e","name":"Update Sensor State","server":"d83da4b3.5bea38","debugenabled":false,"protocol":"http","method":"post","path":"/states/sensor.{{payload.sensor_name}}","data":"{\"state\":\"{{payload.aqi}}\",\"attributes\":{\"icon\":\"mdi:chemical-weapon\",\"friendly_name\":\"{{payload.friendly_name}}\",\"description\":\"{{payload.description}}\",\"particulate_size\":\"{{payload.particulate}}\",\"unit_of_measurement\":\"AQI\",\"message\":\"{{payload.message}}\"}}","dataType":"json","location":"none","locationType":"none","responseType":"json","x":800,"y":1380,"wires":[[]]},{"id":"d83da4b3.5bea38","type":"server","z":"","name":"Home Assistant"}]

Having the AQI sensor in Home Assistant allows me to quickly glance and see how bad the air is outside (at this point, I can actually see the poor air!).

AQI Graph

Good air quality is less than 50 and from the graph above, we haven’t seen that in awhile!

I mounted my finished product under a second floor deck which should keep the major rain out of it. PurpleAir recommends not covering the bottom with anything, so I’m going to go with that and see what happens. Having it completely exposed outside isn’t great. It is powered by a PoE to USB adapter as I had Ethernet going outside there anyway.

Final mounting

The AQI data is interesting and is actually useful in telling me how much physical activity I should do. Other pieces of data I collect are neat, but not all that useful.