lifx-emulator-core 3.1.0__py3-none-any.whl → 3.2.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.
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import copy
7
7
  import logging
8
+ import random
8
9
  import time
9
10
  from typing import Any
10
11
 
@@ -304,6 +305,24 @@ class EmulatedLifxDevice:
304
305
 
305
306
  # Handle specific packet types - handlers always return list
306
307
  response_packets = self._handle_packet_type(header, packet)
308
+
309
+ # Apply partial_responses: truncate multi-packet responses to random subset
310
+ if len(response_packets) > 1:
311
+ first_pkt = response_packets[0]
312
+ if (
313
+ hasattr(first_pkt, "PKT_TYPE")
314
+ and first_pkt.PKT_TYPE in scenario.partial_responses
315
+ ):
316
+ original_count = len(response_packets)
317
+ partial_count = random.randint(1, original_count - 1) # nosec
318
+ response_packets = response_packets[:partial_count]
319
+ logger.info(
320
+ "Sending partial response for packet type %s (%d of %d packets)",
321
+ first_pkt.PKT_TYPE,
322
+ partial_count,
323
+ original_count,
324
+ )
325
+
307
326
  # Handlers now always return list (empty if no response)
308
327
  for resp_packet in response_packets:
309
328
  # Cache packed payload to avoid double packing (performance optimization)
@@ -124,22 +124,26 @@ class ExtendedGetColorZonesHandler(PacketHandler):
124
124
  if not device_state.has_multizone:
125
125
  return []
126
126
 
127
- colors_count = min(82, len(device_state.zone_colors))
128
- colors = []
129
- for i in range(colors_count):
130
- colors.append(device_state.zone_colors[i])
131
- # Pad to 82 colors
132
- while len(colors) < 82:
133
- colors.append(LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500))
134
-
135
- return [
136
- MultiZone.ExtendedStateMultiZone(
137
- count=device_state.zone_count,
138
- index=0,
139
- colors_count=colors_count,
140
- colors=colors,
127
+ responses = []
128
+ index = 0
129
+ while index < device_state.zone_count:
130
+ end = min(index + 82, device_state.zone_count)
131
+ colors_count = end - index
132
+ colors = list(device_state.zone_colors[index:end])
133
+ # Pad to 82 colors
134
+ while len(colors) < 82:
135
+ colors.append(LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500))
136
+ responses.append(
137
+ MultiZone.ExtendedStateMultiZone(
138
+ count=device_state.zone_count,
139
+ index=index,
140
+ colors_count=colors_count,
141
+ colors=colors,
142
+ )
141
143
  )
142
- ]
144
+ index += 82
145
+
146
+ return responses
143
147
 
144
148
 
145
149
  class ExtendedSetColorZonesHandler(PacketHandler):
@@ -128,64 +128,71 @@ class Get64Handler(PacketHandler):
128
128
  if not device_state.has_matrix or not packet:
129
129
  return []
130
130
 
131
- tile_index = packet.tile_index
132
131
  rect = packet.rect
132
+ length = max(1, packet.length)
133
133
 
134
- if tile_index >= len(device_state.tile_devices):
135
- return []
136
-
137
- tile = device_state.tile_devices[tile_index]
138
- tile_width = tile["width"]
139
- tile_height = tile["height"]
140
-
141
- # Get64 always returns framebuffer 0 (the visible buffer)
142
- # regardless of which fb_index is in the request
143
- tile_colors = tile["colors"]
144
-
145
- # Calculate how many rows fit in 64 zones
146
- rows_to_return = 64 // rect.width if rect.width > 0 else 1
147
- rows_to_return = min(rows_to_return, tile_height - rect.y)
148
-
149
- # Extract colors from the requested rectangle
150
- colors = []
151
- zones_extracted = 0
152
-
153
- for row in range(rows_to_return):
154
- y = rect.y + row
155
- if y >= tile_height:
134
+ responses = []
135
+ for i in range(length):
136
+ idx = packet.tile_index + i
137
+ if idx >= len(device_state.tile_devices):
156
138
  break
157
139
 
