Sunday, March 10, 2024

Staying out of the Ditch (Part II)

 

Staying Out of the Ditch

About four years ago, now, we had a helluva scare in Wyoming. A sudden gust of wind pushed us into the ditch. Emmy was driving; we had slowed down to about 45mph, thankfully.  

I'd like to try to avoid that again, if possible.

See Part I of this post where I cover off how I get the forecasted weather conditions for my current location.

A quick recap:

  • there's a GPS receiver on top of the RV that tells me my current location, as a latitude/longitude pair every minute
  • I'm calling the NOAA's weather API to get the weather forecast for my current location
  • I'm also calling the NOAA's Alerts API to pull any severe weather conditions that are around me.


"The World Needs More Lerts."

Just like I take my current latitude and longitude and get the hourly weather forecast for "here", I pass those coordinates into the NOAA Alerts API. The NOAA call returns a JSON packet with no alerts, or the ones issued for my area.



NOAA returns a ton of information. And, as before in the forecast return data, I only clip out the information I think is necessary to display.  That's

  • The Event (eg. Wind, Snow, Rain, Flood...)
  • Urgency (Immediate, Expected, Future, Past)
  • Certainty (Observed, Likely, Possible, Unlikely)
  • Severity (Extreme, Severe, Moderate, Minor)
  • SAME Code
  • Details

You can read the NOAA documentation for more information about the return values.   The first four are self-explanatory. The SAME code is a number that you can enter into a Weather Radio that supports such codes. Punch the SAME code into the weather radio, and it will watch for alerts in this area. A SAME code designates a geographic area (more or less).

The last column, details, is simply a constructed hyperlink to the details on this alert. Click it and it will retrieve the full alert data.

Here's an example:



We'll "start to end" Part II here.

Part III, when I get around to it, will go into the implementation details.  But before I end Part II, some thoughts on the "Why?"

Why Bother?

Isn't this "re-inventing the wheel"? Isn't this information -- and more -- already available from a number of apps on any phone in 2024?

Sure.

So why?

A couple of reasons, some selfish, some pragmatic:

  • The apps on a phone are "pull" in nature. I have to take action to get the information. I have to stop watching the road, and press buttons on my phone to request the data.  Pulling data in 2024 is insanely stupid, IMO. The computer(s) have all of the capability to push data to me.
  • The apps on a phone have a user-experience that appeals to their developers. That may or may not be the way I want to consume the data.
  • The apps on the phone have a UI that changes whenever the developer feels like it. That sometimes aggravates me. I just get the muscle-memory set to use the app with minimal thought, and the developer decides to change things.

I can continue to refine the experience to tailor the output to my needs.  Some of the things I've thought about for V2 are:

  • tailor the wind warnings for heightened alert if the direction will be broadside
  • play the Star Trek Red Alert sound when some urgent alert comes in
  • pull up alerts and forecasts for where I'm heading, instead of just where I am
  • add a NOAA radar screen so I can see a current radar

And so on. I'm sure I'll come up with some new ideas for the next revision. Even more so after I use this one in the RV for a while.









Staying out of the Ditch (Part I)

Staying Out of the Ditch

About four years ago, now, we had a helluva scare in Wyoming. A sudden gust of wind pushed us into the ditch. Emmy was driving; we had slowed down to about 45mph, thankfully.  

How she kept us upright is our best evidence of Divine Intervention to date.  Getting out and walking around our landing area, it was apparent that had we entered the ditch 20 feet sooner or later, the slope was much steeper, and we surely would have tipped over with a horrific outcome.

I'd like to try to avoid that again, if possible.

In the interim, I've kept two apps up and running on my phone: a NOAA weather radar and an app that displays Wind Speeds in the area.

But that meant I was glancing at the phone instead of the road.  I figured it was time to try to do one better.


Latitude and Longitude

There's already a GPS Receiver on top of the RV. I put it there. It's an inexpensive (<$10 USD) USB dongle with an older "ublox-7" GPS chip inside. While dated and likely not good enough for high precision needs, it consistently sends me my current location.

