lifx-async 4.3.9__py3-none-any.whl → 4.4.0__py3-none-any.whl

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.
lifx/__init__.py CHANGED
@@ -21,12 +21,17 @@ from lifx.devices import (
21
21
  DeviceVersion,
22
22
  FirmwareInfo,
23
23
  HevLight,
24
+ HevLightState,
24
25
  InfraredLight,
26
+ InfraredLightState,
25
27
  Light,
28
+ LightState,
26
29
  MatrixEffect,
27
30
  MatrixLight,
31
+ MatrixLightState,
28
32
  MultiZoneEffect,
29
33
  MultiZoneLight,
34
+ MultiZoneLightState,
30
35
  TileInfo,
31
36
  WifiInfo,
32
37
  )
@@ -57,10 +62,15 @@ __all__ = [
57
62
  # Core classes
58
63
  "Device",
59
64
  "Light",
65
+ "LightState",
60
66
  "HevLight",
67
+ "HevLightState",
61
68
  "InfraredLight",
69
+ "InfraredLightState",
62
70
  "MultiZoneLight",
71
+ "MultiZoneLightState",
63
72
  "MatrixLight",
73
+ "MatrixLightState",
64
74
  # Color
65
75
  "HSBK",
66
76
  "Colors",
lifx/api.py CHANGED
@@ -10,7 +10,6 @@ This module provides simplified interfaces for common operations:
10
10
  from __future__ import annotations
11
11
 
12
12
  import asyncio
13
- import logging
14
13
  from collections import defaultdict
15
14
  from collections.abc import AsyncGenerator, Iterator, Sequence
16
15
  from dataclasses import dataclass
@@ -27,12 +26,11 @@ from lifx.const import (
27
26
  MAX_RESPONSE_TIME,
28
27
  )
29
28
  from lifx.devices import (
29
+ CollectionInfo,
30
30
  Device,
31
- GroupInfo,
32
31
  HevLight,
33
32
  InfraredLight,
34
33
  Light,
35
- LocationInfo,
36
34
  MatrixLight,
37
35
  MultiZoneLight,
38
36
  )
@@ -44,14 +42,12 @@ from lifx.network.discovery import (
44
42
  from lifx.protocol import packets
45
43
  from lifx.theme import Theme
46
44
 
47
- _LOGGER = logging.getLogger(__name__)
48
-
49
45
 
50
46
  @dataclass
51
47
  class LocationGrouping:
52
48
  """Organizational structure for location-based grouping."""
53
49
 
54
- uuid: bytes
50
+ uuid: str
55
51
  label: str
56
52
  devices: list[Device]
57
53
  updated_at: int # Most recent updated_at from all devices
@@ -65,7 +61,7 @@ class LocationGrouping:
65
61
  class GroupGrouping:
66
62
  """Organizational structure for group-based grouping."""
67
63
 
68
- uuid: bytes
64
+ uuid: str
69
65
  label: str
70
66
  devices: list[Device]
71
67
  updated_at: int
@@ -117,8 +113,8 @@ class DeviceGroup:
117
113
  self._matrix_lights = [light for light in devices if type(light) is MatrixLight]
118
114
  self._locations_cache: dict[str, DeviceGroup] | None = None
119
115
  self._groups_cache: dict[str, DeviceGroup] | None = None
120
- self._location_metadata: dict[bytes, LocationGrouping] | None = None
121
- self._group_metadata: dict[bytes, GroupGrouping] | None = None
116
+ self._location_metadata: dict[str, LocationGrouping] | None = None
117
+ self._group_metadata: dict[str, GroupGrouping] | None = None
122
118
 
123
119
  async def __aenter__(self) -> DeviceGroup:
124
120
  """Enter async context manager."""
@@ -278,17 +274,17 @@ class DeviceGroup:
278
274
  Skips devices with empty UUID (b'\\x00' * 16).
279
275
  Logs warnings for failed queries but continues gracefully.
280
276
  """
281
- location_data: dict[bytes, list[tuple[Device, LocationInfo]]] = defaultdict(
277
+ location_data: dict[str, list[tuple[Device, CollectionInfo]]] = defaultdict(
282
278
  list
283
279
  )
284
280
 
285
281
  # Fetch all location info concurrently
286
- tasks: dict[str, asyncio.Task[LocationInfo | None]] = {}
282
+ tasks: dict[str, asyncio.Task[CollectionInfo | None]] = {}
287
283
  async with asyncio.TaskGroup() as tg:
288
284
  for device in self._devices:
289
285
  tasks[device.serial] = tg.create_task(device.get_location())
290
286
 
291
- results: list[tuple[Device, LocationInfo | None]] = []
287
+ results: list[tuple[Device, CollectionInfo | None]] = []
292
288
  for device in self._devices:
293
289
  results.append((device, tasks[device.serial].result()))
294
290
 
@@ -298,10 +294,10 @@ class DeviceGroup:
298
294
  continue
299
295
 
300
296
  # Skip empty UUIDs (unassigned)
301
- if location_info.location == b"\x00" * 16:
297
+ if location_info.uuid == "0000000000000000":
302
298
  continue
303
299
 
304
- location_data[location_info.location].append((device, location_info))
300
+ location_data[location_info.uuid].append((device, location_info))
305
301
 
306
302
  # Build metadata dictionary with conflict resolution
307
303
  self._location_metadata = {}
@@ -333,15 +329,15 @@ class DeviceGroup:
333
329
  Logs warnings for failed queries but continues gracefully.
334
330
  """
335
331
  # Collect group info from all devices concurrently
336
- group_data: dict[bytes, list[tuple[Device, GroupInfo]]] = defaultdict(list)
332
+ group_data: dict[str, list[tuple[Device, CollectionInfo]]] = defaultdict(list)
337
333
 
338
- tasks: dict[str, asyncio.Task[GroupInfo | None]] = {}
334
+ tasks: dict[str, asyncio.Task[CollectionInfo | None]] = {}
339
335
  async with asyncio.TaskGroup() as tg:
340
336
  for device in self._devices:
341
337
  tasks[device.serial] = tg.create_task(device.get_group())
342
338
 
343
339
  # Fetch all group info concurrently
344
- results: list[tuple[Device, GroupInfo | None]] = []
340
+ results: list[tuple[Device, CollectionInfo | None]] = []
345
341
  for device in self._devices:
346
342
  results.append((device, tasks[device.serial].result()))
347
343
 
@@ -351,10 +347,10 @@ class DeviceGroup:
351
347
  continue
352
348
 
353
349
  # Skip empty UUIDs (unassigned)
354
- if group_info.group == b"\x00" * 16:
350
+ if group_info.uuid == "0000000000000000":
355
351
  continue
356
352
 
357
- group_data[group_info.group].append((device, group_info))
353
+ group_data[group_info.uuid].append((device, group_info))
358
354
 
359
355
  # Build metadata dictionary with conflict resolution
360
356
  self._group_metadata = {}
@@ -397,7 +393,7 @@ class DeviceGroup:
397
393
  )
398
394
 
399
395
  result: dict[str, DeviceGroup] = {}
400
- label_uuids: dict[str, bytes] = {}
396
+ label_uuids: dict[str, str] = {}
401
397
 
402
398
  for location_uuid, grouping in self._location_metadata.items():
403
399
  label = grouping.label
@@ -405,7 +401,7 @@ class DeviceGroup:
405
401
  # Handle naming conflicts: if two different UUIDs have the same label,
406
402
  # append UUID suffix
407
403
  if label in label_uuids and label_uuids[label] != location_uuid:
408
- label = f"{label} ({location_uuid.hex()[:8]})"
404
+ label = f"{label} ({location_uuid[:8]})"
409
405
 
410
406
  label_uuids[label] = location_uuid
411
407
  result[label] = DeviceGroup(grouping.devices)
@@ -436,7 +432,7 @@ class DeviceGroup:
436
432
  )
