HomeKit integration of Netatmo CO2 sensors

Tried to connect the sensor through IP and port but get a HTTP error 470. What are other ways to retrieve the JSON data?

Using CURL it tells me that it requires authentication. This must be the pairing number?

Use the Homekit binding. Turn on trace logging. Disable/enable the thing in OH. The change the CO2 concentration. And repeat.

1 Like

Have overall 3 CO2 sensors. I just used another one than i observed yesterday for testing.

Can confirm that the alarm contact changes from CLOSED at low CO2 levels to OPEN at high CO2 levels (JSON & binding). But i am not able to confirm 1600ppm as threshold level like specified. Need to explore this in more depth, maybe using all my sensors.

After pushing the contact to OPEN by my breath, i am back again at 1200ppm but the contact is still OPEN.

Probably this feature is not available using the Netatmo binding or the Netatmo iOS app, because it is nothing accurate? :frowning:

Ok. So it sounds like the binding is actually functioning as it is supposed to.

Have analyzed the log-file now in more detail and have a finding:

Comming back again from high levels, here the contact “iid”:11 is still “value”:1 @ 1662ppm, which is okay.

2026-01-06 13:25:30.733 [TRACE] [mekit.internal.transport.IpTransport] - HTTP response:
HTTP/1.1 200 OK
Content-Type: application/hap+json
Content-Length: 176

{"characteristics":[{"aid":1,"iid":8,"value":3},{"aid":1,"iid":11,"value":1},{"aid":1,"iid":12,"value":1662.0},{"aid":1,"iid":15,"value":41.0},{"aid":1,"iid":18,"value":24.8}]}
2026-01-06 13:25:38.832 [TRACE] [mekit.internal.transport.IpTransport] - HTTP request:
GET /characteristics?id=1.18,1.15,1.12,1.8,1.11 HTTP/1.1
Host: Healthy\032Home\032Coach._hap._tcp.local.:5001

Comming further down, the contact “iid”:11 is back to “value”:0 @ 1546ppm, which is also okay.

2026-01-06 13:26:31.352 [TRACE] [mekit.internal.transport.IpTransport] - HTTP response:
HTTP/1.1 200 OK
Content-Type: application/hap+json
Content-Length: 176

{"characteristics":[{"aid":1,"iid":8,"value":3},{"aid":1,"iid":11,"value":0},{"aid":1,"iid":12,"value":1546.0},{"aid":1,"iid":15,"value":41.0},{"aid":1,"iid":18,"value":24.6}]}
2026-01-06 13:26:39.089 [TRACE] [mekit.internal.transport.IpTransport] - HTTP request:
GET /characteristics?id=1.18,1.15,1.12,1.8,1.11 HTTP/1.1
Host: Healthy\032Home\032Coach._hap._tcp.local.:5001

Conclusion:

JSON data proves that the sensor threshold value @ 1600ppm works out (what makes sense if its a digital threshold value) but the binding doesn’t update in this direction and is still on OPEN equivalent to “iid:11, “value”:1.

Edit: comming further down i detected that the the contact comes back to CLOSED @ <1100ppm. Strange?

Thanks for this, but perhaps your “analysis” was over enthusiastic: It looks like you wrongly picked out some wrongly matched request/response pairs from the log (the HTTP/1.1 200 OK responses are quoted before the GET /characteristics?id=1.18,1.15,1.12,1.8,1.11 HTTP/1.1 requests). => So can you please PM me the complete original log without any edits?

Will send the file to you via PM.

You are right, i have copied the sections wrong. But what counts is the data string which shows that 1662ppm is OPEN and that 1546ppm is CLOSED, means that the switching threshold is indeed 1600ppm.

The binding was still OPEN @ 1143ppm and switched to CLOSED at 1100ppm. In my view, this is not okay.

Do you probably know, what “Health Index” 5 is? Found in the log that a 5 can’t be transformed by my .map file. This contains:

0=Healthy, 1=Fine, 2=Fair, 3=Poor, 4=Unhealthy (equal to the Netatmo binding but no 5 yet)

Should match to the Netatmo binding because, as Jakob described, i have linked the same item to the related channels of both bindings.

Edit: “Health Index” ranges and threshold levels from the “Healthy Home Coach” app (Netatmo)

I guess that Chapter 9.9 of the Apple HomeKit Specification refers.

1 Like

Thanks for this detailed information. This speciification explains that there are differences between Netatmo app and Netatmo binding in comparison to HomeKit.

In the meantime i have verified the 1600ppm threshold level for the CO2 detection contact/switch in both directions. Looking to the JSON it works out like this.

