blebox-uniapi 2.4.2__tar.gz → 2.5.1__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.
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/HISTORY.rst +8 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/PKG-INFO +12 -3
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/__init__.py +1 -1
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/feature.py +4 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/sensor.py +41 -10
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi.egg-info/PKG-INFO +13 -4
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi.egg-info/SOURCES.txt +1 -12
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/setup.cfg +1 -1
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/setup.py +1 -1
- blebox_uniapi-2.4.2/tests/test_box.py +0 -169
- blebox_uniapi-2.4.2/tests/test_box_types.py +0 -80
- blebox_uniapi-2.4.2/tests/test_button.py +0 -49
- blebox_uniapi-2.4.2/tests/test_climate.py +0 -386
- blebox_uniapi-2.4.2/tests/test_cover.py +0 -749
- blebox_uniapi-2.4.2/tests/test_light.py +0 -1151
- blebox_uniapi-2.4.2/tests/test_light_unit.py +0 -141
- blebox_uniapi-2.4.2/tests/test_sensor.py +0 -401
- blebox_uniapi-2.4.2/tests/test_session.py +0 -155
- blebox_uniapi-2.4.2/tests/test_switch.py +0 -446
- blebox_uniapi-2.4.2/tests/test_unknown.py +0 -59
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/CONTRIBUTING.rst +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/LICENSE +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/MANIFEST.in +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/README.rst +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/binary_sensor.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/box.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/box_types.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/button.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/climate.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/cover.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/error.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/jfollow.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/light.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/session.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi/switch.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi.egg-info/dependency_links.txt +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi.egg-info/not-zip-safe +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi.egg-info/requires.txt +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/blebox_uniapi.egg-info/top_level.txt +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/Makefile +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/authors.rst +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/conf.py +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/contributing.rst +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/history.rst +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/index.rst +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/installation.rst +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/make.bat +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/readme.rst +0 -0
- {blebox_uniapi-2.4.2 → blebox_uniapi-2.5.1}/docs/usage.rst +0 -0
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
=======
|
|
2
2
|
History
|
|
3
3
|
=======
|
|
4
|
+
2.5.1 (2026-04-21)
|
|
5
|
+
------------------
|
|
6
|
+
* fix: multisensor current and energy sensor scales
|
|
7
|
+
|
|
8
|
+
2.5.0 (2024-08-20)
|
|
9
|
+
------------------
|
|
10
|
+
* feature: expose sensor_id in sensors and alias in all features by @swistakm in https://github.com/blebox/blebox_uniapi/pull/176
|
|
11
|
+
|
|
4
12
|
2.4.2 (2024-06-04)
|
|
5
13
|
------------------
|
|
6
14
|
* fix: add missing support for active power sensors on switchbox/switchboxd devices by @swistakm in https://github.com/blebox/blebox_uniapi/pull/175
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: blebox_uniapi
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.1
|
|
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
|
|
7
7
|
Author-email: opensource@blebox.eu
|
|
8
8
|
License: Apache Software License 2.0
|
|
9
9
|
Keywords: blebox_uniapi
|
|
10
|
+
Platform: UNKNOWN
|
|
10
11
|
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -16,8 +17,6 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
16
17
|
Requires-Python: >=3.9
|
|
17
18
|
Description-Content-Type: text/x-rst
|
|
18
19
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: aiohttp>=3
|
|
20
|
-
Requires-Dist: jmespath>1.0.0
|
|
21
20
|
|
|
22
21
|
====================
|
|
23
22
|
BleBox Python UniAPI
|
|
@@ -57,6 +56,14 @@ Credits
|
|
|
57
56
|
=======
|
|
58
57
|
History
|
|
59
58
|
=======
|
|
59
|
+
2.5.1 (2026-04-21)
|
|
60
|
+
------------------
|
|
61
|
+
* fix: multisensor current and energy sensor scales
|
|
62
|
+
|
|
63
|
+
2.5.0 (2024-08-20)
|
|
64
|
+
------------------
|
|
65
|
+
* feature: expose sensor_id in sensors and alias in all features by @swistakm in https://github.com/blebox/blebox_uniapi/pull/176
|
|
66
|
+
|
|
60
67
|
2.4.2 (2024-06-04)
|
|
61
68
|
------------------
|
|
62
69
|
* fix: add missing support for active power sensors on switchbox/switchboxd devices by @swistakm in https://github.com/blebox/blebox_uniapi/pull/175
|
|
@@ -220,3 +227,5 @@ History
|
|
|
220
227
|
------------------
|
|
221
228
|
|
|
222
229
|
* First release on PyPI.
|
|
230
|
+
|
|
231
|
+
|
|
@@ -73,7 +73,10 @@ class SensorFactory:
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
feature = constructor(
|
|
76
|
-
product=product,
|
|
76
|
+
product=product,
|
|
77
|
+
alias=alias,
|
|
78
|
+
methods=materialized_methods,
|
|
79
|
+
sensor_id=sensor_id,
|
|
77
80
|
)
|
|
78
81
|
object_list.append(feature)
|
|
79
82
|
|
|
@@ -98,11 +101,18 @@ class BaseSensor(Feature):
|
|
|
98
101
|
_device_class: str
|
|
99
102
|
_native_value: Union[float, int, str]
|
|
100
103
|
_sensor_type: Optional[str]
|
|
104
|
+
_sensor_id: Optional[int]
|
|
101
105
|
|
|
102
106
|
def __init__(
|
|
103
|
-
self,
|
|
107
|
+
self,
|
|
108
|
+
product: "Box",
|
|
109
|
+
alias: str,
|
|
110
|
+
methods: dict,
|
|
111
|
+
sensor_type: str = None,
|
|
112
|
+
sensor_id: Optional[int] = None,
|
|
104
113
|
):
|
|
105
114
|
self._sensor_type = sensor_type
|
|
115
|
+
self._sensor_id = sensor_id
|
|
106
116
|
super().__init__(product, alias, methods)
|
|
107
117
|
|
|
108
118
|
@property
|
|
@@ -117,6 +127,14 @@ class BaseSensor(Feature):
|
|
|
117
127
|
def native_value(self):
|
|
118
128
|
return self._native_value
|
|
119
129
|
|
|
130
|
+
@property
|
|
131
|
+
def sensor_id(self):
|
|
132
|
+
return self._sensor_id
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def probe_id(self):
|
|
136
|
+
return self.sensor_id
|
|
137
|
+
|
|
120
138
|
@classmethod
|
|
121
139
|
def many_from_config(cls, product, box_type_config, extended_state):
|
|
122
140
|
raise NotImplementedError("Please use SensorFactory")
|
|
@@ -126,13 +144,13 @@ class BaseSensor(Feature):
|
|
|
126
144
|
|
|
127
145
|
|
|
128
146
|
@SensorFactory.register("frequency", unit="Hz", scale=1_000)
|
|
129
|
-
@SensorFactory.register("current", unit="mA"
|
|
147
|
+
@SensorFactory.register("current", unit="mA")
|
|
130
148
|
@SensorFactory.register("voltage", unit="V", scale=10)
|
|
131
149
|
@SensorFactory.register("apparentPower", unit="va")
|
|
132
150
|
@SensorFactory.register("reactivePower", unit="var")
|
|
133
151
|
@SensorFactory.register("activePower", unit="W")
|
|
134
|
-
@SensorFactory.register("reverseActiveEnergy", unit="kWh")
|
|
135
|
-
@SensorFactory.register("forwardActiveEnergy", unit="kWh")
|
|
152
|
+
@SensorFactory.register("reverseActiveEnergy", unit="kWh", scale=1_000)
|
|
153
|
+
@SensorFactory.register("forwardActiveEnergy", unit="kWh", scale=1_000)
|
|
136
154
|
@SensorFactory.register("illuminance", unit="lx", scale=100)
|
|
137
155
|
@SensorFactory.register("humidity", unit="percentage", scale=100)
|
|
138
156
|
@SensorFactory.register("wind", unit="m/s", scale=10)
|
|
@@ -143,6 +161,7 @@ class GenericSensor(BaseSensor):
|
|
|
143
161
|
product: "Box",
|
|
144
162
|
alias: str,
|
|
145
163
|
methods: dict,
|
|
164
|
+
sensor_id: Optional[int],
|
|
146
165
|
*,
|
|
147
166
|
# generalization params
|
|
148
167
|
sensor_type: str,
|
|
@@ -150,7 +169,7 @@ class GenericSensor(BaseSensor):
|
|
|
150
169
|
scale: float = 1,
|
|
151
170
|
precision: Optional[int] = None,
|
|
152
171
|
):
|
|
153
|
-
super().__init__(product, alias, methods)
|
|
172
|
+
super().__init__(product, alias, methods, sensor_id=sensor_id)
|
|
154
173
|
self._unit = unit
|
|
155
174
|
self._scale = scale
|
|
156
175
|
self._precision = precision
|
|
@@ -200,8 +219,14 @@ class PowerConsumption(GenericSensor):
|
|
|
200
219
|
class Temperature(BaseSensor):
|
|
201
220
|
_current: Union[float, int, None]
|
|
202
221
|
|
|
203
|
-
def __init__(
|
|
204
|
-
|
|
222
|
+
def __init__(
|
|
223
|
+
self,
|
|
224
|
+
product: "Box",
|
|
225
|
+
alias: str,
|
|
226
|
+
methods: dict,
|
|
227
|
+
sensor_id: Optional[int] = None,
|
|
228
|
+
):
|
|
229
|
+
super().__init__(product, alias, methods, sensor_id=sensor_id)
|
|
205
230
|
self._unit = "celsius"
|
|
206
231
|
self._device_class = "temperature"
|
|
207
232
|
|
|
@@ -227,8 +252,14 @@ class Temperature(BaseSensor):
|
|
|
227
252
|
class AirQuality(BaseSensor):
|
|
228
253
|
_pm: Optional[int]
|
|
229
254
|
|
|
230
|
-
def __init__(
|
|
231
|
-
|
|
255
|
+
def __init__(
|
|
256
|
+
self,
|
|
257
|
+
product: "Box",
|
|
258
|
+
alias: str,
|
|
259
|
+
methods: dict,
|
|
260
|
+
sensor_id: Optional[str] = None,
|
|
261
|
+
):
|
|
262
|
+
super().__init__(product, alias, methods, sensor_id)
|
|
232
263
|
self._unit = "concentration_of_mp"
|
|
233
264
|
self._device_class = alias
|
|
234
265
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
|
-
Name:
|
|
3
|
-
Version: 2.
|
|
2
|
+
Name: blebox-uniapi
|
|
3
|
+
Version: 2.5.1
|
|
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
|
|
7
7
|
Author-email: opensource@blebox.eu
|
|
8
8
|
License: Apache Software License 2.0
|
|
9
9
|
Keywords: blebox_uniapi
|
|
10
|
+
Platform: UNKNOWN
|
|
10
11
|
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -16,8 +17,6 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
16
17
|
Requires-Python: >=3.9
|
|
17
18
|
Description-Content-Type: text/x-rst
|
|
18
19
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: aiohttp>=3
|
|
20
|
-
Requires-Dist: jmespath>1.0.0
|
|
21
20
|
|
|
22
21
|
====================
|
|
23
22
|
BleBox Python UniAPI
|
|
@@ -57,6 +56,14 @@ Credits
|
|
|
57
56
|
=======
|
|
58
57
|
History
|
|
59
58
|
=======
|
|
59
|
+
2.5.1 (2026-04-21)
|
|
60
|
+
------------------
|
|
61
|
+
* fix: multisensor current and energy sensor scales
|
|
62
|
+
|
|
63
|
+
2.5.0 (2024-08-20)
|
|
64
|
+
------------------
|
|
65
|
+
* feature: expose sensor_id in sensors and alias in all features by @swistakm in https://github.com/blebox/blebox_uniapi/pull/176
|
|
66
|
+
|
|
60
67
|
2.4.2 (2024-06-04)
|
|
61
68
|
------------------
|
|
62
69
|
* fix: add missing support for active power sensors on switchbox/switchboxd devices by @swistakm in https://github.com/blebox/blebox_uniapi/pull/175
|
|
@@ -220,3 +227,5 @@ History
|
|
|
220
227
|
------------------
|
|
221
228
|
|
|
222
229
|
* First release on PyPI.
|
|
230
|
+
|
|
231
|
+
|
|
@@ -34,15 +34,4 @@ docs/index.rst
|
|
|
34
34
|
docs/installation.rst
|
|
35
35
|
docs/make.bat
|
|
36
36
|
docs/readme.rst
|
|
37
|
-
docs/usage.rst
|
|
38
|
-
tests/test_box.py
|
|
39
|
-
tests/test_box_types.py
|
|
40
|
-
tests/test_button.py
|
|
41
|
-
tests/test_climate.py
|
|
42
|
-
tests/test_cover.py
|
|
43
|
-
tests/test_light.py
|
|
44
|
-
tests/test_light_unit.py
|
|
45
|
-
tests/test_sensor.py
|
|
46
|
-
tests/test_session.py
|
|
47
|
-
tests/test_switch.py
|
|
48
|
-
tests/test_unknown.py
|
|
37
|
+
docs/usage.rst
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from unittest import mock
|
|
3
|
-
from blebox_uniapi.box import Box
|
|
4
|
-
from blebox_uniapi import error
|
|
5
|
-
from blebox_uniapi.jfollow import follow
|
|
6
|
-
|
|
7
|
-
pytestmark = pytest.mark.asyncio
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@pytest.fixture
|
|
11
|
-
def mock_session():
|
|
12
|
-
return mock.MagicMock(host="172.1.2.3", port=80)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@pytest.fixture
|
|
16
|
-
def sample_data():
|
|
17
|
-
return {
|
|
18
|
-
"id": "abcd1234ef",
|
|
19
|
-
"type": "airSensor",
|
|
20
|
-
"deviceName": "foobar",
|
|
21
|
-
"fv": "1.23",
|
|
22
|
-
"hv": "4.56",
|
|
23
|
-
"apiLevel": "20180403",
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@pytest.fixture
|
|
28
|
-
def config(sample_data):
|
|
29
|
-
return Box._match_device_config(sample_data)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
async def test_without_type(mock_session, sample_data, config):
|
|
33
|
-
del sample_data["type"]
|
|
34
|
-
|
|
35
|
-
with pytest.raises(error.UnsupportedBoxResponse, match="has no type"):
|
|
36
|
-
Box(mock_session, sample_data, config, None)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
async def test_with_unknown_type(mock_session, sample_data):
|
|
40
|
-
sample_data["type"] = "unknownBox"
|
|
41
|
-
|
|
42
|
-
with pytest.raises(error.UnsupportedBoxResponse, match="not a supported type"):
|
|
43
|
-
Box._match_device_config(sample_data)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
async def test_without_name(mock_session, sample_data, config):
|
|
47
|
-
del sample_data["deviceName"]
|
|
48
|
-
|
|
49
|
-
with pytest.raises(error.UnsupportedBoxResponse, match="has no name"):
|
|
50
|
-
Box(mock_session, sample_data, config, None)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
async def test_without_firmware_version(mock_session, sample_data, config):
|
|
54
|
-
del sample_data["fv"]
|
|
55
|
-
|
|
56
|
-
with pytest.raises(error.UnsupportedBoxResponse, match="has no firmware version"):
|
|
57
|
-
Box(mock_session, sample_data, config, None)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
async def test_without_hardware_version(mock_session, sample_data, config):
|
|
61
|
-
del sample_data["hv"]
|
|
62
|
-
|
|
63
|
-
with pytest.raises(error.UnsupportedBoxResponse, match="has no hardware version"):
|
|
64
|
-
Box(mock_session, sample_data, config, None)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
async def test_without_api_level(mock_session, sample_data, config):
|
|
68
|
-
del sample_data["apiLevel"]
|
|
69
|
-
|
|
70
|
-
with pytest.raises(error.UnsupportedBoxVersion, match=r"unsupported version"):
|
|
71
|
-
Box._match_device_config(sample_data)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
async def test_json_path_extraction(mock_session, sample_data, config):
|
|
75
|
-
# note: follow is thin wrapper over jmespath so there's no real reason to test it.
|
|
76
|
-
# However, this tests makes sure we understand the syntax and know how it works.
|
|
77
|
-
# We can also use it as a canary for any unexpected change in syntax that may come
|
|
78
|
-
# in newer version.
|
|
79
|
-
|
|
80
|
-
# succesfull extraction
|
|
81
|
-
assert follow(["foo"], "[0]") == "foo"
|
|
82
|
-
assert follow([{"foo": "3", "value": 4}], "[?foo=='3'].value") == [4]
|
|
83
|
-
|
|
84
|
-
# "dud" extraction
|
|
85
|
-
assert follow([{"foo": "ab", "value": 4}], "[?foo=='bc'].value") == []
|
|
86
|
-
assert follow([{"value": 4}], "[1].value") is None
|
|
87
|
-
assert follow({"value": 4}, "foo") is None
|
|
88
|
-
assert follow({"foo": [4]}, "[?bar==`0`].value") is None
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
async def test_missing_device_id(mock_session, sample_data, config):
|
|
92
|
-
del sample_data["id"]
|
|
93
|
-
with pytest.raises(error.UnsupportedBoxResponse, match="has no id"):
|
|
94
|
-
Box(mock_session, sample_data, config, None)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
# Add more test cases for other missing fields (type, name, versions, etc.)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
async def test_invalid_init(mock_session, sample_data, config):
|
|
101
|
-
with mock.patch(
|
|
102
|
-
"blebox_uniapi.sensor.SensorFactory.many_from_config",
|
|
103
|
-
spec_set=True,
|
|
104
|
-
autospec=True,
|
|
105
|
-
) as mock_sensor:
|
|
106
|
-
mock_sensor.side_effect = KeyError
|
|
107
|
-
with pytest.raises(
|
|
108
|
-
error.UnsupportedBoxResponse, match=r"Failed to initialize:"
|
|
109
|
-
):
|
|
110
|
-
Box(mock_session, sample_data, config, None)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
async def test_properties(mock_session, sample_data, config):
|
|
114
|
-
box = Box(mock_session, sample_data, config, None)
|
|
115
|
-
assert box.name == "foobar"
|
|
116
|
-
assert box.last_data is None
|
|
117
|
-
assert box.type == "airSensor"
|
|
118
|
-
assert box.model == "airSensor"
|
|
119
|
-
assert box.unique_id == "abcd1234ef"
|
|
120
|
-
assert box.firmware_version == "1.23"
|
|
121
|
-
assert box.hardware_version == "4.56"
|
|
122
|
-
assert box.brand == "BleBox"
|
|
123
|
-
assert box.api_version == 20180403
|
|
124
|
-
assert box.address == "172.1.2.3:80"
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
async def test_field_validations(mock_session, sample_data, config):
|
|
128
|
-
box = Box(mock_session, sample_data, config, None)
|
|
129
|
-
|
|
130
|
-
with pytest.raises(
|
|
131
|
-
error.BadFieldExceedsMax,
|
|
132
|
-
match=r"foobar.field1 is 123 which exceeds max \(100\)",
|
|
133
|
-
):
|
|
134
|
-
box.check_int_range(123, "field1", 100, 0)
|
|
135
|
-
|
|
136
|
-
with pytest.raises(
|
|
137
|
-
error.BadFieldLessThanMin,
|
|
138
|
-
match=r"foobar.field1 is 123 which is less than minimum \(200\)",
|
|
139
|
-
):
|
|
140
|
-
box.check_int_range(123, "field1", 300, 200)
|
|
141
|
-
|
|
142
|
-
with pytest.raises(error.BadFieldMissing, match=r"foobar.field1 is missing"):
|
|
143
|
-
box.check_int(None, "field1", 300, 200)
|
|
144
|
-
|
|
145
|
-
with pytest.raises(
|
|
146
|
-
error.BadFieldNotANumber, match=r"foobar.field1 is '123' which is not a number"
|
|
147
|
-
):
|
|
148
|
-
box.check_int("123", "field1", 300, 200)
|
|
149
|
-
|
|
150
|
-
with pytest.raises(error.BadFieldMissing, match=r"foobar.field1 is missing"):
|
|
151
|
-
box.check_hex_str(None, "field1", 300, 200)
|
|
152
|
-
|
|
153
|
-
with pytest.raises(
|
|
154
|
-
error.BadFieldNotAString, match=r"foobar.field1 is 123 which is not a string"
|
|
155
|
-
):
|
|
156
|
-
box.check_hex_str(123, "field1", 300, 200)
|
|
157
|
-
|
|
158
|
-
with pytest.raises(error.BadFieldMissing, match=r"foobar.field1 is missing"):
|
|
159
|
-
box.check_rgbw(None, "field1")
|
|
160
|
-
|
|
161
|
-
with pytest.raises(
|
|
162
|
-
error.BadFieldNotAString, match=r"foobar.field1 is 123 which is not a string"
|
|
163
|
-
):
|
|
164
|
-
box.check_rgbw(123, "field1")
|
|
165
|
-
|
|
166
|
-
with pytest.raises(
|
|
167
|
-
error.BadFieldNotRGBW, match=r"foobar.field1 is 123 which is not a rgbw string"
|
|
168
|
-
):
|
|
169
|
-
box.check_rgbw("123", "field1")
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
from random import choice
|
|
2
|
-
|
|
3
|
-
from blebox_uniapi.box_types import (
|
|
4
|
-
BOX_TYPE_CONF,
|
|
5
|
-
get_conf,
|
|
6
|
-
get_conf_set,
|
|
7
|
-
get_latest_api_level,
|
|
8
|
-
get_latest_conf,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TestBoxTypesOrder:
|
|
13
|
-
def test_conf_order(self):
|
|
14
|
-
for device_type, conf_set in BOX_TYPE_CONF.items():
|
|
15
|
-
api_levels = list(conf_set.keys())
|
|
16
|
-
sorted_api_levels = sorted(api_levels)
|
|
17
|
-
|
|
18
|
-
assert (
|
|
19
|
-
api_levels == sorted_api_levels
|
|
20
|
-
), f"Entries for '{device_type}' are not ordered by API level."
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class TestBoxTypes:
|
|
24
|
-
box_types = tuple(BOX_TYPE_CONF.keys())
|
|
25
|
-
simple_conf_set = {5: {"tag": "first_entry"}, 10: {"tag": "second_entry"}}
|
|
26
|
-
|
|
27
|
-
async def test_get_conf_set_valid(self):
|
|
28
|
-
conf_set = get_conf_set(choice(self.box_types))
|
|
29
|
-
|
|
30
|
-
assert isinstance(conf_set, dict)
|
|
31
|
-
assert conf_set != {}
|
|
32
|
-
|
|
33
|
-
async def test_get_conf_set_invalid(self):
|
|
34
|
-
conf_set = get_conf_set("nonexistent_type")
|
|
35
|
-
|
|
36
|
-
assert isinstance(conf_set, dict)
|
|
37
|
-
assert conf_set == {}
|
|
38
|
-
|
|
39
|
-
async def test_get_conf_valid(self):
|
|
40
|
-
"""Test choosing functionality of get_conf function on exemplary conf_set."""
|
|
41
|
-
for api_level, tag_value in (
|
|
42
|
-
(5, "first_entry"), # a marginal example
|
|
43
|
-
(7, "first_entry"), # 'in between' example
|
|
44
|
-
(10, "second_entry"), # a marginal example
|
|
45
|
-
(17, "second_entry"), # future example
|
|
46
|
-
):
|
|
47
|
-
conf = get_conf(api_level, self.simple_conf_set)
|
|
48
|
-
|
|
49
|
-
assert isinstance(conf, dict)
|
|
50
|
-
assert conf["tag"] == tag_value
|
|
51
|
-
|
|
52
|
-
async def test_get_conf_invalid(self):
|
|
53
|
-
conf = get_conf(3, self.simple_conf_set) # not supported example
|
|
54
|
-
|
|
55
|
-
assert isinstance(conf, dict)
|
|
56
|
-
assert conf == {}
|
|
57
|
-
|
|
58
|
-
async def test_get_latest_conf_valid(self):
|
|
59
|
-
conf = get_latest_conf(choice(self.box_types))
|
|
60
|
-
|
|
61
|
-
assert isinstance(conf, dict)
|
|
62
|
-
assert conf != {}
|
|
63
|
-
|
|
64
|
-
async def test_get_latest_conf_invalid(self):
|
|
65
|
-
conf = get_latest_conf("nonexistent_type")
|
|
66
|
-
|
|
67
|
-
assert isinstance(conf, dict)
|
|
68
|
-
assert conf == {}
|
|
69
|
-
|
|
70
|
-
async def test_get_latest_api_level_valid(self):
|
|
71
|
-
api_level = get_latest_api_level(choice(self.box_types))
|
|
72
|
-
|
|
73
|
-
assert isinstance(api_level, int)
|
|
74
|
-
assert api_level
|
|
75
|
-
|
|
76
|
-
async def test_get_latest_api_level_invalid(self):
|
|
77
|
-
api_level = get_latest_api_level("nonexistent_type")
|
|
78
|
-
|
|
79
|
-
assert isinstance(api_level, int)
|
|
80
|
-
assert not api_level
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
from unittest.mock import Mock
|
|
2
|
-
import pytest
|
|
3
|
-
|
|
4
|
-
from blebox_uniapi.button import Button
|
|
5
|
-
from blebox_uniapi.box import Box
|
|
6
|
-
from blebox_uniapi.box_types import BOX_TYPE_CONF
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@pytest.fixture
|
|
10
|
-
def product():
|
|
11
|
-
return Mock(spec=Box)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@pytest.fixture
|
|
15
|
-
def tv_lift_box_0(product):
|
|
16
|
-
product.type = "tvLiftBox"
|
|
17
|
-
extended_state = {"tvLift": {"controlType": 4}}
|
|
18
|
-
many = Button.many_from_config(
|
|
19
|
-
product,
|
|
20
|
-
BOX_TYPE_CONF["tvLiftBox"][20200518]["buttons"],
|
|
21
|
-
extended_state=extended_state,
|
|
22
|
-
)
|
|
23
|
-
assert len(many) == 3
|
|
24
|
-
return many[0]
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@pytest.fixture
|
|
28
|
-
def tv_lift_box_1(product):
|
|
29
|
-
product.type = "tvLiftBox"
|
|
30
|
-
extended_state = {"tvLift": {"controlType": 4}}
|
|
31
|
-
many = Button.many_from_config(
|
|
32
|
-
product,
|
|
33
|
-
BOX_TYPE_CONF["tvLiftBox"][20200518]["buttons"],
|
|
34
|
-
extended_state=extended_state,
|
|
35
|
-
)
|
|
36
|
-
assert len(many) == 3
|
|
37
|
-
return many[1]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
async def test_tv_lift_0_box_pressed(tv_lift_box_0: Button, product: Box):
|
|
41
|
-
await tv_lift_box_0.set()
|
|
42
|
-
product.async_api_command.assert_called_with("set", "open_or_stop")
|
|
43
|
-
assert tv_lift_box_0.control_type
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
async def test_tv_lift_1_box_pressed(tv_lift_box_1: Button, product: Box):
|
|
47
|
-
await tv_lift_box_1.set()
|
|
48
|
-
product.async_api_command.assert_called_with("set", "close_or_stop")
|
|
49
|
-
assert tv_lift_box_1.control_type
|