158
- for col in range(rect.width):
159
- x = rect.x + col
160
- if x >= tile_width or zones_extracted >= 64:
161
- colors.append(
162
- LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500)
163
- )
140
+ tile = device_state.tile_devices[idx]
141
+ tile_width = tile["width"]
142
+ tile_height = tile["height"]
143
+
144
+ # Get64 always returns framebuffer 0 (the visible buffer)
145
+ # regardless of which fb_index is in the request
146
+ tile_colors = tile["colors"]
147
+
148
+ # Calculate how many rows fit in 64 zones
149
+ rows_to_return = 64 // rect.width if rect.width > 0 else 1
150
+ rows_to_return = min(rows_to_return, tile_height - rect.y)
151
+
152
+ # Extract colors from the requested rectangle
153
+ colors = []
154
+ zones_extracted = 0
155
+
156
+ for row in range(rows_to_return):
157
+ y = rect.y + row
158
+ if y >= tile_height:
159
+ break
160
+
161
+ for col in range(rect.width):
162
+ x = rect.x + col
163
+ if x >= tile_width or zones_extracted >= 64:
164
+ colors.append(
165
+ LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500)
166
+ )
167
+ zones_extracted += 1
168
+ continue
169
+
170
+ # Calculate zone index in flat color array
171
+ zone_idx = y * tile_width + x
172
+ if zone_idx < len(tile_colors):
173
+ colors.append(tile_colors[zone_idx])
174
+ else:
175
+ colors.append(
176
+ LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500)
177
+ )
164
178
  zones_extracted += 1
165
- continue
166
179
 
167
- # Calculate zone index in flat color array
168
- zone_idx = y * tile_width + x
169
- if zone_idx < len(tile_colors):
170
- colors.append(tile_colors[zone_idx])
171
- else:
172
- colors.append(
173
- LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500)
174
- )
175
- zones_extracted += 1
176
-
177
- # Pad to exactly 64 colors
178
- while len(colors) < 64:
179
- colors.append(LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500))
180
-
181
- # Return with fb_index forced to 0 (visible buffer)
182
- return_rect = TileBufferRect(
183
- fb_index=0, # Always return FB0
184
- x=rect.x,
185
- y=rect.y,
186
- width=rect.width,
187
- )
188
- return [Tile.State64(tile_index=tile_index, rect=return_rect, colors=colors)]
180
+ # Pad to exactly 64 colors
181
+ while len(colors) < 64:
182
+ colors.append(LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500))
183
+
184
+ # Return with fb_index forced to 0 (visible buffer)
185
+ return_rect = TileBufferRect(
186
+ fb_index=0, # Always return FB0
187
+ x=rect.x,
188
+ y=rect.y,
189
+ width=rect.width,
190
+ )
191
+ responses.append(
192
+ Tile.State64(tile_index=idx, rect=return_rect, colors=colors)
193
+ )
194
+
195
+ return responses
189
196
 
190
197
 
191
198
  class Set64Handler(PacketHandler):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lifx-emulator-core
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Core LIFX Emulator library for testing LIFX LAN protocol libraries
5
5
  Author-email: Avi Miller <me@dje.li>
6
6
  Maintainer-email: Avi Miller <me@dje.li>
@@ -2,7 +2,7 @@ lifx_emulator/__init__.py,sha256=SSnQg0RiCaID7DhHOGZoVIDQ3_8Lyt0J9cA_2StF63s,824
2
2
  lifx_emulator/constants.py,sha256=DFZkUsdewE-x_3MgO28tMGkjUCWPeYc3xLj_EXViGOw,1032
3
3
  lifx_emulator/server.py,sha256=Fc0AiSeJ43XnF1MMTqWBkeYIW-KOhZpqNp0Y2x4RqEE,16622
4
4
  lifx_emulator/devices/__init__.py,sha256=QlBTPnFErJcSKLvGyeDwemh7xcpjYvB_L5siKsjr3s8,1089
5
- lifx_emulator/devices/device.py,sha256=yEOXc_xr1X45bJzG2qB-A-oIHwnA8qqYlIsFialobGc,15780
5
+ lifx_emulator/devices/device.py,sha256=99uxNdK3vFnBHh9M2F92zxNeFkq3w-XXV0Mt8wRTUQw,16556
6
6
  lifx_emulator/devices/manager.py,sha256=XDrT82um5sgNpNihLj5RsNvHqdVI1bK9YY2eBzWIcf0,8162
