python-aidot-cameras 0.11.13__tar.gz → 0.11.14__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 (138) hide show
  1. {python_aidot_cameras-0.11.13/python_aidot_cameras.egg-info → python_aidot_cameras-0.11.14}/PKG-INFO +1 -1
  2. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/device_client.py +15 -0
  3. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/pyproject.toml +1 -1
  4. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
  5. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/python_aidot_cameras.egg-info/SOURCES.txt +1 -0
  6. python_aidot_cameras-0.11.14/tests/test_light_active_color_mode.py +58 -0
  7. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/LICENSE +0 -0
  8. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/MANIFEST.in +0 -0
  9. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/README.md +0 -0
  10. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/__init__.py +0 -0
  11. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/__main__.py +0 -0
  12. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/__init__.py +0 -0
  13. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/__init__.py +0 -0
  14. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/clock.py +0 -0
  15. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/codecs/__init__.py +0 -0
  16. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/codecs/base.py +0 -0
  17. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/codecs/g711.py +0 -0
  18. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/codecs/g722.py +0 -0
  19. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/codecs/h264.py +0 -0
  20. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/codecs/opus.py +0 -0
  21. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/codecs/vpx.py +0 -0
  22. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/contrib/__init__.py +0 -0
  23. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/contrib/media.py +0 -0
  24. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/contrib/signaling.py +0 -0
  25. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/events.py +0 -0
  26. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/exceptions.py +0 -0
  27. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/jitterbuffer.py +0 -0
  28. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/mediastreams.py +0 -0
  29. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rate.py +0 -0
  30. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcconfiguration.py +0 -0
  31. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcdatachannel.py +0 -0
  32. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcdtlstransport.py +0 -0
  33. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcicetransport.py +0 -0
  34. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcpeerconnection.py +0 -0
  35. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcrtpparameters.py +0 -0
  36. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcrtpreceiver.py +0 -0
  37. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcrtpsender.py +0 -0
  38. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcrtptransceiver.py +0 -0
  39. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcsctptransport.py +0 -0
  40. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtcsessiondescription.py +0 -0
  41. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/rtp.py +0 -0
  42. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/sdp.py +0 -0
  43. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/stats.py +0 -0
  44. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/_vendor/aiortc/utils.py +0 -0
  45. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/aes_utils.py +0 -0
  46. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/__init__.py +0 -0
  47. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/client.py +0 -0
  48. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/constants.py +0 -0
  49. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/controls.py +0 -0
  50. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/go2rtc.py +0 -0
  51. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/lan_control.py +0 -0
  52. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/models.py +0 -0
  53. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/playback.py +0 -0
  54. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/protocol.py +0 -0
  55. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/sdes.py +0 -0
  56. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/sdes_open.py +0 -0
  57. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/tutk.py +0 -0
  58. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/webrtc.py +0 -0
  59. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/camera/webrtc_open.py +0 -0
  60. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/client.py +0 -0
  61. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/const.py +0 -0
  62. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/credentials.py +0 -0
  63. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/discover.py +0 -0
  64. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/exceptions.py +0 -0
  65. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/g711.py +0 -0
  66. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/login_const.py +0 -0
  67. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/models/__init__.py +0 -0
  68. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/models/device_client_model.py +0 -0
  69. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/models/device_model.py +0 -0
  70. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/models/discover_model.py +0 -0
  71. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/aidot/py.typed +0 -0
  72. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  73. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
  74. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/python_aidot_cameras.egg-info/requires.txt +0 -0
  75. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/python_aidot_cameras.egg-info/top_level.txt +0 -0
  76. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/setup.cfg +0 -0
  77. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_aioice_compat.py +0 -0
  78. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_alarm_event.py +0 -0
  79. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_backoff.py +0 -0
  80. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_deferred_hardening.py +0 -0
  81. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_device_login_guard.py +0 -0
  82. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_device_user_info_cache.py +0 -0
  83. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_dtls_not_ready_burst.py +0 -0
  84. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_dtls_pinning.py +0 -0
  85. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_dtls_serve_decode_skip.py +0 -0
  86. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_dtls_serve_open_timeout.py +0 -0
  87. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_dtls_skip_signaling_wait.py +0 -0
  88. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_dtls_slow_probe.py +0 -0
  89. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_egress_guard.py +0 -0
  90. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_go2rtc.py +0 -0
  91. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_go2rtc_cli.py +0 -0
  92. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_h264_decode_rate_limit.py +0 -0
  93. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_highport_nomination.py +0 -0
  94. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_ice_config_cache.py +0 -0
  95. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_keepalive_renew.py +0 -0
  96. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_keyframe_prompter.py +0 -0
  97. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_lan_control.py +0 -0
  98. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_live_stream_param.py +0 -0
  99. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_logging_caps.py +0 -0
  100. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_motion_poll.py +0 -0
  101. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_mqtt_session_reconnect.py +0 -0
  102. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_narrow_pc_ice.py +0 -0
  103. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_no_undefined_names.py +0 -0
  104. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_offline_keepalive_pause.py +0 -0
  105. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_open_fail_logger.py +0 -0
  106. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_open_gate_delay.py +0 -0
  107. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_persistent_mqtt.py +0 -0
  108. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_persistent_mqtt_robustness.py +0 -0
  109. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_playback_tls.py +0 -0
  110. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_post_merge_hardening.py +0 -0
  111. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_retry_policy.py +0 -0
  112. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_adaptive.py +0 -0
  113. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_bridge_break_gate.py +0 -0
  114. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_echo_wait_timeout.py +0 -0
  115. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_fast_liveplay.py +0 -0
  116. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
  117. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_idle_release.py +0 -0
  118. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_open_cleanup.py +0 -0
  119. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_serve_audio.py +0 -0
  120. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_serve_cmd.py +0 -0
  121. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_sprop.py +0 -0
  122. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_talk.py +0 -0
  123. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_teardown_exit_log.py +0 -0
  124. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_sdes_watchdog.py +0 -0
  125. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_security_hardening.py +0 -0
  126. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_serializable_login_info.py +0 -0
  127. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_serve_relay.py +0 -0
  128. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_session_stats.py +0 -0
  129. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_speak.py +0 -0
  130. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_stream_cap.py +0 -0
  131. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_stream_drain_ownership.py +0 -0
  132. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_stream_idle.py +0 -0
  133. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_stream_teardown.py +0 -0
  134. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_talk.py +0 -0
  135. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_terminal_ack.py +0 -0
  136. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_token_file_robustness.py +0 -0
  137. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_token_refresh.py +0 -0
  138. {python_aidot_cameras-0.11.13 → python_aidot_cameras-0.11.14}/tests/test_wait_or_event.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.11.13