Like everything else in my RV, home and life, the data comes in as a MQTT message, JSON formatted. For example:

{"topic":"GPS","version":"2.0","dateTime":"2024-03-10T14:27:24-0600", "mode":"3D", "latitude":44.021032, "longitude":-125.088211, "altitude":5193.28, "speed":1.2, "track":-100.0, "climb":0.0, "GDOP":"GOOD", "HDOP":"MODERATE", "VDOP":"MODERATE", "distance":82.87}

[NB: the xDOP values are not great because the testing receiver I use is indoor. Outdoor, the xDOP values are typically 'EXCELLENT'. ]

So every minute, I know my location on the planet.

National Oceanic and Atmospheric Administration


Or NOAA, for short. NOAA has a weather department. That weather department has a public, free API that we can query for all sorts of fun weather data. In my case, I'm most interested in the Forecast and Alerts.

You can read more about the API; in my case I decided to ask the API for the hourly forecast data for my location. You pass in your lat/lon coordinates to the API and NOAA sends back a large JSON response with a ton of information.

I'll spare you the details here, I may go into them in a subsequent post. But I trim down the NOAA response to the data I'm interested in.

I'm only interested in the next four hours. The conditions, the forecasted temperature, wind speed and direction and the chance of precipitation.

If the wind speeds are forecasted to be above 10 MPH, then I'll color the data red:



My GPS receiver sends me my current location every minute. But I'm only calling the NOAA forecast API every 15 minutes.  (I doubt I could drive the RV fast enough to exit the current NOAA forecast bounding area to make more frequent updates worth the effort.)

A little implementation detail - every 'last' forecast data is cached. I'm using Node-Red to code a lot of the functionality here.  When this webpage is brought up, it asks for the last cached forecast.  That way there's relevant data to display immediately; the webpage does not have to wait 1..15 minutes for forecast data.


I'll end this post here. And cover the Weather Alerts section in the next post, Part II.


Tuesday, April 4, 2023


Freezing your you-know-whats off


Last winter, the prep work I did on the exposed RV plumbing worked well. Nothing froze. Well, almost nothing. The drain valve froze.  Annoying but not catastrophic. 

It's time to see if we can cobble something up to tackle that. There are commercial products available, but now that I have more time than sense, I thought I'd see if I could make something.

I found some 12V heating pads on Amazon. Four 20 watt pads, each about 6x2". Fifteen bucks.



When they arrived, I plugged them up to a 12V battery and was immediately shocked, figuratively not literally, by just how hot they got. Within minutes, they were over 200° F!  That's way too hot!  

Reducing The Heat

Rather than return them for something with lower wattage, I scrummaged around the parts drawer and found my DC-DC buck converters. I could use that to lower the wattage and measure the resulting temperatures.


Quick experimentation at 9V, 7.5V and 6V showed that the last one -- 6V -- gave the best (least plastic melting) temperatures. The pads maxed out at about 125° F. That should be safe.





On Only When Needed

Now came the issue of controlling the pads, I'd like them to come on near freezing and turn off when things are warm enough.  The scrummaging in the parts bin revealed that I had bought two W1209 Temperature Controlled Relays from Amazon. I had used one to turn on some additional fans in the RV's refrigde compartment when things got hot.

The W1209 Relays have a "cooling" and a "heating" mode. In the cooling mode, the relay turns on (energizes) when temperatures go above a Trigger temperature. In heating mode, the relay de-energizes at the trigger temp, but energize a temperatures below.

Note, figuring out the exact settings to get Heating Mode to work was a bit of a puzzle. In the end, you program the device: set the Trigger Temperature to the temperature to turn off. In my case, I picked 15° C. Then I set the P1 (hysterisis) value also to 15° C. This results in the relay energizing at 0° C. (Trigger Temp - P1 value = relay-on temp). 

So I have a relay which energizes at 32° F and turns off at about 60° F. 

