blebox-uniapi 2.2.2__tar.gz → 2.4.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 (55) hide show
  1. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/HISTORY.rst +16 -0
  2. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/PKG-INFO +19 -1
  3. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/__init__.py +1 -1
  4. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/binary_sensor.py +41 -3
  5. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/box.py +8 -99
  6. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/box_types.py +180 -96
  7. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/climate.py +11 -13
  8. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/cover.py +176 -26
  9. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/error.py +3 -38
  10. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/feature.py +2 -1
  11. blebox_uniapi-2.4.0/blebox_uniapi/jfollow.py +11 -0
  12. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/light.py +5 -2
  13. blebox_uniapi-2.4.0/blebox_uniapi/sensor.py +252 -0
  14. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/session.py +3 -3
  15. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi.egg-info/PKG-INFO +19 -1
  16. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi.egg-info/SOURCES.txt +1 -3
  17. blebox_uniapi-2.4.0/blebox_uniapi.egg-info/requires.txt +2 -0
  18. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/setup.cfg +1 -1
  19. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/setup.py +3 -2
  20. blebox_uniapi-2.4.0/tests/test_box.py +169 -0
  21. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_box_types.py +11 -1
  22. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_climate.py +1 -0
  23. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_cover.py +24 -3
  24. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_light.py +9 -15
  25. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_light_unit.py +1 -1
  26. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_switch.py +144 -160
  27. blebox_uniapi-2.2.2/blebox_uniapi/sensor.py +0 -209
  28. blebox_uniapi-2.2.2/blebox_uniapi.egg-info/requires.txt +0 -1
  29. blebox_uniapi-2.2.2/tests/__init__.py +0 -1
  30. blebox_uniapi-2.2.2/tests/conftest.py +0 -331
  31. blebox_uniapi-2.2.2/tests/test_box.py +0 -210
  32. blebox_uniapi-2.2.2/tests/test_error.py +0 -48
  33. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/CONTRIBUTING.rst +0 -0
  34. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/LICENSE +0 -0
  35. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/MANIFEST.in +0 -0
  36. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/README.rst +0 -0
  37. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/button.py +0 -0
  38. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi/switch.py +0 -0
  39. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi.egg-info/dependency_links.txt +0 -0
  40. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi.egg-info/not-zip-safe +0 -0
  41. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/blebox_uniapi.egg-info/top_level.txt +0 -0
  42. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/Makefile +0 -0
  43. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/authors.rst +0 -0
  44. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/conf.py +0 -0
  45. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/contributing.rst +0 -0
  46. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/history.rst +0 -0
  47. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/index.rst +0 -0
  48. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/installation.rst +0 -0
  49. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/make.bat +0 -0
  50. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/readme.rst +0 -0
  51. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/docs/usage.rst +0 -0
  52. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_button.py +0 -0
  53. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_sensor.py +0 -0
  54. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_session.py +0 -0
  55. {blebox_uniapi-2.2.2 → blebox_uniapi-2.4.0}/tests/test_unknown.py +0 -0
@@ -1,6 +1,22 @@
1
1
  =======
2
2
  History
3
3
  =======
4
+ 2.4.0 (2024-06-03)
5
+ ------------------
6
+ * Fix: Refactor sensor_factory by @Pastucha in https://github.com/blebox/blebox_uniapi/pull/163
7
+ * Order in BOX_TYPES by @Pastucha in https://github.com/blebox/blebox_uniapi/pull/164
8
+ * Feature: smart meter by @swistakm in https://github.com/blebox/blebox_uniapi/pull/168
9
+ * Smartmeter by @pvsti in https://github.com/blebox/blebox_uniapi/pull/170
10
+ * fix: resolve regressions in cover and climate due to jmespath introduction by @swistakm in https://github.com/blebox/blebox_uniapi/pull/173
11
+
12
+ 2.3.0 (2024-03-13)
13
+ ------------------
14
+ * feat: add new methods to cover feature enabling handling of tilt open/close actions by @swistakm in https://github.com/blebox/blebox_uniapi/pull/154
15
+ * add support for flood sensing for multisensors as binary moisture sensor by @swistakm in https://github.com/blebox/blebox_uniapi/pull/153
16
+ * feat/fix: Add Ruff and Pre-commit Configuration, Resolve Undefined Name by @Pastucha in https://github.com/blebox/blebox_uniapi/pull/158
17
+ * gatebox and shutterbox improvements: by @swistakm in https://github.com/blebox/blebox_uniapi/pull/156
18
+ * BleBox Multisensor Illuminance Integration by @Pastucha in https://github.com/blebox/blebox_uniapi/pull/161
19
+
4
20
  2.2.2 (2024-02-07)
