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.
Files changed (23) hide show
  1. {python_rako_2025-0.1.0/python_rako_2025.egg-info → python_rako_2025-0.2.0}/PKG-INFO +1 -1
  2. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/pyproject.toml +1 -1
  3. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/__version__.py +1 -1
  4. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/bridge.py +26 -6
  5. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0/python_rako_2025.egg-info}/PKG-INFO +1 -1
  6. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/setup.py +1 -1
  7. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/tests/test_bridge.py +82 -0
  8. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/LICENSE +0 -0
  9. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/README.md +0 -0
  10. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/__init__.py +0 -0
  11. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/const.py +0 -0
  12. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/exceptions.py +0 -0
  13. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/helpers.py +0 -0
  14. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/model.py +0 -0
  15. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako/py.typed +0 -0
  16. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/SOURCES.txt +0 -0
  17. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/dependency_links.txt +0 -0
  18. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/not-zip-safe +0 -0
  19. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/requires.txt +0 -0
  20. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/python_rako_2025.egg-info/top_level.txt +0 -0
  21. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/setup.cfg +0 -0
  22. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/tests/test_helpers.py +0 -0
  23. {python_rako_2025-0.1.0 → python_rako_2025-0.2.0}/tests/test_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-rako-2025
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Asynchronous Python client for Rako Controls Lighting
5
5
  Home-page: https://github.com/simonleigh/python-rako
6
6
  Author: Simon Leigh
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-rako-2025"
7
- version = "0.1.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"]
@@ -1,3 +1,3 @@
1
1
  """AUTOGENERATED ON RELEASE."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.2.0"
@@ -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, device_type: 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 != device_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 device_type == "Lights":
245
+ if room_type == "Lights":
226
246
  yield RoomLight(room_id, room_title)
227
- elif device_type == "Ventilation":
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 device_type == "Lights":
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 device_type == "Ventilation":
272
+ elif room_type == "Ventilation":
253
273
  yield ChannelVentilation(
254
274
  room_id,
255
275
  room_title,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-rako-2025
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Asynchronous Python client for Rako Controls Lighting
5
5
  Home-page: https://github.com/simonleigh/python-rako
6
6
  Author: Simon Leigh
@@ -15,7 +15,7 @@ with open("README.md") as readme_file:
15
15
 
16
16
  setup(
17
17
  name="python-rako-2025",
18
- version="0.1.0",
18
+ version="0.2.0",
19
19
  license="MIT",
20
20
  url="https://github.com/simonleigh/python-rako",
21
21
  author="Simon Leigh",
@@ -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
+ )