blueair-api 1.24.0__tar.gz → 1.25.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {blueair_api-1.24.0 → blueair_api-1.25.0}/PKG-INFO +1 -1
  2. {blueair_api-1.24.0 → blueair_api-1.25.0}/pyproject.toml +1 -1
  3. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/device.py +11 -10
  4. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/device_aws.py +2 -0
  5. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api.egg-info/PKG-INFO +1 -1
  6. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api.egg-info/SOURCES.txt +2 -1
  7. blueair_api-1.25.0/tests/test_device_aws.py +295 -0
  8. {blueair_api-1.24.0 → blueair_api-1.25.0}/LICENSE +0 -0
  9. {blueair_api-1.24.0 → blueair_api-1.25.0}/README.md +0 -0
  10. {blueair_api-1.24.0 → blueair_api-1.25.0}/setup.cfg +0 -0
  11. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/__init__.py +0 -0
  12. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/callbacks.py +0 -0
  13. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/const.py +0 -0
  14. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/errors.py +0 -0
  15. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/http_aws_blueair.py +0 -0
  16. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/http_blueair.py +0 -0
  17. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/model_enum.py +0 -0
  18. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/stub.py +0 -0
  19. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/util.py +0 -0
  20. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/util_bootstrap.py +0 -0
  21. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api/util_http.py +0 -0
  22. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api.egg-info/dependency_links.txt +0 -0
  23. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api.egg-info/requires.txt +0 -0
  24. {blueair_api-1.24.0 → blueair_api-1.25.0}/src/blueair_api.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: blueair_api
3
- Version: 1.24.0
3
+ Version: 1.25.0
4
4
  Summary: Blueair Api Wrapper
5
5
  Author-email: Brendan Dahl <dahl.brendan@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dahlb/blueair_api
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
8
8
 
9
9
  [project]
10
10
  name = "blueair_api"
11
- version = "1.24.0"
11
+ version = "1.25.0"
12
12
  authors = [
13
13
  { name="Brendan Dahl", email="dahl.brendan@gmail.com" },
14
14
  ]
@@ -81,16 +81,17 @@ class Device(CallbacksMixin):
81
81
  self.wifi_working = attributes["wifi_status"] == "1"
82
82
  else:
83
83
  self.wifi_working = False
84
- for data_point in transform_data_points(await self.api.get_data_points_since(self.uuid)):
85
- _LOGGER.debug(data_point)
86
- self.pm25 = safely_get_json_value(data_point, "pm25", int)
87
- self.pm10 = safely_get_json_value(data_point, "pm10", int)
88
- self.pm1 = safely_get_json_value(data_point, "pm1", int)
89
- self.voc = safely_get_json_value(data_point, "voc", int)
90
- self.co2 = safely_get_json_value(data_point, "co2", int)
91
- self.temperature = safely_get_json_value(data_point, "temperature", int)
92
- self.humidity = safely_get_json_value(data_point, "humidity", int)
93
- self.all_pollution = safely_get_json_value(data_point, "all_pollution", int)
84
+ if self.compatibility != "sense+":
85
+ for data_point in transform_data_points(await self.api.get_data_points_since(self.uuid)):
86
+ _LOGGER.debug(data_point)
87
+ self.pm25 = safely_get_json_value(data_point, "pm25", int)
88
+ self.pm10 = safely_get_json_value(data_point, "pm10", int)
89
+ self.pm1 = safely_get_json_value(data_point, "pm1", int)
90
+ self.voc = safely_get_json_value(data_point, "voc", int)
91
+ self.co2 = safely_get_json_value(data_point, "co2", int)
92
+ self.temperature = safely_get_json_value(data_point, "temperature", int)
93
+ self.humidity = safely_get_json_value(data_point, "humidity", int)
94
+ self.all_pollution = safely_get_json_value(data_point, "all_pollution", int)
94
95
  _LOGGER.debug(f"refreshed blueair device: {self}")
95
96
  self.publish_updates()
96
97
 
@@ -1,5 +1,6 @@
1
1
  import dataclasses
2
2
  import logging
3
+ from json import dumps
3
4
 
4
5
  from .callbacks import CallbacksMixin
5
6
  from .http_aws_blueair import HttpAwsBlueair
@@ -64,6 +65,7 @@ class DeviceAws(CallbacksMixin):
64
65
  async def refresh(self):
65
66
  _LOGGER.debug(f"refreshing blueair device aws: {self}")
66
67
  info = await self.api.device_info(self.name_api, self.uuid)
68
+ _LOGGER.debug(dumps(info, indent=2))
67
69
  sensor_data = convert_api_array_to_dict(info["sensordata"])
68
70
  self.pm1 = safely_get_json_value(sensor_data, "pm1", int)
69
71
  self.pm2_5 = safely_get_json_value(sensor_data, "pm2_5", int)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: blueair_api
3
- Version: 1.24.0
3
+ Version: 1.25.0
4
4
  Summary: Blueair Api Wrapper
5
5
  Author-email: Brendan Dahl <dahl.brendan@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dahlb/blueair_api
@@ -18,4 +18,5 @@ src/blueair_api.egg-info/PKG-INFO
18
18
  src/blueair_api.egg-info/SOURCES.txt
19
19
  src/blueair_api.egg-info/dependency_links.txt
20
20
  src/blueair_api.egg-info/requires.txt
21
- src/blueair_api.egg-info/top_level.txt
21
+ src/blueair_api.egg-info/top_level.txt
22
+ tests/test_device_aws.py
@@ -0,0 +1,295 @@
1
+ """Tests for DeviceAws.
2
+
3
+ Here is one way to run it:
4
+
5
+ First install the package in developer mode
6
+
7
+ $ pip install -e .
8
+
9
+ Then use pytest to drive the tests
10
+
11
+ $ pytest tests
12
+ """
13
+
14
+ from typing import Any
15
+
16
+ from importlib import resources
17
+ from unittest import mock
18
+ from unittest import IsolatedAsyncioTestCase
19
+
20
+ import pytest
21
+
22
+ from blueair_api.device_aws import DeviceAws
23
+ from blueair_api.model_enum import ModelEnum
24
+ from blueair_api import http_aws_blueair
25
+
26
+ class FakeDeviceInfoHelper:
27
+ """Fake for the 'device info' interface of HttpAwsBlueAir class."""
28
+ def __init__(self, info: dict[str, Any]):
29
+ self.info = info
30
+
31
+ async def device_info(self, *args, **kwargs):
32
+ return self.info
33
+
34
+ async def set_device_info(self, device_uuid, service_name, action_verb, action_value):
35
+ # this function seems to be only updating the states consider rename the method.
36
+ # action_verb seems to be a type annotation:
37
+ # c.f. senml: https://www.rfc-editor.org/rfc/rfc8428.html#section-5
38
+ # the senml parsing library (utils.py) could use some additional love.
39
+ # to make it more conformal to the RFC standard.
40
+ for state in self.info['states']:
41
+ if state['n'] == service_name:
42
+ break
43
+ else:
44
+ state = {'n': service_name}
45
+ self.info['states'].append(state)
46
+ # Watch out: mutate after append produces desired mutation to info.
47
+ state[action_verb] = action_value
48
+
49
+ class DeviceAwsTestBase(IsolatedAsyncioTestCase):
50
+
51
+ def setUp(self):
52
+
53
+ patcher = mock.patch('blueair_api.http_aws_blueair.HttpAwsBlueair', autospec=True)
54
+ self.api_class = patcher.start()
55
+ self.addCleanup(patcher.stop)
56
+ self.api = self.api_class(username="fake-username", password="fake-password")
57
+
58
+ self.device = DeviceAws(self.api, name_api="fake-name-api", uuid="fake-uuid", name="fake-name",
59
+ mac="fake-mac", type_name='fake-type-name')
60
+
61
+ self.device_info_helper = FakeDeviceInfoHelper({"sensordata": {}, "states": []})
62
+
63
+ self.api.device_info.side_effect = self.device_info_helper.device_info
64
+ self.api.set_device_info.side_effect = self.device_info_helper.set_device_info
65
+
66
+
67
+ class DeviceAwsSetterTest(DeviceAwsTestBase):
68
+ """Tests for all of the setters."""
69
+
70
+ async def test_brightness(self):
71
+ # test cache works
72
+ self.device.brightness = None
73
+ await self.device.set_brightness(1)
74
+ assert self.device.brightness == 1
75
+
76
+ # test refresh works
77
+ await self.device.set_brightness(2)
78
+ self.device.brightness = None
79
+ await self.device.refresh()
80
+ assert self.device.brightness == 2
81
+
82
+ async def test_fan_speed(self):
83
+ # test cache works
84
+ self.device.fan_speed = None
85
+ await self.device.set_fan_speed(1)
86
+ assert self.device.fan_speed == 1
87
+
88
+ # test refresh works
89
+ await self.device.set_fan_speed(2)
90
+ self.device.fan_speed = None
91
+ await self.device.refresh()
92
+ assert self.device.fan_speed == 2
93
+
94
+ async def test_running(self):
95
+ # test cache works
96
+ self.device.running = None
97
+ await self.device.set_running(False)
98
+ assert self.device.running is False
99
+
100
+ # test refresh works
101
+ await self.device.set_running(True)
102
+ self.device.running = None
103
+ await self.device.refresh()
104
+ assert self.device.running is True
105
+
106
+ async def test_fan_auto_mode(self):
107
+ # test cache works
108
+ self.device.fan_auto_mode = None
109
+ await self.device.set_fan_auto_mode(False)
110
+ assert self.device.fan_auto_mode is False
111
+
112
+ # test refresh works
113
+ await self.device.set_fan_auto_mode(True)
114
+ self.device.fan_auto_mode = None
115
+ await self.device.refresh()
116
+ assert self.device.fan_auto_mode is True
117
+
118
+ async def test_auto_regulated_humidity(self):
119
+ # test cache works
120
+ self.device.auto_regulated_humidity = None
121
+ await self.device.set_auto_regulated_humidity(1)
122
+ assert self.device.auto_regulated_humidity == 1
123
+
124
+ # test refresh works
125
+ await self.device.set_auto_regulated_humidity(2)
126
+ self.device.auto_regulated_humidity = None
127
+ await self.device.refresh()
128
+ assert self.device.auto_regulated_humidity == 2
129
+
130
+ async def test_child_lock(self):
131
+ # test cache works
132
+ self.device.child_lock = None
133
+ await self.device.set_child_lock(False)
134
+ assert self.device.child_lock is False
135
+
136
+ # test refresh works
137
+ await self.device.set_child_lock(True)
138
+ self.device.child_lock = None
139
+ await self.device.refresh()
140
+ assert self.device.child_lock is True
141
+
142
+ async def test_night_mode(self):
143
+ # test cache works
144
+ self.device.night_mode = None
145
+ await self.device.set_night_mode(False)
146
+ assert self.device.night_mode is False
147
+
148
+ # test refresh works
149
+ await self.device.set_night_mode(True)
150
+ self.device.night_mode = None
151
+ await self.device.refresh()
152
+ assert self.device.night_mode is True
153
+
154
+ async def test_wick_dry_mode(self):
155
+ # test cache works
156
+ self.device.wick_dry_mode = None
157
+ await self.device.set_wick_dry_mode(False)
158
+ assert self.device.wick_dry_mode is False
159
+
160
+ # test refresh works
161
+ await self.device.set_wick_dry_mode(True)
162
+ self.device.wick_dry_mode = None
163
+ await self.device.refresh()
164
+ assert self.device.wick_dry_mode is True
165
+
166
+
167
+ class UnavailableDeviceAwsTest(DeviceAwsTestBase):
168
+ """Tests for a fake device.
169
+
170
+ For this fake device, all attrs are unavailable.
171
+
172
+ Other device types shall override setUp and populate self.info with the
173
+ golden dataset.
174
+ """
175
+
176
+ async def test_attributes(self):
177
+
178
+ await self.device.refresh()
179
+ self.api.device_info.assert_awaited_with("fake-name-api", "fake-uuid")
180
+
181
+ device = self.device
182
+
183
+ assert device.model == ModelEnum.UNKNOWN
184
+
185
+ assert device.pm1 is None
186
+ assert device.pm2_5 is None
187
+ assert device.pm10 is None
188
+ assert device.tVOC is None
189
+ assert device.temperature is None
190
+ assert device.humidity is None
191
+ assert device.name is None
192
+ assert device.firmware is None
193
+ assert device.mcu_firmware is None
194
+ assert device.serial_number is None
195
+ assert device.sku is None
196
+
197
+ assert device.running is False
198
+ assert device.night_mode is None
199
+ assert device.germ_shield is None
200
+ assert device.brightness is None
201
+ assert device.child_lock is None
202
+ assert device.fan_speed is None
203
+ assert device.fan_auto_mode is None
204
+ assert device.filter_usage is None
205
+ assert device.wifi_working is None
206
+ assert device.wick_usage is None
207
+ assert device.auto_regulated_humidity is None
208
+ assert device.water_shortage is None
209
+
210
+
211
+ class H35iTest(DeviceAwsTestBase):
212
+ """Tests for H35i."""
213
+
214
+ def setUp(self):
215
+ super().setUp()
216
+ info = eval(resources.files().joinpath('device_info/H35i.txt').read_text())
217
+ self.device_info_helper.info.update(info)
218
+
219
+ async def test_attributes(self):
220
+
221
+ await self.device.refresh()
222
+ self.api.device_info.assert_awaited_with("fake-name-api", "fake-uuid")
223
+
224
+ device = self.device
225
+
226
+ assert device.model == ModelEnum.HUMIDIFIER_H35I
227
+
228
+ assert device.pm1 is None
229
+ assert device.pm2_5 is None
230
+ assert device.pm10 is None
231
+ assert device.tVOC is None
232
+ assert device.temperature == 19
233
+ assert device.humidity == 50
234
+ assert device.name == "Bedroom"
235
+ assert device.firmware == "1.0.1"
236
+ assert device.mcu_firmware == "1.0.1"
237
+ assert device.serial_number == "111163300201110210004036"
238
+ assert device.sku == "111633"
239
+
240
+ assert device.running is True
241
+ assert device.night_mode is False
242
+ assert device.germ_shield is None
243
+ assert device.brightness == 49
244
+ assert device.child_lock is False
245
+ assert device.fan_speed == 24
246
+ assert device.fan_auto_mode is False
247
+ assert device.filter_usage is None
248
+ assert device.wifi_working is True
249
+ assert device.wick_usage == 13
250
+ assert device.auto_regulated_humidity == 50
251
+ assert device.water_shortage is False
252
+
253
+
254
+ class T10iTest(DeviceAwsTestBase):
255
+ """Tests for T10i."""
256
+
257
+ def setUp(self):
258
+ super().setUp()
259
+ info = eval(resources.files().joinpath('device_info/T10i.txt').read_text())
260
+ self.device_info_helper.info.update(info)
261
+
262
+ async def test_attributes(self):
263
+
264
+ await self.device.refresh()
265
+ self.api.device_info.assert_awaited_with("fake-name-api", "fake-uuid")
266
+
267
+ device = self.device
268
+
269
+ assert device.model == ModelEnum.T10I
270
+
271
+ assert device.pm1 is None
272
+ assert device.pm2_5 == 0
273
+ assert device.pm10 is None
274
+ assert device.tVOC is None
275
+ assert device.temperature == 18
276
+ assert device.humidity == 28
277
+ assert device.name == "Allen's Office"
278
+ assert device.firmware == "1.0.4"
279
+ assert device.mcu_firmware == "1.0.4"
280
+ assert device.serial_number == "111212400002313210001961"
281
+ assert device.sku == "112124"
282
+
283
+ assert device.running is True
284
+ assert device.night_mode is None
285
+ assert device.germ_shield is None
286
+ assert device.brightness == 100
287
+ assert device.child_lock is False
288
+ assert device.fan_speed is None
289
+ assert device.fan_auto_mode is None
290
+ assert device.filter_usage == 0
291
+ assert device.wifi_working is True
292
+ assert device.wick_usage is None
293
+ assert device.auto_regulated_humidity is None
294
+ assert device.water_shortage is None
295
+
File without changes
File without changes
File without changes