3
+ Version: 0.11.14
4
4
  Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
5
5
  Author-email: cbrightly <chris.brightly@gmail.com>
6
6
  License-Expression: MIT
@@ -56,6 +56,7 @@ class DeviceStatusData:
56
56
  rgbw: tuple[int, int, int, int] = (255, 0, 0, 0)
57
57
  cct: int = 2700
58
58
  dimming: int = 100
59
+ active_color_mode: "str | None" = None
59
60
 
60
61
  def update(self, attr: DeviceAttr) -> None:
61
62
  """Update status from DeviceAttr model."""
@@ -82,6 +83,20 @@ class DeviceStatusData:
82
83
  if attr.CCT is not None:
83
84
  self.cct = attr.CCT
84
85
 
86
+ # Track which color mode is actually active. Bulbs report state as
87
+ # deltas: a CCT-mode push carries only CCT, an RGB-mode push carries
88
+ # only RGBW. The getDevAttr login-sync instead returns BOTH the
89
+ # retained RGBW register and CCT together, which is ambiguous about
90
+ # which mode is active; that case (and RGBW==0, which is just the
91
+ # register's power-on default rather than a real color pick) must
92
+ # update the values above without changing active_color_mode.
93
+ rgbw_present = attr.RGBW is not None
94
+ cct_present = attr.CCT is not None
95
+ if rgbw_present and not cct_present and attr.RGBW != 0:
96
+ self.active_color_mode = "rgbw"
97
+ elif cct_present and not rgbw_present:
98
+ self.active_color_mode = "cct"
99
+
85
100
 
86
101
  class DeviceInformation:
87
102
  """Static device metadata and capability flags parsed from the cloud record."""
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-aidot-cameras"
7
- version = "0.11.13"
7
+ version = "0.11.14"
8
8
  description = "Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.11.13
3
+ Version: 0.11.14
4
4
  Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
5
5
  Author-email: cbrightly <chris.brightly@gmail.com>
6
6
  License-Expression: MIT
@@ -92,6 +92,7 @@ tests/test_ice_config_cache.py
92
92
  tests/test_keepalive_renew.py
93
93
  tests/test_keyframe_prompter.py
94
94
  tests/test_lan_control.py
95
+ tests/test_light_active_color_mode.py
95
96
  tests/test_live_stream_param.py
96
97
  tests/test_logging_caps.py
97
98
  tests/test_motion_poll.py
@@ -0,0 +1,58 @@
1
+ """Tests for DeviceStatusData.active_color_mode tracking.
2
+
3
+ AiDot RGBW+CCT bulbs report state as deltas: a CCT-mode push carries only
4
+ CCT (RGBW is None); an RGB-mode push carries only RGBW (CCT is None). But
5
+ the getDevAttr login-sync returns BOTH the retained RGBW register and CCT
6
+ together, which is ambiguous about which mode is actually active. This
7
+ must update the retained values but must NOT change active_color_mode.
8
+ """
9
+
10
+ from aidot.device_client import DeviceStatusData
11
+ from aidot.models.device_client_model import DeviceAttr
12
+
13
+
14
+ def test_cct_only_delta_sets_cct_mode():
15
+ status = DeviceStatusData()
16
+ status.update(DeviceAttr(OnOff=1, Dimming=100, CCT=3000))
17
+ assert status.active_color_mode == "cct"
18
+ assert status.cct == 3000
19
+
20
+
21
+ def test_rgbw_only_delta_sets_rgbw_mode():
22
+ status = DeviceStatusData()
23
+ status.update(DeviceAttr(RGBW=0x00FF0000))
24
+ assert status.active_color_mode == "rgbw"
25
+ assert status.rgbw == (0, 255, 0, 0)
26
+
27
+
28
+ def test_ambiguous_sync_does_not_change_mode_from_unknown():
29
+ # Starting from a fresh (unknown/None) mode, an ambiguous sync carrying
30
+ # both RGBW and CCT must update the values but leave the mode None.
31
+ status = DeviceStatusData()
32
+ assert status.active_color_mode is None
33
+ status.update(DeviceAttr(OnOff=1, Dimming=100, RGBW=-1660909312, CCT=3000))
34
+ assert status.active_color_mode is None
35
+ assert status.rgbw == (157, 0, 137, 0)
36
+ assert status.cct == 3000
37
+
38
+
39
+ def test_ambiguous_sync_does_not_revert_established_mode():
40
+ status = DeviceStatusData()
41
+ # Unambiguous CCT-only delta establishes "cct".
42
+ status.update(DeviceAttr(OnOff=1, Dimming=100, CCT=3000))
43
+ assert status.active_color_mode == "cct"
44
+ # A subsequent ambiguous sync (both RGBW and CCT present) must NOT flip
45
+ # the mode back to "rgbw", even though RGBW is present in the payload.
46
+ status.update(DeviceAttr(OnOff=1, Dimming=100, RGBW=-1660909312, CCT=3000))
47
+ assert status.active_color_mode == "cct"
48
+ assert status.rgbw == (157, 0, 137, 0)
49
+ assert status.cct == 3000
50
+
51
+
52
+ def test_rgbw_zero_only_does_not_set_rgbw_mode():
53
+ status = DeviceStatusData()
54
+ status.update(DeviceAttr(RGBW=0))
55
+ assert status.active_color_mode is None
56
+ # Existing default-red value behavior must be unchanged.
57
+ assert status.rgdb == 0xFF000000
58
+ assert status.rgbw == (255, 0, 0, 0)