python-rako-2025 0.1.0__tar.gz → 0.2.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.
- {python_rako_2025-0.1.0/python_rako_2025.egg-info → python_rako_2025-0.2.0}/PKG-INFO +1 -1
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/pyproject.toml +1 -1
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/__version__.py +1 -1
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/bridge.py +26 -6
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0/python_rako_2025.egg-info}/PKG-INFO +1 -1
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/setup.py +1 -1
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/tests/test_bridge.py +82 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/LICENSE +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/README.md +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/__init__.py +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/const.py +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/exceptions.py +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/helpers.py +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/model.py +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/py.typed +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/SOURCES.txt +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/dependency_links.txt +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/not-zip-safe +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/requires.txt +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/top_level.txt +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/setup.cfg +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/tests/test_helpers.py +0 -0
- {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/tests/test_model.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-rako-2025"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Asynchronous Python client for Rako Controls Lighting"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = ["rako", "controls", "api", "async", "client"]
|
|
@@ -175,6 +175,13 @@ class Bridge:
|
|
|
175
175
|
if isinstance(device, (RoomVentilation, ChannelVentilation)):
|
|
176
176
|
yield device
|
|
177
177
|
|
|
178
|
+
async def discover_all_devices(
|
|
179
|
+
self, session: aiohttp.ClientSession
|
|
180
|
+
) -> AsyncGenerator[Light | Ventilation]:
|
|
181
|
+
rako_xml = await self.get_rako_xml(session)
|
|
182
|
+
for device in self.get_devices_from_discovery_xml(rako_xml):
|
|
183
|
+
yield device
|
|
184
|
+
|
|
178
185
|
async def get_info(self, session: aiohttp.ClientSession) -> BridgeInfo:
|
|
179
186
|
try:
|
|
180
187
|
rako_xml = await self.get_rako_xml(session)
|
|
@@ -209,22 +216,35 @@ class Bridge:
|
|
|
209
216
|
if isinstance(device, (RoomLight, ChannelLight)):
|
|
210
217
|
yield device
|
|
211
218
|
|
|
219
|
+
@staticmethod
|
|
220
|
+
def get_all_devices_from_discovery_xml(xml: str) -> Generator[Light | Ventilation]:
|
|
221
|
+
"""Get all devices (lights and ventilation) from discovery XML."""
|
|
222
|
+
return Bridge.get_devices_from_discovery_xml(xml)
|
|
223
|
+
|
|
212
224
|
@staticmethod
|
|
213
225
|
def get_devices_from_discovery_xml(
|
|
214
|
-
xml: str,
|
|
226
|
+
xml: str, device_types: str | list[str] | None = None
|
|
215
227
|
) -> Generator[Light | Ventilation]:
|
|
228
|
+
# Handle different input types for backward compatibility
|
|
229
|
+
if device_types is None or device_types == "All":
|
|
230
|
+
target_types = {"Lights", "Ventilation"}
|
|
231
|
+
elif isinstance(device_types, str):
|
|
232
|
+
target_types = {device_types}
|
|
233
|
+
else:
|
|
234
|
+
target_types = set(device_types)
|
|
235
|
+
|
|
216
236
|
xml_dict = xmltodict.parse(xml, force_list={"Room"})
|
|
217
237
|
for room in xml_dict["rako"]["rooms"]["Room"]:
|
|
218
238
|
room_id = int(room["@id"])
|
|
219
239
|
room_type = room.get("Type", "Lights")
|
|
220
|
-
if room_type
|
|
240
|
+
if room_type not in target_types:
|
|
221
241
|
continue
|
|
222
242
|
room_title = room["Title"]
|
|
223
243
|
|
|
224
244
|
# Yield room-level device
|
|
225
|
-
if
|
|
245
|
+
if room_type == "Lights":
|
|
226
246
|
yield RoomLight(room_id, room_title)
|
|
227
|
-
elif
|
|
247
|
+
elif room_type == "Ventilation":
|
|
228
248
|
yield RoomVentilation(room_id, room_title)
|
|
229
249
|
|
|
230
250
|
# Yield channel-level devices
|
|
@@ -240,7 +260,7 @@ class Bridge:
|
|
|
240
260
|
channel_name = channel["Name"]
|
|
241
261
|
channel_levels = channel["Levels"]
|
|
242
262
|
|
|
243
|
-
if
|
|
263
|
+
if room_type == "Lights":
|
|
244
264
|
yield ChannelLight(
|
|
245
265
|
room_id,
|
|
246
266
|
room_title,
|
|
@@ -249,7 +269,7 @@ class Bridge:
|
|
|
249
269
|
channel_name,
|
|
250
270
|
channel_levels,
|
|
251
271
|
)
|
|
252
|
-
elif
|
|
272
|
+
elif room_type == "Ventilation":
|
|
253
273
|
yield ChannelVentilation(
|
|
254
274
|
room_id,
|
|
255
275
|
room_title,
|
|
@@ -141,3 +141,85 @@ def test_ventilation_command_compatibility():
|
|
|
141
141
|
# Test that ventilation channel can use lighting level commands
|
|
142
142
|
channel_level_command = CommandLevelHTTP(room=161, channel=1, level=128)
|
|
143
143
|
assert channel_level_command.as_params() == {"room": 161, "ch": 1, "lev": 128}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_get_all_devices_from_discovery_xml(rako_xml3):
|
|
147
|
+
"""Test discovering all device types from XML"""
|
|
148
|
+
all_devices = list(Bridge.get_all_devices_from_discovery_xml(rako_xml3))
|
|
149
|
+
|
|
150
|
+
# Should contain both lights and ventilation
|
|
151
|
+
lights = [dev for dev in all_devices if isinstance(dev, (RoomLight, ChannelLight))]
|
|
152
|
+
ventilation = [
|
|
153
|
+
dev
|
|
154
|
+
for dev in all_devices
|
|
155
|
+
if isinstance(dev, (RoomVentilation, ChannelVentilation))
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
# Should have many lights and 2 ventilation devices
|
|
159
|
+
assert len(lights) > 10 # rako3.xml has many light rooms
|
|
160
|
+
assert len(ventilation) == 2 # 1 RoomVentilation + 1 ChannelVentilation
|
|
161
|
+
|
|
162
|
+
# Check specific ventilation devices are included
|
|
163
|
+
ventilation_room = next(
|
|
164
|
+
(dev for dev in ventilation if isinstance(dev, RoomVentilation)), None
|
|
165
|
+
)
|
|
166
|
+
assert ventilation_room is not None
|
|
167
|
+
assert ventilation_room.room_id == 161
|
|
168
|
+
assert ventilation_room.room_title == "Fans"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_get_devices_with_specific_types(rako_xml3):
|
|
172
|
+
"""Test discovering specific device types using list parameter"""
|
|
173
|
+
# Test with list of device types
|
|
174
|
+
lights_only = list(Bridge.get_devices_from_discovery_xml(rako_xml3, ["Lights"]))
|
|
175
|
+
ventilation_only = list(
|
|
176
|
+
Bridge.get_devices_from_discovery_xml(rako_xml3, ["Ventilation"])
|
|
177
|
+
)
|
|
178
|
+
both_types = list(
|
|
179
|
+
Bridge.get_devices_from_discovery_xml(rako_xml3, ["Lights", "Ventilation"])
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Verify filtering works correctly
|
|
183
|
+
assert all(isinstance(dev, (RoomLight, ChannelLight)) for dev in lights_only)
|
|
184
|
+
assert all(
|
|
185
|
+
isinstance(dev, (RoomVentilation, ChannelVentilation))
|
|
186
|
+
for dev in ventilation_only
|
|
187
|
+
)
|
|
188
|
+
assert len(both_types) == len(lights_only) + len(ventilation_only)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def test_get_devices_backward_compatibility(rako_xml3):
|
|
192
|
+
"""Test that existing string parameter still works"""
|
|
193
|
+
# Test backward compatibility with string parameter
|
|
194
|
+
lights_str = list(Bridge.get_devices_from_discovery_xml(rako_xml3, "Lights"))
|
|
195
|
+
lights_list = list(Bridge.get_devices_from_discovery_xml(rako_xml3, ["Lights"]))
|
|
196
|
+
|
|
197
|
+
# Should return the same devices
|
|
198
|
+
assert lights_str == lights_list
|
|
199
|
+
|
|
200
|
+
# Test ventilation backward compatibility
|
|
201
|
+
ventilation_str = list(
|
|
202
|
+
Bridge.get_devices_from_discovery_xml(rako_xml3, "Ventilation")
|
|
203
|
+
)
|
|
204
|
+
ventilation_list = list(
|
|
205
|
+
Bridge.get_devices_from_discovery_xml(rako_xml3, ["Ventilation"])
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
assert ventilation_str == ventilation_list
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_get_devices_all_parameter_variants(rako_xml3):
|
|
212
|
+
"""Test different ways to get all devices"""
|
|
213
|
+
all_devices_none = list(Bridge.get_devices_from_discovery_xml(rako_xml3, None))
|
|
214
|
+
all_devices_all = list(Bridge.get_devices_from_discovery_xml(rako_xml3, "All"))
|
|
215
|
+
all_devices_method = list(Bridge.get_all_devices_from_discovery_xml(rako_xml3))
|
|
216
|
+
|
|
217
|
+
# All should return the same devices
|
|
218
|
+
assert all_devices_none == all_devices_all == all_devices_method
|
|
219
|
+
|
|
220
|
+
# Should contain both lights and ventilation
|
|
221
|
+
assert any(isinstance(dev, (RoomLight, ChannelLight)) for dev in all_devices_none)
|
|
222
|
+
assert any(
|
|
223
|
+
isinstance(dev, (RoomVentilation, ChannelVentilation))
|
|
224
|
+
for dev in all_devices_none
|
|
225
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|