5
21
  ------------------
6
22
  * fixed wind reading units to get proper raw m/s value (division by 10, see PR #150)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: blebox_uniapi
3
- Version: 2.2.2
3
+ Version: 2.4.0
4
4
  Summary: Python API for accessing BleBox smart home devices
5
5
  Home-page: https://github.com/blebox/blebox_uniapi
6
6
  Author: BleBox
@@ -14,8 +14,10 @@ Classifier: Natural Language :: English
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Requires-Python: >=3.9
17
+ Description-Content-Type: text/x-rst
17
18
  License-File: LICENSE
18
19
  Requires-Dist: aiohttp>=3
20
+ Requires-Dist: jmespath>1.0.0
19
21
 
20
22
  ====================
21
23
  BleBox Python UniAPI
@@ -67,6 +69,22 @@ Credits
67
69
  =======
68
70
  History
69
71
  =======
72
+ 2.4.0 (2024-06-03)
73
+ ------------------
74
+ * Fix: Refactor sensor_factory by @Pastucha in https://github.com/blebox/blebox_uniapi/pull/163
75
+ * Order in BOX_TYPES by @Pastucha in https://github.com/blebox/blebox_uniapi/pull/164
76
+ * Feature: smart meter by @swistakm in https://github.com/blebox/blebox_uniapi/pull/168
77
+ * Smartmeter by @pvsti in https://github.com/blebox/blebox_uniapi/pull/170
78
+ * fix: resolve regressions in cover and climate due to jmespath introduction by @swistakm in https://github.com/blebox/blebox_uniapi/pull/173
79
+
80
+ 2.3.0 (2024-03-13)
81
+ ------------------
82
+ * feat: add new methods to cover feature enabling handling of tilt open/close actions by @swistakm in https://github.com/blebox/blebox_uniapi/pull/154
83
+ * add support for flood sensing for multisensors as binary moisture sensor by @swistakm in https://github.com/blebox/blebox_uniapi/pull/153
84
+ * feat/fix: Add Ruff and Pre-commit Configuration, Resolve Undefined Name by @Pastucha in https://github.com/blebox/blebox_uniapi/pull/158
85
+ * gatebox and shutterbox improvements: by @swistakm in https://github.com/blebox/blebox_uniapi/pull/156
86
+ * BleBox Multisensor Illuminance Integration by @Pastucha in https://github.com/blebox/blebox_uniapi/pull/161
87
+
70
88
  2.2.2 (2024-02-07)
71
89
  ------------------
72
90
  * fixed wind reading units to get proper raw m/s value (division by 10, see PR #150)
@@ -2,4 +2,4 @@
2
2
 
3
3
  __author__ = """BleBox"""
4
4
  __email__ = "opensource@blebox.eu"
5
- __version__ = "2.2.2"
5
+ __version__ = "2.4.0"
@@ -1,7 +1,10 @@
1
- from typing import Union
1
+ from typing import Union, TYPE_CHECKING
2
2
 
3
3
  from .feature import Feature
4
4
 
5
+ if TYPE_CHECKING:
6
+ from .box import Box
7
+
5
8
 
6
9
  class BinarySensor(Feature):
7
10
  """Class representing sensor with bool state."""
@@ -13,17 +16,26 @@ class BinarySensor(Feature):
13
16
  def many_from_config(
14
17
  cls, product, box_type_config, extended_state
15
18
  ) -> list["Feature"]:
19
+ type_class_map = {
20
+ "rain": Rain,
21
+ "flood": Flood,
22
+ }
23
+
16
24
  output_list = list()
17
25
  sensors_list = extended_state.get("multiSensor").get("sensors", {})
18
26
  alias, methods = box_type_config[0]
27
+
19
28
  for sensor in sensors_list:
20
29
  sensor_type = sensor.get("type")
21
30
  sensor_id = sensor.get("id")
22
- if sensor.get("type") in ("rain", "flood"):
31
+
32
+ if sensor_type in type_class_map:
33
+ klass = type_class_map[sensor_type]
34
+
23
35
  if methods.get(sensor_type) is not None:
24
36
  value_method = {sensor_type: methods[sensor_type](sensor_id)}
25
37
  output_list.append(
26
- Rain(
38
+ klass(
27
39
  product=product,
28
40
  alias=sensor_type + "_" + str(sensor_id),
29
41
  methods=value_method,
@@ -56,3 +68,29 @@ class Rain(BinarySensor):
56
68
 
57
69
  def after_update(self) -> None:
58
70
  self._current = self._read_rain("rain")
71
+
72
+
73
+ class Flood(BinarySensor):
74
+ def __init__(self, product: "Box", alias: str, methods: dict):
75
+ self._device_class = "moisture"
76
+ super().__init__(product, alias, methods)
77
+
78
+ @property
79
+ def state(self) -> bool:
80
+ return self._current > 0
81
+
82
+ @property
83
+ def device_class(self) -> str:
84
+ return self._device_class
85
+
86
+ def _read_flood(self, field: str) -> Union[float, int, None]:
87
+ product = self._product
88
+
89
+ if product.last_data is not None:
90
+ raw = self.raw_value(field)
91
+ if raw is not None: # no reading
92
+ return self.raw_value("flood")
93
+ return 0
94
+
95
+ def after_update(self) -> None:
96
+ self._current = self._read_flood("flood")
@@ -1,12 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- import re
4
3
  import asyncio
5
4
  import time
6
5
 
7
6
  from typing import Optional, Any, Dict
8
7
 
9
- from .box_types import default_api_level, get_conf, get_conf_set
8
+ from .box_types import _DEFAULT_API_LEVEL, get_conf, get_conf_set
10
9
  from .button import Button
11
10
  from .climate import Climate
12
11
  from .cover import Cover
@@ -19,7 +18,6 @@ from .switch import Switch
19
18
  from .error import (
20
19
  UnsupportedBoxResponse,
21
20
  UnsupportedBoxVersion,
22
- JPathFailed,
23
21
  BadFieldExceedsMax,
24
22
  BadFieldLessThanMin,
25
23
  BadFieldMissing,
@@ -107,7 +105,7 @@ class Box:
107
105
  info, f"{location} has no hardware version"
108
106
  ) from ex
109
107
 
110
- level = int(info.get("apiLevel", default_api_level))
108
+ level = int(info.get("apiLevel", _DEFAULT_API_LEVEL))
111
109
 
112
110
  self._data_path = config["api_path"]
113
111
  self._type = type
@@ -117,21 +115,15 @@ class Box:
117
115
  self._firmware_version = firmware_version
118
116
  self._hardware_version = hardware_version
119
117
  self._api_version = level
120
-
121
118
  self._model = config.get("model", type)
122
-
123
119
  self._api = config.get("api", {})
124
-
125
120
  self._features = self.create_features(config, info, extended_state)
126
-
127
121
  self._config = config
128
-
129
- self._update_last_data(None)
122
+ self._update_last_data(extended_state)
130
123
 
131
124
  def create_features(
132
125
  self, config: dict, info: dict, extended_state: Optional[dict]
133
126
  ) -> dict:
134
-
135
127
  features = {}
136
128
  for field, klass in {
137
129
  "covers": Cover,
@@ -185,7 +177,7 @@ class Box:
185
177
  product = device_type
186
178
  if device_type == "wLightBox" and product == "wLightBoxS":
187
179
  device_type = "wLightBoxS"
188
- level = int(info.get("apiLevel", default_api_level))
180
+ level = int(info.get("apiLevel", _DEFAULT_API_LEVEL))
189
181
  config_set = get_conf_set(device_type)
190
182
  if not config_set:
191
183
  raise UnsupportedBoxResponse(f"{device_type} is not a supported type")
@@ -260,9 +252,9 @@ class Box:
260
252
  # Note that SwitchboxD is just an example. It is possible that APIs of other
261
253
  # box types also exhibit this kind of behavior.
262
254
  if (
263
- isinstance(self._last_data, dict) and
264
- isinstance(new_data, dict) and
265
- self._last_data.keys() != new_data.keys()
255
+ isinstance(self._last_data, dict)
256
+ and isinstance(new_data, dict)
257
+ and self._last_data.keys() != new_data.keys()
266
258
  ):
267
259
  # ... In such a case we need to merge both states instead of overwriting
268
260
  # the old one as-is.
@@ -288,89 +280,6 @@ class Box:
288
280
  self._last_real_update = None # force update
289
281
  return await self._async_api(False, method, *args)
290
282
 
291
- def follow(self, data: dict, path: str) -> Any:
292
- """
293
- Return payload from device response json.
294
- :param self:
295
- :param data:
296
- :param path:
297
- :return:
298
- """
299
- if data is None:
300
- raise RuntimeError(f"bad argument: data {data}") # pragma: no cover
301
-
302
- results = path.split("/")
303
- current_tree = data
304
-
305
- for chunk in results:
306
- with_string_value = re.compile(r"^\[(.*)='(.*)']$")
307
- match = with_string_value.match(chunk)
308
- if match:
309
- name = match.group(1)
310
- value = match.group(2)
311
-
312
- found = False
313
-
314
- for item in current_tree:
315
- if item[name] == value:
316
- current_tree = item
317
- found = True
318
- break
319
-
320
- if not found:
321
- raise JPathFailed(f"with: {name}={value}", path, data)
322
-
323
- continue # pragma: no cover
324
-
325
- with_int_value = re.compile(r"^\[(.*)=(\d+)]$")
326
- match = with_int_value.match(chunk)
327
- if match:
328
- name = match.group(1)
329
- value = int(match.group(2))
330
-
331
- found = False
332
-
333
- if not isinstance(current_tree, list):
334
- raise JPathFailed(
335
- f"list expected but got {current_tree}", path, data
336
- )
337
-
338
- for item in current_tree:
339
- if item[name] == value:
340
- current_tree = item
341
- found = True
342
- break
343
-
344
- if not found:
345
- raise JPathFailed(f"with: {name}={value}", path, data)
346
- continue # pragma: no cover
347
-
348
- with_index = re.compile(r"^\[(\d+)]$")
349
- match = with_index.match(chunk)
350
- if match:
351
- index = int(match.group(1))
352
- if not isinstance(current_tree, list) or index >= len(current_tree):
353
- raise JPathFailed(f"with value at index {index}", path, data)
354
-
355
- current_tree = current_tree[index]
356
- continue
357
-
358
- if isinstance(current_tree, dict):
359
- names = current_tree.keys()
360
- if chunk not in names:
361
- raise JPathFailed(
362
- f"item '{chunk}' not among {list(names)}", path, data
363
- )
364
-
365
- current_tree = current_tree[chunk]
366
- else:
367
- raise JPathFailed(
368
- f"unexpected item type: '{chunk}' not in: {current_tree}",
369
- path,
370
- data,
371
- )
372
- return current_tree
373
-
374
283
  def expect_int(
375
284
  self, field: str, raw_value: int, maximum: int = -1, minimum: int = 0
376
285
  ) -> int:
@@ -398,7 +307,7 @@ class Box:
398
307
  if value is None:
399
308
  raise BadFieldMissing(self.name, field)
400
309
 
401
- if not type(value) is int:
310
+ if not isinstance(value, int):
402
311
  raise BadFieldNotANumber(self.name, field, value)
403
312
 
404
313
  return self.check_int_range(value, field, max_value, min_value)