Awair Element air quality monitor - data charting without using a binding

My setup (Prerequisites for openHAB v3):

  • openHAB3 installed on a RPi4 using openhabian image:
    download page
  • Grafana and Influx installed via menu. Some familiarity with using these in openHAB will be needed!! Grafana gauges are a plugin called D3 gauge by Brian Gann. To install Influx and Grafana use a console window and open the config menu:
    sudo openhabian-config … menu item 20-24
  • HTTP binding installed via openhab addons menu:
    http://“IP of your openHAB server”:8080/settings/addons

Awair is a mid to high end (200euro) air quality monitor for the home. It monitors temperature, humidity, C02, volatile organic chemicals(VOC), particles smaller than 2.5um (PM25) and can send alerts and other info to your phone. It also calculates an overall air quality score 0-100.

The unit has a very simple LED display but you can also use an app to check the detailed data. Charting is a bit limited and I prefer to have one place for all my charts >>> Grafana. The unit has a simple web page that can be accessed from the LAN. There are cloud options too but I didn’t check these out. Of course once the data is in openHAB you can then create rules to act on it eg switch on AC, dehumidifier etc

From Awair website:

The easiest way to access and review data through the Local API feature is to find the webpage (see: Discovering your Awair device on the LAN below) and load it into your browser.

For developers and more advanced users, you can use popular technologies such as Python and JavaScript to fetch data and use it in custom programs; or platforms such as Homebridge, Home Assistant, and OpenHAB to use the data in a home automation system.

Below are the Grafana gauges and a example chart that I created for the Awair monitor. After those images there are instructions on how do it for yourself. There is more data available via HTTP eg raw data and device parameters if interested.

How it looks in Grafana

Here’s how to do it using the HTTP method to obtain the data from the Awair Element every 5mins

N.b. Below assumes some basic knowledge of: openHAB, use of openHAB textual config files, Grafana dashboards and charts setup.

I enabled the local API option for the Awair using the app and then in openHAB setup HTTP caching and regex to extract the needed data.

eg to see the webpage to cache

http://<IP address of your Awair device>

This comes up wth some links and the following information:

Local Sensors

issue a GET request / refresh this page to retrieve the latest air-data samples and the UTC timestamp (ISO8610 formatted) on the internal clock, or supply a timestamp as an optional query parameter (e.g. “?current_time=1970-09-23T17:12:19.630Z”)

Device settings:

issue a GET request to retrieve device configuration data

Click on the link and you can see the data we want to bring into openHAB

http://<IP address of your Awair device>/air-data/latest

The 5min caching of the data from the local API webpage uses the http binding. Here is the binding .thing file

Thing http:url:AWAIR "AWAIR" [baseURL="http://<IP of Awair Element on your network>/air-data/latest", refresh=300, timeout=10000] { //refresh = seconds
    Channels:
        Type string : cache "AWAIR cache"
}

items file

String AwairCache               "Awair HTTP cache"                  <text>         {channel="http:url:AWAIR:cache"}
Number AwairScore               "Air quality score [%d %%]"         <vacation>
Number AwairCO2                 "CO2 [%d ppm]"                      <smoke>
Number AwairVOC                 "Chemicals [%d ppm]"                <smoke>
Number AwairPM25                "Fine dust [%d ug/m3]"              <smoke>
Number AwairTemperature         "Temperature [%.1f °C]"             <temperature>
Number AwairHumidity            "Humidity [%d %%]"                  <humidity>

rule file

var newval = new String

rule "Awair HTTP cache"
when
    Item AwairCache received update
then
        if (AwairCache.state.toString.contains("NULL") || AwairCache.state.toString.contains("null") || AwairCache.state.toString == "") { //Check for problem with Awair
            logError ("Awair", "Awair cache = " + AwairCache.state.toString)
            return; //Exit from rule
        }
    else {
        newval = transform("JS", "getAwairScore.js", AwairCache.state.toString)
        AwairScore.sendCommand(newval)
        //logInfo ("Awair", "Awair air quality score = " + newval + " %")

        newval = transform("JS", "getAwairCO2.js", AwairCache.state.toString)
        AwairCO2.sendCommand(newval)
        //logInfo ("Awair", "Awair CO2 = " + newval + " ppm")

        newval = transform("JS", "getAwairVOC.js", AwairCache.state.toString)
        AwairVOC.sendCommand(newval)
        //logInfo ("Awair", "Awair chemicals = " + newval + " ppm")

        newval = transform("JS", "getAwairPM25.js", AwairCache.state.toString)
        AwairPM25.sendCommand(newval)
        //logInfo ("Awair", "Awair PM2.5 fine dust = " + newval + " ug/m3")

        newval = transform("JS", "getAwairTemperature.js", AwairCache.state.toString)
        AwairTemperature.sendCommand(newval)
        //logInfo ("Awair", "Awair temperature = " + newval + " °C")

        newval = transform("JS", "getAwairHumidity.js", AwairCache.state.toString)
        AwairHumidity.sendCommand(newval)
        //logInfo ("Awair", "Awair humidity = " + newval + " %RH")
    }
end

Example regex for CO2 .js file

(function(i) {
    var re = new RegExp(',"co2":([0-9]+),');
    var out = i.match(re)[1];
    return parseFloat(out);
})(input)

Site map

                        Default item=AwairScore
                        Default item=AwairCO2
                        Default item=AwairVOC
                        Default item=AwairPM25
                        Default item=AwairHumidity
                        Default item=AwairTemperature

influx persistence file

Strategies {
    everyMinute 		: "0 * * * * ?"
	every5Minutes 		: "0 */5 * * * ?"
	every15Minutes 		: "0 */15 * * * ?" //openwebnet thermo items are only updated every 15mins so no point in logging faster
    everyHour   		: "0 0 * * * ?"
    everyDay    		: "0 0 0 * * ?"
	default 			= everyChange
}

	//Awair Element monitor
	AwairCO2 											: strategy = everyUpdate, everyChange, every5Minutes
	AwairVOC 											: strategy = everyUpdate, everyChange, every5Minutes
	AwairPM25 											: strategy = everyUpdate, everyChange, every5Minutes
	AwairScore 											: strategy = everyUpdate, everyChange, every5Minutes
	AwairTemperature 									: strategy = everyUpdate, everyChange, every5Minutes
	AwairHumidity 										: strategy = everyUpdate, everyChange, every5Minutes

Useful info about the Awair local API feature:

M

2 Likes

Fantastic write-up! Thank you. I have added a link in the Awair Element and Awair 2nd Edition documentation. Let me know if you need anything.

Also, one suggestion would be to mention the mDNS feature as opposed to looking up the device IP address. It might be a little easier to form the URL with the .local address. For example: http://awair-elem-123abc.local/

1 Like

Hi,

Thanks for the feedback. I am guessing that you are from Awair. Nice to see some support here :slight_smile:

Regarding DNS. I never had much luck with that being reliable for me. Maybe due to my dual NAT setup and complex enviroment. For hardware on my LAN I prefer to reserve the IP address either on the hardware or in the router setup. I linked local API documentation above. This explains more if anyone wants to try another way. Must admit I did’t really read it carefully or spend too much time on this integration as it was simple to do with a bit of openHAb experience, but there are possibly better ways.

If anyone gets here and is wondering how to use Grafana… there is a tutorial and lots of posts in the community on how to do that. Please search. The easiest way I know if you have your OH running on a raspberryPi is to use the config tool to install it along with the needed Influx data base.(>sudo openhabian-config…Menu 20-24).

1 Like

Yes, I work for Awair. :slightly_smiling_face:

Really cool. I’m more of a Homebridge-r, but you’ve convinced me to start up an OpenHAB Raspberry Pi instance.

1 Like

Thank you for the very useful write-up. I just purchased an Awair and I am working through the integration with OpenHAB.

One thing I found is that the Local API feature is not enabled by default. It needs to be enabled via the Awair app. Awair Local API Feature – Awair Support

1 Like

The frustrating thing is that it was working in OH2.5 (albeit without ever getting the number formatting right in the channels), and in OH3 the only problem seems to be compatibility with the ever-changing current snapshot.

I can do a pull request if I have time and see if someone else can sort out the remaining problems.

I haven’t looked at the local API feature.

About 2 days ago (4.4.2022, 3am CET) my unit stopped working. It seems there is a problem at Awair end as many users report the same issue starting at the same time. Support do not respond.

Symptoms:

  • No data on the display
  • Phone app shows unit as disconnected
  • Local API returns null for all values including the time stamp
  • Steady white light for some minutes then slow flashing and then bakc to steady and repeats endlessly

Reboot, reset, delete device in app does not help.

Update…

Awair are working the problem. There is a fix which will be pushed out when they are ready, but for now they are checking its reliable. My unit is now working again. So, if you have this problem I suggest you wait a while for them to push the update out.

M

Hi there.

Thanks for the guide, the setup today works almost seamlessly.

I do have 1 issue though, I was hoping you, or someone else can help me with.

In your rule, the initial if statement gives me an error when running the script, so my response must contain NULL? if I comment it out, I get the items updated. And I can see the response from the URL is fine, as all the data is being filled out correctly.

Cheers

Hi,

I can’t do much testing at the moment as my AWAIR is dead due to a bad firmware upgrade. Hopefully I will get a replacement unit in the near future.

edit. Actually since my AWAIR is dead the AWAIR cache is NULL … so its a good time test.

I found ‘null’ needs to be ‘NULL’. If ‘null’ is used for the if test then I get this error

Cannot convert 'NULL' to a command type which item 'AwairPM25' accepts: [DecimalType, QuantityType, RefreshType].

Updated rule is as follows

But I am not sure this really needed. I had it when I was debugging the rule but normally the cache is not NULL. I am not sure under what circumstances the cache is NULL or null. Maybe at startup?

        if (AwairCache.state.toString.contains("NULL") || AwairCache.state.toString.contains("null") || AwairCache.state.toString == "") { //Check for problem with Awair
            logError ("Awair", "Awair cache = " + AwairCache.state.toString)
            return; //Exit from rule
        }

Then with my current dead AWAIR I see in the log highlighted in red

[ERROR] [org.openhab.core.model.script.Awair ] - Awair cache = NULL

I am not sure why your cache contained a null!!! Could you copy the HTTP cache item value and paste it here. It is easy to do with VSC. You can simply hover over the item with the cursor and the value is displayed.

I also just noticed that if the AWAIR cannot be reached then I see this in the log

Requesting 'http://192.168.1.105/air-data/latest' (method='GET', content='null') failed: java.net.NoRouteToHostException: No route to host

You could set a test eg using log reader, and log/notify the problem if you really wanted to.

Thanks for looking up on this.

I tried taking your update, and it worked. Then I wanted to do some troubleshooting and went back to the original to see if I could find out the problem, but now this also worked :exploding_head:

After some time, I think I realised my problem - when the cache item is initialised, either from just creating the item, or when OH is restarted, and the cache item isn’t persisted, or given another value different than NULL at startup, then the value will be NULL, and the error is present. That is what I think was going on… :face_with_monocle:

I think for now, to solve the problem, I might just leave the error checking section commented out, and do the error checking by making sure the data items receive new commands regularly - if they are not updated, then something is wrong with the cache items.

Thanks for taking your time to help me! :metal:

1 Like

If that’s correct then null would only last until the next update of the cache.

Like I said that part of the rule is only for debugging or in case their is problem getting the data from the AWAIR Element. It is not essential for the rule to work normally.