Skip to content

Add support for Ambient Weather variant#174

Open
PhracturedBlue wants to merge 1 commit intohome-assistant-libs:mainfrom
PhracturedBlue:ambientweather
Open

Add support for Ambient Weather variant#174
PhracturedBlue wants to merge 1 commit intohome-assistant-libs:mainfrom
PhracturedBlue:ambientweather

Conversation

@PhracturedBlue
Copy link
Copy Markdown
Contributor

AmbientWeather and Ecowitt use nearly identical protocols. Ecowitt uses a POST form, whereas AmbientWeather sends all data as params with a GET request.

The only other differences I found with my WS2902 was it sends 'batt_co2' instead of 'co2_batt' (even though it doesn't have any co2 sensor)

This plus a small home-asistant patch enabling 'GET' in the webhook were the only changes I needed to get my weather station working with HASS.

@GSzabados
Copy link
Copy Markdown
Contributor

@joostlek, this one should be verified with some documentation on the "AmbientWeather" protocol. It would be a nice to have, and would likely make happy a lot of Ambient Weather station owners, as I remember it doesn't have a local option in HA, but from the Ambient Weather site. (And some quirky solutions, as I read in this thread https://community.home-assistant.io/t/ambient-weather-available-locally-now-integration-anyone/299180

@PhracturedBlue, could you provide some documentation on this? Logs, settings to activate the protocol. And any info if there is any PR to core to implement in HA. (That would need a documentation update as well.) I am not fully convinced that this is just a minor change.

@PhracturedBlue
Copy link
Copy Markdown
Contributor Author

The only changes in homeassistant are:

--- /usr/src/homeassistant/homeassistant/components/ecowitt/__init__.py
+++ ./__init__.py
@@ -30,7 +30,8 @@
         return await ecowitt.handler(request)
 
     webhook.async_register(
-        hass, DOMAIN, entry.title, entry.data[CONF_WEBHOOK_ID], handle_webhook
+        hass, DOMAIN, entry.title, entry.data[CONF_WEBHOOK_ID], handle_webhook,
+        allowed_methods=[METH_GET, METH_POST, METH_PUT]
     )
 
     @callback

Other than that is just works. In the ambient weather app, I just paste the webhook url as:

server IP:
hass ip address
Path:
/api/webhook/<hass webhook-id>?

And everything works

I can't really file a PR for HASS until this change is accepted, since I need to update the proper version of aioecowitt.

The aioecowitt module doesn't seem to have any documentation, but I can file a PR for Hass documentation as well. But again, it makes little sense to to do util I've filed the HASS PR, which is gated by getting the changes into this module.

@GSzabados
Copy link
Copy Markdown
Contributor

@PhracturedBlue, I will test your PR with an Ecowitt station for backward compatibility and will make a custom component of ecowitt with the rest of your suggested changes to test it with other Ambient Weather owners as well. It looks good to me, but need some confirmation, that it would work with both.

Otherwise, can you implement the batt_co2 into the calc.py?

Also, can you verify, that everything is correct according this API documentation?

https://github.com/ambient-weather/api-docs/wiki/Device-Data-Specs#data-timing

@GSzabados
Copy link
Copy Markdown
Contributor

  •    allowed_methods=[METH_GET, METH_POST, METH_PUT]
    

It requires an additional

from aiohttp.hdrs import METH_POST, METH_GET, METH_PUT

Otherwise it fails with an error message.

@PhracturedBlue, would you mind to try this as a full proof of concept for Ambient Weather?

https://github.com/GSzabados/ecowitt

@PhracturedBlue
Copy link
Copy Markdown
Contributor Author

I had to upgrade to the latest home-assistant, but it seems to be working fine.

@GSzabados
Copy link
Copy Markdown
Contributor

Do you have low battery indicator binary entities? Are those correct?

@PhracturedBlue
Copy link
Copy Markdown
Contributor Author

My weather station doesn't send the battery health, and my base-station is hardwired, so I don't have any batteries to monitor. I do have a 'CO2 battery' sensor showing up in HASS, but the WS does not have any such sensor.
In HASS, I see:
image
The WH45 is what used to be sent before your update (I had it disabled so I don't recall what was sent)
The 'CO2 battery' is what is sent now (but the value doesn't seem to correspond to any interesting data)

@GSzabados
Copy link
Copy Markdown
Contributor

The WH45 is what used to be sent before your update (I had it disabled so I don't recall what was sent)
The 'CO2 battery' is what is sent now (but the value doesn't seem to correspond to any interesting data)

How Ambient Weather calls your device? I assume you have an external device with PM, CO2 and whatever else. It is a WH45 in Ecowitt, but what is it for Ambient?
I just called it CO2 because that was the description in the linked API documentation. Or you not even have an external WH45 sensor?
https://ambientweather.com/indoor-wireless-air-quality-monitor-aqin

I think your WH45 Battery was showing 1 or 100% before. But really it is a low battery indicator with the value 1 as full and 0 as low. But the Ecowitt custom component interprets it wrongly. I have not cross checked how it was working with Ecowitt devices. But I know this already, a guy reported the same on the community forum. So thanks for confirming.

@PhracturedBlue
Copy link
Copy Markdown
Contributor Author

right, I don't have an external device (or maybe more recent base-stations have it built in).

@GSzabados
Copy link
Copy Markdown
Contributor

I don't have an external device

I think, either the base station reports it by default even without a sensor (obviously wrongly), or it is reporting battin but with a wrong key as batt_co2. Both of them suggests that the device is doing something wrongly, but it is out of the scope of this integration to correct the bugs of the Ambient Weather software or protocol.

@PhracturedBlue
Copy link
Copy Markdown
Contributor Author

I agree. From my perspective your changes work well (and better than any of the alteratives I've tried). If you file a PR for the ecowitt chages in HA, I can file a PR for updating the documentation with how to setup an AmbientWeather device with it.

@GSzabados
Copy link
Copy Markdown
Contributor

It is not that easy. Because the Ambient Weather Local should be a virtual integration, which I am not sure how to set up. As I have never looked at that option for an integration before.

@PhracturedBlue
Copy link
Copy Markdown
Contributor Author

I don't follow. The currently recommended 'Ambient Weather' integration is to use the cloud. Why is any more needed than to just document that the ecowitt integration is also capable of doing AmbientWeather local (rather than trying to make it THE official way of doing so)?

@GSzabados
Copy link
Copy Markdown
Contributor

@PhracturedBlue, could you please confirm how many batteries you have? And paste here the raw section of the Diagnostics log of your Ambient Weather unit?

I assume you have batt_co2 (CO2 battery) and battout (Outdoor Battery) and both shows low, right?

@PhracturedBlue
Copy link
Copy Markdown
Contributor Author

These are the 3 sensors which report no/invalid data:
image
There are no batteries present in the device, and I do not have a co2 sensor.

Here is the json:

{
  "home_assistant": {
    "installation_type": "Home Assistant Container",
    "version": "2025.3.0",
    "dev": false,
    "hassio": false,
    "virtualenv": false,
    "python_version": "3.13.2",
    "docker": true,
    "arch": "aarch64",
    "timezone": "America/Los_Angeles",
    "os_name": "Linux",
    "os_version": "6.12.13-current-rockchip64",
    "run_as_root": true
  },
  "custom_components": {
    "ecowitt": {
      "documentation": "https://www.home-assistant.io/integrations/ecowitt",
      "version": "2025.3.3",
      "requirements": [
        "git+https://github.com/GSzabados/aioecowitt.git@main#aioecowitt==2025.3.2"
      ]
    }
  },
  "integration_manifest": {
    "version": "2025.3.3",
    "domain": "ecowitt",
    "name": "Ecowitt",
    "codeowners": [
      "GSzabados"
    ],
    "config_flow": true,
    "dependencies": [
      "webhook"
    ],
    "documentation": "https://www.home-assistant.io/integrations/ecowitt",
    "iot_class": "local_push",
    "requirements": [
      "git+https://github.com/GSzabados/aioecowitt.git@main#aioecowitt==2025.3.2"
    ],
    "is_built_in": false,
    "overwrites_built_in": true
  },
  "setup_times": {
    "null": {
      "setup": 3.937399014830589e-05
    },
    "01JMP3H6DB5NWW61FV5XEPV8T5": {
      "wait_import_platforms": -0.0018657599575817585,
      "config_entry_setup": 0.0024146679788827896
    }
  },
  "data": {
    "device": {
      "name": "AMBWeatherV4.3.5",
      "model": null,
      "frequency": null,
      "version": null
    },
    "raw": {
      "__type": "<class 'multidict._multidict.MultiDict'>",
      "repr": "<MultiDict('stationtype': 'AMBWeatherV4.3.5', 'dateutc': '2025-04-09 13:35:03', 'tempinf': '61.3', 'humidityin': '58', 'baromrelin': '30.496', 'baromabsin': '29.909', 'tempf': '42.1', 'humidity': '99', 'winddir': '24', 'windspeedmph': '0.0', 'windgustmph': '0.0', 'maxdailygust': '2.2', 'hourlyrainin': '0.000', 'eventrainin': '1.429', 'dailyrainin': '0.000', 'weeklyrainin': '1.449', 'monthlyrainin': '1.598', 'totalrainin': '12.130', 'solarradiation': '0.00', 'uv': '0', 'batt_co2': '1')>"
    },
    "sensors": {
      "dateutc": "2025-04-09 13:35:03",
      "tempinf": 61.3,
      "humidityin": 58,
      "baromrelin": 30.496,
      "baromabsin": 29.909,
      "tempf": 42.1,
      "humidity": 99,
      "winddir": 24,
      "windspeedmph": 0.0,
      "windgustmph": 0.0,
      "maxdailygust": 2.2,
      "hourlyrainin": 0.0,
      "eventrainin": 1.429,
      "dailyrainin": 0.0,
      "weeklyrainin": 1.449,
      "monthlyrainin": 1.598,
      "totalrainin": 12.13,
      "solarradiation": 0.0,
      "uv": 0,
      "batt_co2": 1,
      "solarradiation_lux": 0.0,
      "tempc": 5.6,
      "tempinc": 16.3,
      "windspeedkmh": 0.0,
      "windgustkmh": 0.0,
      "maxdailygustkmh": 3.5,
      "eventrainmm": 36.3,
      "hourlyrainmm": 0.0,
      "dailyrainmm": 0.0,
      "weeklyrainmm": 36.8,
      "monthlyrainmm": 40.6,
      "totalrainmm": 308.1,
      "baromrelhpa": 1032.6,
      "baromabshpa": 1012.7,
      "windchillf": null,
      "windchillc": null,
      "dewpointc": 5.5,
      "dewpointf": 41.8,
      "dewpointinc": 8.0,
      "dewpointinf": 46.4,
      "tempfeelsf": 42.1,
      "tempfeelsc": 5.6
    }
  }
}

@GSzabados
Copy link
Copy Markdown
Contributor

The Windchill is as expected. It is provided as the wind blows, if it is less than something, it is not even provided, so in HA goes unavailable.

I've opened an issue with Ambient Weather to get a clarification on the batt_co2. It is either a bug, or the it is not co2, but meant to be console or the outside unit.
ambient-weather/api-docs#56

@GSzabados
Copy link
Copy Markdown
Contributor

@PhracturedBlue, could you check this?

ambient-weather/api-docs#56 (comment)

@PhracturedBlue
Copy link
Copy Markdown
Contributor Author

I upgraded to v4.3.6 which is the latest available firmware for my rather old device (original WS-2902 from 2017). I still have batt_co2:

{
  "home_assistant": {
    "installation_type": "Home Assistant Container",
    "version": "2025.3.0",
    "dev": false,
    "hassio": false,
    "virtualenv": false,
    "python_version": "3.13.2",
    "docker": true,
    "arch": "aarch64",
    "timezone": "America/Los_Angeles",
    "os_name": "Linux",
    "os_version": "6.12.13-current-rockchip64",
    "run_as_root": true
  },
  "custom_components": {
    "ecowitt": {
      "documentation": "https://www.home-assistant.io/integrations/ecowitt",
      "version": "2025.3.3",
      "requirements": [
        "git+https://github.com/GSzabados/aioecowitt.git@main#aioecowitt==2025.3.2"
      ]
    }
  },
  "integration_manifest": {
    "version": "2025.3.3",
    "domain": "ecowitt",
    "name": "Ecowitt",
    "codeowners": [
      "GSzabados"
    ],
    "config_flow": true,
    "dependencies": [
      "webhook"
    ],
    "documentation": "https://www.home-assistant.io/integrations/ecowitt",
    "iot_class": "local_push",
    "requirements": [
      "git+https://github.com/GSzabados/aioecowitt.git@main#aioecowitt==2025.3.2"
    ],
    "is_built_in": false,
    "overwrites_built_in": true
  },
  "setup_times": {
    "null": {
      "setup": 3.937399014830589e-05
    },
    "01JMP3H6DB5NWW61FV5XEPV8T5": {
      "wait_import_platforms": -0.0018657599575817585,
      "config_entry_setup": 0.0024146679788827896
    }
  },
  "data": {
    "device": {
      "name": "AMBWeatherV4.3.6",
      "model": null,
      "frequency": null,
      "version": null
    },
    "raw": {
      "__type": "<class 'multidict._multidict.MultiDict'>",
      "repr": "<MultiDict('stationtype': 'AMBWeatherV4.3.6', 'dateutc': '2025-04-12 02:40:02', 'tempinf': '71.8', 'humidityin': '50', 'baromrelin': '30.464', 'baromabsin': '29.876', 'tempf': '50.5', 'humidity': '68', 'winddir': '26', 'windspeedmph': '0.0', 'windgustmph': '0.0', 'maxdailygust': '10.3', 'hourlyrainin': '0.000', 'eventrainin': '0.000', 'dailyrainin': '0.000', 'weeklyrainin': '1.571', 'monthlyrainin': '1.720', 'totalrainin': '12.252', 'solarradiation': '2.08', 'uv': '0', 'batt_co2': '1')>"
    },
    "sensors": {
      "dateutc": "2025-04-12 02:40:02",
      "tempinf": 71.8,
      "humidityin": 50,
      "baromrelin": 30.464,
      "baromabsin": 29.876,
      "tempf": 50.5,
      "humidity": 68,
      "winddir": 26,
      "windspeedmph": 0.0,
      "windgustmph": 0.0,
      "maxdailygust": 10.3,
      "hourlyrainin": 0.0,
      "eventrainin": 0.0,
      "dailyrainin": 0.0,
      "weeklyrainin": 1.571,
      "monthlyrainin": 1.72,
      "totalrainin": 12.252,
      "solarradiation": 2.08,
      "uv": 0,
      "batt_co2": 1,
      "solarradiation_lux": 263.3,
      "tempc": 10.3,
      "tempinc": 22.1,
      "windspeedkmh": 0.0,
      "windgustkmh": 0.0,
      "maxdailygustkmh": 16.6,
      "eventrainmm": 0.0,
      "hourlyrainmm": 0.0,
      "dailyrainmm": 0.0,
      "weeklyrainmm": 39.9,
      "monthlyrainmm": 43.7,
      "totalrainmm": 311.2,
      "baromrelhpa": 1031.5,
      "baromabshpa": 1011.6,
      "windchillf": null,
      "windchillc": null,
      "dewpointc": 4.7,
      "dewpointf": 40.4,
      "dewpointinc": 11.2,
      "dewpointinf": 52.2,
      "tempfeelsf": 50.5,
      "tempfeelsc": 10.3
    }
  }
}

Comment thread aioecowitt/server.py
Comment on lines +98 to +103
else:
if request.method != "POST":
return web.Response(status=405)
if self.path is not None and request.path != self.path:
return web.Response(status=404)
data = await request.post()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
else:
if request.method != "POST":
return web.Response(status=405)
if self.path is not None and request.path != self.path:
return web.Response(status=404)
data = await request.post()
elif request.method != "POST":
return web.Response(status=405)
elif self.path is not None and request.path != self.path:
return web.Response(status=404)
else:
# regular POST request
data = await request.post()

Comment thread aioecowitt/server.py
return web.Response(status=404)
data = await request.post()
if request.method == "GET":
# Ambient Weather variant
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explain in the comment that this device is not doing a post request but a get

Comment thread aioecowitt/station.py
station = data.pop("stationtype")
passkey = data.pop("PASSKEY")
model = data.pop("model")
model = data.pop("model", None)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe set something like "UNKNOWN" as default value for the model ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants