From a25103a855637c2874d627c7963f7eb50064eff9 Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Sat, 28 Feb 2026 11:07:29 +0800 Subject: [PATCH 1/9] add const --- .gitignore | 2 ++ switchbot/const/__init__.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index ad2cfbfc..82c23aab 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ __pycache__/ # C extensions *.so +.idea/ + # Distribution / packaging .Python build/ diff --git a/switchbot/const/__init__.py b/switchbot/const/__init__.py index 4e6e2512..bff99166 100644 --- a/switchbot/const/__init__.py +++ b/switchbot/const/__init__.py @@ -97,6 +97,8 @@ class SwitchbotModel(StrEnum): PLUG_MINI_EU = "Plug Mini (EU)" RGBICWW_STRIP_LIGHT = "RGBICWW Strip Light" RGBICWW_FLOOR_LAMP = "RGBICWW Floor Lamp" + RGBIC_NEON_ROPE_LIGHT = "RGBIC Neon Rope Light" + RGBIC_NEON_WIRE_ROPE_LIGHT = "RGBIC Neon Wire Rope Light" K11_VACUUM = "K11+ Vacuum" CLIMATE_PANEL = "Climate Panel" SMART_THERMOSTAT_RADIATOR = "Smart Thermostat Radiator" From fbeadebeb8fc78123c456e4404139dc95f7c0cfd Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Sat, 28 Feb 2026 11:11:28 +0800 Subject: [PATCH 2/9] add const --- .gitignore | 2 -- .idea/workspace.xml | 76 +++++++++++++++++++++++++++++++++++++++++ switchbot/adv_parser.py | 12 +++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 .idea/workspace.xml diff --git a/.gitignore b/.gitignore index 82c23aab..ad2cfbfc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ __pycache__/ # C extensions *.so -.idea/ - # Distribution / packaging .Python build/ diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..9f8ee61e --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + { + "associatedIndex": 1 +} + + + + + + + + + + + + + + + + + + + 1769740916677 + + + + + + \ No newline at end of file diff --git a/switchbot/adv_parser.py b/switchbot/adv_parser.py index 7c121029..eeb17b2e 100644 --- a/switchbot/adv_parser.py +++ b/switchbot/adv_parser.py @@ -659,6 +659,18 @@ class SwitchbotSupportedType(TypedDict): "func": process_rgbic_light, "manufacturer_id": 2409, }, + b"\x01\x10\xd0\xb6": { + "modelName": SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, + "modelFriendlyName": "RGBIC Neon Rope Light", + "func": process_rgbic_light, + "manufacturer_id": 2409, + }, + b"\x01\x10\xd0\xb5": { + "modelName": SwitchbotModel.RGBIC_NEON_WIRE_ROPE_LIGHT, + "modelFriendlyName": "RGBIC Neon Wire Rope Light", + "func": process_rgbic_light, + "manufacturer_id": 2409, + }, b"\x00\x10\xfb\xa8": { "modelName": SwitchbotModel.K11_VACUUM, "modelFriendlyName": "K11+ Vacuum", From b18cd54e2b0355dfcaa60992b69a99540d42ab1a Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Sat, 28 Feb 2026 11:13:18 +0800 Subject: [PATCH 3/9] format code --- switchbot/adv_parser.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/switchbot/adv_parser.py b/switchbot/adv_parser.py index eeb17b2e..199c0969 100644 --- a/switchbot/adv_parser.py +++ b/switchbot/adv_parser.py @@ -659,18 +659,19 @@ class SwitchbotSupportedType(TypedDict): "func": process_rgbic_light, "manufacturer_id": 2409, }, - b"\x01\x10\xd0\xb6": { - "modelName": SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, - "modelFriendlyName": "RGBIC Neon Rope Light", - "func": process_rgbic_light, - "manufacturer_id": 2409, - }, + b"\x01\x10\xd0\xb5": { "modelName": SwitchbotModel.RGBIC_NEON_WIRE_ROPE_LIGHT, "modelFriendlyName": "RGBIC Neon Wire Rope Light", "func": process_rgbic_light, "manufacturer_id": 2409, }, + b"\x01\x10\xd0\xb6": { + "modelName": SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, + "modelFriendlyName": "RGBIC Neon Rope Light", + "func": process_rgbic_light, + "manufacturer_id": 2409, + }, b"\x00\x10\xfb\xa8": { "modelName": SwitchbotModel.K11_VACUUM, "modelFriendlyName": "K11+ Vacuum", From 7b55fa75a9c35a5cb1a29da1b5f753bc1415dbbd Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Sat, 28 Feb 2026 11:30:44 +0800 Subject: [PATCH 4/9] format code --- switchbot/adv_parser.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/switchbot/adv_parser.py b/switchbot/adv_parser.py index 199c0969..10127c39 100644 --- a/switchbot/adv_parser.py +++ b/switchbot/adv_parser.py @@ -659,19 +659,18 @@ class SwitchbotSupportedType(TypedDict): "func": process_rgbic_light, "manufacturer_id": 2409, }, - b"\x01\x10\xd0\xb5": { - "modelName": SwitchbotModel.RGBIC_NEON_WIRE_ROPE_LIGHT, - "modelFriendlyName": "RGBIC Neon Wire Rope Light", - "func": process_rgbic_light, - "manufacturer_id": 2409, - }, + "modelName": SwitchbotModel.RGBIC_NEON_WIRE_ROPE_LIGHT, + "modelFriendlyName": "RGBIC Neon Wire Rope Light", + "func": process_wostrip, + "manufacturer_id": 2409, + }, b"\x01\x10\xd0\xb6": { - "modelName": SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, - "modelFriendlyName": "RGBIC Neon Rope Light", - "func": process_rgbic_light, - "manufacturer_id": 2409, - }, + "modelName": SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, + "modelFriendlyName": "RGBIC Neon Rope Light", + "func": process_wostrip, + "manufacturer_id": 2409, + }, b"\x00\x10\xfb\xa8": { "modelName": SwitchbotModel.K11_VACUUM, "modelFriendlyName": "K11+ Vacuum", From e77be46b957a3e61de5378a6de2d414c4eec5ba3 Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Sat, 28 Feb 2026 18:39:24 +0800 Subject: [PATCH 5/9] add Rgbic neon rope light --- switchbot/__init__.py | 2 ++ switchbot/adv_parser.py | 12 ++++++++++ switchbot/devices/light_strip.py | 40 ++++++++++++++++++++++++++++++++ tests/__init__.py | 16 +++++++++++++ tests/test_adv_parser.py | 38 ++++++++++++++++++++++++++++++ tests/test_strip_light.py | 8 +++---- 6 files changed, 112 insertions(+), 4 deletions(-) diff --git a/switchbot/__init__.py b/switchbot/__init__.py index f5981fb6..01d18b37 100644 --- a/switchbot/__init__.py +++ b/switchbot/__init__.py @@ -49,6 +49,7 @@ from .devices.light_strip import ( SwitchbotLightStrip, SwitchbotRgbicLight, + SwitchbotRgbicNeonLight, SwitchbotStripLight3, ) from .devices.lock import SwitchbotLock @@ -111,6 +112,7 @@ "SwitchbotRelaySwitch", "SwitchbotRelaySwitch2PM", "SwitchbotRgbicLight", + "SwitchbotRgbicNeonLight", "SwitchbotRollerShade", "SwitchbotSmartThermostatRadiator", "SwitchbotStripLight3", diff --git a/switchbot/adv_parser.py b/switchbot/adv_parser.py index 10127c39..93521867 100644 --- a/switchbot/adv_parser.py +++ b/switchbot/adv_parser.py @@ -659,6 +659,18 @@ class SwitchbotSupportedType(TypedDict): "func": process_rgbic_light, "manufacturer_id": 2409, }, + b"\x00\x10\xd0\xb5": { + "modelName": SwitchbotModel.RGBIC_NEON_WIRE_ROPE_LIGHT, + "modelFriendlyName": "RGBIC Neon Wire Rope Light", + "func": process_wostrip, + "manufacturer_id": 2409, + }, + b"\x00\x10\xd0\xb6": { + "modelName": SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, + "modelFriendlyName": "RGBIC Neon Rope Light", + "func": process_wostrip, + "manufacturer_id": 2409, + }, b"\x01\x10\xd0\xb5": { "modelName": SwitchbotModel.RGBIC_NEON_WIRE_ROPE_LIGHT, "modelFriendlyName": "RGBIC Neon Wire Rope Light", diff --git a/switchbot/devices/light_strip.py b/switchbot/devices/light_strip.py index 7a5dd250..f6e69af3 100644 --- a/switchbot/devices/light_strip.py +++ b/switchbot/devices/light_strip.py @@ -343,3 +343,43 @@ def color_mode(self) -> ColorMode: """Return the current color mode.""" device_mode = RGBICStripLightColorMode(self._get_adv_value("color_mode") or 10) return _RGBICWW_STRIP_LIGHT_COLOR_MODE_MAP.get(device_mode, ColorMode.OFF) + + +class SwitchbotRgbicNeonLight(SwitchbotEncryptedDevice, SwitchbotLightStrip): + """Support for Switchbot RGBIC Neon lights.""" + + _effect_dict = RGBIC_EFFECTS + + def __init__( + self, + device: BLEDevice, + key_id: str, + encryption_key: str, + interface: int = 0, + model: SwitchbotModel = SwitchbotModel.RGBICWW_STRIP_LIGHT, + **kwargs: Any, + ) -> None: + super().__init__(device, key_id, encryption_key, model, interface, **kwargs) + + @classmethod + async def verify_encryption_key( + cls, + device: BLEDevice, + key_id: str, + encryption_key: str, + model: SwitchbotModel = SwitchbotModel.RGBICWW_STRIP_LIGHT, + **kwargs: Any, + ) -> bool: + return await super().verify_encryption_key( + device, key_id, encryption_key, model, **kwargs + ) + + @property + def color_modes(self) -> set[ColorMode]: + """Return the supported color modes.""" + return {ColorMode.RGB} + + @property + def color_mode(self) -> ColorMode: + """Return the current color mode.""" + return ColorMode.RGB diff --git a/tests/__init__.py b/tests/__init__.py index e6ea3e06..a35f916e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -81,6 +81,22 @@ class AdvTestCase: SwitchbotModel.RGBICWW_FLOOR_LAMP, ) +RGBIC_NEON_LIGHT_INFO = AdvTestCase( + b'\xdc\x06u\xa6\xfb\xb2y\x9e"\x00\x11\xb8\x00', + b"\x00\x00\x00\x00\x10\xd0\xb6", + { + "sequence_number": 121, + "isOn": True, + "brightness": 30, + "delay": False, + "network_state": 2, + "color_mode": 2, + }, + b"\x00\x10\xd0\xb4", + "Rgbic Neno Rope Light", + SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, +) + SMART_THERMOSTAT_RADIATOR_INFO = AdvTestCase( b"\xb0\xe9\xfe\xa2T|6\xe4\x00\x9c\xa3A\x00", diff --git a/tests/test_adv_parser.py b/tests/test_adv_parser.py index c71c3b30..259cadf0 100644 --- a/tests/test_adv_parser.py +++ b/tests/test_adv_parser.py @@ -3575,6 +3575,21 @@ def test_humidifer_with_empty_data() -> None: "RGBICWW Strip Light", SwitchbotModel.RGBICWW_STRIP_LIGHT, ), + AdvTestCase( + b"@L\xca!pz/\x8b'\x00\x11:\x00", + b"\x00\x00\x00\x00\x10\xd0\xb6", + { + "sequence_number": 47, + "isOn": True, + "brightness": 11, + "delay": False, + "network_state": 2, + "color_mode": 7, + }, + b"\x00\x10\xd0\xb6", + "RGBIC Neon Rope Light", + SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, + ), AdvTestCase( b"\xb0\xe9\xfe\xe4\xbf\xd8\x0b\x01\x11f\x00\x16M\x15", b"\x00\x00M\x00\x10\xfb\xa8", @@ -3947,6 +3962,21 @@ def test_adv_active(test_case: AdvTestCase) -> None: "RGBICWW Strip Light", SwitchbotModel.RGBICWW_STRIP_LIGHT, ), + AdvTestCase( + b"@L\xca!pz/\x8b'\x00\x11:\x00", + None, + { + "sequence_number": 47, + "isOn": True, + "brightness": 11, + "delay": False, + "network_state": 2, + "color_mode": 7, + }, + b"\x00\x10\xd0\xb6", + "RGBIC Neon Rope Light", + SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, + ), AdvTestCase( b"\xb0\xe9\xfe\xe4\xbf\xd8\x0b\x01\x11f\x00\x16M\x15", None, @@ -4300,6 +4330,14 @@ def test_adv_passive(test_case: AdvTestCase) -> None: "Keypad Vision", SwitchbotModel.KEYPAD_VISION, ), + AdvTestCase( + None, + b"\x00\x00\x00\x00\x10\xd0\xb6", + {}, + b"\x00\x10\xd0\xb6", + "RGBIC Neon Rope Light", + SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, + ), AdvTestCase( None, b"\x00\x00`\x01\x11Q\x98", diff --git a/tests/test_strip_light.py b/tests/test_strip_light.py index bc667157..faa726c2 100644 --- a/tests/test_strip_light.py +++ b/tests/test_strip_light.py @@ -11,6 +11,7 @@ from . import ( FLOOR_LAMP_INFO, + RGBIC_NEON_LIGHT_INFO, RGBICWW_FLOOR_LAMP_INFO, RGBICWW_STRIP_LIGHT_INFO, STRIP_LIGHT_3_INFO, @@ -24,6 +25,7 @@ (FLOOR_LAMP_INFO, light_strip.SwitchbotStripLight3), (RGBICWW_STRIP_LIGHT_INFO, light_strip.SwitchbotRgbicLight), (RGBICWW_FLOOR_LAMP_INFO, light_strip.SwitchbotRgbicLight), + (RGBIC_NEON_LIGHT_INFO, light_strip.SwitchbotRgbicNeonLight), ] ) def device_case(request): @@ -38,6 +40,7 @@ def expected_effects(device_case): SwitchbotModel.FLOOR_LAMP: ("christmas", "halloween", "sunset"), SwitchbotModel.RGBICWW_STRIP_LIGHT: ("romance", "energy", "heartbeat"), SwitchbotModel.RGBICWW_FLOOR_LAMP: ("romance", "energy", "heartbeat"), + SwitchbotModel.RGBIC_NEON_ROPE_LIGHT: ("romance", "energy", "heartbeat"), } return EXPECTED[adv_info.modelName] @@ -96,10 +99,7 @@ async def test_default_info(device_case, expected_effects): assert device.is_on() is True assert device.on is True assert device.color_mode == ColorMode.RGB - assert device.color_modes == { - ColorMode.RGB, - ColorMode.COLOR_TEMP, - } + assert ColorMode.RGB in device.color_modes assert device.rgb == (30, 0, 0) assert device.color_temp == 3200 assert device.brightness == adv_info.data["brightness"] From 98a7a123dd8786b999080d009be56514b49943fe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 10:57:38 +0000 Subject: [PATCH 6/9] chore(pre-commit.ci): auto fixes --- .idea/workspace.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 9f8ee61e..a9fba35b 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -73,4 +73,4 @@ - \ No newline at end of file + From aecc10f29cf8d3c47c9abac8b85dedd9201783ac Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Mon, 2 Mar 2026 10:18:32 +0800 Subject: [PATCH 7/9] fixed --- switchbot/devices/light_strip.py | 2 +- tests/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/switchbot/devices/light_strip.py b/switchbot/devices/light_strip.py index f6e69af3..4fa9b936 100644 --- a/switchbot/devices/light_strip.py +++ b/switchbot/devices/light_strip.py @@ -367,7 +367,7 @@ async def verify_encryption_key( device: BLEDevice, key_id: str, encryption_key: str, - model: SwitchbotModel = SwitchbotModel.RGBICWW_STRIP_LIGHT, + model: SwitchbotModel = SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, **kwargs: Any, ) -> bool: return await super().verify_encryption_key( diff --git a/tests/__init__.py b/tests/__init__.py index a35f916e..ef118e8c 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -92,7 +92,7 @@ class AdvTestCase: "network_state": 2, "color_mode": 2, }, - b"\x00\x10\xd0\xb4", + b"\x00\x10\xd0\xb6", "Rgbic Neno Rope Light", SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, ) From 44a8a82a57275b637050936b0ad909a0f1071623 Mon Sep 17 00:00:00 2001 From: Samuel Xiao <40679757+XiaoLing-git@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:21:23 +0800 Subject: [PATCH 8/9] Update tests/__init__.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/__init__.py b/tests/__init__.py index ef118e8c..762ec029 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -93,7 +93,7 @@ class AdvTestCase: "color_mode": 2, }, b"\x00\x10\xd0\xb6", - "Rgbic Neno Rope Light", + "Rgbic Neon Rope Light", SwitchbotModel.RGBIC_NEON_ROPE_LIGHT, ) From 3a36c5acc6b36929ef26116365111ab159cce25d Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Mon, 16 Mar 2026 11:14:57 +0800 Subject: [PATCH 9/9] fixed --- .idea/workspace.xml | 76 --------------------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index a9fba35b..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - { - "associatedIndex": 1 -} - - - - - - - - - - - - - - - - - - - 1769740916677 - - - - - -