The temperature probe accuracy is astonishing. Clearly within a tenth or two degrees C.

The Prototype

Here's the prototype:


The temperature probe is in ice water, and is reading -0.1° C. The W1209 relay has energized and is feeding 12V to the LM2596 buck converter which is dropping it down to 6V before sending it to the heating pads.

Note, each pad was consuming 0.84A at 6V.  Both pads, together, took 1.7A at 6V. Well below the rated 3A of the buck converter.

The results

Some temperature measurements with the prototype.
Time    Pad 1        Pad 2
T0        68° F        68° F 
T1        95° F        105° F 
T2        106° F      114° F 
T3        112° F      123° F 
T4        119° F      124° F 
T5        120° F      126° F 
T10      126° F      129° F 

After five minutes they both leveled off at around 120-125° F.

Next up, a case to hold the electronics and installation on the RV.



Tuesday, October 18, 2022

(#44) It's all about the weather this time

Let's make this one short. 

We're still pumping data from legacy sensors into Home Assistant. This time it's data from the two weather stations here.  The long-employed LaCrosse WS2308. Jeez this thing has to be over ten years old by now.  And a newer Acurite 5n1 station.

Both systems use the 433MHz spectrum to send data from their outside sensors to their base stations. Both 433MHz protocols are recognized and decoded by the "rtl_433" open source software.

Rtl_433 can output the intercepted data as JSON payloads which, in turn, can be pumped right into our MQTT (mosquitto) broker.


Those JSON payloads aren't immediately edible by Home Assistant. 

Or, if they are, they require an expertise in programming Home Assistant, that I don't have. So, I've stuck something in the middle.

Something easy. 

Something that subscribes to the RTL JSON payloads and republishes out new JSON payloads in the MQTT Device Discovery Format that Home Assistant understands.

A lesson learned from the previous success was to treat each sensor separately. The Acurite 5n1 sends over: temperature, humidity, wind speed, wind direction and rain totals.  

That's five separate sensors, five different MQTT Device Discovery Configuration Messages.

MQTT Data Payloads

The Acurite 5n1 sends this sensor data across two different JSON payloads via rtl_433. We break out the data to five sensors. It's similar for the WS2308 station (which is labeled as WS2310 by rtl_433). The sensor data for four sensors are scattered across three MQTT messages.

Here's the rtl_433 output for the WS2308 (aka WS2310) station:

{"time" : "2022-10-18T13:47:05", "protocol" : 34, "model" : "LaCrosse-WS2310", "id" : 28, "humidity" : 35, "mod" : "ASK", "freq" : 434.020, "rssi" : -6.215, "snr" : 31.158, "noise" : -37.373}

{"time" : "2022-10-18T13:47:05", "protocol" : 34, "model" : "LaCrosse-WS2310", "id" : 28, "wind_avg_m_s" : 0.000, "wind_dir_deg" : 22.500, "mod" : "ASK", "freq" : 434.019, "rssi" : -6.265, "snr" : 31.108, "noise" : -37.373}

{"time" : "2022-10-18T13:47:06", "protocol" : 34, "model" : "LaCrosse-WS2310", "id" : 28, "temperature_F" : 65.480, "mod" : "ASK", "freq" : 434.020, "rssi" : -6.238, "snr" : 29.886, "noise" : -36.124}

MQTT Device Discovery Configuration Messages

And here's a code snippet to create the configuration MQTT topic and message.



For each station, create the configuration messages for each sensor in that station.

Forward Station Data to Home Assistant

With the configuration messages created and sent to Home Assistant, now just forward the data packets from the weather stations to the new topics specificed in the configuration messages:

Topic: 

home/LaCrosse-WS2310-28/temperature 

Payload:

{"time" : "2022-10-18T14:53:17", "protocol" : 34, "model" : "LaCrosse-WS2310", "id" : 28, "temperature_F" : 66.200, "mod" : "ASK", "freq" : 434.020, "rssi" : -6.537, "snr" : 29.587, "noise" : -36.124}

And don't forget the state message.

Topic:

home/LaCrosse-WS2310-28/state 

Payload: 

{"state":"online"}


The sensors now all show up in Home Assistant!






Tuesday, October 4, 2022

(#43) Home Assistant Legacy Sensors - Part 3

It's working. Mostly.


With success from hooking the Tuya Air Quality Monitor (non-Zigbee) WiFi up to Home Assistant using the MQTT Device Discovery Protocol, it was not a lot of work to get the Eaton Home Heartbeat (HHB) sensors added.

As mentioned before, the HHB sensors are Zigbee based but I'm unable to quickly see how to unpair them from the Eaton base station and see if they'll re-pair to my Sonoff Zigbee dongle.

So, since the code that reads the HHB sensor data by polling the base station has been up and running for eight years, I made the decision to just add in the MQTT packets to support MQTT Device Discovery. And see if the sensors will appear in Home Assistant.

For the impatient, yes it works. The HHB sensors are now showing up in Home Assistant. The HHB Open/Close sensors, the Tilt Sensor, the Power Sensor, the Wet/Dry Sensor and the Motion sensors all are reporting their status in Home Assistant.


The only downside that I've discovered so far is the lag between a change in a sensor's status and when Home Assistant reflects the state change. That's because the old HHB code polls the gateway for sensor status changes.  So there's a couple of seconds of delay between a state change and the UI reflecting the state change.

No biggie. I can live with that.

Implementation - Summarized

The details are irrelevant; they're specific to my original code from 2013. And you can't buy these sensors anymore.

Nonetheless, for the curious, here's a brief summary:

- original code 'discovered' all of the HHB sensors at startup, by polling the gateway

- new Home Assistant (HA) code, added a call to "HA_CreateConfigurationMessage( HHB_Device )" as each sensor was discovered

Configuration messages were type "binary sensor". Here's the one for the HHB Open/Close Sensor:

 const   char *configTemplate_OpenCloseSensor = 
        "{\"availability\":[{\"topic\":\"%s\",\"value_template\":\"{{ value_json.state }}\"}],"       
        "\"device\":{\"identifiers\": [ \"%s\"],"                                                     
        "\"manufacturer\":\"Eaton\",\"model\":\"HHB OpenClose\",\"name\":\"%s\",\"via_device\": \"HHB\" },"  
        "\"device_class\":\"door\",\"enabled_by_default\":true,\"expire_after\" : 3600,\"force_update\" : true," 
        "\"name\":\"%s [%s] OpenClose\","               
        "\"state_topic\":\"HHB/%s\","                   
        "\"unique_id\":\"%s_OpenClose\","           
        "\"payload_on\":\"OPEN\", \"payload_off\":\"CLOSED\","   
        "\"value_template\":\"{{ value_json.state }}\"}";

The "%s" string substitutions were the MAC address for each sensor.

And then published to an MQTT Topic like:

homeassistant/binary_sensor/%s/config

Again, where the subsitution was the device MAC address.

Most of the sensors (all but the Power Sensor) also had a battery configuration message sent.
// The Eaton HHB battery levels are binary - either OK or not
    const   char *configTemplate_Battery = 
        "{\"availability\":[{\"topic\":\"%s\",\"value_template\":\"{{ value_json.state }}\"}],"       
        "\"device\":{\"identifiers\": [ \"%s\"],"                                                     
        "\"manufacturer\":\"Eaton\",\"model\":\"HHB OpenClose\",\"name\":\"%s\",\"via_device\": \"HHB\" },"  
        "\"device_class\":\"battery\",\"enabled_by_default\":true,\"expire_after\" : 3600,\"force_update\" : true," 
        "\"name\":\"%s [%s] Battery\","               
        "\"state_topic\":\"HHB/%s\","                   
        "\"unique_id\":\"%s_Battery\","           
        "\"payload_on\":\"LOW BATTERY\", \"payload_off\":\"BATTERY OK\","   
        "\"value_template\":\"{{ value_json.battery }}\"}";


To a topic of:

homeassistant/binary_sensor/%s/battery/config

That took care of telling Home Assistant to expect new sensors. Then it was a small addition to publish the unchanged data payload to a new MQTT topic.

Data Payloads still look like:

{"topic" : "HHB/STATUS",  "dateTime" : "2022-10-04T13:37:31-0600" , "deviceType" : 23 , "type" : "MOTION SENSOR" , "name" : "Dining Room Motion" , "state" : "MOTION" , "duration" : 60 , "setAlarmAction" : "ALARM ON MOTION" , "unsetAlarmAction" : "NO ALARM ON NO MOTION" , "setCallAction" : "DO NOT CALL ON MOTION" , "unsetCallAction" : "CALL ON NO MOTION" , "online" : "ONLINE" , "battery" : "BATTERY OK" , "triggered" : "TRIGGERED" , "MACAddress" : "0000093BF5" }

And published to a topic like:

HHB/0000093BF5 


There's a new payload to indicate sensor state. I think I could have told HA to parse state from the existing payload, but since this approach worked for the Tuya sensor, I just added it here.

{"state":"online"}

HHB/0000093BF5/STATE 


And that's about it. Like I said - the original code is from 2013 and updated a personal web page. So that's unique to me. And you can't buy these HHB sensors any more.  There's no sense going into more detail.

It's working.

I salvaged the existing sensors and have them integrated with Home Assistant. 




That's good enough for me, for today.





Tuesday, September 27, 2022

(#42) Home Assistant Legacy Sensors - continued

Legacy Sensors - Configuration - Part 2

Home Assistant MQTT Device Discovery


With the MQTT Configuration topics figured out, now it's on to the message payloads. I'll have six configuration payloads to craft for Home Assistant MQTT Device Discovery to work.


There are six sensors in the Tuya Air Quality Monitor, that means six configuration messages to the six MQTT topics created in Part 1.


The unique device ID we created in Part 1 is part of these payloads.  

Something else to decide now is the MQTT topic and JSON payload for the sensor data. When those sensors send their data to the broker, what MQTT topic will they use? What will their data payload format be?

In my case, my Tuya Air Quality Monitor Sensors will publish their values on MQTT topic:

home/eb0b-5a81

And the sensor data payload will look like: 

{"topic": "home/eb0b-5a81", "dateTime": "2022-09-27T15:45:43", "version": "1.0", "state": "online", "temperature": 25.2, "humidity": 38.8, "PM25": 3, "CH2O": 0.002, "VOC": 0.008, "CO2": 359}

Note - the sensor data comes in as a single JSON message to a single MQTT topic.

Configuration Payloads

The documentation for a Sensor Configuration payload is outlined here. Recall that our device ID is "eb0b-05a81"

Each of the six Tuya Air Quality Monitor configuration message carries a payload similar to:

   "availability":[
      {
         "topic":"home/eb0b-05a81/state",
         "value_template":"{{ value_json.state }}"
      }
   ],
   "device":{
      "identifiers":[
         "eb0b-05a81"
      ],
      "manufacturer":"Tuya",
      "model":"Air Quality Sensor",
      "name":"eb0b-05a81",
      "hw_version" : "?",
      "via_device": "python"
   },
   "device_class":"volatile_organic_compounds",
   "enabled_by_default":true,
   "expire_after" : 3600,
   "force_update" : true,
   "name":"AirQuality/A volatile organic compounds",
   "state_class":"measurement",
   "state_topic":"home/eb0b-05a81",
   "unique_id":"eb0b-05a81_volatile_organic_compounds",
   "unit_of_measurement":"ppm",
   "value_template":"{{ value_json.VOC}}"
}

Let's go through the major sections. I'm sure there's a few different ways to do this, but here's what I did.

Availability

Availability - in this section I tell Home Assistant that the state of this sensor, online or off, will come in a message posted to "home/eb0b-05a81/state". And the state value will be in the JSON payload "state". 

Here's my online payload: { "state":"online" }

And my offline payload: { "state":"offline" }

Note! The topics embedded in these messages do NOT start with "homeassistant" as the root topic. The configuration messages are letting Home Assistant know that it will need to subscribe to these topics to pick up sensor data.

We use the idea of "state" again, farther down in the payload. It's a potential for confusion. A better topic for the "availability" section might have been "home/eb0b-05a81/availability" with an availability value in the payload.

As it is - what I've done is tell Home Assistant that it can find the availability status by subscribing to the MQTT topic and extracting the state message from the payload.

Note that I'm NOT using an availability/state topic specific to this sensor. It's NOT "home/eb0b-05a81/volatile_organic_compound/state".  It could be, but because all six sensors are tied to a single monitor, I chose one topic. All six sensors will go online or offline together.


Device and...


This section holds i
nformation about the device. Home Assistant uses this data to tie it into the device registry.  You can read about the attributes; I chose a handful to populate with data meaningful to me.

Moving down, "device_class" controls the Icon displayed in the UI.  For devices of type 'binary sensor' a list of the classes is here: Home Assistant Binary Sensors Device Class.

For a device type of 'sensor' a list of the classes is here: Home Assistant Sensors Device Class.

"expire_after" : means I want this sensor to show offline if no data comes in after 3600 seconds / 1 hour.  

Setting "name" to "AirQuality/A volatile organic compounds" controls how this sensor is displayed in the UI.   I call this monitor "AirQuality/A". I'll call a second monitor "AirQuality/B".

The  "state_topic":"home/eb0b-05a81" tells Home Assistant that the sensor data will be coming in on this MQTT topic. So Home Assistant needs to subscribe to this topic.

The "unique_id" for this VoC sensor is the device ID with a string appended.  And finally the    "value_template":"{{ value_json.VOC}}" tells Home Assistant to parse the VoC value out of the JSON payload's attribute VOC.


That rounds out one of the six Configuration payloads.  For brevity here are the other five.

Temperature Sensor Configuration Payload

{
   "availability":[
      {
         "topic":"home/eb0b-05a81/state",
         "value_template":"{{ value_json.state }}"
      }
   ],
   "device":{
      "identifiers":[
         "eb0b-05a81"
      ],
      "manufacturer":"Tuya",
      "model":"Air Quality Sensor",
      "name":"eb0b-05a81",
      "hw_version":"?",
      "via_device":"python"
   },
   "device_class":"temperature",
   "enabled_by_default":true,
   "expire_after":3600,
   "force_update":true,
   "name":"AirQuality/A_temperature",
   "unique_id":" eb0b-05a81_temperature",
   "state_class":"measurement",
   "state_topic":"home/eb0b-05a81",
   "unit_of_measurement":"°C",
   "value_template":"{{ value_json.temperature }}"
}

Humidity Sensor Configuration Payload

{
   "availability":[
      {
         "topic":"home/eb0b-05a81/state",
         "value_template":"{{ value_json.state }}"
      }
   ],
   "device":{
      "identifiers":[
         "eb0b-05a81"
      ],
      "manufacturer":"Tuya",
      "model":"Air Quality Sensor",
      "name":"eb0b-05a81",
      "hw_version":"?",
      "via_device":"python"
   },
   "device_class":"humidity",
   "enabled_by_default":true,
   "expire_after":3600,
   "force_update":true,
   "name":"AirQuality/A humidity",
   "state_class":"measurement",
   "state_topic":"home/eb0b-05a81",
   "unique_id":"eb0b-05a81_humidity",
   "unit_of_measurement":"%",
   "value_template":"{{ value_json.humidity }}"
}

Carbon Dioxide Sensor Configuration Payload

{
   "availability":[
      {
         "topic":"home/eb0b-05a81/state",
         "value_template":"{{ value_json.state }}"
      }
   ],
   "device":{
      "identifiers":[
         "eb0b-05a81"
      ],
      "manufacturer":"Tuya",
      "model":"Air Quality Sensor",
      "name":"eb0b-05a81",
      "hw_version":"?",
      "via_device":"python"
   },
   "device_class":"carbon_dioxide",
   "enabled_by_default":true,
   "expire_after":3600,
   "force_update":true,
   "name":"AirQuality/A carbon dioxide",
   "state_class":"measurement",
   "state_topic":"home/eb0b-05a81",
   "unique_id":"eb0b-05a81_carbon_dioxide",
   "unit_of_measurement":"ppm",
   "value_template":"{{ value_json.CO2 }}"
}

Formaldehyde Sensor Configuration Payload

{
   "availability":[
      {
         "topic":"home/eb0b-05a81/state",
         "value_template":"{{ value_json.state }}"
      }
   ],
   "device":{
      "identifiers":[
         "eb0b-05a81"
      ],
      "manufacturer":"Tuya",
      "model":"Air Quality Sensor",
      "name":"eb0b-05a81",
      "hw_version":"?",
      "via_device":"python"
   },
   "enabled_by_default":true,
   "expire_after":3600,
   "force_update":true,
   "name":"AirQuality/A formaldehyde",
   "state_class":"measurement",
   "state_topic":"home/eb0b-05a81",
   "unique_id":"eb0b-05a81_formaldehyde",
   "unit_of_measurement":"ppm",
   "value_template":"{{ value_json.CH2O}}"
}

Particulate Matter (2.5) Sensor Configuration Payload

{
   "availability":[
      {
         "topic":"home/eb0b-05a81/state",
         "value_template":"{{ value_json.state }}"
      }
   ],
   "device":{
      "identifiers":[
         "eb0b-05a81"
      ],
      "manufacturer":"Tuya",
      "model":"Air Quality Sensor",
      "name":"eb0b-05a81",
      "hw_version":"?",
      "via_device":"python"
   },
   "device_class":"pm25",
   "enabled_by_default":true,
   "expire_after":3600,
   "force_update":true,
   "name":"AirQuality/A particulate 2.5",
   "state_class":"measurement",
   "state_topic":"home/eb0b-05a81",
   "unique_id":"eb0b-05a81_particulate_matter_2_5",
   "unit_of_measurement":"ug/m³",
   "value_template":"{{ value_json.PM25 }}"
}

Now try it!

Use "mosquitto_pub" to see if you can get a sensor to appear in the Home Assistant UI. Something like this:

mosquitto_pub -h mqttbroker.local -d -t "homeassistant/sensor/eb0b-05a81/humidity/config" -m '{"availability":[{"topic":"home/eb0b-05a81/state","value_template":"{{ value_json.state }}"}],"device":{"identifiers": [ "home_eb0b-05a81"], "manufacturer":"Tuya","model":"Air Quality Sensor","name":"eb0b-05a81","hw_version" : "?","via_device": "python" },"device_class":"humidity", "enabled_by_default":true, "expire_after" : 3600, "force_update" : true, "name":"Air Quality/A humidity",   "state_class":"measurement", "state_topic":"home/eb0b-05a81", "unique_id":"eb0b-05a81_humidity",   "unit_of_measurement":"%",   "value_template":"{{ value_json.humidity }}"}'


Flip over to the Home Assistant Overview page and you should see a new sensor - labeled "AirQuality/A humidity".

It should be greyed-out to indicate that it's offline.  Let's bring it online and send it a value

mosquitto_pub -h mqttbroker.local -d -t "home/eb0b-05a81/state" -m '{ "state":"online" }'

mosquitto_pub -h mqttbroker.local -d -t "home/eb0b-05a81" -m '{ "temperature" : 20.0, "humidity" : 37.2 , "CO2" : 364, "state" : "online", "PM25" : 4, "CH2O" : 0.004, "VOC": 0.012 }'

If all went well, you should have a new sensor in the UI!





After all six sensors are configured, you should see something like this:



Note that I'm not using the "Retain Flag" when publishing configuration messages. I'm still debugging things and didn't want this configuration messages persisted by the broker.  Yet.