python-rako-2025 0.3.0__tar.gz → 0.3.8__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.3.0 → python_rako_2025-0.3.8}/PKG-INFO +1 -1
  2. python_rako_2025-0.3.8/python_rako/__version__.py +1 -0
  3. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako/bridge.py +52 -37
  4. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako_2025.egg-info/PKG-INFO +1 -1
  5. python_rako_2025-0.3.0/python_rako/__version__.py +0 -1
  6. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/LICENSE +0 -0
  7. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/README.md +0 -0
  8. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/pyproject.toml +0 -0
  9. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako/__init__.py +0 -0
  10. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako/const.py +0 -0
  11. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako/exceptions.py +0 -0
  12. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako/helpers.py +0 -0
  13. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako/model.py +0 -0
  14. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako/py.typed +0 -0
  15. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako_2025.egg-info/SOURCES.txt +0 -0
  16. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako_2025.egg-info/dependency_links.txt +0 -0
  17. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako_2025.egg-info/requires.txt +0 -0
  18. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/python_rako_2025.egg-info/top_level.txt +0 -0
  19. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/setup.cfg +0 -0
  20. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/setup.py +0 -0
  21. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/tests/test_bridge.py +0 -0
  22. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/tests/test_helpers.py +0 -0
  23. {python_rako_2025-0.3.0 → python_rako_2025-0.3.8}/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.3.0
3
+ Version: 0.3.8
4
4
  Summary: Asynchronous Python client for Rako Controls Lighting
5
5
  Author-email: Simon Leigh <simonleigh@users.noreply.github.com>
6
6
  Project-URL: Homepage, https://github.com/simonleigh/python-rako
@@ -0,0 +1 @@
1
+ 0.3.8
@@ -32,11 +32,9 @@ from python_rako.model import (
32
32
  CommandUDP,
33
33
  EOFResponse,
34
34
  LevelCache,
35
- Light,
36
35
  RoomLight,
37
36
  RoomVentilation,
38
37
  SceneCache,
39
- Ventilation,
40
38
  )
41
39
 
42
40
  _LOGGER = logging.getLogger(__name__)
@@ -150,64 +148,76 @@ class Bridge:
150
148
  )
151
149
  self.level_cache: LevelCache = LevelCache()
152
150
  self.scene_cache: SceneCache = SceneCache()
151
+ self._cached_xml: str | None = None
153
152
 
154
153
  @property
155
154
  def _discovery_url(self) -> str:
156
155
  return f"http://{self.host}/rako.xml"
157
156
 
158
- async def get_rako_xml(self, session: aiohttp.ClientSession) -> str:
159
- async with session.get(self._discovery_url) as response:
160
- rako_xml: str = await response.text()
161
- return rako_xml
157
+ async def get_rako_xml(
158
+ self, session: aiohttp.ClientSession, force_refresh: bool = False
159
+ ) -> str:
160
+ if self._cached_xml is None or force_refresh:
161
+ async with session.get(self._discovery_url) as response:
162
+ self._cached_xml = await response.text()
163
+ assert self._cached_xml is not None
164
+ return self._cached_xml
162
165
 
163
166
  async def discover_devices(
164
- self, session: aiohttp.ClientSession
165
- ) -> tuple[list[Light], list[Ventilation]]:
166
- """
167
- Discover all devices by fetching XML once and parsing all device types.
167
+ self, session: aiohttp.ClientSession, force_refresh: bool = False
168
+ ) -> tuple[
169
+ list[RoomLight | ChannelLight], list[RoomVentilation | ChannelVentilation]
170
+ ]:
171
+ """Discover all devices by fetching XML once and parsing all device types.
172
+
168
173
  Returns a tuple of (lights, ventilation) to avoid race conditions.