Comming from low CO2 levels, the binding was still showing CLOSED (0) @ 1900ppm which should be OPEN (1). Within the spec you provided, i didn’t find a defined level, but 1600ppm matches to the Netatmo threshold from “Poor” (orange) to “Unhealthy” (red) or vice versa.

The binding seems to have something like a hysteresis w.r.t. the 1600ppm. Will check next if the switching value comming from low CO2 levels is probably 2100ppm. The value comming from high CO2 levels is 1100ppm as figured out yesterday.

Many thanks for sending the file. Unfortunately due to non specificity of the current binding log messages it is almost impossible to track what is going on. It looks like you have three identical Netatmo Things in your system (see below). Is that correct? And if so your log evidently contains interleaved polling GET requests to all three things in parallel. And also receiving interleaved EVENT notifications from all three. And furthermore there is a huge amount of communication exception chatter from different things.

So can you please simplify it for me by DISABLING all Netatmo Things EXCEPT just the one single Netatmo HomeKit Thing that you want me to actually test. Including disabling the Netatmo binding Thing as well. And then please run the test again with just this Thing and PM the new log to me as before.

Host: Healthy\032Home\032Coach._hap._tcp.local.:5001
Host: Healthy\032Home\032Coach\032(2)._hap._tcp.local.:5001
Host: Healthy\032Home\032Coach\032(3)._hap._tcp.local.:5001

PS your log did identify two small issues, but these are NOT the actual cause of your apparent problem:

  1. The log messages must uniquely identify the Ip address associated with GET and EVENT trace logs.
  2. There was a “freak” timing overlap between a GET at 2026-01-06 13:31:40.271 and an EVENT at 2026-01-06 13:31:40.544 .. the latter being unexpected response from a GET .. which made the handler disconnect and reconnect.

I will raise a Git Issue to mark these..

1 Like

Will take care to get this done. I have 3 sensors of this type. I tried to set the “HTTP Host Header” like this:

Hope that this is better for you?

That is is much much worse! As it says in your screen shot HTTP Host Header must be “The device fully qualified host name as discovered by mDNS (needed for HTTP Host headers”. And if you set anything different than that, then it is very likely that your accessories will either fail to respond at all, or fail with errors. => Normally the HTTP Host Header is set automatically via the Inbox discovery, so since you have now manually messed them up, you will probably have to run an mDNS discovery app in to find the correct texts again.

1 Like

Just tested 2300ppm but the binding contact/switch doesn’t change while the JSON switched @ 1600ppm.

Conclusion: the contact CLOSED threshold is 1100ppm (tested earlier) and the contact OPEN threshold is above 2300ppm. Its hard to increase CO2 in small steps to evaluate this level more accurate.

I have made a mistake testing the alarm contact provided by the HomeKit binding. The contact item (channel default) should not be mixed up with a switch item. Now i can confirm proper functionality of the alarm contact, even in small steps arround 1600ppm. The threshold value is 1600ppm.

Kudos to @AndrewFG who supported me and finally figured out the solution.

1 Like

For other OH users who might work with Netatmo binding and HomeKit binding in parallel, there is one information w.r.t. the “Health Index”. Health index scale and CO2 ranges in ppm are different between Netatmo and Apple HomeKit. Thanks to @AndrewFG for providing me the Apple specification.

Netatmo binding and iOS app: 0=Healthy, 1=Fine, 2=Fair, 3=Poor, 4=Unhealthy (5 steps)

HomeKit binding: 0=Unknown, 1=Excellent, 2=Good, 3=Fair, 4=Inferior, 5=Poor (6 steps)

1 Like

Since openHAB doesn’t have health index as a concept, bindings cannot be aligned. However, maybe 0 here should rather be mapped to UNDEF?

Absolutely no! I am vehemently against the idea of customised “mapping” of any values within the Homekit binding itself. We should 100% stick to the Apple specification. Only. The Characteristic delivers a numeric value, so the binding must deliver a numeric value (what you see is what you get). Otherwise we become open to double guessing the intentions of different manufucturers and devices. And it opens the door to a potentially endless flood of manufacturer / device specific customisations for all possible types of characteristics values. It would be total maintenance nightmare, with potential for breaking changes at every step.

If you want to do “mapping” it nevertheless can, resp. must, be done at invididual system level by the user via map transforms.

Understand. Have currently both “Health Indexes” in my sitemap because linking the same item to two different channels makes no sense here. That’s not a problem. The item with the utmost importance is CO2 and for this item it works out.

In the meantime i have also recognized, that the 60secs poll time using the HomeKit binding doesn’t really retrieve more new CO2 values than the Netatmo binding. There seems to be a sensor internal update cycle time which limits this?