From 4b4f119457976539a31a7931a847e566bd552b20 Mon Sep 17 00:00:00 2001 From: Jeff Steinbok Date: Fri, 11 Oct 2024 22:01:32 -0700 Subject: [PATCH] Fix handling unknown device (#218) --- custom_components/dreo/pydreo/__init__.py | 4 + .../dreo/pydreo/pydreounknowndevice.py | 7 +- .../get_device_state_UNKNOWN_1.json | 99 ++++ .../api_responses/get_devices_UNKNOWN.json | 528 ++++++++++++++++++ tests/pydreo/test_all_devices.py | 8 + 5 files changed, 642 insertions(+), 4 deletions(-) create mode 100644 tests/pydreo/api_responses/get_device_state_UNKNOWN_1.json create mode 100644 tests/pydreo/api_responses/get_devices_UNKNOWN.json diff --git a/custom_components/dreo/pydreo/__init__.py b/custom_components/dreo/pydreo/__init__.py index df8f58f..5cbc04a 100644 --- a/custom_components/dreo/pydreo/__init__.py +++ b/custom_components/dreo/pydreo/__init__.py @@ -170,8 +170,12 @@ def _process_devices(self, dev_list: list) -> bool: # If device_details is None at this point, we have an unknown device model. # Unsupported/Unknown Device. Load the state, but store it in an "unsupported objects" # list for later use in diagnostics. + device_class = None + if device_details is not None: device_class = _DREO_DEVICE_TYPE_TO_CLASS.get(device_details.device_type, None) + else: + device_details = DreoDeviceDetails(device_type = DreoDeviceType.UNKNOWN) if device_class is None: device_class = PyDreoUnknownDevice diff --git a/custom_components/dreo/pydreo/pydreounknowndevice.py b/custom_components/dreo/pydreo/pydreounknowndevice.py index 4f69b53..b538160 100644 --- a/custom_components/dreo/pydreo/pydreounknowndevice.py +++ b/custom_components/dreo/pydreo/pydreounknowndevice.py @@ -4,8 +4,7 @@ from typing import TYPE_CHECKING, Dict from .constant import ( - LOGGER_NAME, - DreoDeviceType + LOGGER_NAME ) from .pydreobasedevice import PyDreoBaseDevice @@ -19,6 +18,6 @@ class PyDreoUnknownDevice(PyDreoBaseDevice): """Dreo Device class for unknown devices.""" - def __init__(self, details: Dict[str, list], dreo: "PyDreo"): + def __init__(self, device_definition: DreoDeviceDetails, details: Dict[str, list], dreo: "PyDreo"): #pylint: disable=useless-super-delegation """Initialize the Dreo Device.""" - super().__init__(DreoDeviceDetails(device_type = DreoDeviceType.UNKNOWN), details, dreo) + super().__init__(device_definition, details, dreo) diff --git a/tests/pydreo/api_responses/get_device_state_UNKNOWN_1.json b/tests/pydreo/api_responses/get_device_state_UNKNOWN_1.json new file mode 100644 index 0000000..217ce1d --- /dev/null +++ b/tests/pydreo/api_responses/get_device_state_UNKNOWN_1.json @@ -0,0 +1,99 @@ +{ + "code": 0, + "msg": "OK", + "data": { + "mixed": { + "mcu_hardware_model": { + "state": "SC95F8613B/EU", + "timestamp": 1724748442 + }, + "wifi_ssid": "**REDACTED**", + "windlevel": { + "state": 4, + "timestamp": 1724759313 + }, + "wifi_rssi": { + "state": -30, + "timestamp": 1724748442 + }, + "poweron": { + "state": true, + "timestamp": 1724782631 + }, + "tempunit": { + "state": 1, + "timestamp": 1724748442 + }, + "timeron": { + "state": { + "du": 0, + "ts": 1724748442 + }, + "timestamp": null + }, + "module_firmware_version": { + "state": "1.2.15", + "timestamp": 1724748442 + }, + "mode": { + "state": 1, + "timestamp": 1724748442 + }, + "mcuon": { + "state": true, + "timestamp": 1724748442 + }, + "connected": { + "state": true, + "timestamp": 1724748442 + }, + "timeroff": { + "state": { + "du": 0, + "ts": 1724748442 + }, + "timestamp": null + }, + "network_latency": { + "state": 90, + "timestamp": 1724748442 + }, + "module_hardware_model": { + "state": "PAI-051", + "timestamp": 1724748442 + }, + "mcu_firmware_version": { + "state": "1.0.8", + "timestamp": 1724748442 + }, + "customconf": { + "state": "temp:11111222222333333444444", + "timestamp": 1724748442 + }, + "ledkepton": { + "state": false, + "timestamp": 1724748442 + }, + "temperature": { + "state": 81, + "timestamp": 1724778379 + }, + "module_hardware_mac": "**REDACTED**", + "childlockon": { + "state": false, + "timestamp": 1724748442 + }, + "muteon": { + "state": true, + "timestamp": 1724748442 + }, + "hoscon": { + "state": false, + "timestamp": 1724783181 + } + }, + "sn": "UNKNOWN_1", + "productId": "***REMOVED BY ME ***", + "region": "eu-central-1" + } + } \ No newline at end of file diff --git a/tests/pydreo/api_responses/get_devices_UNKNOWN.json b/tests/pydreo/api_responses/get_devices_UNKNOWN.json new file mode 100644 index 0000000..77a4095 --- /dev/null +++ b/tests/pydreo/api_responses/get_devices_UNKNOWN.json @@ -0,0 +1,528 @@ +{ + "code": 0, + "msg": "OK", + "data": { + "currentPage": 1, + "pageSize": 100, + "totalNum": 2, + "totalPage": 1, + "familyRooms": null, + "list": [ + { + "deviceId": "1834678463454253057", + "sn": "UNKNOWN_1", + "brand": "Dreo", + "model": "DR-UNKNOWN", + "productId": "**REDACTED**", + "productName": "Air Conditioner", + "deviceName": "AC", + "shared": false, + "series": null, + "seriesName": "AC516S", + "type": 0, + "owner": true, + "familyId": null, + "familyName": null, + "roomId": null, + "roomName": null, + "roomNameI18Key": "", + "color": "w", + "controlsConf": { + "template": "DR-HAC006S", + "lottie": { + "key": "mode", + "frames": [ + { + "value": 0, + "frame": [ + 0 + ] + }, + { + "value": 1, + "frame": [ + 3 + ] + }, + { + "value": 2, + "frame": [ + 4 + ] + }, + { + "value": 3, + "frame": [ + 2 + ] + }, + { + "value": 4, + "frame": [ + 3 + ] + }, + { + "value": 5, + "frame": [ + 3 + ] + } + ] + }, + "schedule": { + "modes": [ + { + "controls": [ + { + "groupId": 0, + "type": 3, + "identity": "mode", + "items": [ + { + "valueType": 1, + "isSelected": true, + "cmd": [ + "mode" + ], + "title": "schedule_ac_cool", + "value": 1, + "subItems": [ + { + "endValue": 86, + "endColor": "#0051CF", + "valueType": 1, + "groupId": 0, + "isSelected": false, + "cmd": [ + "templevel" + ], + "startValue": 65, + "type": 4, + "title": "base_temp", + "value": 76, + "startColor": "#0051CF", + "identity": "cooltemp" + } + ] + }, + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "mode" + ], + "title": "schedule_ac_sleep", + "value": 4, + "identity": "sleep" + }, + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "mode" + ], + "title": "schedule_ac_eco", + "value": 5, + "subItems": [ + { + "endValue": 86, + "endColor": "#0051CF", + "valueType": 1, + "groupId": 0, + "isSelected": false, + "cmd": [ + "templevel" + ], + "startValue": 76, + "type": 4, + "title": "base_temp", + "value": 76, + "startColor": "#0051CF", + "identity": "ecotemp" + } + ] + } + ] + }, + { + "groupId": 0, + "type": 3, + "title": "device_control_speed", + "identity": "speed", + "value": 4, + "items": [ + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_1", + "value": 1 + }, + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_2", + "value": 2 + }, + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_3", + "value": 3 + }, + { + "valueType": 1, + "isSelected": true, + "cmd": [ + "windlevel" + ], + "title": "base_auto", + "value": 4 + } + ] + } + ], + "valueType": 1, + "icon": "ic_cool_mode_hac", + "isSelected": true, + "attention": [ + "mode", + "windlevel", + "poweron", + "templevel", + "oscmode" + ], + "cmd": "mode", + "title": "base_cool", + "value": 1 + }, + { + "controls": [ + { + "endValue": 80, + "endColor": "#0051CF", + "valueType": 1, + "groupId": 0, + "isSelected": false, + "cmd": [ + "rhlevel" + ], + "startValue": 30, + "type": 0, + "title": "home_humidity", + "value": 60, + "startColor": "#0051CF", + "unit": "%", + "identity": "temperature" + }, + { + "groupId": 0, + "type": 3, + "title": "device_control_speed", + "identity": "speed", + "items": [ + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_1", + "value": 1 + }, + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_2", + "value": 2 + }, + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_3", + "value": 3 + }, + { + "valueType": 1, + "isSelected": true, + "cmd": [ + "windlevel" + ], + "title": "base_auto", + "value": 4 + } + ] + } + ], + "valueType": 1, + "icon": "ic_dry_mode", + "isSelected": false, + "attention": [ + "mode", + "windlevel", + "poweron", + "rhlevel", + "oscmode" + ], + "cmd": "mode", + "title": "base_dry", + "value": 2 + }, + { + "controls": [ + { + "groupId": 0, + "type": 3, + "title": "device_control_speed", + "identity": "speed", + "items": [ + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_1", + "value": 1 + }, + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_2", + "value": 2 + }, + { + "valueType": 1, + "isSelected": false, + "cmd": [ + "windlevel" + ], + "title": "base_3", + "value": 3 + }, + { + "valueType": 1, + "isSelected": true, + "cmd": [ + "windlevel" + ], + "title": "base_auto", + "value": 4 + } + ] + } + ], + "valueType": 1, + "icon": "ic_cool_mode", + "isSelected": false, + "attention": [ + "mode", + "windlevel", + "poweron", + "oscmode" + ], + "cmd": "mode", + "title": "base_fan", + "value": 3 + } + ] + }, + "timer": { + "minHour": 0, + "maxHour": 23 + }, + "cards": [ + { + "type": 2, + "title": "device_control_temp", + "icon": "", + "image": "", + "url": "dreo://nav/device/report?deviceSn=${sn}&type=temp", + "show": true, + "minVersion": "2.3.1" + }, + { + "type": 8, + "title": "", + "icon": "", + "image": "", + "url": "dreo://nav/device/schedule?deviceSn={sn}", + "show": true, + "key": "" + }, + { + "type": 3, + "title": "home_humidifier_cleaning", + "icon": "ic_hum_cleanning", + "image": "", + "url": "dreo://nav/device/cleanreminder?deviceSn=${sn}&start=50&end=300&step=10", + "show": true, + "key": "worktime" + }, + { + "type": 6, + "title": "device_chart_usage", + "icon": "ic_usage", + "image": "", + "url": "dreo://nav/device/hacstatistics?deviceSn=${sn}", + "show": true, + "key": "usage" + }, + { + "type": 6, + "title": "base_maintenance", + "icon": "https://resources.dreo-cloud.com/app/202312/4/c06d58240e114f019c92abf5d3bd6f8c.png", + "image": "", + "url": "https://fe.dreo.com/article/dreo-air-conditioner-installation-maintenance", + "show": true + }, + { + "type": 6, + "title": "device_settings_title", + "icon": "ic_setting", + "image": "", + "url": "dreo://nav/device/setting?deviceSn=${sn}", + "show": true, + "key": "setting" + } + ], + "urlConfig": [ + { + "key": "clean", + "url": "https://fe.dreo.com/en/guides/videos/v?source=https://resources.dreo-cloud.com/app/202404/9/1094c8bcd7fc4811bd5d944b6a70f658.mp4&coverImage=https://resources.dreo-cloud.com/app/202404/10/47672f46958c4605ba621683d5db57fb.png" + } + ], + "feature": { + "schedule": { + "enable": true, + "localSupport": true, + "module": [ + { + "type": "HeFi", + "version": "0.0.4" + } + ] + } + }, + "preference": [ + { + "image": "ic_display", + "id": "240", + "cmd": "lighton", + "type": "Display", + "title": "dev_ctrl_heater_display", + "reverse": false + }, + { + "id": "210", + "type": "Panel Sound", + "title": "device_control_panelsound", + "image": "ic_mute", + "cmd": "muteon", + "reverse": true + }, + { + "id": "220", + "type": "Child Lock", + "title": "device_control_childlock", + "image": "ic_child_lock", + "reverse": false, + "cmd": "childlockon" + }, + { + "id": "230", + "type": "Temperature Unit", + "title": "device_control_temp_unit", + "image": "ic_temp_unit" + } + ], + "control": [ + { + "id": "130", + "type": "Speed", + "title": "device_control_speed", + "items": [ + { + "text": "base_1", + "cmd": "windlevel", + "value": 1 + }, + { + "text": "base_2", + "cmd": "windlevel", + "value": 2 + }, + { + "text": "base_3", + "cmd": "windlevel", + "value": 3 + }, + { + "text": "device_control_mode_auto", + "cmd": "windlevel", + "value": 4 + } + ] + }, + { + "id": "120", + "type": "Oscillation", + "title": "base_swing", + "items": [], + "cmd": "oscmode" + } + ], + "category": "Air Conditioner", + "version": { + "minControlVer": "2.6.0", + "minPairingVer": "2.6.0" + } + }, + "mainConf": { + "isSmart": true, + "isWifi": true, + "isBluetooth": true, + "isVoiceControl": true + }, + "resourcesConf": { + "imageSmallSrc": "https://resources.dreo-cloud.com/app/preSigned202409/11bfb25502ca5c483ba8ff6652fb758765.png", + "imageFullSrc": "https://resources.dreo-cloud.com/app/202401/15/129cd1fa2c564d6d88c48099778b0aca.zip", + "imageSmallDarkSrc": "", + "imageFullDarkSrc": "" + }, + "servicesConf": [ + { + "key": "user_manual", + "value": "https://resources.dreo-cloud.com/app/202403/16/a4677db0363b4a09841a4f668f39893a.pdf" + } + ], + "userManuals": [ + { + "url": "https://resources.dreo-cloud.com/app/202403/16/a4677db0363b4a09841a4f668f39893a.pdf", + "icon": null, + "desc": "User Manual", + "lang": "en" + } + ] + } + ] + } + } \ No newline at end of file diff --git a/tests/pydreo/test_all_devices.py b/tests/pydreo/test_all_devices.py index fe01aa5..c9c244f 100644 --- a/tests/pydreo/test_all_devices.py +++ b/tests/pydreo/test_all_devices.py @@ -30,3 +30,11 @@ def test_load_devices(self): assert len(self.pydreo_manager.devices) == 1 assert self.pydreo_manager.devices[0].speed_range == (1, 5) assert self.pydreo_manager.devices[0].preset_modes == ['normal', 'natural', 'sleep', 'auto'] + + def test_load_devices_unknown(self): + """Test get_devices() method request and API response.""" + + self.get_devices_file_name = "get_devices_UNKNOWN.json" + self.pydreo_manager.load_devices() + assert len(self.pydreo_manager.devices) == 1 + assert self.pydreo_manager.devices[0].type == "Unknown" \ No newline at end of file