7
7
  lifx_emulator/devices/observers.py,sha256=-KnUgFcKdhlNo7CNVstP-u0wU2W0JAGg055ZPV15Sj0,3874
8
8
  lifx_emulator/devices/persistence.py,sha256=9Mhj46-xrweOmyzjORCi2jKIwa8XJWpQ5CgaKcw6U98,10513
@@ -19,9 +19,9 @@ lifx_emulator/handlers/__init__.py,sha256=3Hj1hRo3yL3E7GKwG9TaYh33ymk_N3bRiQ8nvq
19
19
  lifx_emulator/handlers/base.py,sha256=0avCLXY_rNlw16PpJ5JrRCwXNE4uMpBqF3PfSfNJ0b8,1654
20
20
  lifx_emulator/handlers/device_handlers.py,sha256=1AmslA4Ut6L7b3SfduDdvnQizTpzUB3KKWBXmp4WYLQ,9462
21
21
  lifx_emulator/handlers/light_handlers.py,sha256=255aoiIjSIL63kbHQa6wqUpEwFzFFx7SG6P1nWM9jgU,17769
22
- lifx_emulator/handlers/multizone_handlers.py,sha256=2dYsitq0KzEaxEAJmz7ixtir1tvFMOAnfkBQqslqbPM,7914
22
+ lifx_emulator/handlers/multizone_handlers.py,sha256=ypv9G7od2bdQc7plRb38hyPyd02ugiMsinUHKCB2cdM,8094
23
23
  lifx_emulator/handlers/registry.py,sha256=s1ht4PmPhXhAcwu1hoY4yW39wy3SPJBMY-9Uxd0FWuE,3292
24
- lifx_emulator/handlers/tile_handlers.py,sha256=L4fNKGTSSIxRuqKrfDrMSrNPvDJr3aIuaEqbhRCOt04,17176
24
+ lifx_emulator/handlers/tile_handlers.py,sha256=TRrXfq1L-1WR35dhYMv3_GE-8pDX9yrYDHZUGbBNlSE,17495
25
25
  lifx_emulator/products/__init__.py,sha256=qcNop_kRYFF3zSjNemzQEgu3jPrIxfyQyLv9GsnaLEI,627
26
26
  lifx_emulator/products/generator.py,sha256=fvrhw_b7shLCtEtUFxWF5VBEQAeSrsaiXxoGIP5Vn4g,34675
27
27
  lifx_emulator/products/registry.py,sha256=1SZ3fXVFFL8jhKYIZBqwtIQDN3qL1Lvf86P3N1_Kdx8,47323
@@ -42,6 +42,6 @@ lifx_emulator/scenarios/__init__.py,sha256=CGjudoWvyysvFj2xej11N2cr3mYROGtRb9zVH
42
42
  lifx_emulator/scenarios/manager.py,sha256=1esxRdz74UynNk1wb86MGZ2ZFAuMzByuu74nRe3D-Og,11163
43
43
  lifx_emulator/scenarios/models.py,sha256=BKS_fGvrbkGe-vK3arZ0w2f9adS1UZhiOoKpu7GENnc,4099
44
44
  lifx_emulator/scenarios/persistence.py,sha256=3vjtPNFYfag38tUxuqxkGpWhQ7uBitc1rLroSAuw9N8,8881
45
- lifx_emulator_core-3.1.0.dist-info/METADATA,sha256=bWWlrkVVkfijJUeTVb615O6Xly_Gx8I5xbsapx-TCQg,3217
46
- lifx_emulator_core-3.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
47
- lifx_emulator_core-3.1.0.dist-info/RECORD,,
45
+ lifx_emulator_core-3.2.0.dist-info/METADATA,sha256=qxUBmkizy2Eu0QNVQ8y6FNppwJaqbR5pEIDnZzTasu8,3217
46
+ lifx_emulator_core-3.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
47
+ lifx_emulator_core-3.2.0.dist-info/RECORD,,