437
433
 
438
434
  result: dict[str, DeviceGroup] = {}
439
- label_uuids: dict[str, bytes] = {}
435
+ label_uuids: dict[str, str] = {}
440
436
 
441
437
  for group_uuid, grouping in self._group_metadata.items():
442
438
  label = grouping.label
@@ -444,7 +440,7 @@ class DeviceGroup:
444
440
  # Handle naming conflicts: if two different UUIDs have the same label,
445
441
  # append UUID suffix
446
442
  if label in label_uuids and label_uuids[label] != group_uuid:
447
- label = f"{label} ({group_uuid.hex()[:8]})"
443
+ label = f"{label} ({group_uuid[:8]})"
448
444
 
449
445
  label_uuids[label] = group_uuid
450
446
  result[label] = DeviceGroup(grouping.devices)
lifx/const.py CHANGED
@@ -32,7 +32,8 @@ MAX_RESPONSE_TIME: Final[float] = 1.0 # 1 second
32
32
  IDLE_TIMEOUT_MULTIPLIER: Final[float] = 4.0 # 4 seconds (1.0 x 4.0)
33
33
 
34
34
  # Default timeout for device requests in seconds
35
- DEFAULT_REQUEST_TIMEOUT: Final[float] = 8.0
35
+ DEFAULT_REQUEST_TIMEOUT: Final[float] = 16.0
36
+ STATE_REFRESH_DEBOUNCE_MS: Final[int] = 300
36
37
 
37
38
  # Default maximum number of retry attempts for failed requests
38
39
  DEFAULT_MAX_RETRIES: Final[int] = 8
lifx/devices/__init__.py CHANGED
@@ -3,34 +3,37 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from lifx.devices.base import (
6
+ CollectionInfo,
6
7
  Device,
7
8
  DeviceInfo,
8
9
  DeviceVersion,
9
10
  FirmwareInfo,
10
- GroupInfo,
11
- LocationInfo,
12
11
  WifiInfo,
13
12
  )
14
- from lifx.devices.hev import HevLight
15
- from lifx.devices.infrared import InfraredLight
16
- from lifx.devices.light import Light
17
- from lifx.devices.matrix import MatrixEffect, MatrixLight, TileInfo
18
- from lifx.devices.multizone import MultiZoneEffect, MultiZoneLight
13
+ from lifx.devices.hev import HevLight, HevLightState
14
+ from lifx.devices.infrared import InfraredLight, InfraredLightState
15
+ from lifx.devices.light import Light, LightState
16
+ from lifx.devices.matrix import MatrixEffect, MatrixLight, MatrixLightState, TileInfo
17
+ from lifx.devices.multizone import MultiZoneEffect, MultiZoneLight, MultiZoneLightState
19
18
 
20
19
  __all__ = [
20
+ "CollectionInfo",
21
21
  "Device",
22
22
  "DeviceInfo",
23
23
  "DeviceVersion",
24
24
  "FirmwareInfo",
25
- "GroupInfo",
26
25
  "HevLight",
26
+ "HevLightState",
27
27
  "InfraredLight",
28
+ "InfraredLightState",
28
29
  "Light",
29
- "LocationInfo",
30
+ "LightState",
30
31
  "MatrixEffect",
31
32
  "MatrixLight",
33
+ "MatrixLightState",
32
34
  "MultiZoneEffect",
33
35
  "MultiZoneLight",
36
+ "MultiZoneLightState",
34
37
  "TileInfo",
35
38
  "WifiInfo",
36
39
  ]