lifx-emulator 2.2.0__tar.gz → 2.2.1__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 (136) hide show
  1. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/PKG-INFO +1 -1
  2. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/changelog.md +8 -0
  3. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/pyproject.toml +1 -1
  4. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/handlers/tile_handlers.py +17 -4
  5. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_tile_handlers_extended.py +68 -0
  6. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/uv.lock +1 -1
  7. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/.github/workflows/ci.yml +0 -0
  8. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/.github/workflows/docs.yml +0 -0
  9. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/.gitignore +0 -0
  10. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/.pre-commit-config.yaml +0 -0
  11. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/CLAUDE.md +0 -0
  12. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/LICENSE +0 -0
  13. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/README.md +0 -0
  14. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/advanced/device-management-api.md +0 -0
  15. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/advanced/index.md +0 -0
  16. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/advanced/scenario-api.md +0 -0
  17. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/advanced/scenarios.md +0 -0
  18. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/advanced/storage.md +0 -0
  19. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/api/device.md +0 -0
  20. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/api/factories.md +0 -0
  21. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/api/index.md +0 -0
  22. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/api/products.md +0 -0
  23. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/api/protocol.md +0 -0
  24. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/api/server.md +0 -0
  25. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/api/storage.md +0 -0
  26. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/architecture/device-state.md +0 -0
  27. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/architecture/index.md +0 -0
  28. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/architecture/overview.md +0 -0
  29. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/architecture/packet-flow.md +0 -0
  30. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/architecture/protocol.md +0 -0
  31. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/assets/favicon.png +0 -0
  32. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/faq.md +0 -0
  33. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/getting-started/cli.md +0 -0
  34. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/getting-started/index.md +0 -0
  35. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/getting-started/installation.md +0 -0
  36. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/getting-started/quickstart.md +0 -0
  37. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/guide/best-practices.md +0 -0
  38. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/guide/device-types.md +0 -0
  39. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/guide/index.md +0 -0
  40. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/guide/integration-testing.md +0 -0
  41. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/guide/products-and-specs.md +0 -0
  42. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/guide/testing-scenarios.md +0 -0
  43. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/guide/web-interface.md +0 -0
  44. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/index.md +0 -0
  45. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/reference/glossary.md +0 -0
  46. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/reference/troubleshooting.md +0 -0
  47. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/stylesheets/extra.css +0 -0
  48. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/tutorials/01-first-device.md +0 -0
  49. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/tutorials/02-basic.md +0 -0
  50. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/tutorials/03-integration.md +0 -0
  51. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/tutorials/04-advanced-scenarios.md +0 -0
  52. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/tutorials/05-cicd.md +0 -0
  53. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/docs/tutorials/index.md +0 -0
  54. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/mkdocs.yml +0 -0
  55. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/renovate.json +0 -0
  56. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/__init__.py +0 -0
  57. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/__main__.py +0 -0
  58. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/__init__.py +0 -0
  59. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/app.py +0 -0
  60. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/mappers/__init__.py +0 -0
  61. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/mappers/device_mapper.py +0 -0
  62. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/models.py +0 -0
  63. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/routers/__init__.py +0 -0
  64. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/routers/devices.py +0 -0
  65. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/routers/monitoring.py +0 -0
  66. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/routers/scenarios.py +0 -0
  67. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/services/__init__.py +0 -0
  68. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/services/device_service.py +0 -0
  69. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/api/templates/dashboard.html +0 -0
  70. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/constants.py +0 -0
  71. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/devices/__init__.py +0 -0
  72. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/devices/device.py +0 -0
  73. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/devices/manager.py +0 -0
  74. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/devices/observers.py +0 -0
  75. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/devices/persistence.py +0 -0
  76. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/devices/state_restorer.py +0 -0
  77. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/devices/state_serializer.py +0 -0
  78. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/devices/states.py +0 -0
  79. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/factories/__init__.py +0 -0
  80. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/factories/builder.py +0 -0
  81. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/factories/default_config.py +0 -0
  82. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/factories/factory.py +0 -0
  83. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/factories/firmware_config.py +0 -0
  84. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/factories/serial_generator.py +0 -0
  85. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/handlers/__init__.py +0 -0
  86. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/handlers/base.py +0 -0
  87. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/handlers/device_handlers.py +0 -0
  88. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/handlers/light_handlers.py +0 -0
  89. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/handlers/multizone_handlers.py +0 -0
  90. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/handlers/registry.py +0 -0
  91. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/products/__init__.py +0 -0
  92. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/products/generator.py +0 -0
  93. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/products/registry.py +0 -0
  94. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/products/specs.py +0 -0
  95. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/products/specs.yml +0 -0
  96. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/protocol/__init__.py +0 -0
  97. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/protocol/base.py +0 -0
  98. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/protocol/const.py +0 -0
  99. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/protocol/generator.py +0 -0
  100. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/protocol/header.py +0 -0
  101. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/protocol/packets.py +0 -0
  102. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/protocol/protocol_types.py +0 -0
  103. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/protocol/serializer.py +0 -0
  104. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/repositories/__init__.py +0 -0
  105. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/repositories/device_repository.py +0 -0
  106. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/repositories/storage_backend.py +0 -0
  107. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/scenarios/__init__.py +0 -0
  108. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/scenarios/manager.py +0 -0
  109. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/scenarios/models.py +0 -0
  110. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/scenarios/persistence.py +0 -0
  111. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/src/lifx_emulator/server.py +0 -0
  112. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/conftest.py +0 -0
  113. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_api.py +0 -0
  114. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_api_validation.py +0 -0
  115. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_async_storage.py +0 -0
  116. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_cli.py +0 -0
  117. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_cli_validation.py +0 -0
  118. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_device.py +0 -0
  119. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_device_edge_cases.py +0 -0
  120. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_device_handlers_extended.py +0 -0
  121. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_device_manager.py +0 -0
  122. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_handler_registry.py +0 -0
  123. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_integration.py +0 -0
  124. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_light_handlers_extended.py +0 -0
  125. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_multizone_handlers_extended.py +0 -0
  126. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_observers.py +0 -0
  127. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_products_generator.py +0 -0
  128. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_products_specs.py +0 -0
  129. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_protocol_generator.py +0 -0
  130. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_protocol_types_coverage.py +0 -0
  131. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_repositories.py +0 -0
  132. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_scenario_manager.py +0 -0
  133. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_scenario_persistence.py +0 -0
  134. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_serializer.py +0 -0
  135. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_server.py +0 -0
  136. {lifx_emulator-2.2.0 → lifx_emulator-2.2.1}/tests/test_state_restorer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lifx-emulator
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: LIFX Emulator 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,6 +2,14 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v2.2.1 (2025-11-18)
6
+
7
+ ### Bug Fixes
8
+
9
+ - **tile**: Preserve SUNRISE sky type in GetEffect responses
10
+ ([`70b2d48`](https://github.com/Djelibeybi/lifx-emulator/commit/70b2d48c1ee037591688abb345020e0cd515d2f1))
11
+
12
+
5
13
  ## v2.2.0 (2025-11-18)
6
14
 
7
15
  ### Features
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lifx-emulator"
3
- version = "2.2.0"
3
+ version = "2.2.1"
4
4
  description = "LIFX Emulator for testing LIFX LAN protocol libraries"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -240,12 +240,25 @@ class GetEffectHandler(PacketHandler):
240
240
  # Create effect settings with Sky parameters
241
241
  from lifx_emulator.protocol.protocol_types import TileEffectSkyType
242
242
 
243
- # Use defaults for SKY effect (type=5), otherwise use stored values
243
+ # Use defaults for SKY effect when values are None, otherwise use stored values
244
+ # NOTE: Must check for None explicitly, not use 'or', because SUNRISE=0 is falsy
244
245
  effect_type = TileEffectType(device_state.tile_effect_type)
245
246
  if effect_type == TileEffectType.SKY:
246
- sky_type = device_state.tile_effect_sky_type or TileEffectSkyType.CLOUDS
247
- cloud_sat_min = device_state.tile_effect_cloud_sat_min or 50
248
- cloud_sat_max = device_state.tile_effect_cloud_sat_max or 180
247
+ sky_type = (
248
+ device_state.tile_effect_sky_type
249
+ if device_state.tile_effect_sky_type is not None
250
+ else TileEffectSkyType.CLOUDS
251
+ )
252
+ cloud_sat_min = (
253
+ device_state.tile_effect_cloud_sat_min
254
+ if device_state.tile_effect_cloud_sat_min is not None
255
+ else 50
256
+ )
257
+ cloud_sat_max = (
258
+ device_state.tile_effect_cloud_sat_max
259
+ if device_state.tile_effect_cloud_sat_max is not None
260
+ else 180
261
+ )
249
262
  else:
250
263
  sky_type = device_state.tile_effect_sky_type
251
264
  cloud_sat_min = device_state.tile_effect_cloud_sat_min
@@ -645,6 +645,74 @@ class TestTileEffects:
645
645
  # Palette should be stored with only the specified count
646
646
  assert device.state.tile_effect_palette_count == 10
647
647
 
648
+ def test_sky_effect_sunrise_preserved(self):
649
+ """Test that SKY effect with SUNRISE (value=0) is properly preserved.
650
+
651
+ Regression test: SUNRISE has value 0, which is falsy. The code must
652
+ check for None explicitly, not use 'or' operator which treats 0 as falsy.
653
+ """
654
+ # Create a LIFX Ceiling device (supports SKY effect with firmware 4.4+)
655
+ device = create_device(176, tile_count=1, firmware_version=(4, 4))
656
+
657
+ palette = [
658
+ LightHsbk(hue=0, saturation=0, brightness=0, kelvin=3500) for _ in range(16)
659
+ ]
660
+ parameter = TileEffectParameter(
661
+ sky_type=TileEffectSkyType.SUNRISE, # Value = 0 (falsy!)
662
+ cloud_saturation_min=100,
663
+ cloud_saturation_max=200,
664
+ )
665
+
666
+ settings = TileEffectSettings(
667
+ instanceid=1,
668
+ type=TileEffectType.SKY,
669
+ speed=5000,
670
+ duration=0,
671
+ parameter=parameter,
672
+ palette_count=1,
673
+ palette=palette,
674
+ )
675
+
676
+ # Set the effect
677
+ set_packet = Tile.SetEffect(settings=settings)
678
+ set_header = LifxHeader(
679
+ source=12345,
680
+ target=device.state.get_target_bytes(),
681
+ sequence=1,
682
+ pkt_type=719,
683
+ res_required=False,
684
+ )
685
+ device.process_packet(set_header, set_packet)
686
+
687
+ # Verify state was stored correctly
688
+ assert device.state.tile_effect_type == int(TileEffectType.SKY)
689
+ assert device.state.tile_effect_sky_type == int(TileEffectSkyType.SUNRISE)
690
+ assert device.state.tile_effect_cloud_sat_min == 100
691
+ assert device.state.tile_effect_cloud_sat_max == 200
692
+
693
+ # Get the effect - should return SUNRISE, not default to CLOUDS
694
+ get_packet = Tile.GetEffect()
695
+ get_header = LifxHeader(
696
+ source=12345,
697
+ target=device.state.get_target_bytes(),
698
+ sequence=2,
699
+ pkt_type=718,
700
+ res_required=True,
701
+ )
702
+ responses = device.process_packet(get_header, get_packet)
703
+
704
+ # Should return StateEffect
705
+ state_effect_responses = [r for r in responses if r[0].pkt_type == 720]
706
+ assert len(state_effect_responses) == 1
707
+
708
+ resp_header, resp_packet = state_effect_responses[0]
709
+ assert resp_packet.settings.type == TileEffectType.SKY
710
+ assert (
711
+ resp_packet.settings.parameter.sky_type == TileEffectSkyType.SUNRISE
712
+ ) # Must be SUNRISE, not CLOUDS!
713
+ assert resp_packet.settings.parameter.cloud_saturation_min == 100
714
+ assert resp_packet.settings.parameter.cloud_saturation_max == 200
715
+
648
716
 
649
717
  class TestSkyEffectRestrictions:
650
718
  """Test that SKY effect is only supported on Ceiling devices with firmware 4.4+."""
@@ -454,7 +454,7 @@ wheels = [
454
454
 
455
455
  [[package]]
456
456
  name = "lifx-emulator"
457
- version = "2.2.0"
457
+ version = "2.2.1"
458
458
  source = { editable = "." }
459
459
  dependencies = [
460
460
  { name = "cyclopts" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes