diff --git a/README.md b/README.md index 64450aa4..5c96b0ba 100644 --- a/README.md +++ b/README.md @@ -281,6 +281,8 @@ At the moment, the following devices have been confirmed to work: * Tapo P110 (plug) * Tapo L530E (bulb) +* Kasa KS205 (Wifi/Matter Wall Switch) +* Kasa KS225 (Wifi/Matter Wall Dimmer Switch) Some newer hardware versions of Kasa branded devices are now using the same protocol as Tapo branded devices. Support for these devices is currently limited as per TAPO branded diff --git a/kasa/device_factory.py b/kasa/device_factory.py index 505b6487..5ffabda0 100755 --- a/kasa/device_factory.py +++ b/kasa/device_factory.py @@ -132,6 +132,7 @@ def get_device_class_from_family(device_type: str) -> Optional[Type[SmartDevice] "SMART.TAPOPLUG": TapoPlug, "SMART.TAPOBULB": TapoBulb, "SMART.KASAPLUG": TapoPlug, + "SMART.KASASWITCH": TapoBulb, "IOT.SMARTPLUGSWITCH": SmartPlug, "IOT.SMARTBULB": SmartBulb, } diff --git a/kasa/deviceconfig.py b/kasa/deviceconfig.py index 63b08d3c..c753c2bc 100644 --- a/kasa/deviceconfig.py +++ b/kasa/deviceconfig.py @@ -26,6 +26,7 @@ class DeviceFamilyType(Enum): IotSmartPlugSwitch = "IOT.SMARTPLUGSWITCH" IotSmartBulb = "IOT.SMARTBULB" SmartKasaPlug = "SMART.KASAPLUG" + SmartKasaSwitch = "SMART.KASASWITCH" SmartTapoPlug = "SMART.TAPOPLUG" SmartTapoBulb = "SMART.TAPOBULB" diff --git a/kasa/tapo/tapobulb.py b/kasa/tapo/tapobulb.py index d1e953d7..416658bf 100644 --- a/kasa/tapo/tapobulb.py +++ b/kasa/tapo/tapobulb.py @@ -41,6 +41,9 @@ class TapoBulb(TapoDevice, SmartBulb): :return: White temperature range in Kelvin (minimum, maximum) """ + if not self.is_variable_color_temp: + raise SmartDeviceException("Color temperature not supported") + ct_range = self._info.get("color_temp_range", [0, 0]) return ColorTempRange(min=ct_range[0], max=ct_range[1]) diff --git a/kasa/tests/conftest.py b/kasa/tests/conftest.py index 11efe693..fc5a1b9e 100644 --- a/kasa/tests/conftest.py +++ b/kasa/tests/conftest.py @@ -46,7 +46,7 @@ SUPPORTED_DEVICES = SUPPORTED_IOT_DEVICES + SUPPORTED_SMART_DEVICES BULBS_SMART_VARIABLE_TEMP = {"L530E"} BULBS_SMART_COLOR = {"L530E"} BULBS_SMART_LIGHT_STRIP: Set[str] = set() -BULBS_SMART_DIMMABLE: Set[str] = set() +BULBS_SMART_DIMMABLE = {"KS225"} BULBS_SMART = ( BULBS_SMART_VARIABLE_TEMP.union(BULBS_SMART_COLOR) .union(BULBS_SMART_DIMMABLE) @@ -98,7 +98,7 @@ PLUGS_IOT = { "KP401", "KS200M", } -PLUGS_SMART = {"P110", "KP125M", "EP25"} +PLUGS_SMART = {"P110", "KP125M", "EP25", "KS205"} PLUGS = { *PLUGS_IOT, *PLUGS_SMART, @@ -115,7 +115,7 @@ DIMMERS = { } WITH_EMETER_IOT = {"HS110", "HS300", "KP115", "KP125", *BULBS_IOT} -WITH_EMETER_SMART = {*PLUGS_SMART} +WITH_EMETER_SMART = {"P110", "KP125M", "EP25"} WITH_EMETER = {*WITH_EMETER_IOT, *WITH_EMETER_SMART} DIMMABLE = {*BULBS, *DIMMERS} diff --git a/kasa/tests/fixtures/smart/KS205(US)_1.0_1.0.2.json b/kasa/tests/fixtures/smart/KS205(US)_1.0_1.0.2.json new file mode 100644 index 00000000..c94d4f2a --- /dev/null +++ b/kasa/tests/fixtures/smart/KS205(US)_1.0_1.0.2.json @@ -0,0 +1,243 @@ +{ + "component_nego": { + "component_list": [ + { + "id": "device", + "ver_code": 2 + }, + { + "id": "firmware", + "ver_code": 2 + }, + { + "id": "quick_setup", + "ver_code": 3 + }, + { + "id": "time", + "ver_code": 1 + }, + { + "id": "wireless", + "ver_code": 1 + }, + { + "id": "schedule", + "ver_code": 2 + }, + { + "id": "countdown", + "ver_code": 2 + }, + { + "id": "antitheft", + "ver_code": 1 + }, + { + "id": "account", + "ver_code": 1 + }, + { + "id": "synchronize", + "ver_code": 1 + }, + { + "id": "sunrise_sunset", + "ver_code": 1 + }, + { + "id": "led", + "ver_code": 1 + }, + { + "id": "cloud_connect", + "ver_code": 1 + }, + { + "id": "iot_cloud", + "ver_code": 1 + }, + { + "id": "device_local_time", + "ver_code": 1 + }, + { + "id": "default_states", + "ver_code": 1 + }, + { + "id": "auto_off", + "ver_code": 2 + }, + { + "id": "matter", + "ver_code": 2 + }, + { + "id": "delay_action", + "ver_code": 1 + } + ] + }, + "discovery_result": { + "device_id": "00000000000000000000000000000000", + "device_model": "KS205(US)", + "device_type": "SMART.KASASWITCH", + "factory_default": false, + "ip": "127.0.0.123", + "is_support_iot_cloud": true, + "mac": "00-00-00-00-00-00", + "mgt_encrypt_schm": { + "encrypt_type": "AES", + "http_port": 80, + "is_support_https": false, + "lv": 2 + }, + "obd_src": "tplink", + "owner": "00000000000000000000000000000000" + }, + "get_antitheft_rules": { + "antitheft_rule_max_count": 1, + "enable": false, + "rule_list": [] + }, + "get_auto_off_config": { + "delay_min": 120, + "enable": false + }, + "get_auto_update_info": { + "enable": false, + "random_range": 120, + "time": 180 + }, + "get_connect_cloud_state": { + "status": 0 + }, + "get_countdown_rules": { + "countdown_rule_max_count": 1, + "enable": false, + "rule_list": [] + }, + "get_device_info": { + "auto_off_remain_time": 0, + "auto_off_status": "off", + "avatar": "switch_s500", + "default_states": { + "state": {}, + "type": "last_states" + }, + "device_id": "0000000000000000000000000000000000000000", + "device_on": false, + "fw_id": "00000000000000000000000000000000", + "fw_ver": "1.0.2 Build 230810 Rel.140202", + "has_set_location_info": true, + "hw_id": "00000000000000000000000000000000", + "hw_ver": "1.0", + "ip": "127.0.0.123", + "lang": "en_US", + "latitude": 0, + "longitude": 0, + "mac": "00-00-00-00-00-00", + "model": "KS205", + "nickname": "I01BU0tFRF9OQU1FIw==", + "oem_id": "00000000000000000000000000000000", + "on_time": 0, + "overheated": false, + "region": "America/Toronto", + "rssi": -49, + "signal_level": 3, + "specs": "", + "ssid": "I01BU0tFRF9TU0lEIw==", + "time_diff": -300, + "type": "SMART.KASASWITCH" + }, + "get_device_time": { + "region": "America/Toronto", + "time_diff": -300, + "timestamp": 1704216652 + }, + "get_device_usage": { + "time_usage": { + "past30": 10908, + "past7": 3476, + "today": 10 + } + }, + "get_fw_download_state": { + "auto_upgrade": false, + "download_progress": 0, + "reboot_time": 5, + "status": 0, + "upgrade_time": 5 + }, + "get_latest_fw": { + "fw_size": 0, + "fw_ver": "1.0.2 Build 230810 Rel.140202", + "hw_id": "", + "need_to_upgrade": false, + "oem_id": "", + "release_date": "", + "release_note": "", + "type": 0 + }, + "get_led_info": { + "led_rule": "always", + "led_status": true, + "night_mode": { + "end_time": 476, + "night_mode_type": "sunrise_sunset", + "start_time": 1020, + "sunrise_offset": 0, + "sunset_offset": 0 + } + }, + "get_next_event": {}, + "get_schedule_rules": { + "enable": false, + "rule_list": [], + "schedule_rule_max_count": 32, + "start_index": 0, + "sum": 0 + }, + "get_wireless_scan_info": { + "ap_list": [], + "wep_supported": false + }, + "qs_component_nego": { + "component_list": [ + { + "id": "quick_setup", + "ver_code": 3 + }, + { + "id": "sunrise_sunset", + "ver_code": 1 + }, + { + "id": "ble_whole_setup", + "ver_code": 1 + }, + { + "id": "matter", + "ver_code": 2 + }, + { + "id": "iot_cloud", + "ver_code": 1 + }, + { + "id": "inherit", + "ver_code": 1 + }, + { + "id": "firmware", + "ver_code": 2 + } + ], + "extra_info": { + "device_model": "KS205", + "device_type": "SMART.KASASWITCH", + "is_klap": false + } + } +} diff --git a/kasa/tests/fixtures/smart/KS225(US)_1.0_1.0.2.json b/kasa/tests/fixtures/smart/KS225(US)_1.0_1.0.2.json new file mode 100644 index 00000000..e6945cb8 --- /dev/null +++ b/kasa/tests/fixtures/smart/KS225(US)_1.0_1.0.2.json @@ -0,0 +1,269 @@ +{ + "component_nego": { + "component_list": [ + { + "id": "device", + "ver_code": 2 + }, + { + "id": "firmware", + "ver_code": 2 + }, + { + "id": "quick_setup", + "ver_code": 3 + }, + { + "id": "inherit", + "ver_code": 1 + }, + { + "id": "time", + "ver_code": 1 + }, + { + "id": "wireless", + "ver_code": 1 + }, + { + "id": "schedule", + "ver_code": 2 + }, + { + "id": "countdown", + "ver_code": 2 + }, + { + "id": "antitheft", + "ver_code": 1 + }, + { + "id": "account", + "ver_code": 1 + }, + { + "id": "synchronize", + "ver_code": 1 + }, + { + "id": "sunrise_sunset", + "ver_code": 1 + }, + { + "id": "led", + "ver_code": 1 + }, + { + "id": "cloud_connect", + "ver_code": 1 + }, + { + "id": "iot_cloud", + "ver_code": 1 + }, + { + "id": "device_local_time", + "ver_code": 1 + }, + { + "id": "default_states", + "ver_code": 1 + }, + { + "id": "brightness", + "ver_code": 1 + }, + { + "id": "preset", + "ver_code": 1 + }, + { + "id": "on_off_gradually", + "ver_code": 2 + }, + { + "id": "dimmer_calibration", + "ver_code": 1 + }, + { + "id": "overheat_protection", + "ver_code": 1 + }, + { + "id": "matter", + "ver_code": 2 + } + ] + }, + "discovery_result": { + "device_id": "00000000000000000000000000000000", + "device_model": "KS225(US)", + "device_type": "SMART.KASASWITCH", + "factory_default": false, + "ip": "127.0.0.123", + "is_support_iot_cloud": true, + "mac": "00-00-00-00-00-00", + "mgt_encrypt_schm": { + "encrypt_type": "AES", + "http_port": 80, + "is_support_https": false, + "lv": 2 + }, + "obd_src": "tplink", + "owner": "00000000000000000000000000000000" + }, + "get_antitheft_rules": { + "antitheft_rule_max_count": 1, + "enable": false, + "rule_list": [] + }, + "get_auto_update_info": { + "enable": true, + "random_range": 120, + "time": 180 + }, + "get_connect_cloud_state": { + "status": 0 + }, + "get_countdown_rules": { + "countdown_rule_max_count": 1, + "enable": false, + "rule_list": [] + }, + "get_device_info": { + "avatar": "switch_s500d", + "brightness": 34, + "default_states": { + "re_power_type": "always_off", + "re_power_type_capability": [ + "last_states", + "always_on", + "always_off" + ], + "type": "last_states" + }, + "device_id": "0000000000000000000000000000000000000000", + "device_on": false, + "fw_id": "00000000000000000000000000000000", + "fw_ver": "1.0.2 Build 230810 Rel.141013", + "has_set_location_info": true, + "hw_id": "00000000000000000000000000000000", + "hw_ver": "1.0", + "ip": "127.0.0.123", + "lang": "en_US", + "latitude": 0, + "longitude": 0, + "mac": "00-00-00-00-00-00", + "model": "KS225", + "nickname": "I01BU0tFRF9OQU1FIw==", + "oem_id": "00000000000000000000000000000000", + "on_time": 0, + "overheat_status": "normal", + "region": "America/Toronto", + "rssi": -51, + "signal_level": 2, + "specs": "", + "ssid": "I01BU0tFRF9TU0lEIw==", + "time_diff": -300, + "type": "SMART.KASASWITCH" + }, + "get_device_time": { + "region": "America/Toronto", + "time_diff": -300, + "timestamp": 1704216678 + }, + "get_device_usage": { + "time_usage": { + "past30": 7410, + "past7": 2190, + "today": 1 + } + }, + "get_fw_download_state": { + "auto_upgrade": false, + "download_progress": 0, + "reboot_time": 5, + "status": 0, + "upgrade_time": 5 + }, + "get_inherit_info": null, + "get_latest_fw": { + "fw_size": 0, + "fw_ver": "1.0.2 Build 230810 Rel.141013", + "hw_id": "", + "need_to_upgrade": false, + "oem_id": "", + "release_date": "", + "release_note": "", + "type": 0 + }, + "get_led_info": { + "led_rule": "always", + "led_status": true, + "night_mode": { + "end_time": 476, + "night_mode_type": "sunrise_sunset", + "start_time": 1020, + "sunrise_offset": 0, + "sunset_offset": 0 + } + }, + "get_next_event": {}, + "get_preset_rules": { + "brightness": [ + 100, + 75, + 50, + 25, + 1 + ] + }, + "get_schedule_rules": { + "enable": false, + "rule_list": [], + "schedule_rule_max_count": 32, + "start_index": 0, + "sum": 0 + }, + "get_wireless_scan_info": { + "ap_list": [], + "wep_supported": false + }, + "qs_component_nego": { + "component_list": [ + { + "id": "quick_setup", + "ver_code": 3 + }, + { + "id": "sunrise_sunset", + "ver_code": 1 + }, + { + "id": "ble_whole_setup", + "ver_code": 1 + }, + { + "id": "matter", + "ver_code": 2 + }, + { + "id": "iot_cloud", + "ver_code": 1 + }, + { + "id": "inherit", + "ver_code": 1 + }, + { + "id": "firmware", + "ver_code": 2 + } + ], + "extra_info": { + "device_model": "KS225", + "device_type": "SMART.KASASWITCH", + "is_klap": false + } + } +} diff --git a/kasa/tests/test_bulb.py b/kasa/tests/test_bulb.py index 5bacf3cc..0676022b 100644 --- a/kasa/tests/test_bulb.py +++ b/kasa/tests/test_bulb.py @@ -171,7 +171,7 @@ async def test_non_variable_temp(dev: SmartBulb): await dev.set_color_temp(2700) with pytest.raises(SmartDeviceException): - dev.valid_temperature_range() + print(dev.valid_temperature_range) with pytest.raises(SmartDeviceException): print(dev.color_temp)