Danielhiversen / pyAirthings

Python Airthings
MIT License
5 stars 1 forks source link

Use location endpoint #5

Closed LaStrada closed 8 months ago

LaStrada commented 11 months ago

To reduce the number of network requests, we can use the location endpoint.

Instead of fetching device list + iterating all devices (n+1), we can fetch locations+device list, then iterate the locations to fetch latest samples.

Nothing wrong with the current implementation, but this caused some users to experience 429 error (too many requests) if they had >10 devices. For users with 10 devices, this will now be:

In total 3 requests, and when locations and devices are cached, this will be 1 request. Most users has only one location, but in some cases they might have a cabin, maybe an autogenerated "hidden" location etc. In my case on my account, I have 3 locations. In my case I'll go from 7 (+ device list) to 3 (+ locations and device list).

Some user could have many locations without any devices, this can be solved by ignoring empty locations. To keep this simple for now this is not covered in this PR.

This PR also includes product_name. Then we can use this directly in Home Assistant:

airthings_device.product_name,

instead of this:

airthings_device.device_type.replace("_", " ").lower().title(),

https://github.com/home-assistant/core/blob/dev/homeassistant/components/airthings/sensor.py#L152

Also added the location_name which can be helpful for debugging if the user has multiple devices with more or less the same name, for example kitchen in your home + cabin. Not sure where to use it / present it to the user, but it's now available to be used.

Documentation: https://developer.airthings.com/consumer-api-docs#tag/Locations

Will fix these issues: https://github.com/home-assistant/core/issues/101666 https://github.com/Danielhiversen/pyAirthings/issues/4

Example ``` {'xxxxxxxxxx': AirthingsDevice(device_id='xxxxxxxxxx', name='Emilie', sensors={'battery': 54, 'co2': 569.0, 'humidity': 39.0, 'pressure': 998.5, 'radonShortTermAvg': 30.0, 'rssi': -42, 'temp': 21.9, 'time': 1697452315, 'voc': 46.0, 'relayDeviceType': 'hub'}, is_active=None, location_name='Home', device_type='Wave Plus', product_name='WAVE_PLUS'), 'xxxxxxxxxx': AirthingsDevice(device_id='xxxxxxxxxx', name='Basement Mini', sensors={'battery': 97, 'humidity': 79.0, 'mold': 3.0, 'temp': 14.5, 'time': 1697447439, 'voc': 75.0, 'relayDeviceType': 'hub'}, is_active=None, location_name='Home', device_type='Wave Mini', product_name='WAVE_MINI'), 'xxxxxxxxxx': AirthingsDevice(device_id='xxxxxxxxxx', name='Office', sensors={'battery': 83, 'co2': 572.0, 'humidity': 37.0, 'pressure': 998.4, 'radonShortTermAvg': 22.0, 'rssi': -51, 'temp': 22.7, 'time': 1697452384, 'voc': 46.0, 'relayDeviceType': 'hub'}, is_active=None, location_name='Home', device_type='Wave Plus', product_name='WAVE_PLUS'), 'xxxxxxxxxx': AirthingsDevice(device_id='xxxxxxxxxx', name='Basement Wave', sensors={'battery': 54, 'humidity': 81.0, 'radonShortTermAvg': 899.0, 'temp': 14.3, 'time': 1697433607, 'relayDeviceType': 'hub'}, is_active=None, location_name='Home', device_type='Wave', product_name='WAVE_GEN2'), 'xxxxxxxxxx': AirthingsDevice(device_id='xxxxxxxxxx', name='Bathroom', sensors={'battery': 31, 'humidity': 45.0, 'mold': 0.0, 'temp': 25.5, 'time': 1695641686, 'voc': 59.0, 'relayDeviceType': 'hub'}, is_active=None, location_name='Home', device_type='Wave Mini', product_name='WAVE_MINI'), 'xxxxxxxxxx': AirthingsDevice(device_id='xxxxxxxxxx', name='Living Room', sensors={'battery': 7, 'co2': 583.0, 'humidity': 34.0, 'pm1': 0.0, 'pm25': 0.0, 'pressure': 998.0, 'radonShortTermAvg': 24.0, 'temp': 23.1, 'time': 1697452327, 'voc': 46.0, 'relayDeviceType': 'hub'}, is_active=None, location_name='Home', device_type='View Plus', product_name='VIEW_PLUS'), 'xxxxxxxxxx': AirthingsDevice(device_id='xxxxxxxxxx', name='Kitchen', sensors={'battery': 73, 'co2': 561.0, 'humidity': 36.0, 'pm1': 0.0, 'pm25': 0.0, 'pressure': 998.3, 'radonShortTermAvg': 23.0, 'temp': 22.1, 'time': 1697451917, 'voc': 48.0, 'relayDeviceType': 'hub'}, is_active=None, location_name='Home', device_type='View Plus', product_name='VIEW_PLUS'), 'xxxxxxxxxx': AirthingsDevice(device_id='xxxxxxxxxx', name='Hub', sensors={}, is_active=None, location_name='-', device_type='Hub', product_name='HUB')} ```

Disclaimer: I work for Airthings. Discussed this problem with the cloud developers and this is the way to go. Less load on our cloud, and fewer 429 errors for the users.

LaStrada commented 10 months ago

If you need any test data, please contact me on Discord (same username) and I'll provide a user with test data.

stevefal commented 8 months ago

Related to https://github.com/Danielhiversen/pyAirthings/issues/4. As there, I propose a better and more equitable solution to the 429 problem is for Airthings to limit requests to N*num_devices rather than N per client, the latter of which essentially penalizes customers who have paid the most buying many Airthings products.