169
174
  """
170
- rako_xml = await self.get_rako_xml(session)
171
-
172
- lights = []
173
- ventilation = []
174
-
175
+ rako_xml = await self.get_rako_xml(session, force_refresh)
176
+
177
+ lights: list[RoomLight | ChannelLight] = []
178
+ ventilation: list[RoomVentilation | ChannelVentilation] = []
179
+
175
180
  for device in self.get_devices_from_discovery_xml(rako_xml):
176
181
  if isinstance(device, (RoomLight, ChannelLight)):
177
182
  lights.append(device)
178
183
  elif isinstance(device, (RoomVentilation, ChannelVentilation)):
179
184
  ventilation.append(device)
180
-
185
+
181
186
  return lights, ventilation
182
187
 
183
188
  async def discover_lights(
184
- self, session: aiohttp.ClientSession
185
- ) -> AsyncGenerator[Light]:
189
+ self, session: aiohttp.ClientSession, force_refresh: bool = False
190
+ ) -> AsyncGenerator[RoomLight | ChannelLight]:
186
191
  """Discover lights by fetching XML once and filtering for lights."""
187
- rako_xml = await self.get_rako_xml(session)
188
- for light in self.get_lights_from_discovery_xml(rako_xml):
192
+ lights, _ = await self.discover_devices(session, force_refresh)
193
+ for light in lights:
189
194
  yield light
190
195
 
191
196
  async def discover_ventilation(
192
- self, session: aiohttp.ClientSession
193
- ) -> AsyncGenerator[Ventilation]:
197
+ self, session: aiohttp.ClientSession, force_refresh: bool = False
198
+ ) -> AsyncGenerator[RoomVentilation | ChannelVentilation]:
194
199
  """Discover ventilation by fetching XML once and filtering for ventilation."""
195
- rako_xml = await self.get_rako_xml(session)
196
- for device in self.get_devices_from_discovery_xml(rako_xml, "Ventilation"):
197
- if isinstance(device, (RoomVentilation, ChannelVentilation)):
198
- yield device
200
+ _, ventilation = await self.discover_devices(session, force_refresh)
201
+ for vent in ventilation:
202
+ yield vent
199
203
 
200
204
  async def discover_all_devices(
201
- self, session: aiohttp.ClientSession
202
- ) -> AsyncGenerator[Light | Ventilation]:
205
+ self, session: aiohttp.ClientSession, force_refresh: bool = False
206
+ ) -> AsyncGenerator[
207
+ RoomLight | ChannelLight | RoomVentilation | ChannelVentilation
208
+ ]:
203
209
  """Discover all devices by fetching XML once."""
204
- rako_xml = await self.get_rako_xml(session)
205
- for device in self.get_devices_from_discovery_xml(rako_xml):
206
- yield device
210
+ lights, ventilation = await self.discover_devices(session, force_refresh)
211
+ for light in lights:
212
+ yield light
213
+ for vent in ventilation:
214
+ yield vent
207
215
 
208
- async def get_info(self, session: aiohttp.ClientSession) -> BridgeInfo:
216
+ async def get_info(
217
+ self, session: aiohttp.ClientSession, force_refresh: bool = False
218
+ ) -> BridgeInfo:
209
219
  try:
210
- rako_xml = await self.get_rako_xml(session)
220
+ rako_xml = await self.get_rako_xml(session, force_refresh)
211
221
  info = self.get_bridge_info_from_discovery_xml(rako_xml)
212
222
  except (KeyError, ValueError) as ex:
213
223
  raise RakoBridgeError(f"unsupported bridge: {ex}")
@@ -234,20 +244,24 @@ class Bridge:
234
244
  )
235
245
 
236
246
  @staticmethod
237
- def get_lights_from_discovery_xml(xml: str) -> Generator[Light]:
247
+ def get_lights_from_discovery_xml(
248
+ xml: str,
249
+ ) -> Generator[RoomLight | ChannelLight]:
238
250
  for device in Bridge.get_devices_from_discovery_xml(xml, "Lights"):
239
251
  if isinstance(device, (RoomLight, ChannelLight)):
240
252
  yield device
241
253
 
242
254
  @staticmethod
243
- def get_all_devices_from_discovery_xml(xml: str) -> Generator[Light | Ventilation]:
255
+ def get_all_devices_from_discovery_xml(
256
+ xml: str,
257
+ ) -> Generator[RoomLight | ChannelLight | RoomVentilation | ChannelVentilation]:
244
258
  """Get all devices (lights and ventilation) from discovery XML."""
245
259
  return Bridge.get_devices_from_discovery_xml(xml)
246
260
 
247
261
  @staticmethod
248
262
  def get_devices_from_discovery_xml(
249
263
  xml: str, device_types: str | list[str] | None = None
250
- ) -> Generator[Light | Ventilation]:
264
+ ) -> Generator[RoomLight | ChannelLight | RoomVentilation | ChannelVentilation]:
251
265
  # Handle different input types for backward compatibility
252
266
  if device_types is None or device_types == "All":
253
267
  target_types = {"Lights", "Ventilation"}
@@ -307,7 +321,8 @@ class Bridge:
307
321
  if not resp:
308
322
  return None
309
323
 
310
- data, (remote_ip, _) = resp
324
+ data, addr = resp
325
+ remote_ip, _ = addr
311
326
  if remote_ip != self.host:
312
327
  return None
313
328
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-rako-2025
3
- Version: 0.3.0
3
+ Version: 0.3.8
4
4
  Summary: Asynchronous Python client for Rako Controls Lighting
5
5
  Author-email: Simon Leigh <simonleigh@users.noreply.github.com>
6
6
  Project-URL: Homepage, https://github.com/simonleigh/python-rako
@@ -1 +0,0 @@
1
- 0.3.0