diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 34774ab8..240978f6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,18 +27,18 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index fa783c06..6a2f092e 100755 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -35,5 +35,5 @@ jobs: VALIDATE_JSCPD: false VALIDATE_PYTHON_PYLINT: false VALIDATE_PYTHON_MYPY: false - VALIDATE_PYTHON_BLACK: false - VALIDATE_PYTHON_ISORT: false + # VALIDATE_PYTHON_BLACK: false + # VALIDATE_PYTHON_ISORT: false diff --git a/custom_components/midea_ac_lan/__init__.py b/custom_components/midea_ac_lan/__init__.py index a78ee2d0..014d7650 100644 --- a/custom_components/midea_ac_lan/__init__.py +++ b/custom_components/midea_ac_lan/__init__.py @@ -1,31 +1,32 @@ import logging -import voluptuous as vol + import homeassistant.helpers.config_validation as cv +import voluptuous as vol +from homeassistant.const import ( + CONF_CUSTOMIZE, + CONF_DEVICE_ID, + CONF_IP_ADDRESS, + CONF_NAME, + CONF_PORT, + CONF_PROTOCOL, + CONF_TOKEN, + CONF_TYPE, +) +from homeassistant.core import HomeAssistant + from .const import ( - DOMAIN, + ALL_PLATFORM, CONF_ACCOUNT, CONF_KEY, CONF_MODEL, - CONF_SUBTYPE, CONF_REFRESH_INTERVAL, + CONF_SUBTYPE, DEVICES, + DOMAIN, EXTRA_SWITCH, - ALL_PLATFORM, -) -from .midea_devices import MIDEA_DEVICES - -from homeassistant.core import HomeAssistant -from homeassistant.const import ( - CONF_NAME, - CONF_TOKEN, - CONF_IP_ADDRESS, - CONF_PORT, - CONF_PROTOCOL, - CONF_DEVICE_ID, - CONF_TYPE, - CONF_CUSTOMIZE, ) from .midea.devices import async_device_selector +from .midea_devices import MIDEA_DEVICES _LOGGER = logging.getLogger(__name__) @@ -34,18 +35,13 @@ async def update_listener(hass, config_entry): for platform in ALL_PLATFORM: await hass.config_entries.async_forward_entry_unload(config_entry, platform) for platform in ALL_PLATFORM: - hass.async_create_task(hass.config_entries.async_forward_entry_setup( - config_entry, platform)) + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(config_entry, platform) + ) device_id = config_entry.data.get(CONF_DEVICE_ID) - customize = config_entry.options.get( - CONF_CUSTOMIZE, "" - ) - ip_address = config_entry.options.get( - CONF_IP_ADDRESS, None - ) - refresh_interval = config_entry.options.get( - CONF_REFRESH_INTERVAL, None - ) + customize = config_entry.options.get(CONF_CUSTOMIZE, "") + ip_address = config_entry.options.get(CONF_IP_ADDRESS, None) + refresh_interval = config_entry.options.get(CONF_REFRESH_INTERVAL, None) dev = hass.data[DOMAIN][DEVICES].get(device_id) if dev: dev.set_customize(customize) @@ -60,7 +56,10 @@ async def async_setup(hass: HomeAssistant, hass_config: dict): attributes = [] for device_entities in MIDEA_DEVICES.values(): for attribute_name, attribute in device_entities.get("entities").items(): - if attribute.get("type") in EXTRA_SWITCH and attribute_name.value not in attributes: + if ( + attribute.get("type") in EXTRA_SWITCH + and attribute_name.value not in attributes + ): attributes.append(attribute_name.value) def service_set_attribute(service): @@ -72,11 +71,20 @@ def service_set_attribute(service): if attr == "fan_speed" and value == "auto": value = 102 item = MIDEA_DEVICES.get(dev.device_type).get("entities").get(attr) - if (item and (item.get("type") in EXTRA_SWITCH) or - (dev.device_type == 0xAC and attr == "fan_speed" and value in range(0, 103))): + if ( + item + and (item.get("type") in EXTRA_SWITCH) + or ( + dev.device_type == 0xAC + and attr == "fan_speed" + and value in range(0, 103) + ) + ): dev.set_attribute(attr=attr, value=value) else: - _LOGGER.error(f"Appliance [{device_id}] has no attribute {attr} or value is invalid") + _LOGGER.error( + f"Appliance [{device_id}] has no attribute {attr} or value is invalid" + ) def service_send_command(service): device_id = service.data.get("device_id") @@ -85,7 +93,9 @@ def service_send_command(service): try: cmd_body = bytearray.fromhex(cmd_body) except ValueError: - _LOGGER.error(f"Appliance [{device_id}] invalid cmd_body, a hexadecimal string required") + _LOGGER.error( + f"Appliance [{device_id}] invalid cmd_body, a hexadecimal string required" + ) return dev = hass.data[DOMAIN][DEVICES].get(device_id) if dev: @@ -99,9 +109,9 @@ def service_send_command(service): { vol.Required("device_id"): vol.Coerce(int), vol.Required("attribute"): vol.In(attributes), - vol.Required("value"): vol.Any(int, cv.boolean, str) + vol.Required("value"): vol.Any(int, cv.boolean, str), } - ) + ), ) hass.services.async_register( @@ -112,9 +122,9 @@ def service_send_command(service): { vol.Required("device_id"): vol.Coerce(int), vol.Required("cmd_type"): vol.In([2, 3]), - vol.Required("cmd_body"): str + vol.Required("cmd_body"): str, } - ) + ), ) return True @@ -128,7 +138,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry): if name is None: name = f"{device_id}" if device_type is None: - device_type = 0xac + device_type = 0xAC token = config_entry.data.get(CONF_TOKEN) key = config_entry.data.get(CONF_KEY) ip_address = config_entry.options.get(CONF_IP_ADDRESS, None) @@ -167,8 +177,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry): hass.data[DOMAIN][DEVICES] = {} hass.data[DOMAIN][DEVICES][device_id] = device for platform in ALL_PLATFORM: - hass.async_create_task(hass.config_entries.async_forward_entry_setup( - config_entry, platform)) + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(config_entry, platform) + ) config_entry.add_update_listener(update_listener) return True return False diff --git a/custom_components/midea_ac_lan/binary_sensor.py b/custom_components/midea_ac_lan/binary_sensor.py index 860ed591..2dcc4b3a 100644 --- a/custom_components/midea_ac_lan/binary_sensor.py +++ b/custom_components/midea_ac_lan/binary_sensor.py @@ -1,19 +1,15 @@ from homeassistant.components.binary_sensor import BinarySensorEntity -from homeassistant.const import Platform, CONF_DEVICE_ID, CONF_SENSORS -from .const import ( - DOMAIN, - DEVICES -) -from .midea_entity import MideaEntity +from homeassistant.const import CONF_DEVICE_ID, CONF_SENSORS, Platform + +from .const import DEVICES, DOMAIN from .midea_devices import MIDEA_DEVICES +from .midea_entity import MideaEntity async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_sensors = config_entry.options.get( - CONF_SENSORS, [] - ) + extra_sensors = config_entry.options.get(CONF_SENSORS, []) binary_sensors = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): if config["type"] == Platform.BINARY_SENSOR and entity_key in extra_sensors: diff --git a/custom_components/midea_ac_lan/climate.py b/custom_components/midea_ac_lan/climate.py index 7c9bb525..1e0df6bc 100644 --- a/custom_components/midea_ac_lan/climate.py +++ b/custom_components/midea_ac_lan/climate.py @@ -1,12 +1,11 @@ +import logging + from homeassistant.components.climate import ( ATTR_HVAC_MODE, - ClimateEntity, - ClimateEntityFeature, FAN_AUTO, FAN_HIGH, FAN_LOW, FAN_MEDIUM, - HVACMode, PRESET_AWAY, PRESET_BOOST, PRESET_COMFORT, @@ -18,23 +17,23 @@ SWING_OFF, SWING_ON, SWING_VERTICAL, + ClimateEntity, + ClimateEntityFeature, + HVACMode, ) from homeassistant.const import ( + ATTR_TEMPERATURE, + CONF_DEVICE_ID, + CONF_SWITCHES, MAJOR_VERSION, MINOR_VERSION, + PRECISION_HALVES, + PRECISION_WHOLE, Platform, UnitOfTemperature, - PRECISION_WHOLE, - PRECISION_HALVES, - ATTR_TEMPERATURE, - CONF_DEVICE_ID, - CONF_SWITCHES, ) -from .const import ( - DOMAIN, - DEVICES, -) +from .const import DEVICES, DOMAIN from .midea.devices.ac.device import DeviceAttributes as ACAttributes from .midea.devices.c3.device import DeviceAttributes as C3Attributes from .midea.devices.cc.device import DeviceAttributes as CCAttributes @@ -43,7 +42,6 @@ from .midea_devices import MIDEA_DEVICES from .midea_entity import MideaEntity -import logging _LOGGER = logging.getLogger(__name__) @@ -57,12 +55,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) devs = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): - if config["type"] == Platform.CLIMATE and (config.get("default") or entity_key in extra_switches): + if config["type"] == Platform.CLIMATE and ( + config.get("default") or entity_key in extra_switches + ): if device.device_type == 0xAC: devs.append(MideaACClimate(device, entity_key)) elif device.device_type == 0xCC: @@ -79,7 +77,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class MideaClimate(MideaEntity, ClimateEntity): # https://developers.home-assistant.io/blog/2024/01/24/climate-climateentityfeatures-expanded - _enable_turn_on_off_backwards_compatibility: bool = False # maybe remove after 2025.1 + _enable_turn_on_off_backwards_compatibility: bool = ( + False # maybe remove after 2025.1 + ) def __init__(self, device, entity_key): super().__init__(device, entity_key) @@ -87,10 +87,10 @@ def __init__(self, device, entity_key): @property def supported_features(self): features = ( - ClimateEntityFeature.TARGET_TEMPERATURE | - ClimateEntityFeature.FAN_MODE | - ClimateEntityFeature.PRESET_MODE | - ClimateEntityFeature.SWING_MODE + ClimateEntityFeature.TARGET_TEMPERATURE + | ClimateEntityFeature.FAN_MODE + | ClimateEntityFeature.PRESET_MODE + | ClimateEntityFeature.SWING_MODE ) if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 2): features |= ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON @@ -184,7 +184,8 @@ def set_temperature(self, **kwargs) -> None: try: mode = self._modes.index(hvac_mode.lower()) if hvac_mode else None self._device.set_target_temperature( - target_temperature=temperature, mode=mode) + target_temperature=temperature, mode=mode + ) except ValueError as e: _LOGGER.error(f"set_temperature {e}, kwargs = {kwargs}") @@ -223,28 +224,44 @@ def update_state(self, status): try: self.schedule_update_ha_state() except Exception as e: - _LOGGER.debug(f"Entity {self.entity_id} update_state {repr(e)}, status = {status}") + _LOGGER.debug( + f"Entity {self.entity_id} update_state {repr(e)}, status = {status}" + ) class MideaACClimate(MideaClimate): def __init__(self, device, entity_key): super().__init__(device, entity_key) - self._modes = [HVACMode.OFF, HVACMode.AUTO, HVACMode.COOL, HVACMode.DRY, HVACMode.HEAT, HVACMode.FAN_ONLY] + self._modes = [ + HVACMode.OFF, + HVACMode.AUTO, + HVACMode.COOL, + HVACMode.DRY, + HVACMode.HEAT, + HVACMode.FAN_ONLY, + ] self._fan_speeds = { FAN_SILENT.capitalize(): 20, FAN_LOW.capitalize(): 40, FAN_MEDIUM.capitalize(): 60, FAN_HIGH.capitalize(): 80, FAN_FULL_SPEED.capitalize(): 100, - FAN_AUTO.capitalize(): 102 + FAN_AUTO.capitalize(): 102, } self._swing_modes = [ SWING_OFF.capitalize(), SWING_VERTICAL.capitalize(), SWING_HORIZONTAL.capitalize(), - SWING_BOTH.capitalize() + SWING_BOTH.capitalize(), + ] + self._preset_modes = [ + PRESET_NONE, + PRESET_COMFORT, + PRESET_ECO, + PRESET_BOOST, + PRESET_SLEEP, + PRESET_AWAY, ] - self._preset_modes = [PRESET_NONE, PRESET_COMFORT, PRESET_ECO, PRESET_BOOST, PRESET_SLEEP, PRESET_AWAY] @property def fan_modes(self): @@ -268,12 +285,15 @@ def fan_mode(self) -> str: @property def target_temperature_step(self): - return PRECISION_WHOLE if self._device.temperature_step == 1 else PRECISION_HALVES + return ( + PRECISION_WHOLE if self._device.temperature_step == 1 else PRECISION_HALVES + ) @property def swing_mode(self): - swing_mode = (1 if self._device.get_attribute(ACAttributes.swing_vertical) else 0) + \ - (2 if self._device.get_attribute(ACAttributes.swing_horizontal) else 0) + swing_mode = ( + 1 if self._device.get_attribute(ACAttributes.swing_vertical) else 0 + ) + (2 if self._device.get_attribute(ACAttributes.swing_horizontal) else 0) return self._swing_modes[swing_mode] @property @@ -289,17 +309,23 @@ def set_swing_mode(self, swing_mode: str) -> None: swing = self._swing_modes.index(swing_mode.capitalize()) swing_vertical = swing & 1 > 0 swing_horizontal = swing & 2 > 0 - self._device.set_swing(swing_vertical=swing_vertical, swing_horizontal=swing_horizontal) + self._device.set_swing( + swing_vertical=swing_vertical, swing_horizontal=swing_horizontal + ) class MideaCCClimate(MideaClimate): def __init__(self, device, entity_key): super().__init__(device, entity_key) - self._modes = [HVACMode.OFF, HVACMode.FAN_ONLY, HVACMode.DRY, HVACMode.HEAT, HVACMode.COOL, HVACMode.AUTO] - self._swing_modes = [ - SWING_OFF.capitalize(), - SWING_ON.capitalize() + self._modes = [ + HVACMode.OFF, + HVACMode.FAN_ONLY, + HVACMode.DRY, + HVACMode.HEAT, + HVACMode.COOL, + HVACMode.AUTO, ] + self._swing_modes = [SWING_OFF.capitalize(), SWING_ON.capitalize()] self._preset_modes = [PRESET_NONE, PRESET_SLEEP, PRESET_ECO] @property @@ -316,7 +342,11 @@ def target_temperature_step(self): @property def swing_mode(self): - return SWING_ON.capitalize() if self._device.get_attribute(CCAttributes.swing) else SWING_OFF.capitalize() + return ( + SWING_ON.capitalize() + if self._device.get_attribute(CCAttributes.swing) + else SWING_OFF.capitalize() + ) def set_fan_mode(self, fan_mode: str) -> None: self._device.set_attribute(attr=CCAttributes.fan_speed, value=fan_mode) @@ -324,7 +354,7 @@ def set_fan_mode(self, fan_mode: str) -> None: def set_swing_mode(self, swing_mode: str) -> None: self._device.set_attribute( attr=CCAttributes.swing, - value=swing_mode.capitalize() == SWING_ON.capitalize() + value=swing_mode.capitalize() == SWING_ON.capitalize(), ) @@ -386,8 +416,11 @@ def supported_features(self): @property def target_temperature_step(self): - return PRECISION_WHOLE if \ - self._device.get_attribute(C3Attributes.zone_temp_type)[self._zone] else PRECISION_HALVES + return ( + PRECISION_WHOLE + if self._device.get_attribute(C3Attributes.zone_temp_type)[self._zone] + else PRECISION_HALVES + ) @property def min_temp(self): @@ -437,7 +470,8 @@ def set_temperature(self, **kwargs) -> None: try: mode = self._modes.index(hvac_mode.lower()) if hvac_mode else None self._device.set_target_temperature( - zone=self._zone, target_temperature=temperature, mode=mode) + zone=self._zone, target_temperature=temperature, mode=mode + ) except ValueError as e: _LOGGER.error(f"set_temperature {e}, kwargs = {kwargs}") @@ -457,7 +491,9 @@ def __init__(self, device, entity_key): @property def supported_features(self): - features = ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE + features = ( + ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE + ) if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 2): features |= ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON return features @@ -492,7 +528,11 @@ def target_temperature_high(self): @property def hvac_mode(self) -> str: - return HVACMode.HEAT if self._device.get_attribute(attr=FBAttributes.power) else HVACMode.OFF + return ( + HVACMode.HEAT + if self._device.get_attribute(attr=FBAttributes.power) + else HVACMode.OFF + ) @property def current_temperature(self): @@ -506,7 +546,9 @@ def set_temperature(self, **kwargs) -> None: if hvac_mode == HVACMode.OFF: self.turn_off() else: - self._device.set_attribute(attr=FBAttributes.target_temperature, value=temperature) + self._device.set_attribute( + attr=FBAttributes.target_temperature, value=temperature + ) def set_hvac_mode(self, hvac_mode: str) -> None: hvac_mode = hvac_mode.lower() diff --git a/custom_components/midea_ac_lan/config_flow.py b/custom_components/midea_ac_lan/config_flow.py index 900bd5ce..6c5b1ecc 100644 --- a/custom_components/midea_ac_lan/config_flow.py +++ b/custom_components/midea_ac_lan/config_flow.py @@ -1,48 +1,57 @@ -import voluptuous as vol import os + +import voluptuous as vol + try: from homeassistant.helpers.json import save_json except ImportError: from homeassistant.util.json import save_json -from homeassistant.util.json import load_json + import logging -from .const import ( - DOMAIN, - EXTRA_SENSOR, - EXTRA_CONTROL, - CONF_ACCOUNT, - CONF_SERVER, - CONF_KEY, - CONF_MODEL, - CONF_SUBTYPE, - CONF_REFRESH_INTERVAL -) + +import homeassistant.helpers.config_validation as cv from homeassistant import config_entries -from homeassistant.core import callback from homeassistant.const import ( - CONF_NAME, + CONF_CUSTOMIZE, CONF_DEVICE, - CONF_TOKEN, CONF_DEVICE_ID, - CONF_TYPE, CONF_IP_ADDRESS, - CONF_PROTOCOL, + CONF_NAME, + CONF_PASSWORD, CONF_PORT, - CONF_SWITCHES, + CONF_PROTOCOL, CONF_SENSORS, - CONF_CUSTOMIZE, - CONF_PASSWORD, + CONF_SWITCHES, + CONF_TOKEN, + CONF_TYPE, ) -import homeassistant.helpers.config_validation as cv +from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_create_clientsession -from .midea.core.discover import discover +from homeassistant.util.json import load_json + +from .const import ( + CONF_ACCOUNT, + CONF_KEY, + CONF_MODEL, + CONF_REFRESH_INTERVAL, + CONF_SERVER, + CONF_SUBTYPE, + DOMAIN, + EXTRA_CONTROL, + EXTRA_SENSOR, +) from .midea.core.cloud import get_midea_cloud from .midea.core.device import MiedaDevice +from .midea.core.discover import discover from .midea_devices import MIDEA_DEVICES _LOGGER = logging.getLogger(__name__) -ADD_WAY = {"discovery": "Discover automatically", "manually": "Configure manually", "list": "List all appliances only"} +ADD_WAY = { + "discovery": "Discover automatically", + "manually": "Configure manually", + "list": "List all appliances only", +} PROTOCOLS = {1: "V1", 2: "V2", 3: "V3"} STORAGE_PATH = f".storage/{DOMAIN}" @@ -57,7 +66,7 @@ PRESET_ACCOUNT = [ 39182118275972017797890111985649342047468653967530949796945843010512, 29406100301096535908214728322278519471982973450672552249652548883645, - 39182118275972017797890111985649342050088014265865102175083010656997 + 39182118275972017797890111985649342050088014265865102175083010656997, ] @@ -79,7 +88,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def _save_device_config(self, data: dict): os.makedirs(self.hass.config.path(STORAGE_PATH), exist_ok=True) - record_file = self.hass.config.path(f"{STORAGE_PATH}/{data[CONF_DEVICE_ID]}.json") + record_file = self.hass.config.path( + f"{STORAGE_PATH}/{data[CONF_DEVICE_ID]}.json" + ) save_json(record_file, data) def _load_device_config(self, device_id): @@ -90,32 +101,46 @@ def _load_device_config(self, device_id): def _save_account(self, account: dict): os.makedirs(self.hass.config.path(STORAGE_PATH), exist_ok=True) record_file = self.hass.config.path(f"{STORAGE_PATH}/account.json") - account[CONF_PASSWORD] = format((int(account[CONF_ACCOUNT].encode("utf-8").hex(), 16) ^ - int(account[CONF_PASSWORD].encode("utf-8").hex(), 16)), 'x') + account[CONF_PASSWORD] = format( + ( + int(account[CONF_ACCOUNT].encode("utf-8").hex(), 16) + ^ int(account[CONF_PASSWORD].encode("utf-8").hex(), 16) + ), + "x", + ) save_json(record_file, account) def _load_account(self): record_file = self.hass.config.path(f"{STORAGE_PATH}/account.json") json_data = load_json(record_file, default={}) if CONF_ACCOUNT in json_data.keys(): - json_data[CONF_PASSWORD] = bytes.fromhex(format(( - int(json_data[CONF_PASSWORD], 16) ^ - int(json_data[CONF_ACCOUNT].encode("utf-8").hex(), 16)), 'X') - ).decode('UTF-8') + json_data[CONF_PASSWORD] = bytes.fromhex( + format( + ( + int(json_data[CONF_PASSWORD], 16) + ^ int(json_data[CONF_ACCOUNT].encode("utf-8").hex(), 16) + ), + "X", + ) + ).decode("UTF-8") return json_data @staticmethod def _check_storage_device(device: dict, storage_device: dict): if storage_device.get(CONF_SUBTYPE) is None: return False - if (device.get(CONF_PROTOCOL) == 3 and - (storage_device.get(CONF_TOKEN) is None or storage_device.get(CONF_KEY) is None)): + if device.get(CONF_PROTOCOL) == 3 and ( + storage_device.get(CONF_TOKEN) is None + or storage_device.get(CONF_KEY) is None + ): return False return True def _already_configured(self, device_id, ip_address): for entry in self._async_current_entries(): - if device_id == entry.data.get(CONF_DEVICE_ID) or ip_address == entry.data.get(CONF_IP_ADDRESS): + if device_id == entry.data.get( + CONF_DEVICE_ID + ) or ip_address == entry.data.get(CONF_IP_ADDRESS): return True return False @@ -130,10 +155,10 @@ async def async_step_user(self, user_input=None, error=None): return await self.async_step_list() return self.async_show_form( step_id="user", - data_schema=vol.Schema({ - vol.Required("action", default="discovery"): vol.In(ADD_WAY) - }), - errors={"base": error} if error else None + data_schema=vol.Schema( + {vol.Required("action", default="discovery"): vol.In(ADD_WAY)} + ), + errors={"base": error} if error else None, ) async def async_step_login(self, user_input=None, error=None): @@ -145,13 +170,13 @@ async def async_step_login(self, user_input=None, error=None): session=self.session, cloud_name=SERVERS[user_input[CONF_SERVER]], account=user_input[CONF_ACCOUNT], - password=user_input[CONF_PASSWORD] + password=user_input[CONF_PASSWORD], ) if await self.cloud.login(): self.account = { CONF_ACCOUNT: user_input[CONF_ACCOUNT], CONF_PASSWORD: user_input[CONF_PASSWORD], - CONF_SERVER: SERVERS[user_input[CONF_SERVER]] + CONF_SERVER: SERVERS[user_input[CONF_SERVER]], } self._save_account(self.account) return await self.async_step_auto() @@ -159,29 +184,35 @@ async def async_step_login(self, user_input=None, error=None): return await self.async_step_login(error="login_failed") return self.async_show_form( step_id="login", - data_schema=vol.Schema({ - vol.Required(CONF_ACCOUNT): str, - vol.Required(CONF_PASSWORD): str, - vol.Required(CONF_SERVER, default=1): vol.In(SERVERS) - }), - errors={"base": error} if error else None + data_schema=vol.Schema( + { + vol.Required(CONF_ACCOUNT): str, + vol.Required(CONF_PASSWORD): str, + vol.Required(CONF_SERVER, default=1): vol.In(SERVERS), + } + ), + errors={"base": error} if error else None, ) async def async_step_list(self, user_input=None, error=None): all_devices = discover() if len(all_devices) > 0: - table = "Appliance code|Type|IP address|SN|Supported\n:--:|:--:|:--:|:--:|:--:" + table = ( + "Appliance code|Type|IP address|SN|Supported\n:--:|:--:|:--:|:--:|:--:" + ) for device_id, device in all_devices.items(): supported = device.get(CONF_TYPE) in self.supports.keys() - table += f"\n{device_id}|{'%02X' % device.get(CONF_TYPE)}|{device.get(CONF_IP_ADDRESS)}|" \ - f"{device.get('sn')}|" \ - f"{'YES' if supported else 'NO'}" + table += ( + f"\n{device_id}|{'%02X' % device.get(CONF_TYPE)}|{device.get(CONF_IP_ADDRESS)}|" + f"{device.get('sn')}|" + f"{'YES' if supported else 'NO'}" + ) else: table = "Not found" return self.async_show_form( step_id="list", description_placeholders={"table": table}, - errors={"base": error} if error else None + errors={"base": error} if error else None, ) async def async_step_discovery(self, user_input=None, error=None): @@ -194,18 +225,19 @@ async def async_step_discovery(self, user_input=None, error=None): self.available_device = {} for device_id, device in self.devices.items(): if not self._already_configured(device_id, device.get(CONF_IP_ADDRESS)): - self.available_device[device_id] = \ + self.available_device[device_id] = ( f"{device_id} ({self.supports.get(device.get(CONF_TYPE))})" + ) if len(self.available_device) > 0: return await self.async_step_auto() else: return await self.async_step_discovery(error="no_devices") return self.async_show_form( step_id="discovery", - data_schema=vol.Schema({ - vol.Required(CONF_IP_ADDRESS, default="auto"): str - }), - errors={"base": error} if error else None + data_schema=vol.Schema( + {vol.Required(CONF_IP_ADDRESS, default="auto"): str} + ), + errors={"base": error} if error else None, ) async def async_step_auto(self, user_input=None, error=None): @@ -224,9 +256,11 @@ async def async_step_auto(self, user_input=None, error=None): CONF_NAME: storage_device.get(CONF_NAME), CONF_SUBTYPE: storage_device.get(CONF_SUBTYPE), CONF_TOKEN: storage_device.get(CONF_TOKEN), - CONF_KEY: storage_device.get(CONF_KEY) + CONF_KEY: storage_device.get(CONF_KEY), } - _LOGGER.debug(f"Loaded configuration for device {device_id} from storage") + _LOGGER.debug( + f"Loaded configuration for device {device_id} from storage" + ) return await self.async_step_manually() else: if CONF_ACCOUNT not in self.account.keys(): @@ -237,8 +271,11 @@ async def async_step_auto(self, user_input=None, error=None): self.session = async_create_clientsession(self.hass) if self.cloud is None: self.cloud = get_midea_cloud( - self.account[CONF_SERVER], self.session, self.account[CONF_ACCOUNT], - self.account[CONF_PASSWORD]) + self.account[CONF_SERVER], + self.session, + self.account[CONF_ACCOUNT], + self.account[CONF_PASSWORD], + ) if not await self.cloud.login(): return await self.async_step_login() self.found_device = { @@ -254,12 +291,19 @@ async def async_step_auto(self, user_input=None, error=None): self.found_device[CONF_SUBTYPE] = device_info.get("model_number") if device.get(CONF_PROTOCOL) == 3: if self.account[CONF_SERVER] == "美的美居": - _LOGGER.debug("Try to get the Token and the Key use the preset MSmartHome account") + _LOGGER.debug( + "Try to get the Token and the Key use the preset MSmartHome account" + ) self.cloud = get_midea_cloud( "MSmartHome", self.session, - bytes.fromhex(format((PRESET_ACCOUNT[0] ^ PRESET_ACCOUNT[1]), 'X')).decode('ASCII'), - bytes.fromhex(format((PRESET_ACCOUNT[0] ^ PRESET_ACCOUNT[2]), 'X')).decode('ASCII')) + bytes.fromhex( + format((PRESET_ACCOUNT[0] ^ PRESET_ACCOUNT[1]), "X") + ).decode("ASCII"), + bytes.fromhex( + format((PRESET_ACCOUNT[0] ^ PRESET_ACCOUNT[2]), "X") + ).decode("ASCII"), + ) if not await self.cloud.login(): return await self.async_step_auto(error="preset_account") keys = await self.cloud.get_keys(user_input[CONF_DEVICE]) @@ -275,7 +319,7 @@ async def async_step_auto(self, user_input=None, error=None): protocol=3, model=device.get(CONF_MODEL), subtype=0, - attributes={} + attributes={}, ) if dm.connect(refresh_status=False): dm.close_socket() @@ -288,11 +332,14 @@ async def async_step_auto(self, user_input=None, error=None): return self.async_show_form( step_id="auto", - data_schema=vol.Schema({ - vol.Required(CONF_DEVICE, default=list(self.available_device.keys())[0]): - vol.In(self.available_device), - }), - errors={"base": error} if error else None + data_schema=vol.Schema( + { + vol.Required( + CONF_DEVICE, default=list(self.available_device.keys())[0] + ): vol.In(self.available_device), + } + ), + errors={"base": error} if error else None, ) async def async_step_manually(self, user_input=None, error=None): @@ -305,14 +352,16 @@ async def async_step_manually(self, user_input=None, error=None): CONF_PORT: user_input[CONF_PORT], CONF_MODEL: user_input[CONF_MODEL], CONF_TOKEN: user_input[CONF_TOKEN], - CONF_KEY: user_input[CONF_KEY] + CONF_KEY: user_input[CONF_KEY], } try: bytearray.fromhex(user_input[CONF_TOKEN]) bytearray.fromhex(user_input[CONF_KEY]) except ValueError: return await self.async_step_manually(error="invalid_token") - if user_input[CONF_PROTOCOL] == 3 and (len(user_input[CONF_TOKEN]) == 0 or len(user_input[CONF_KEY]) == 0): + if user_input[CONF_PROTOCOL] == 3 and ( + len(user_input[CONF_TOKEN]) == 0 or len(user_input[CONF_KEY]) == 0 + ): return await self.async_step_manually(error="invalid_token") dm = MiedaDevice( name="", @@ -325,7 +374,7 @@ async def async_step_manually(self, user_input=None, error=None): protocol=user_input[CONF_PROTOCOL], model=user_input[CONF_MODEL], subtype=0, - attributes={} + attributes={}, ) if dm.connect(refresh_status=False): dm.close_socket() @@ -340,61 +389,90 @@ async def async_step_manually(self, user_input=None, error=None): CONF_SUBTYPE: user_input[CONF_SUBTYPE], CONF_TOKEN: user_input[CONF_TOKEN], CONF_KEY: user_input[CONF_KEY], - } + } self._save_device_config(data) return self.async_create_entry( - title=f"{user_input[CONF_NAME]}", - data=data + title=f"{user_input[CONF_NAME]}", data=data ) else: return await self.async_step_manually(error="config_incorrect") return self.async_show_form( step_id="manually", - data_schema=vol.Schema({ - vol.Required( - CONF_NAME, - default=(self.found_device.get(CONF_NAME) - if self.found_device.get(CONF_NAME) - else self.supports.get(self.found_device.get(CONF_TYPE))) - ): str, - vol.Required( - CONF_DEVICE_ID, - default=self.found_device.get(CONF_DEVICE_ID) - ): int, - vol.Required( - CONF_TYPE, - default=self.found_device.get(CONF_TYPE) if self.found_device.get(CONF_TYPE) else 0xac - ): vol.In(self.supports), - vol.Required( - CONF_IP_ADDRESS, - default=self.found_device.get(CONF_IP_ADDRESS) - ): str, - vol.Required( - CONF_PORT, - default=self.found_device.get(CONF_PORT) if self.found_device.get(CONF_PORT) else 6444 - ): int, - vol.Required( - CONF_PROTOCOL, - default=self.found_device.get(CONF_PROTOCOL) if self.found_device.get(CONF_PROTOCOL) else 3 - ): vol.In(PROTOCOLS), - vol.Required( - CONF_MODEL, - default=self.found_device.get(CONF_MODEL) if self.found_device.get(CONF_MODEL) else "Unknown" - ): str, - vol.Required( - CONF_SUBTYPE, - default=self.found_device.get(CONF_SUBTYPE) if self.found_device.get(CONF_SUBTYPE) else 0 - ): int, - vol.Optional( - CONF_TOKEN, - default=self.found_device.get(CONF_TOKEN) if self.found_device.get(CONF_TOKEN) else "" - ): str, - vol.Optional( - CONF_KEY, - default=self.found_device.get(CONF_KEY) if self.found_device.get(CONF_KEY) else "" - ): str, - }), - errors={"base": error} if error else None + data_schema=vol.Schema( + { + vol.Required( + CONF_NAME, + default=( + self.found_device.get(CONF_NAME) + if self.found_device.get(CONF_NAME) + else self.supports.get(self.found_device.get(CONF_TYPE)) + ), + ): str, + vol.Required( + CONF_DEVICE_ID, default=self.found_device.get(CONF_DEVICE_ID) + ): int, + vol.Required( + CONF_TYPE, + default=( + self.found_device.get(CONF_TYPE) + if self.found_device.get(CONF_TYPE) + else 0xAC + ), + ): vol.In(self.supports), + vol.Required( + CONF_IP_ADDRESS, default=self.found_device.get(CONF_IP_ADDRESS) + ): str, + vol.Required( + CONF_PORT, + default=( + self.found_device.get(CONF_PORT) + if self.found_device.get(CONF_PORT) + else 6444 + ), + ): int, + vol.Required( + CONF_PROTOCOL, + default=( + self.found_device.get(CONF_PROTOCOL) + if self.found_device.get(CONF_PROTOCOL) + else 3 + ), + ): vol.In(PROTOCOLS), + vol.Required( + CONF_MODEL, + default=( + self.found_device.get(CONF_MODEL) + if self.found_device.get(CONF_MODEL) + else "Unknown" + ), + ): str, + vol.Required( + CONF_SUBTYPE, + default=( + self.found_device.get(CONF_SUBTYPE) + if self.found_device.get(CONF_SUBTYPE) + else 0 + ), + ): int, + vol.Optional( + CONF_TOKEN, + default=( + self.found_device.get(CONF_TOKEN) + if self.found_device.get(CONF_TOKEN) + else "" + ), + ): str, + vol.Optional( + CONF_KEY, + default=( + self.found_device.get(CONF_KEY) + if self.found_device.get(CONF_KEY) + else "" + ), + ): str, + } + ), + errors={"base": error} if error else None, ) @staticmethod @@ -408,7 +486,7 @@ def __init__(self, config_entry: config_entries.ConfigEntry): self._config_entry = config_entry self._device_type = config_entry.data.get(CONF_TYPE) if self._device_type is None: - self._device_type = 0xac + self._device_type = 0xAC if CONF_SENSORS in self._config_entry.options: for key in self._config_entry.options[CONF_SENSORS]: if key not in MIDEA_DEVICES[self._device_type]["entities"]: @@ -425,66 +503,61 @@ async def async_step_init(self, user_input=None): return self.async_create_entry(title="", data=user_input) sensors = {} switches = {} - for attribute, attribute_config in MIDEA_DEVICES.get(self._device_type).get("entities").items(): - attribute_name = attribute if isinstance(attribute, str) else attribute.value + for attribute, attribute_config in ( + MIDEA_DEVICES.get(self._device_type).get("entities").items() + ): + attribute_name = ( + attribute if isinstance(attribute, str) else attribute.value + ) if attribute_config.get("type") in EXTRA_SENSOR: sensors[attribute_name] = attribute_config.get("name") - elif attribute_config.get("type") in EXTRA_CONTROL and not attribute_config.get("default"): + elif attribute_config.get( + "type" + ) in EXTRA_CONTROL and not attribute_config.get("default"): switches[attribute_name] = attribute_config.get("name") - ip_address = self._config_entry.options.get( - CONF_IP_ADDRESS, None - ) + ip_address = self._config_entry.options.get(CONF_IP_ADDRESS, None) if ip_address is None: - ip_address = self._config_entry.data.get( - CONF_IP_ADDRESS, None - ) - refresh_interval = self._config_entry.options.get( - CONF_REFRESH_INTERVAL, 30 + ip_address = self._config_entry.data.get(CONF_IP_ADDRESS, None) + refresh_interval = self._config_entry.options.get(CONF_REFRESH_INTERVAL, 30) + extra_sensors = list( + set(sensors.keys()) & set(self._config_entry.options.get(CONF_SENSORS, [])) ) - extra_sensors = list(set(sensors.keys()) & set(self._config_entry.options.get( - CONF_SENSORS, [] - ))) - extra_switches = list(set(switches.keys()) & set(self._config_entry.options.get( - CONF_SWITCHES, [] - ))) - customize = self._config_entry.options.get( - CONF_CUSTOMIZE, "" + extra_switches = list( + set(switches.keys()) + & set(self._config_entry.options.get(CONF_SWITCHES, [])) + ) + customize = self._config_entry.options.get(CONF_CUSTOMIZE, "") + data_schema = vol.Schema( + { + vol.Required(CONF_IP_ADDRESS, default=ip_address): str, + vol.Required(CONF_REFRESH_INTERVAL, default=refresh_interval): int, + } ) - data_schema = vol.Schema({ - vol.Required( - CONF_IP_ADDRESS, - default=ip_address - ): str, - vol.Required( - CONF_REFRESH_INTERVAL, - default=refresh_interval - ): int - }) if len(sensors) > 0: - data_schema = data_schema.extend({ - vol.Required( - CONF_SENSORS, - default=extra_sensors, - ): - cv.multi_select(sensors) - }) + data_schema = data_schema.extend( + { + vol.Required( + CONF_SENSORS, + default=extra_sensors, + ): cv.multi_select(sensors) + } + ) if len(switches) > 0: - data_schema = data_schema.extend({ - vol.Required( - CONF_SWITCHES, - default=extra_switches, - ): - cv.multi_select(switches) - }) - data_schema = data_schema.extend({ - vol.Optional( - CONF_CUSTOMIZE, - default=customize, - ): - str - }) - - return self.async_show_form( - step_id="init", - data_schema=data_schema + data_schema = data_schema.extend( + { + vol.Required( + CONF_SWITCHES, + default=extra_switches, + ): cv.multi_select(switches) + } + ) + data_schema = data_schema.extend( + { + vol.Optional( + CONF_CUSTOMIZE, + default=customize, + ): str + } ) + + return self.async_show_form(step_id="init", data_schema=data_schema) diff --git a/custom_components/midea_ac_lan/const.py b/custom_components/midea_ac_lan/const.py index dc5439db..d6d89474 100644 --- a/custom_components/midea_ac_lan/const.py +++ b/custom_components/midea_ac_lan/const.py @@ -1,4 +1,5 @@ from homeassistant.const import Platform + DOMAIN = "midea_ac_lan" COMPONENT = "component" DEVICES = "devices" @@ -10,6 +11,11 @@ CONF_REFRESH_INTERVAL = "refresh_interval" EXTRA_SENSOR = [Platform.SENSOR, Platform.BINARY_SENSOR] EXTRA_SWITCH = [Platform.SWITCH, Platform.LOCK, Platform.SELECT, Platform.NUMBER] -EXTRA_CONTROL = [Platform.CLIMATE, Platform.WATER_HEATER, Platform.FAN, Platform.HUMIDIFIER, Platform.LIGHT] + \ - EXTRA_SWITCH +EXTRA_CONTROL = [ + Platform.CLIMATE, + Platform.WATER_HEATER, + Platform.FAN, + Platform.HUMIDIFIER, + Platform.LIGHT, +] + EXTRA_SWITCH ALL_PLATFORM = EXTRA_SENSOR + EXTRA_CONTROL diff --git a/custom_components/midea_ac_lan/fan.py b/custom_components/midea_ac_lan/fan.py index bc49c8f4..7ddd8614 100644 --- a/custom_components/midea_ac_lan/fan.py +++ b/custom_components/midea_ac_lan/fan.py @@ -1,36 +1,34 @@ +import logging from typing import Any from homeassistant.components.fan import FanEntity, FanEntityFeature from homeassistant.const import ( - Platform, CONF_DEVICE_ID, CONF_SWITCHES, + STATE_OFF, STATE_ON, - STATE_OFF -) -from .const import ( - DOMAIN, - DEVICES, + Platform, ) + +from .const import DEVICES, DOMAIN from .midea.devices.ac.device import DeviceAttributes as ACAttributes from .midea.devices.ce.device import DeviceAttributes as CEAttributes from .midea.devices.x40.device import DeviceAttributes as X40Attributes from .midea_devices import MIDEA_DEVICES from .midea_entity import MideaEntity -import logging _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) devs = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): - if config["type"] == Platform.FAN and (config.get("default") or entity_key in extra_switches): + if config["type"] == Platform.FAN and ( + config.get("default") or entity_key in extra_switches + ): if device.device_type == 0xFA: devs.append(MideaFAFan(device, entity_key)) elif device.device_type == 0xB6: @@ -62,7 +60,9 @@ def turn_on( @property def preset_modes(self): - return self._device.preset_modes if hasattr(self._device, "preset_modes") else None + return ( + self._device.preset_modes if hasattr(self._device, "preset_modes") else None + ) @property def is_on(self) -> bool: @@ -111,27 +111,37 @@ def update_state(self, status): try: self.schedule_update_ha_state() except Exception as e: - _LOGGER.debug(f"Entity {self.entity_id} update_state {repr(e)}, status = {status}") + _LOGGER.debug( + f"Entity {self.entity_id} update_state {repr(e)}, status = {status}" + ) class MideaFAFan(MideaFan): def __init__(self, device, entity_key): super().__init__(device, entity_key) - self._attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.OSCILLATE | FanEntityFeature.PRESET_MODE + self._attr_supported_features = ( + FanEntityFeature.SET_SPEED + | FanEntityFeature.OSCILLATE + | FanEntityFeature.PRESET_MODE + ) self._attr_speed_count = self._device.speed_count class MideaB6Fan(MideaFan): def __init__(self, device, entity_key): super().__init__(device, entity_key) - self._attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE + self._attr_supported_features = ( + FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE + ) self._attr_speed_count = self._device.speed_count class MideaACFreshAirFan(MideaFan): def __init__(self, device, entity_key): super().__init__(device, entity_key) - self._attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE + self._attr_supported_features = ( + FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE + ) self._attr_speed_count = 100 @property @@ -140,7 +150,11 @@ def preset_modes(self): @property def state(self): - return STATE_ON if self._device.get_attribute(ACAttributes.fresh_air_power) else STATE_OFF + return ( + STATE_ON + if self._device.get_attribute(ACAttributes.fresh_air_power) + else STATE_OFF + ) @property def is_on(self) -> bool: @@ -162,7 +176,9 @@ def toggle(self): def set_percentage(self, percentage: int): fan_speed = int(percentage / self.percentage_step + 0.5) - self._device.set_attribute(attr=ACAttributes.fresh_air_fan_speed, value=fan_speed) + self._device.set_attribute( + attr=ACAttributes.fresh_air_fan_speed, value=fan_speed + ) def set_preset_mode(self, preset_mode: str): self._device.set_attribute(attr=ACAttributes.fresh_air_mode, value=preset_mode) @@ -175,7 +191,9 @@ def preset_mode(self): class MideaCEFan(MideaFan): def __init__(self, device, entity_key): super().__init__(device, entity_key) - self._attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE + self._attr_supported_features = ( + FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE + ) self._attr_speed_count = self._device.speed_count def turn_on(self, percentage, preset_mode, **kwargs): @@ -188,12 +206,18 @@ async def async_set_percentage(self, percentage: int): class Midea40Fan(MideaFan): def __init__(self, device, entity_key): super().__init__(device, entity_key) - self._attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.OSCILLATE + self._attr_supported_features = ( + FanEntityFeature.SET_SPEED | FanEntityFeature.OSCILLATE + ) self._attr_speed_count = 2 @property def state(self): - return STATE_ON if self._device.get_attribute(attr=X40Attributes.fan_speed) > 0 else STATE_OFF + return ( + STATE_ON + if self._device.get_attribute(attr=X40Attributes.fan_speed) > 0 + else STATE_OFF + ) def turn_on(self, percentage, preset_mode, **kwargs): self._device.set_attribute(attr=X40Attributes.fan_speed, value=1) diff --git a/custom_components/midea_ac_lan/humidifier.py b/custom_components/midea_ac_lan/humidifier.py index 35ec5a0a..1027b585 100644 --- a/custom_components/midea_ac_lan/humidifier.py +++ b/custom_components/midea_ac_lan/humidifier.py @@ -1,33 +1,28 @@ +import logging + from homeassistant.components.humidifier import ( HumidifierDeviceClass, HumidifierEntity, HumidifierEntityFeature, ) -from homeassistant.const import ( - Platform, - CONF_DEVICE_ID, - CONF_SWITCHES, -) -from .const import ( - DOMAIN, - DEVICES, -) +from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform + +from .const import DEVICES, DOMAIN from .midea_devices import MIDEA_DEVICES from .midea_entity import MideaEntity -import logging _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) devs = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): - if config["type"] == Platform.HUMIDIFIER and (config.get("default") or entity_key in extra_switches): + if config["type"] == Platform.HUMIDIFIER and ( + config.get("default") or entity_key in extra_switches + ): if device.device_type == 0xA1: devs.append(MideaA1Humidifier(device, entity_key)) if device.device_type == 0xFD: @@ -79,7 +74,9 @@ def update_state(self, status): try: self.schedule_update_ha_state() except Exception as e: - _LOGGER.debug(f"Entity {self.entity_id} update_state {repr(e)}, status = {status}") + _LOGGER.debug( + f"Entity {self.entity_id} update_state {repr(e)}, status = {status}" + ) class MideaA1Humidifier(MideaHumidifier): diff --git a/custom_components/midea_ac_lan/light.py b/custom_components/midea_ac_lan/light.py index 5834574d..91747b87 100644 --- a/custom_components/midea_ac_lan/light.py +++ b/custom_components/midea_ac_lan/light.py @@ -5,25 +5,19 @@ ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, - LightEntity, - LightEntityFeature, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, + LightEntity, + LightEntityFeature, ) -from homeassistant.const import ( - Platform, - CONF_DEVICE_ID, - CONF_SWITCHES, -) -from .const import ( - DOMAIN, - DEVICES -) +from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform + +from .const import DEVICES, DOMAIN from .midea.devices.x13.device import DeviceAttributes as X13Attributes -from .midea_entity import MideaEntity from .midea_devices import MIDEA_DEVICES +from .midea_entity import MideaEntity _LOGGER = logging.getLogger(__name__) @@ -31,12 +25,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) devs = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): - if config["type"] == Platform.LIGHT and (config.get("default") or entity_key in extra_switches): + if config["type"] == Platform.LIGHT and ( + config.get("default") or entity_key in extra_switches + ): devs.append(MideaLight(device, entity_key)) async_add_entities(devs) @@ -110,7 +104,9 @@ def turn_on(self, **kwargs: Any): if key == ATTR_BRIGHTNESS: self._device.set_attribute(attr=X13Attributes.brightness, value=value) if key == ATTR_COLOR_TEMP: - self._device.set_attribute(attr=X13Attributes.color_temperature, value=round(1000000 / value)) + self._device.set_attribute( + attr=X13Attributes.color_temperature, value=round(1000000 / value) + ) if key == ATTR_EFFECT: self._device.set_attribute(attr=X13Attributes.effect, value=value) @@ -121,4 +117,6 @@ def update_state(self, status): try: self.schedule_update_ha_state() except Exception as e: - _LOGGER.debug(f"Entity {self.entity_id} update_state {repr(e)}, status = {status}") + _LOGGER.debug( + f"Entity {self.entity_id} update_state {repr(e)}, status = {status}" + ) diff --git a/custom_components/midea_ac_lan/lock.py b/custom_components/midea_ac_lan/lock.py index df43e084..f62eeb54 100644 --- a/custom_components/midea_ac_lan/lock.py +++ b/custom_components/midea_ac_lan/lock.py @@ -1,23 +1,15 @@ -from .midea_entity import MideaEntity -from .midea_devices import MIDEA_DEVICES from homeassistant.components.lock import LockEntity -from homeassistant.const import ( - Platform, - CONF_DEVICE_ID, - CONF_SWITCHES -) -from .const import ( - DOMAIN, - DEVICES, -) +from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform + +from .const import DEVICES, DOMAIN +from .midea_devices import MIDEA_DEVICES +from .midea_entity import MideaEntity async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) locks = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): if config["type"] == Platform.LOCK and entity_key in extra_switches: diff --git a/custom_components/midea_ac_lan/midea/backports/myenum.py b/custom_components/midea_ac_lan/midea/backports/myenum.py index e3ef7d54..c4de8762 100644 --- a/custom_components/midea_ac_lan/midea/backports/myenum.py +++ b/custom_components/midea_ac_lan/midea/backports/myenum.py @@ -1,4 +1,5 @@ """Enum backports from standard lib.""" + from __future__ import annotations from enum import Enum diff --git a/custom_components/midea_ac_lan/midea/core/cloud.py b/custom_components/midea_ac_lan/midea/core/cloud.py index 286ea204..f4eb2eb0 100644 --- a/custom_components/midea_ac_lan/midea/core/cloud.py +++ b/custom_components/midea_ac_lan/midea/core/cloud.py @@ -1,12 +1,19 @@ -import logging -import time +import base64 import datetime import json -import base64 +import logging +import time +from secrets import token_hex from threading import Lock + from aiohttp import ClientSession -from secrets import token_hex -from .security import CloudSecurity, MeijuCloudSecurity, MSmartCloudSecurity, MideaAirSecurity + +from .security import ( + CloudSecurity, + MeijuCloudSecurity, + MideaAirSecurity, + MSmartCloudSecurity, +) _LOGGER = logging.getLogger(__name__) @@ -16,16 +23,22 @@ "app_id": "900", "app_key": "46579c15", "login_key": "ad0ee21d48a64bf49f4fb583ab76e799", - "iot_key": bytes.fromhex(format(9795516279659324117647275084689641883661667, 'x')).decode(), - "hmac_key": bytes.fromhex(format(117390035944627627450677220413733956185864939010425, 'x')).decode(), + "iot_key": bytes.fromhex( + format(9795516279659324117647275084689641883661667, "x") + ).decode(), + "hmac_key": bytes.fromhex( + format(117390035944627627450677220413733956185864939010425, "x") + ).decode(), "api_url": "https://mp-prod.smartmidea.net/mas/v5/app/proxy?alias=", }, "MSmartHome": { "class_name": "MSmartHomeCloud", "app_id": "1010", "app_key": "ac21b9f9cbfe4ca5a88562ef25e2b768", - "iot_key": bytes.fromhex(format(7882822598523843940, 'x')).decode(), - "hmac_key": bytes.fromhex(format(117390035944627627450677220413733956185864939010425, 'x')).decode(), + "iot_key": bytes.fromhex(format(7882822598523843940, "x")).decode(), + "hmac_key": bytes.fromhex( + format(117390035944627627450677220413733956185864939010425, "x") + ).decode(), "api_url": "https://mp-prod.appsmb.com/mas/v5/app/proxy?alias=", }, "Midea Air": { @@ -45,28 +58,28 @@ "app_id": "1005", "app_key": "434a209a5ce141c3b726de067835d7f0", "api_url": "https://mapp.appsmb.com", - } + }, } default_keys = { 99: { "token": "ee755a84a115703768bcc7c6c13d3d629aa416f1e2fd798beb9f78cbb1381d09" - "1cc245d7b063aad2a900e5b498fbd936c811f5d504b2e656d4f33b3bbc6d1da3", - "key": "ed37bd31558a4b039aaf4e7a7a59aa7a75fd9101682045f69baf45d28380ae5c" + "1cc245d7b063aad2a900e5b498fbd936c811f5d504b2e656d4f33b3bbc6d1da3", + "key": "ed37bd31558a4b039aaf4e7a7a59aa7a75fd9101682045f69baf45d28380ae5c", } } class MideaCloud: def __init__( - self, - session: ClientSession, - security: CloudSecurity, - app_id: str, - app_key: str, - account: str, - password: str, - api_url: str + self, + session: ClientSession, + security: CloudSecurity, + app_id: str, + app_key: str, + account: str, + password: str, + api_url: str, ): self._device_id = CloudSecurity.get_deviceid(account) self._session = session @@ -87,38 +100,36 @@ def _make_general_data(self): async def _api_request(self, endpoint: str, data: dict, header=None) -> dict | None: header = header or {} if not data.get("reqId"): - data.update({ - "reqId": token_hex(16) - }) + data.update({"reqId": token_hex(16)}) if not data.get("stamp"): - data.update({ - "stamp": datetime.datetime.now().strftime("%Y%m%d%H%M%S") - }) + data.update({"stamp": datetime.datetime.now().strftime("%Y%m%d%H%M%S")}) random = str(int(time.time())) url = self._api_url + endpoint dump_data = json.dumps(data) sign = self._security.sign("", dump_data, random) - header.update({ - "content-type": "application/json; charset=utf-8", - "secretVersion": "1", - "sign": sign, - "random": random, - }) + header.update( + { + "content-type": "application/json; charset=utf-8", + "secretVersion": "1", + "sign": sign, + "random": random, + } + ) if self._uid is not None: - header.update({ - "uid": self._uid - }) + header.update({"uid": self._uid}) if self._access_token is not None: - header.update({ - "accessToken": self._access_token - }) + header.update({"accessToken": self._access_token}) response: dict = {"code": -1} for i in range(0, 3): try: with self._api_lock: - r = await self._session.request("POST", url, headers=header, data=dump_data, timeout=10) + r = await self._session.request( + "POST", url, headers=header, data=dump_data, timeout=10 + ) raw = await r.read() - _LOGGER.debug(f"Midea cloud API url: {url}, data: {data}, response: {raw}") + _LOGGER.debug( + f"Midea cloud API url: {url}, data: {data}, response: {raw}" + ) response = json.loads(raw) break except Exception as e: @@ -129,12 +140,9 @@ async def _api_request(self, endpoint: str, data: dict, header=None) -> dict | N async def _get_login_id(self) -> str | None: data = self._make_general_data() - data.update({ - "loginAccount": f"{self._account}" - }) + data.update({"loginAccount": f"{self._account}"}) if response := await self._api_request( - endpoint="/v1/user/login/id/get", - data=data + endpoint="/v1/user/login/id/get", data=data ): return response.get("loginId") return None @@ -147,19 +155,16 @@ async def get_keys(self, appliance_id: int): for method in [1, 2]: udp_id = self._security.get_udp_id(appliance_id, method) data = self._make_general_data() - data.update({ - "udpid": udp_id - }) + data.update({"udpid": udp_id}) response = await self._api_request( - endpoint="/v1/iot/secure/getToken", - data=data + endpoint="/v1/iot/secure/getToken", data=data ) if response and "tokenlist" in response: for token in response["tokenlist"]: if token["udpId"] == udp_id: result[method] = { "token": token["token"].lower(), - "key": token["key"].lower() + "key": token["key"].lower(), } result.update(default_keys) return result @@ -177,22 +182,23 @@ async def get_device_info(self, device_id: int): return None async def download_lua( - self, path: str, - device_type: int, - sn: str, - model_number: str | None, - manufacturer_code: str = "0000", + self, + path: str, + device_type: int, + sn: str, + model_number: str | None, + manufacturer_code: str = "0000", ): raise NotImplementedError() class MeijuCloud(MideaCloud): def __init__( - self, - cloud_name: str, - session: ClientSession, - account: str, - password: str, + self, + cloud_name: str, + session: ClientSession, + account: str, + password: str, ): super().__init__( session=session, @@ -205,7 +211,7 @@ def __init__( app_key=clouds[cloud_name]["app_key"], account=account, password=password, - api_url=clouds[cloud_name]["api_url"] + api_url=clouds[cloud_name]["api_url"], ) async def login(self) -> bool: @@ -216,30 +222,31 @@ async def login(self) -> bool: "iotData": { "clientType": 1, "deviceId": self._device_id, - "iampwd": self._security.encrypt_iam_password(self._login_id, self._password), + "iampwd": self._security.encrypt_iam_password( + self._login_id, self._password + ), "iotAppId": self._app_id, "loginAccount": self._account, - "password": self._security.encrypt_password(self._login_id, self._password), + "password": self._security.encrypt_password( + self._login_id, self._password + ), "reqId": token_hex(16), - "stamp": stamp + "stamp": stamp, }, "data": { "appKey": self._app_key, "deviceId": self._device_id, - "platform": 2 + "platform": 2, }, "timestamp": stamp, - "stamp": stamp + "stamp": stamp, } if response := await self._api_request( - endpoint="/mj/user/login", - data=data + endpoint="/mj/user/login", data=data ): self._access_token = response["mdata"]["accessToken"] self._security.set_aes_keys( - self._security.aes_decrypt_with_fixed_key( - response["key"] - ), None + self._security.aes_decrypt_with_fixed_key(response["key"]), None ) return True @@ -247,24 +254,18 @@ async def login(self) -> bool: async def list_home(self): if response := await self._api_request( - endpoint="/v1/homegroup/list/get", - data={} + endpoint="/v1/homegroup/list/get", data={} ): homes = {} for home in response["homeList"]: - homes.update({ - int(home["homegroupId"]): home["name"] - }) + homes.update({int(home["homegroupId"]): home["name"]}) return homes return None async def list_appliances(self, home_id) -> dict | None: - data = { - "homegroupId": home_id - } + data = {"homegroupId": home_id} if response := await self._api_request( - endpoint="/v1/appliance/home/list/get", - data=data + endpoint="/v1/appliance/home/list/get", data=data ): appliances = {} for home in response.get("homeList") or []: @@ -277,28 +278,37 @@ async def list_appliances(self, home_id) -> dict | None: device_info = { "name": appliance.get("name"), "type": int(appliance.get("type"), 16), - "sn": self._security.aes_decrypt(appliance.get("sn")) if appliance.get("sn") else "", + "sn": ( + self._security.aes_decrypt(appliance.get("sn")) + if appliance.get("sn") + else "" + ), "sn8": appliance.get("sn8", "00000000"), "model_number": model_number, - "manufacturer_code": appliance.get("enterpriseCode", "0000"), + "manufacturer_code": appliance.get( + "enterpriseCode", "0000" + ), "model": appliance.get("productModel"), "online": appliance.get("onlineStatus") == "1", } - if device_info.get("sn8") is None or len(device_info.get("sn8")) == 0: + if ( + device_info.get("sn8") is None + or len(device_info.get("sn8")) == 0 + ): device_info["sn8"] = "00000000" - if device_info.get("model") is None or len(device_info.get("model")) == 0: + if ( + device_info.get("model") is None + or len(device_info.get("model")) == 0 + ): device_info["model"] = device_info["sn8"] appliances[int(appliance["applianceCode"])] = device_info return appliances return None async def get_device_info(self, device_id: int): - data = { - "applianceCode": device_id - } + data = {"applianceCode": device_id} if response := await self._api_request( - endpoint="/v1/appliance/info/get", - data=data + endpoint="/v1/appliance/info/get", data=data ): try: model_number = int(response.get("modelNumber", 0)) @@ -307,7 +317,11 @@ async def get_device_info(self, device_id: int): device_info = { "name": response.get("name"), "type": int(response.get("type"), 16), - "sn": self._security.aes_decrypt(response.get("sn")) if response.get("sn") else "", + "sn": ( + self._security.aes_decrypt(response.get("sn")) + if response.get("sn") + else "" + ), "sn8": response.get("sn8", "00000000"), "model_number": model_number, "manufacturer_code": response.get("enterpriseCode", "0000"), @@ -322,30 +336,32 @@ async def get_device_info(self, device_id: int): return None async def download_lua( - self, path: str, - device_type: int, - sn: str, - model_number: str | None, - manufacturer_code: str = "0000", + self, + path: str, + device_type: int, + sn: str, + model_number: str | None, + manufacturer_code: str = "0000", ): data = { "applianceSn": sn, "applianceType": "0x%02X" % device_type, "applianceMFCode": manufacturer_code, - 'version': "0", - "iotAppId": self._app_id + "version": "0", + "iotAppId": self._app_id, } fnm = None if response := await self._api_request( - endpoint="/v1/appliance/protocol/lua/luaGet", - data=data + endpoint="/v1/appliance/protocol/lua/luaGet", data=data ): res = await self._session.get(response["url"]) if res.status == 200: lua = await res.text() if lua: - stream = ('local bit = require "bit"\n' + - self._security.aes_decrypt_with_fixed_key(lua)) + stream = ( + 'local bit = require "bit"\n' + + self._security.aes_decrypt_with_fixed_key(lua) + ) stream = stream.replace("\r\n", "\n") fnm = f"{path}/{response['fileName']}" with open(fnm, "w") as fp: @@ -355,11 +371,11 @@ async def download_lua( class MSmartHomeCloud(MideaCloud): def __init__( - self, - cloud_name: str, - session: ClientSession, - account: str, - password: str, + self, + cloud_name: str, + session: ClientSession, + account: str, + password: str, ): super().__init__( session=session, @@ -372,7 +388,7 @@ def __init__( app_key=clouds[cloud_name]["app_key"], account=account, password=password, - api_url=clouds[cloud_name]["api_url"] + api_url=clouds[cloud_name]["api_url"], ) self._auth_base = base64.b64encode( f"{self._app_key}:{clouds['MSmartHome']['iot_key']}".encode("ascii") @@ -389,27 +405,22 @@ def _make_general_data(self): "reqId": token_hex(16), "uid": self._uid, "clientType": "1", - "appId": self._app_id + "appId": self._app_id, } async def _api_request(self, endpoint: str, data: dict, header=None) -> dict | None: header = header or {} - header.update({ - "x-recipe-app": self._app_id, - "authorization": f"Basic {self._auth_base}" - }) + header.update( + {"x-recipe-app": self._app_id, "authorization": f"Basic {self._auth_base}"} + ) return await super()._api_request(endpoint, data, header) async def _re_route(self): data = self._make_general_data() - data.update({ - "userType": "0", - "userName": f"{self._account}" - }) + data.update({"userType": "0", "userName": f"{self._account}"}) if response := await self._api_request( - endpoint="/v1/multicloud/platform/user/route", - data=data + endpoint="/v1/multicloud/platform/user/route", data=data ): if api_url := response.get("masUrl"): self._api_url = api_url @@ -421,36 +432,42 @@ async def login(self) -> bool: iot_data = self._make_general_data() iot_data.pop("uid") stamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") - iot_data.update({ - "iampwd": self._security.encrypt_iam_password(self._login_id, self._password), - "loginAccount": self._account, - "password": self._security.encrypt_password(self._login_id, self._password), - "stamp": stamp - }) + iot_data.update( + { + "iampwd": self._security.encrypt_iam_password( + self._login_id, self._password + ), + "loginAccount": self._account, + "password": self._security.encrypt_password( + self._login_id, self._password + ), + "stamp": stamp, + } + ) data = { "iotData": iot_data, "data": { "appKey": self._app_key, "deviceId": self._device_id, - "platform": "2" + "platform": "2", }, - "stamp": stamp + "stamp": stamp, } if response := await self._api_request( - endpoint="/mj/user/login", - data=data + endpoint="/mj/user/login", data=data ): self._uid = response["uid"] self._access_token = response["mdata"]["accessToken"] - self._security.set_aes_keys(response["accessToken"], response["randomData"]) + self._security.set_aes_keys( + response["accessToken"], response["randomData"] + ) return True return False async def list_appliances(self, home_id) -> dict | None: data = self._make_general_data() if response := await self._api_request( - endpoint="/v1/appliance/user/list/get", - data=data + endpoint="/v1/appliance/user/list/get", data=data ): appliances = {} for appliance in response["list"]: @@ -461,21 +478,28 @@ async def list_appliances(self, home_id) -> dict | None: device_info = { "name": appliance.get("name"), "type": int(appliance.get("type"), 16), - "sn": self._security.aes_decrypt(appliance.get("sn")) if appliance.get("sn") else "", + "sn": ( + self._security.aes_decrypt(appliance.get("sn")) + if appliance.get("sn") + else "" + ), "sn8": "", "model_number": model_number, "manufacturer_code": appliance.get("enterpriseCode", "0000"), "model": "", "online": appliance.get("onlineStatus") == "1", } - device_info["sn8"] = device_info.get("sn")[9:17] if len(device_info["sn"]) > 17 else "" + device_info["sn8"] = ( + device_info.get("sn")[9:17] if len(device_info["sn"]) > 17 else "" + ) device_info["model"] = device_info.get("sn8") appliances[int(appliance["id"])] = device_info return appliances return None async def download_lua( - self, path: str, + self, + path: str, device_type: int, sn: str, model_number: str | None, @@ -490,21 +514,24 @@ async def download_lua( "applianceMFCode": manufacturer_code, "applianceType": "0x%02X" % device_type, "modelNumber": model_number, - "applianceSn": self._security.aes_encrypt_with_fixed_key(sn.encode("ascii")).hex(), + "applianceSn": self._security.aes_encrypt_with_fixed_key( + sn.encode("ascii") + ).hex(), "version": "0", - "encryptedType ": "2" + "encryptedType ": "2", } fnm = None if response := await self._api_request( - endpoint="/v2/luaEncryption/luaGet", - data=data + endpoint="/v2/luaEncryption/luaGet", data=data ): res = await self._session.get(response["url"]) if res.status == 200: lua = await res.text() if lua: - stream = ('local bit = require "bit"\n' + - self._security.aes_decrypt_with_fixed_key(lua)) + stream = ( + 'local bit = require "bit"\n' + + self._security.aes_decrypt_with_fixed_key(lua) + ) stream = stream.replace("\r\n", "\n") fnm = f"{path}/{response['fileName']}" with open(fnm, "w") as fp: @@ -514,22 +541,20 @@ async def download_lua( class MideaAirCloud(MideaCloud): def __init__( - self, - cloud_name: str, - session: ClientSession, - account: str, - password: str, + self, + cloud_name: str, + session: ClientSession, + account: str, + password: str, ): super().__init__( session=session, - security=MideaAirSecurity( - login_key=clouds[cloud_name]["app_key"] - ), + security=MideaAirSecurity(login_key=clouds[cloud_name]["app_key"]), app_id=clouds[cloud_name]["app_id"], app_key=clouds[cloud_name]["app_key"], account=account, password=password, - api_url=clouds[cloud_name]["api_url"] + api_url=clouds[cloud_name]["api_url"], ) self._session_id = None @@ -541,45 +566,37 @@ def _make_general_data(self): "deviceId": self._device_id, "reqId": token_hex(16), "clientType": "1", - "appId": self._app_id + "appId": self._app_id, } if self._session_id is not None: - data.update({ - "sessionId": self._session_id - }) + data.update({"sessionId": self._session_id}) return data async def _api_request(self, endpoint: str, data: dict, header=None) -> dict | None: header = header or {} if not data.get("reqId"): - data.update({ - "reqId": token_hex(16) - }) + data.update({"reqId": token_hex(16)}) if not data.get("stamp"): - data.update({ - "stamp": datetime.datetime.now().strftime("%Y%m%d%H%M%S") - }) + data.update({"stamp": datetime.datetime.now().strftime("%Y%m%d%H%M%S")}) url = self._api_url + endpoint sign = self._security.sign(url, data, "") - data.update({ - "sign": sign - }) + data.update({"sign": sign}) if self._uid is not None: - header.update({ - "uid": self._uid - }) + header.update({"uid": self._uid}) if self._access_token is not None: - header.update({ - "accessToken": self._access_token - }) + header.update({"accessToken": self._access_token}) response: dict = {"code": -1} for i in range(0, 3): try: with self._api_lock: - r = await self._session.request("POST", url, headers=header, data=data, timeout=10) + r = await self._session.request( + "POST", url, headers=header, data=data, timeout=10 + ) raw = await r.read() - _LOGGER.debug(f"Midea cloud API url: {url}, data: {data}, response: {raw}") + _LOGGER.debug( + f"Midea cloud API url: {url}, data: {data}, response: {raw}" + ) response = json.loads(raw) break except Exception as e: @@ -592,13 +609,16 @@ async def login(self) -> bool: if login_id := await self._get_login_id(): self._login_id = login_id data = self._make_general_data() - data.update({ - "loginAccount": self._account, - "password": self._security.encrypt_password(self._login_id, self._password), - }) + data.update( + { + "loginAccount": self._account, + "password": self._security.encrypt_password( + self._login_id, self._password + ), + } + ) if response := await self._api_request( - endpoint="/v1/user/login", - data=data + endpoint="/v1/user/login", data=data ): self._access_token = response["accessToken"] self._uid = response["userId"] @@ -609,8 +629,7 @@ async def login(self) -> bool: async def list_appliances(self, home_id) -> dict | None: data = self._make_general_data() if response := await self._api_request( - endpoint="/v1/appliance/user/list/get", - data=data + endpoint="/v1/appliance/user/list/get", data=data ): appliances = {} for appliance in response["list"]: @@ -628,20 +647,21 @@ async def list_appliances(self, home_id) -> dict | None: "model": "", "online": appliance.get("onlineStatus") == "1", } - device_info["sn8"] = device_info.get("sn")[9:17] if len(device_info["sn"]) > 17 else "" + device_info["sn8"] = ( + device_info.get("sn")[9:17] if len(device_info["sn"]) > 17 else "" + ) device_info["model"] = device_info.get("sn8") appliances[int(appliance["id"])] = device_info return appliances return None -def get_midea_cloud(cloud_name: str, session: ClientSession, account: str, password: str) -> MideaCloud | None: +def get_midea_cloud( + cloud_name: str, session: ClientSession, account: str, password: str +) -> MideaCloud | None: cloud = None if cloud_name in clouds.keys(): cloud = globals()[clouds[cloud_name]["class_name"]]( - cloud_name=cloud_name, - session=session, - account=account, - password=password + cloud_name=cloud_name, session=session, account=account, password=password ) return cloud diff --git a/custom_components/midea_ac_lan/midea/core/crc8.py b/custom_components/midea_ac_lan/midea/core/crc8.py index 6d87d33c..8ce11bcf 100644 --- a/custom_components/midea_ac_lan/midea/core/crc8.py +++ b/custom_components/midea_ac_lan/midea/core/crc8.py @@ -1,36 +1,260 @@ crc8_854_table = [ - 0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83, - 0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41, - 0x9D, 0xC3, 0x21, 0x7F, 0xFC, 0xA2, 0x40, 0x1E, - 0x5F, 0x01, 0xE3, 0xBD, 0x3E, 0x60, 0x82, 0xDC, - 0x23, 0x7D, 0x9F, 0xC1, 0x42, 0x1C, 0xFE, 0xA0, - 0xE1, 0xBF, 0x5D, 0x03, 0x80, 0xDE, 0x3C, 0x62, - 0xBE, 0xE0, 0x02, 0x5C, 0xDF, 0x81, 0x63, 0x3D, - 0x7C, 0x22, 0xC0, 0x9E, 0x1D, 0x43, 0xA1, 0xFF, - 0x46, 0x18, 0xFA, 0xA4, 0x27, 0x79, 0x9B, 0xC5, - 0x84, 0xDA, 0x38, 0x66, 0xE5, 0xBB, 0x59, 0x07, - 0xDB, 0x85, 0x67, 0x39, 0xBA, 0xE4, 0x06, 0x58, - 0x19, 0x47, 0xA5, 0xFB, 0x78, 0x26, 0xC4, 0x9A, - 0x65, 0x3B, 0xD9, 0x87, 0x04, 0x5A, 0xB8, 0xE6, - 0xA7, 0xF9, 0x1B, 0x45, 0xC6, 0x98, 0x7A, 0x24, - 0xF8, 0xA6, 0x44, 0x1A, 0x99, 0xC7, 0x25, 0x7B, - 0x3A, 0x64, 0x86, 0xD8, 0x5B, 0x05, 0xE7, 0xB9, - 0x8C, 0xD2, 0x30, 0x6E, 0xED, 0xB3, 0x51, 0x0F, - 0x4E, 0x10, 0xF2, 0xAC, 0x2F, 0x71, 0x93, 0xCD, - 0x11, 0x4F, 0xAD, 0xF3, 0x70, 0x2E, 0xCC, 0x92, - 0xD3, 0x8D, 0x6F, 0x31, 0xB2, 0xEC, 0x0E, 0x50, - 0xAF, 0xF1, 0x13, 0x4D, 0xCE, 0x90, 0x72, 0x2C, - 0x6D, 0x33, 0xD1, 0x8F, 0x0C, 0x52, 0xB0, 0xEE, - 0x32, 0x6C, 0x8E, 0xD0, 0x53, 0x0D, 0xEF, 0xB1, - 0xF0, 0xAE, 0x4C, 0x12, 0x91, 0xCF, 0x2D, 0x73, - 0xCA, 0x94, 0x76, 0x28, 0xAB, 0xF5, 0x17, 0x49, - 0x08, 0x56, 0xB4, 0xEA, 0x69, 0x37, 0xD5, 0x8B, - 0x57, 0x09, 0xEB, 0xB5, 0x36, 0x68, 0x8A, 0xD4, - 0x95, 0xCB, 0x29, 0x77, 0xF4, 0xAA, 0x48, 0x16, - 0xE9, 0xB7, 0x55, 0x0B, 0x88, 0xD6, 0x34, 0x6A, - 0x2B, 0x75, 0x97, 0xC9, 0x4A, 0x14, 0xF6, 0xA8, - 0x74, 0x2A, 0xC8, 0x96, 0x15, 0x4B, 0xA9, 0xF7, - 0xB6, 0xE8, 0x0A, 0x54, 0xD7, 0x89, 0x6B, 0x35 + 0x00, + 0x5E, + 0xBC, + 0xE2, + 0x61, + 0x3F, + 0xDD, + 0x83, + 0xC2, + 0x9C, + 0x7E, + 0x20, + 0xA3, + 0xFD, + 0x1F, + 0x41, + 0x9D, + 0xC3, + 0x21, + 0x7F, + 0xFC, + 0xA2, + 0x40, + 0x1E, + 0x5F, + 0x01, + 0xE3, + 0xBD, + 0x3E, + 0x60, + 0x82, + 0xDC, + 0x23, + 0x7D, + 0x9F, + 0xC1, + 0x42, + 0x1C, + 0xFE, + 0xA0, + 0xE1, + 0xBF, + 0x5D, + 0x03, + 0x80, + 0xDE, + 0x3C, + 0x62, + 0xBE, + 0xE0, + 0x02, + 0x5C, + 0xDF, + 0x81, + 0x63, + 0x3D, + 0x7C, + 0x22, + 0xC0, + 0x9E, + 0x1D, + 0x43, + 0xA1, + 0xFF, + 0x46, + 0x18, + 0xFA, + 0xA4, + 0x27, + 0x79, + 0x9B, + 0xC5, + 0x84, + 0xDA, + 0x38, + 0x66, + 0xE5, + 0xBB, + 0x59, + 0x07, + 0xDB, + 0x85, + 0x67, + 0x39, + 0xBA, + 0xE4, + 0x06, + 0x58, + 0x19, + 0x47, + 0xA5, + 0xFB, + 0x78, + 0x26, + 0xC4, + 0x9A, + 0x65, + 0x3B, + 0xD9, + 0x87, + 0x04, + 0x5A, + 0xB8, + 0xE6, + 0xA7, + 0xF9, + 0x1B, + 0x45, + 0xC6, + 0x98, + 0x7A, + 0x24, + 0xF8, + 0xA6, + 0x44, + 0x1A, + 0x99, + 0xC7, + 0x25, + 0x7B, + 0x3A, + 0x64, + 0x86, + 0xD8, + 0x5B, + 0x05, + 0xE7, + 0xB9, + 0x8C, + 0xD2, + 0x30, + 0x6E, + 0xED, + 0xB3, + 0x51, + 0x0F, + 0x4E, + 0x10, + 0xF2, + 0xAC, + 0x2F, + 0x71, + 0x93, + 0xCD, + 0x11, + 0x4F, + 0xAD, + 0xF3, + 0x70, + 0x2E, + 0xCC, + 0x92, + 0xD3, + 0x8D, + 0x6F, + 0x31, + 0xB2, + 0xEC, + 0x0E, + 0x50, + 0xAF, + 0xF1, + 0x13, + 0x4D, + 0xCE, + 0x90, + 0x72, + 0x2C, + 0x6D, + 0x33, + 0xD1, + 0x8F, + 0x0C, + 0x52, + 0xB0, + 0xEE, + 0x32, + 0x6C, + 0x8E, + 0xD0, + 0x53, + 0x0D, + 0xEF, + 0xB1, + 0xF0, + 0xAE, + 0x4C, + 0x12, + 0x91, + 0xCF, + 0x2D, + 0x73, + 0xCA, + 0x94, + 0x76, + 0x28, + 0xAB, + 0xF5, + 0x17, + 0x49, + 0x08, + 0x56, + 0xB4, + 0xEA, + 0x69, + 0x37, + 0xD5, + 0x8B, + 0x57, + 0x09, + 0xEB, + 0xB5, + 0x36, + 0x68, + 0x8A, + 0xD4, + 0x95, + 0xCB, + 0x29, + 0x77, + 0xF4, + 0xAA, + 0x48, + 0x16, + 0xE9, + 0xB7, + 0x55, + 0x0B, + 0x88, + 0xD6, + 0x34, + 0x6A, + 0x2B, + 0x75, + 0x97, + 0xC9, + 0x4A, + 0x14, + 0xF6, + 0xA8, + 0x74, + 0x2A, + 0xC8, + 0x96, + 0x15, + 0x4B, + 0xA9, + 0xF7, + 0xB6, + 0xE8, + 0x0A, + 0x54, + 0xD7, + 0x89, + 0x6B, + 0x35, ] diff --git a/custom_components/midea_ac_lan/midea/core/device.py b/custom_components/midea_ac_lan/midea/core/device.py index 7ba2fe30..f47c2955 100644 --- a/custom_components/midea_ac_lan/midea/core/device.py +++ b/custom_components/midea_ac_lan/midea/core/device.py @@ -1,20 +1,27 @@ import threading + try: from enum import StrEnum except ImportError: from ..backports.myenum import StrEnum + +import logging +import socket +import time from enum import IntEnum -from .security import LocalSecurity, MSGTYPE_HANDSHAKE_REQUEST, MSGTYPE_ENCRYPTED_REQUEST -from .packet_builder import PacketBuilder + from .message import ( - MessageType, - MessageQuestCustom, + MessageApplianceResponse, MessageQueryAppliance, - MessageApplianceResponse + MessageQuestCustom, + MessageType, +) +from .packet_builder import PacketBuilder +from .security import ( + MSGTYPE_ENCRYPTED_REQUEST, + MSGTYPE_HANDSHAKE_REQUEST, + LocalSecurity, ) -import socket -import logging -import time _LOGGER = logging.getLogger(__name__) @@ -42,18 +49,20 @@ class ParseMessageResult(IntEnum): class MiedaDevice(threading.Thread): - def __init__(self, - name: str, - device_id: int, - device_type: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - attributes: dict): + def __init__( + self, + name: str, + device_id: int, + device_type: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + attributes: dict, + ): threading.Thread.__init__(self) self._attributes = attributes if attributes else {} self._socket = None @@ -122,7 +131,9 @@ def connect(self, refresh_status=True): try: self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.settimeout(10) - _LOGGER.debug(f"[{self._device_id}] Connecting to {self._ip_address}:{self._port}") + _LOGGER.debug( + f"[{self._device_id}] Connecting to {self._ip_address}:{self._port}" + ) self._socket.connect((self._ip_address, self._port)) _LOGGER.debug(f"[{self._device_id}] Connected") if self._protocol == 3: @@ -143,20 +154,21 @@ def connect(self, refresh_status=True): except RefreshFailed: _LOGGER.debug(f"[{self._device_id}] Refresh status is timed out") except Exception as e: - _LOGGER.error(f"[{self._device_id}] Unknown error: {e.__traceback__.tb_frame.f_globals['__file__']}, " - f"{e.__traceback__.tb_lineno}, {repr(e)}") + _LOGGER.error( + f"[{self._device_id}] Unknown error: {e.__traceback__.tb_frame.f_globals['__file__']}, " + f"{e.__traceback__.tb_lineno}, {repr(e)}" + ) self.enable_device(False) return False def authenticate(self): - request = self._security.encode_8370( - self._token, MSGTYPE_HANDSHAKE_REQUEST) + request = self._security.encode_8370(self._token, MSGTYPE_HANDSHAKE_REQUEST) _LOGGER.debug(f"[{self._device_id}] Handshaking") self._socket.send(request) response = self._socket.recv(512) if len(response) < 20: raise AuthException() - response = response[8: 72] + response = response[8:72] self._security.tcp_key(response, self._key) def send_message(self, data): @@ -169,7 +181,9 @@ def send_message_v2(self, data): if self._socket is not None: self._socket.send(data) else: - _LOGGER.debug(f"[{self._device_id}] Send failure, device disconnected, data: {data.hex()}") + _LOGGER.debug( + f"[{self._device_id}] Send failure, device disconnected, data: {data.hex()}" + ) def send_message_v3(self, data, msg_type=MSGTYPE_ENCRYPTED_REQUEST): data = self._security.encode_8370(data, msg_type) @@ -205,8 +219,10 @@ def refresh_status(self, wait_response=False): except socket.timeout: error_count += 1 self._unsupported_protocol.append(cmd.__class__.__name__) - _LOGGER.debug(f"[{self._device_id}] Does not supports " - f"the protocol {cmd.__class__.__name__}, ignored") + _LOGGER.debug( + f"[{self._device_id}] Does not supports " + f"the protocol {cmd.__class__.__name__}, ignored" + ) except ResponseException: error_count += 1 else: @@ -220,7 +236,9 @@ def pre_process_message(self, msg): self._appliance_query = False _LOGGER.debug(f"[{self.device_id}] Received: {message}") self._protocol_version = message.protocol_version - _LOGGER.debug(f"[{self._device_id}] Device protocol version: {self._protocol_version}") + _LOGGER.debug( + f"[{self._device_id}] Device protocol version: {self._protocol_version}" + ) return False return True @@ -252,9 +270,13 @@ def parse_message(self, msg): if len(status) > 0: self.update_all(status) else: - _LOGGER.debug(f"[{self._device_id}] Unidentified protocol") + _LOGGER.debug( + f"[{self._device_id}] Unidentified protocol" + ) except Exception as e: - _LOGGER.error(f"[{self._device_id}] Error in process message: {e}, msg = {decrypted.hex()}") + _LOGGER.error( + f"[{self._device_id}] Error in process message: {e}, msg = {decrypted.hex()}" + ) else: _LOGGER.warning( f"[{self._device_id}] Illegal payload, " @@ -278,12 +300,16 @@ def process_message(self, msg): raise NotImplementedError def send_command(self, cmd_type, cmd_body: bytearray): - cmd = MessageQuestCustom(self._device_type, self._protocol_version, cmd_type, cmd_body) + cmd = MessageQuestCustom( + self._device_type, self._protocol_version, cmd_type, cmd_body + ) try: self.build_send(cmd) except socket.error as e: - _LOGGER.debug(f"[{self._device_id}] Interface send_command failure, {repr(e)}, " - f"cmd_type: {cmd_type}, cmd_body: {cmd_body.hex()}") + _LOGGER.debug( + f"[{self._device_id}] Interface send_command failure, {repr(e)}, " + f"cmd_type: {cmd_type}, cmd_body: {cmd_body.hex()}" + ) def send_heartbeat(self): msg = PacketBuilder(self._device_id, bytearray([0x00])).finalize(msg_type=0) @@ -373,8 +399,10 @@ def run(self): self.close_socket() break except Exception as e: - _LOGGER.error(f"[{self._device_id}] Unknown error :{e.__traceback__.tb_frame.f_globals['__file__']}, " - f"{e.__traceback__.tb_lineno}, {repr(e)}") + _LOGGER.error( + f"[{self._device_id}] Unknown error :{e.__traceback__.tb_frame.f_globals['__file__']}, " + f"{e.__traceback__.tb_lineno}, {repr(e)}" + ) self.close_socket() break diff --git a/custom_components/midea_ac_lan/midea/core/discover.py b/custom_components/midea_ac_lan/midea/core/discover.py index a4be3e26..0deb1790 100644 --- a/custom_components/midea_ac_lan/midea/core/discover.py +++ b/custom_components/midea_ac_lan/midea/core/discover.py @@ -1,8 +1,11 @@ import logging import socket -import ifaddr from ipaddress import IPv4Network + +import ifaddr + from .security import LocalSecurity + try: import xml.etree.cElementTree as ET except ImportError: @@ -10,27 +13,143 @@ _LOGGER = logging.getLogger(__name__) -BROADCAST_MSG = bytearray([ - 0x5a, 0x5a, 0x01, 0x11, 0x48, 0x00, 0x92, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x75, 0xbd, 0x6b, 0x3e, 0x4f, 0x8b, 0x76, - 0x2e, 0x84, 0x9c, 0x6e, 0x57, 0x8d, 0x65, 0x90, - 0x03, 0x6e, 0x9d, 0x43, 0x42, 0xa5, 0x0f, 0x1f, - 0x56, 0x9e, 0xb8, 0xec, 0x91, 0x8e, 0x92, 0xe5 -]) - -DEVICE_INFO_MSG = bytearray([ - 0x5a, 0x5a, 0x15, 0x00, 0x00, 0x38, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x33, 0x05, - 0x13, 0x06, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xca, 0x8d, 0x9b, 0xf9, 0xa0, 0x30, 0x1a, 0xe3, - 0xb7, 0xe4, 0x2d, 0x53, 0x49, 0x47, 0x62, 0xbe -]) +BROADCAST_MSG = bytearray( + [ + 0x5A, + 0x5A, + 0x01, + 0x11, + 0x48, + 0x00, + 0x92, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7F, + 0x75, + 0xBD, + 0x6B, + 0x3E, + 0x4F, + 0x8B, + 0x76, + 0x2E, + 0x84, + 0x9C, + 0x6E, + 0x57, + 0x8D, + 0x65, + 0x90, + 0x03, + 0x6E, + 0x9D, + 0x43, + 0x42, + 0xA5, + 0x0F, + 0x1F, + 0x56, + 0x9E, + 0xB8, + 0xEC, + 0x91, + 0x8E, + 0x92, + 0xE5, + ] +) + +DEVICE_INFO_MSG = bytearray( + [ + 0x5A, + 0x5A, + 0x15, + 0x00, + 0x00, + 0x38, + 0x00, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x27, + 0x33, + 0x05, + 0x13, + 0x06, + 0x14, + 0x14, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0xE8, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xCA, + 0x8D, + 0x9B, + 0xF9, + 0xA0, + 0x30, + 0x1A, + 0xE3, + 0xB7, + 0xE4, + 0x2D, + 0x53, + 0x49, + 0x47, + 0x62, + 0xBE, + ] +) def discover(discover_type=None, ip_address=None): @@ -57,7 +176,9 @@ def discover(discover_type=None, ip_address=None): data, addr = sock.recvfrom(512) ip = addr[0] _LOGGER.debug(f"Received response from {addr}: {data.hex()}") - if len(data) >= 104 and (data[:2].hex() == "5a5a" or data[8:10].hex() == "5a5a"): + if len(data) >= 104 and ( + data[:2].hex() == "5a5a" or data[8:10].hex() == "5a5a" + ): if data[:2].hex() == "5a5a": protocol = 2 elif data[:2].hex() == "8370": @@ -66,25 +187,29 @@ def discover(discover_type=None, ip_address=None): data = data[8:-16] else: continue - device_id = int.from_bytes(bytearray.fromhex(data[20:26].hex()), "little") + device_id = int.from_bytes( + bytearray.fromhex(data[20:26].hex()), "little" + ) if device_id in found_devices: continue encrypt_data = data[40:-16] reply = security.aes_decrypt(encrypt_data) _LOGGER.debug(f"Declassified reply: {reply.hex()}") - ssid = reply[41:41 + reply[40]].decode("utf-8") + ssid = reply[41 : 41 + reply[40]].decode("utf-8") device_type = ssid.split("_")[1] port = bytes2port(reply[4:8]) model = reply[17:25].decode("utf-8") sn = reply[8:40].decode("utf-8") elif data[:6].hex() == "3c3f786d6c20": protocol = 1 - root = ET.fromstring(data.decode( - encoding="utf-8", errors="replace")) + root = ET.fromstring(data.decode(encoding="utf-8", errors="replace")) child = root.find("body/device") m = child.attrib - port, sn, device_type = int(m["port"]), m["apc_sn"], str( - hex(int(m["apc_type"])))[2:] + port, sn, device_type = ( + int(m["port"]), + m["apc_sn"], + str(hex(int(m["apc_type"])))[2:], + ) response = get_device_info(ip, int(port)) device_id = get_id_from_response(response) if len(sn) == 32: @@ -102,7 +227,7 @@ def discover(discover_type=None, ip_address=None): "port": port, "model": model, "sn": sn, - "protocol": protocol + "protocol": protocol, } if len(discover_type) == 0 or device.get("type") in discover_type: found_devices[device_id] = device @@ -148,13 +273,16 @@ def get_device_info(device_ip, device_port: int): sock.settimeout(8) device_address = (device_ip, device_port) sock.connect(device_address) - _LOGGER.debug(f"Sending to {device_ip}:{device_port} {DEVICE_INFO_MSG.hex()}") + _LOGGER.debug( + f"Sending to {device_ip}:{device_port} {DEVICE_INFO_MSG.hex()}" + ) sock.sendall(DEVICE_INFO_MSG) response = sock.recv(512) except socket.timeout: - _LOGGER.warning(f"Connect the device {device_ip}:{device_port} timed out for 8s. " - f"Don't care about a small amount of this. if many maybe not support." - ) + _LOGGER.warning( + f"Connect the device {device_ip}:{device_port} timed out for 8s. " + f"Don't care about a small amount of this. if many maybe not support." + ) except socket.error: _LOGGER.warning(f"Can't connect to Device {device_ip}:{device_port}") return response @@ -166,8 +294,14 @@ def enum_all_broadcast(): for adapter in adapters: for ip in adapter.ips: if ip.is_IPv4 and ip.network_prefix < 32: - local_network = IPv4Network(f"{ip.ip}/{ip.network_prefix}", strict=False) - if local_network.is_private and not local_network.is_loopback and not local_network.is_link_local: + local_network = IPv4Network( + f"{ip.ip}/{ip.network_prefix}", strict=False + ) + if ( + local_network.is_private + and not local_network.is_loopback + and not local_network.is_link_local + ): addr = str(local_network.broadcast_address) if addr not in nets: nets.append(addr) diff --git a/custom_components/midea_ac_lan/midea/core/message.py b/custom_components/midea_ac_lan/midea/core/message.py index 4c744a45..91e485c6 100644 --- a/custom_components/midea_ac_lan/midea/core/message.py +++ b/custom_components/midea_ac_lan/midea/core/message.py @@ -18,12 +18,12 @@ class MessageCheckSumError(Exception): class MessageType(IntEnum): - set = 0x02, - query = 0x03, - notify1 = 0x04, - notify2 = 0x05, - exception = 0x06, - exception2 = 0x0A, + set = (0x02,) + query = (0x03,) + notify1 = (0x04,) + notify2 = (0x05,) + exception = (0x06,) + exception2 = (0x0A,) query_appliance = 0xA0 @@ -38,7 +38,7 @@ def __init__(self): @staticmethod def checksum(data): - return (~ sum(data) + 1) & 0xff + return (~sum(data) + 1) & 0xFF @property def header(self): @@ -85,7 +85,9 @@ def __str__(self) -> str: "header": self.header.hex(), "body": self.body.hex(), "message type": "%02x" % self._message_type, - "body type": ("%02x" % self._body_type) if self._body_type is not None else "None" + "body type": ( + ("%02x" % self._body_type) if self._body_type is not None else "None" + ), } return str(output) @@ -101,26 +103,29 @@ def __init__(self, device_type, protocol_version, message_type, body_type): @property def header(self): length = self.HEADER_LENGTH + len(self.body) - return bytearray([ - # flag - 0xAA, - # length - length, - # device type - self.device_type, - # frame checksum - 0x00, # self._device_type ^ length, - # unused - 0x00, 0x00, - # frame ID - 0x00, - # frame protocol version - 0x00, - # device protocol version - self.protocol_version, - # frame type - self.message_type - ]) + return bytearray( + [ + # flag + 0xAA, + # length + length, + # device type + self.device_type, + # frame checksum + 0x00, # self._device_type ^ length, + # unused + 0x00, + 0x00, + # frame ID + 0x00, + # frame protocol version + 0x00, + # device protocol version + self.protocol_version, + # frame type + self.message_type, + ] + ) @property def _body(self): @@ -147,7 +152,8 @@ def __init__(self, device_type, protocol_version, cmd_type, cmd_body): device_type=device_type, protocol_version=protocol_version, message_type=cmd_type, - body_type=None) + body_type=None, + ) self._cmd_body = cmd_body @property @@ -165,7 +171,8 @@ def __init__(self, device_type): device_type=device_type, protocol_version=0, message_type=MessageType.query_appliance, - body_type=None) + body_type=None, + ) @property def _body(self): @@ -196,7 +203,7 @@ def read_byte(body, byte, default_value=0): class NewProtocolMessageBody(MessageBody): def __init__(self, body, bt): super().__init__(body) - if bt == 0xb5: + if bt == 0xB5: self._pack_len = 4 else: self._pack_len = 5 @@ -220,9 +227,9 @@ def parse(self): pos += 1 length = self.data[pos + 2] if length > 0: - value = self.data[pos + 3: pos + 3 + length] + value = self.data[pos + 3 : pos + 3 + length] result[param] = value - pos += (3 + length) + pos += 3 + length except IndexError: # Some device used non-standard new-protocol(美的乐享三代中央空调?) _LOGGER.debug(f"Non-standard new-protocol {self.data.hex()}") @@ -234,11 +241,11 @@ def __init__(self, message): super().__init__() if message is None or len(message) < self.HEADER_LENGTH + 1: raise MessageLenError - self._header = message[:self.HEADER_LENGTH] + self._header = message[: self.HEADER_LENGTH] self.protocol_version = self._header[-2] self.message_type = self._header[-1] self.device_type = self._header[2] - body = message[self.HEADER_LENGTH: -1] + body = message[self.HEADER_LENGTH : -1] self._body = MessageBody(body) self.body_type = self._body.body_type diff --git a/custom_components/midea_ac_lan/midea/core/packet_builder.py b/custom_components/midea_ac_lan/midea/core/packet_builder.py index 6fed16be..04780fee 100644 --- a/custom_components/midea_ac_lan/midea/core/packet_builder.py +++ b/custom_components/midea_ac_lan/midea/core/packet_builder.py @@ -1,6 +1,7 @@ -from .security import LocalSecurity import datetime +from .security import LocalSecurity + class PacketBuilder: def __init__(self, device_id: int, command): @@ -8,24 +9,58 @@ def __init__(self, device_id: int, command): self.security = LocalSecurity() # aa20ac00000000000003418100ff03ff000200000000000000000000000006f274 # Init the packet with the header data. - self.packet = bytearray([ - # 2 bytes - StaicHeader - 0x5a, 0x5a, - # 2 bytes - mMessageType - 0x01, 0x11, - # 2 bytes - PacketLenght - 0x00, 0x00, - # 2 bytes - 0x20, 0x00, - # 4 bytes - MessageId - 0x00, 0x00, 0x00, 0x00, - # 8 bytes - Date&Time - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - # 6 bytes - mDeviceID - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - # 12 bytes - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ]) + self.packet = bytearray( + [ + # 2 bytes - StaicHeader + 0x5A, + 0x5A, + # 2 bytes - mMessageType + 0x01, + 0x11, + # 2 bytes - PacketLenght + 0x00, + 0x00, + # 2 bytes + 0x20, + 0x00, + # 4 bytes - MessageId + 0x00, + 0x00, + 0x00, + 0x00, + # 8 bytes - Date&Time + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + # 6 bytes - mDeviceID + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + # 12 bytes + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) self.packet[12:20] = self.packet_time() self.packet[20:28] = device_id.to_bytes(8, "little") self.command = command @@ -33,7 +68,7 @@ def __init__(self, device_id: int, command): def finalize(self, msg_type=1): if msg_type != 1: self.packet[3] = 0x10 - self.packet[6] = 0x7b + self.packet[6] = 0x7B else: self.packet.extend(self.security.aes_encrypt(self.command)) # PacketLenght @@ -47,14 +82,13 @@ def encode32(self, data: bytearray): @staticmethod def checksum(data): - return (~ sum(data) + 1) & 0xff + return (~sum(data) + 1) & 0xFF @staticmethod def packet_time(): - t = datetime.datetime.now().strftime("%Y%m%d%H%M%S%f")[ - :16] + t = datetime.datetime.now().strftime("%Y%m%d%H%M%S%f")[:16] b = bytearray() for i in range(0, len(t), 2): - d = int(t[i:i+2]) + d = int(t[i : i + 2]) b.insert(0, d) return b diff --git a/custom_components/midea_ac_lan/midea/core/security.py b/custom_components/midea_ac_lan/midea/core/security.py index 26cf0262..90166ea1 100644 --- a/custom_components/midea_ac_lan/midea/core/security.py +++ b/custom_components/midea_ac_lan/midea/core/security.py @@ -1,12 +1,12 @@ -from Crypto.Cipher import AES -from Crypto.Util.Padding import pad, unpad -from Crypto.Util.strxor import strxor -from Crypto.Random import get_random_bytes -from urllib.parse import unquote_plus, urlencode, urlparse +import hmac from hashlib import md5, sha256 from typing import Any -import hmac +from urllib.parse import unquote_plus, urlencode, urlparse +from Crypto.Cipher import AES +from Crypto.Random import get_random_bytes +from Crypto.Util.Padding import pad, unpad +from Crypto.Util.strxor import strxor MSGTYPE_HANDSHAKE_REQUEST = 0x0 MSGTYPE_HANDSHAKE_RESPONSE = 0x1 @@ -21,8 +21,8 @@ def __init__(self, login_key, iot_key, hmac_key, fixed_key=None, fixed_iv=None): self._hmac_key = hmac_key self._aes_key = None self._aes_iv = None - self._fixed_key = format(fixed_key, 'x').encode("ascii") if fixed_key else None - self._fixed_iv = format(fixed_iv, 'x').encode("ascii") if fixed_iv else None + self._fixed_key = format(fixed_key, "x").encode("ascii") if fixed_key else None + self._fixed_iv = format(fixed_iv, "x").encode("ascii") if fixed_iv else None def sign(self, url: str, data: Any, random: str) -> str: msg = self._iot_key @@ -59,7 +59,7 @@ def get_udp_id(appliance_id, method=0): data = bytearray(sha256(bytes_id).digest()) for i in range(0, 16): data[i] ^= data[i + 16] - return data[0: 16].hex() + return data[0:16].hex() def set_aes_keys(self, key, iv): if isinstance(key, str): @@ -103,15 +103,18 @@ def aes_decrypt(self, data, key=None, iv=None): if isinstance(data, str): data = bytes.fromhex(data) if aes_iv is None: # ECB - return unpad(AES.new(aes_key, AES.MODE_ECB).decrypt(data), len(aes_key)).decode() + return unpad( + AES.new(aes_key, AES.MODE_ECB).decrypt(data), len(aes_key) + ).decode() else: # CBC - return unpad(AES.new(aes_key, AES.MODE_CBC, iv=aes_iv).decrypt(data), len(aes_key)).decode() + return unpad( + AES.new(aes_key, AES.MODE_CBC, iv=aes_iv).decrypt(data), len(aes_key) + ).decode() class MeijuCloudSecurity(CloudSecurity): def __init__(self, login_key, iot_key, hmac_key): - super().__init__(login_key, iot_key, hmac_key, - 10864842703515613082) + super().__init__(login_key, iot_key, hmac_key, 10864842703515613082) def encrypt_iam_password(self, login_id, data) -> str: md = md5() @@ -123,9 +126,9 @@ def encrypt_iam_password(self, login_id, data) -> str: class MSmartCloudSecurity(CloudSecurity): def __init__(self, login_key, iot_key, hmac_key): - super().__init__(login_key, iot_key, hmac_key, - 13101328926877700970, - 16429062708050928556) + super().__init__( + login_key, iot_key, hmac_key, 13101328926877700970, 16429062708050928556 + ) def encrypt_iam_password(self, login_id, data) -> str: md = md5() @@ -141,8 +144,8 @@ def set_aes_keys(self, encrypted_key, encrypted_iv): key_digest = sha256(self._login_key.encode("ascii")).hexdigest() tmp_key = key_digest[:16].encode("ascii") tmp_iv = key_digest[16:32].encode("ascii") - self._aes_key = self.aes_decrypt(encrypted_key, tmp_key, tmp_iv).encode('ascii') - self._aes_iv = self.aes_decrypt(encrypted_iv, tmp_key, tmp_iv).encode('ascii') + self._aes_key = self.aes_decrypt(encrypted_key, tmp_key, tmp_iv).encode("ascii") + self._aes_iv = self.aes_decrypt(encrypted_iv, tmp_key, tmp_iv).encode("ascii") class MideaAirSecurity(CloudSecurity): @@ -161,10 +164,13 @@ def __init__(self): self.blockSize = 16 self.iv = b"\0" * 16 self.aes_key = bytes.fromhex( - format(141661095494369103254425781617665632877, 'x') + format(141661095494369103254425781617665632877, "x") ) self.salt = bytes.fromhex( - format(233912452794221312800602098970898185176935770387238278451789080441632479840061417076563, 'x') + format( + 233912452794221312800602098970898185176935770387238278451789080441632479840061417076563, + "x", + ) ) self._tcp_key = None self._request_count = 0 @@ -172,7 +178,9 @@ def __init__(self): def aes_decrypt(self, raw): try: - return unpad(AES.new(self.aes_key, AES.MODE_ECB).decrypt(bytearray(raw)), 16) + return unpad( + AES.new(self.aes_key, AES.MODE_ECB).decrypt(bytearray(raw)), 16 + ) except ValueError: return bytearray(0) @@ -208,7 +216,7 @@ def encode_8370(self, data, msgtype): size, padding = len(data), 0 if msgtype in (MSGTYPE_ENCRYPTED_RESPONSE, MSGTYPE_ENCRYPTED_REQUEST): if (size + 2) % 16 != 0: - padding = 16 - (size + 2 & 0xf) + padding = 16 - (size + 2 & 0xF) size += padding + 32 data += get_random_bytes(padding) header += size.to_bytes(2, "big") @@ -238,7 +246,7 @@ def decode_8370(self, data): if header[4] != 0x20: raise Exception("missing byte 4") padding = header[5] >> 4 - msgtype = header[5] & 0xf + msgtype = header[5] & 0xF data = data[6:] if msgtype in (MSGTYPE_ENCRYPTED_RESPONSE, MSGTYPE_ENCRYPTED_REQUEST): sign = data[-32:] diff --git a/custom_components/midea_ac_lan/midea/devices/__init__.py b/custom_components/midea_ac_lan/midea/devices/__init__.py index 4a732c88..6a908f26 100644 --- a/custom_components/midea_ac_lan/midea/devices/__init__.py +++ b/custom_components/midea_ac_lan/midea/devices/__init__.py @@ -1,7 +1,8 @@ -from homeassistant.core import HomeAssistant from importlib import import_module from types import ModuleType +from homeassistant.core import HomeAssistant + async def async_device_selector( hass: HomeAssistant, @@ -15,7 +16,7 @@ async def async_device_selector( protocol: int, model: str, subtype: int, - customize: str + customize: str, ): try: @@ -29,6 +30,7 @@ async def async_device_selector( def _load_device_module() -> None: """Load all service modules.""" modules.append(import_module(device_path, __package__)) + await hass.async_add_import_executor_job(_load_device_module) device = modules[0].MideaAppliance( @@ -41,7 +43,7 @@ def _load_device_module() -> None: protocol=protocol, model=model, subtype=subtype, - customize=customize + customize=customize, ) except ModuleNotFoundError: device = None diff --git a/custom_components/midea_ac_lan/midea/devices/a1/device.py b/custom_components/midea_ac_lan/midea/devices/a1/device.py index 4cc990a4..cbff7675 100644 --- a/custom_components/midea_ac_lan/midea/devices/a1/device.py +++ b/custom_components/midea_ac_lan/midea/devices/a1/device.py @@ -1,13 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageA1Response, - MessageSet -) + +from .message import MessageA1Response, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -30,28 +29,29 @@ class DeviceAttributes(StrEnum): class MideaA1Device(MiedaDevice): - _modes = [ - "Manual", "Continuous", "Auto", "Clothes-Dry", "Shoes-Dry" - ] + _modes = ["Manual", "Continuous", "Auto", "Clothes-Dry", "Shoes-Dry"] _speeds = { - 1: "Lowest", 40: "Low", 60: "Medium", 80: "High", 102: "Auto", 127: "Off" + 1: "Lowest", + 40: "Low", + 60: "Medium", + 80: "High", + 102: "Auto", + 127: "Off", } - _water_level_sets = [ - "25", "50", "75", "100" - ] + _water_level_sets = ["25", "50", "75", "100"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -77,8 +77,9 @@ def __init__( DeviceAttributes.water_level_set: 50, DeviceAttributes.tank_full: None, DeviceAttributes.current_humidity: None, - DeviceAttributes.current_temperature: None - }) + DeviceAttributes.current_temperature: None, + }, + ) @property def modes(self): @@ -93,9 +94,7 @@ def water_level_sets(self): return MideaA1Device._water_level_sets def build_query(self): - return [ - MessageQuery(self._protocol_version) - ] + return [MessageQuery(self._protocol_version)] def process_message(self, msg): message = MessageA1Response(msg) @@ -119,9 +118,13 @@ def process_message(self, msg): self._attributes[status] = str(value) else: self._attributes[status] = value - tank_full = (self._attributes[DeviceAttributes.tank] >= - int(self._attributes[DeviceAttributes.water_level_set])) - if self._attributes[DeviceAttributes.tank_full] is None or self._attributes[DeviceAttributes.tank_full] != tank_full: + tank_full = self._attributes[DeviceAttributes.tank] >= int( + self._attributes[DeviceAttributes.water_level_set] + ) + if ( + self._attributes[DeviceAttributes.tank_full] is None + or self._attributes[DeviceAttributes.tank_full] != tank_full + ): self._attributes[DeviceAttributes.tank_full] = tank_full new_status[str(DeviceAttributes.tank_full)] = tank_full new_status[str(status)] = self._attributes[status] @@ -133,17 +136,26 @@ def make_message_set(self): message.prompt_tone = self._attributes[DeviceAttributes.prompt_tone] message.child_lock = self._attributes[DeviceAttributes.child_lock] if self._attributes[DeviceAttributes.mode] in MideaA1Device._modes: - message.mode = MideaA1Device._modes.index(self._attributes[DeviceAttributes.mode]) + 1 + message.mode = ( + MideaA1Device._modes.index(self._attributes[DeviceAttributes.mode]) + 1 + ) else: message.mode = 1 - message.fan_speed = 40 if self._attributes[DeviceAttributes.fan_speed] is None else \ - list(MideaA1Device._speeds.keys())[list(MideaA1Device._speeds.values()).index( - self._attributes[DeviceAttributes.fan_speed] - )] + message.fan_speed = ( + 40 + if self._attributes[DeviceAttributes.fan_speed] is None + else list(MideaA1Device._speeds.keys())[ + list(MideaA1Device._speeds.values()).index( + self._attributes[DeviceAttributes.fan_speed] + ) + ] + ) message.target_humidity = self._attributes[DeviceAttributes.target_humidity] message.swing = self._attributes[DeviceAttributes.swing] message.anion = self._attributes[DeviceAttributes.anion] - message.water_level_set = int(self._attributes[DeviceAttributes.water_level_set]) + message.water_level_set = int( + self._attributes[DeviceAttributes.water_level_set] + ) return message def set_attribute(self, attr, value): diff --git a/custom_components/midea_ac_lan/midea/devices/a1/message.py b/custom_components/midea_ac_lan/midea/devices/a1/message.py index e1dbe517..f6a43b7a 100644 --- a/custom_components/midea_ac_lan/midea/devices/a1/message.py +++ b/custom_components/midea_ac_lan/midea/devices/a1/message.py @@ -1,11 +1,12 @@ from enum import IntEnum + from ...core.crc8 import calculate from ...core.message import ( - MessageType, + MessageBody, MessageRequest, MessageResponse, - MessageBody, - NewProtocolMessageBody + MessageType, + NewProtocolMessageBody, ) @@ -44,17 +45,34 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x41) + body_type=0x41, + ) @property def _body(self): - return bytearray([ - 0x81, 0x00, 0xFF, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 - ]) + return bytearray( + [ + 0x81, + 0x00, + 0xFF, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class MessageNewProtocolQuery(MessageA1Base): @@ -62,13 +80,12 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0xB1) + body_type=0xB1, + ) @property def _body(self): - query_params = [ - NewProtocolTags.light - ] + query_params = [NewProtocolTags.light] _body = bytearray([len(query_params)]) for param in query_params: _body.extend([param & 0xFF, param >> 8]) @@ -80,7 +97,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x48) + body_type=0x48, + ) self.power = False self.prompt_tone = False self.mode = 1 @@ -110,20 +128,30 @@ def _body(self): swing = 0x08 if self.swing else 0x00 # byte 13 water_level_set water_level_set = self.water_level_set - return bytearray([ - power | prompt_tone | 0x02, - mode, - fan_speed, - 0x00, 0x00, 0x00, - target_humidity, - child_lock, - anion, - swing, - 0x00, 0x00, - water_level_set, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 - ]) + return bytearray( + [ + power | prompt_tone | 0x02, + mode, + fan_speed, + 0x00, + 0x00, + 0x00, + target_humidity, + child_lock, + anion, + swing, + 0x00, + 0x00, + water_level_set, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class MessageNewProtocolSet(MessageA1Base): @@ -131,7 +159,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0xB0) + body_type=0xB0, + ) self.light = None @property @@ -143,8 +172,9 @@ def _body(self): payload.extend( NewProtocolMessageBody.pack( param=NewProtocolTags.light, - value=bytearray([0x01 if self.light else 0x00]) - )) + value=bytearray([0x01 if self.light else 0x00]), + ) + ) payload[0] = pack_count return payload @@ -172,13 +202,17 @@ def __init__(self, body, bt): super().__init__(body, bt) params = self.parse() if NewProtocolTags.light in params: - self.light = (params[NewProtocolTags.light][0] > 0) + self.light = params[NewProtocolTags.light][0] > 0 class MessageA1Response(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.query, MessageType.set, MessageType.notify1]: + if self.message_type in [ + MessageType.query, + MessageType.set, + MessageType.notify1, + ]: if self.body_type in [0xB0, 0xB1, 0xB5]: self.set_body(A1NewProtocolMessageBody(super().body, self.body_type)) else: diff --git a/custom_components/midea_ac_lan/midea/devices/ac/device.py b/custom_components/midea_ac_lan/midea/devices/ac/device.py index 977c6050..87ccb1c2 100644 --- a/custom_components/midea_ac_lan/midea/devices/ac/device.py +++ b/custom_components/midea_ac_lan/midea/devices/ac/device.py @@ -1,20 +1,23 @@ -import logging import json +import logging + from .message import ( - MessageQuery, - MessageToggleDisplay, - MessageNewProtocolQuery, MessageACResponse, MessageGeneralSet, + MessageNewProtocolQuery, MessageNewProtocolSet, MessagePowerQuery, + MessageQuery, MessageSubProtocolQuery, - MessageSubProtocolSet + MessageSubProtocolSet, + MessageToggleDisplay, ) + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -58,22 +61,27 @@ class DeviceAttributes(StrEnum): class MideaACDevice(MiedaDevice): _fresh_air_fan_speeds = { - 0: "Off", 20: "Silent", 40: "Low", 60: "Medium", 80: "High", 100: "Full" + 0: "Off", + 20: "Silent", + 40: "Low", + 60: "Medium", + 80: "High", + 100: "Full", } _fresh_air_fan_speeds_rev = dict(reversed(_fresh_air_fan_speeds.items())) def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -120,7 +128,8 @@ def __init__( DeviceAttributes.fresh_air_mode: None, DeviceAttributes.fresh_air_1: None, DeviceAttributes.fresh_air_2: None, - }) + }, + ) self._fresh_air_version = None self._default_temperature_step = 0.5 self._temperature_step = None @@ -144,12 +153,12 @@ def build_query(self): return [ MessageSubProtocolQuery(self._protocol_version, 0x10), MessageSubProtocolQuery(self._protocol_version, 0x11), - MessageSubProtocolQuery(self._protocol_version, 0x30) + MessageSubProtocolQuery(self._protocol_version, 0x30), ] return [ MessageQuery(self._protocol_version), MessageNewProtocolQuery(self._protocol_version), - MessagePowerQuery(self._protocol_version) + MessagePowerQuery(self._protocol_version), ] def process_message(self, msg): @@ -179,9 +188,13 @@ def process_message(self, msg): self._attributes[DeviceAttributes.fresh_air_mode] = v else: self._attributes[DeviceAttributes.fresh_air_mode] = "Off" - new_status[DeviceAttributes.fresh_air_mode.value] = self._attributes[DeviceAttributes.fresh_air_mode] - if not self._attributes[DeviceAttributes.power] or \ - (DeviceAttributes.swing_vertical in new_status and self._attributes[DeviceAttributes.swing_vertical]): + new_status[DeviceAttributes.fresh_air_mode.value] = self._attributes[ + DeviceAttributes.fresh_air_mode + ] + if not self._attributes[DeviceAttributes.power] or ( + DeviceAttributes.swing_vertical in new_status + and self._attributes[DeviceAttributes.swing_vertical] + ): self._attributes[DeviceAttributes.indirect_wind] = False new_status[DeviceAttributes.indirect_wind.value] = False if not self._attributes[DeviceAttributes.power]: @@ -198,7 +211,9 @@ def make_message_set(self): message.power = self._attributes[DeviceAttributes.power] message.prompt_tone = self._attributes[DeviceAttributes.prompt_tone] message.mode = self._attributes[DeviceAttributes.mode] - message.target_temperature = self._attributes[DeviceAttributes.target_temperature] + message.target_temperature = self._attributes[ + DeviceAttributes.target_temperature + ] message.fan_speed = self._attributes[DeviceAttributes.fan_speed] message.swing_vertical = self._attributes[DeviceAttributes.swing_vertical] message.swing_horizontal = self._attributes[DeviceAttributes.swing_horizontal] @@ -220,7 +235,9 @@ def make_subptotocol_message_set(self): message.prompt_tone = self._attributes[DeviceAttributes.prompt_tone] message.aux_heating = self._attributes[DeviceAttributes.aux_heating] message.mode = self._attributes[DeviceAttributes.mode] - message.target_temperature = self._attributes[DeviceAttributes.target_temperature] + message.target_temperature = self._attributes[ + DeviceAttributes.target_temperature + ] message.fan_speed = self._attributes[DeviceAttributes.fan_speed] message.boost_mode = self._attributes[DeviceAttributes.boost_mode] message.dry = self._attributes[DeviceAttributes.dry] @@ -240,13 +257,15 @@ def make_message_uniq_set(self): def set_attribute(self, attr, value): # if nat a sensor message = None - if attr not in [DeviceAttributes.indoor_temperature, - DeviceAttributes.outdoor_temperature, - DeviceAttributes.indoor_humidity, - DeviceAttributes.full_dust, - DeviceAttributes.total_energy_consumption, - DeviceAttributes.current_energy_consumption, - DeviceAttributes.realtime_power]: + if attr not in [ + DeviceAttributes.indoor_temperature, + DeviceAttributes.outdoor_temperature, + DeviceAttributes.indoor_humidity, + DeviceAttributes.full_dust, + DeviceAttributes.total_energy_consumption, + DeviceAttributes.current_energy_consumption, + DeviceAttributes.realtime_power, + ]: if attr == DeviceAttributes.prompt_tone: self._attributes[DeviceAttributes.prompt_tone] = value self.update_all({DeviceAttributes.prompt_tone.value: value}) @@ -254,9 +273,9 @@ def set_attribute(self, attr, value): message = MessageToggleDisplay(self._protocol_version) message.prompt_tone = self._attributes[DeviceAttributes.prompt_tone] elif attr in [ - DeviceAttributes.indirect_wind, - DeviceAttributes.breezeless, - DeviceAttributes.screen_display_alternate + DeviceAttributes.indirect_wind, + DeviceAttributes.breezeless, + DeviceAttributes.screen_display_alternate, ]: message = MessageNewProtocolSet(self._protocol_version) setattr(message, str(attr), value) @@ -267,38 +286,42 @@ def set_attribute(self, attr, value): setattr( message, str(self._fresh_air_version), - [value, self._attributes[DeviceAttributes.fresh_air_fan_speed]] + [value, self._attributes[DeviceAttributes.fresh_air_fan_speed]], ) elif attr == DeviceAttributes.fresh_air_mode: if value in MideaACDevice._fresh_air_fan_speeds.values(): speed = list(MideaACDevice._fresh_air_fan_speeds.keys())[ list(MideaACDevice._fresh_air_fan_speeds.values()).index(value) ] - fresh_air = [True, speed] if speed > 0 else \ - [False, self._attributes[DeviceAttributes.fresh_air_fan_speed]] - message = MessageNewProtocolSet(self._protocol_version) - setattr( - message, - str(self._fresh_air_version), - fresh_air + fresh_air = ( + [True, speed] + if speed > 0 + else [ + False, + self._attributes[DeviceAttributes.fresh_air_fan_speed], + ] ) + message = MessageNewProtocolSet(self._protocol_version) + setattr(message, str(self._fresh_air_version), fresh_air) elif not value: message = MessageNewProtocolSet(self._protocol_version) setattr( message, str(self._fresh_air_version), - [False, self._attributes[DeviceAttributes.fresh_air_fan_speed]] + [False, self._attributes[DeviceAttributes.fresh_air_fan_speed]], ) elif attr == DeviceAttributes.fresh_air_fan_speed: if self._fresh_air_version is not None: message = MessageNewProtocolSet(self._protocol_version) - fresh_air = [True, value] if value > 0 else \ - [False, self._attributes[DeviceAttributes.fresh_air_fan_speed]] - setattr( - message, - str(self._fresh_air_version), - fresh_air + fresh_air = ( + [True, value] + if value > 0 + else [ + False, + self._attributes[DeviceAttributes.fresh_air_fan_speed], + ] ) + setattr(message, str(self._fresh_air_version), fresh_air) elif attr in self._attributes.keys(): message = self.make_message_uniq_set() if attr in [ @@ -306,7 +329,7 @@ def set_attribute(self, attr, value): DeviceAttributes.sleep_mode, DeviceAttributes.frost_protect, DeviceAttributes.comfort_mode, - DeviceAttributes.eco_mode + DeviceAttributes.eco_mode, ]: message.boost_mode = False message.sleep_mode = False diff --git a/custom_components/midea_ac_lan/midea/devices/ac/message.py b/custom_components/midea_ac_lan/midea/devices/ac/message.py index 96037f1b..9c5816b3 100644 --- a/custom_components/midea_ac_lan/midea/devices/ac/message.py +++ b/custom_components/midea_ac_lan/midea/devices/ac/message.py @@ -1,12 +1,13 @@ from enum import IntEnum + +from ...core.crc8 import calculate from ...core.message import ( - MessageType, + MessageBody, MessageRequest, MessageResponse, - MessageBody, - NewProtocolMessageBody + MessageType, + NewProtocolMessageBody, ) -from ...core.crc8 import calculate BB_AC_MODES = [0, 3, 1, 2, 4, 5] @@ -18,7 +19,7 @@ class NewProtocolTags(IntEnum): prompt_tone = 0x001A indirect_wind = 0x0042 fresh_air_1 = 0x0233 - fresh_air_2 = 0x004b + fresh_air_2 = 0x004B class MessageACBase(MessageRequest): @@ -29,7 +30,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xAC, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) MessageACBase._message_serial += 1 if MessageACBase._message_serial >= 254: @@ -52,17 +53,34 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x41) + body_type=0x41, + ) @property def _body(self): - return bytearray([ - 0x81, 0x00, 0xFF, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - ]) + return bytearray( + [ + 0x81, + 0x00, + 0xFF, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class MessagePowerQuery(MessageACBase): @@ -70,13 +88,12 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x41) + body_type=0x41, + ) @property def _body(self): - return bytearray([ - 0x21, 0x01, 0x44, 0x00, 0x01 - ]) + return bytearray([0x21, 0x01, 0x44, 0x00, 0x01]) @property def body(self): @@ -90,20 +107,36 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x41) + body_type=0x41, + ) self.prompt_tone = False @property def _body(self): prompt_tone = 0x40 if self.prompt_tone else 0 - return bytearray([ - 0x02 | prompt_tone, - 0x00, 0xFF, 0x02, - 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 - ]) + return bytearray( + [ + 0x02 | prompt_tone, + 0x00, + 0xFF, + 0x02, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class MessageNewProtocolQuery(MessageACBase): @@ -111,7 +144,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0xB1) + body_type=0xB1, + ) @property def _body(self): @@ -121,7 +155,7 @@ def _body(self): NewProtocolTags.indoor_humidity, NewProtocolTags.screen_display, NewProtocolTags.fresh_air_1, - NewProtocolTags.fresh_air_2 + NewProtocolTags.fresh_air_2, ] _body = bytearray([len(query_params)]) @@ -133,9 +167,8 @@ def _body(self): class MessageSubProtocol(MessageACBase): def __init__(self, protocol_version, message_type, subprotocol_query_type): super().__init__( - protocol_version=protocol_version, - message_type=message_type, - body_type=0xAA) + protocol_version=protocol_version, message_type=message_type, body_type=0xAA + ) self._subprotocol_query_type = subprotocol_query_type @property @@ -152,10 +185,17 @@ def body(self): @property def _body(self): _subprotocol_body = self._subprotocol_body - _body = bytearray([ - 6 + 2 + (len(_subprotocol_body) if _subprotocol_body is not None else 0), - 0x00, 0xFF, 0xFF, self._subprotocol_query_type - ]) + _body = bytearray( + [ + 6 + + 2 + + (len(_subprotocol_body) if _subprotocol_body is not None else 0), + 0x00, + 0xFF, + 0xFF, + self._subprotocol_query_type, + ] + ) if _subprotocol_body is not None: _body.extend(_subprotocol_body) return _body @@ -166,7 +206,8 @@ def __init__(self, protocol_version, subprotocol_query_type): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - subprotocol_query_type=subprotocol_query_type) + subprotocol_query_type=subprotocol_query_type, + ) class MessageSubProtocolSet(MessageSubProtocol): @@ -174,7 +215,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - subprotocol_query_type=0x20) + subprotocol_query_type=0x20, + ) self.power = False self.mode = 0 self.target_temperature = 20.0 @@ -206,18 +248,47 @@ def _subprotocol_body(self): prompt_tone = 0x01 if self.prompt_tone else 0 timer = 0x04 if (self.sn8_flag and self.timer) else 0 - return bytearray([ - 0x02 | boost_mode | power | dry, aux_heating, sleep_mode, 0x00, - 0x00, mode, target_temperature, fan_speed, - 0x32, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x01, water_model_temperature_set, - prompt_tone, target_temperature, 0x32, 0x66, - 0x00, eco | timer, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x08 - ]) + return bytearray( + [ + 0x02 | boost_mode | power | dry, + aux_heating, + sleep_mode, + 0x00, + 0x00, + mode, + target_temperature, + fan_speed, + 0x32, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0x01, + 0x00, + 0x01, + water_model_temperature_set, + prompt_tone, + target_temperature, + 0x32, + 0x66, + 0x00, + eco | timer, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + ] + ) class MessageGeneralSet(MessageACBase): @@ -225,7 +296,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x40) + body_type=0x40, + ) self.power = False self.prompt_tone = True self.mode = 0 @@ -250,14 +322,18 @@ def _body(self): power = 0x01 if self.power else 0 prompt_tone = 0x40 if self.prompt_tone else 0 # Byte2, mode target_temperature - mode = (self.mode << 5) & 0xe0 - target_temperature = (int(self.target_temperature) & 0xf) | \ - (0x10 if int(round(self.target_temperature * 2)) % 2 != 0 else 0) + mode = (self.mode << 5) & 0xE0 + target_temperature = (int(self.target_temperature) & 0xF) | ( + 0x10 if int(round(self.target_temperature * 2)) % 2 != 0 else 0 + ) # Byte 3, fan_speed - fan_speed = self.fan_speed & 0x7f + fan_speed = self.fan_speed & 0x7F # Byte 7, swing_mode - swing_mode = 0x30 | (0x0c if self.swing_vertical else 0) \ + swing_mode = ( + 0x30 + | (0x0C if self.swing_vertical else 0) | (0x03 if self.swing_horizontal else 0) + ) # Byte 8, turbo boost_mode = 0x20 if self.boost_mode else 0 # Byte 9 aux_heating eco_mode @@ -276,22 +352,32 @@ def _body(self): # Byte 22 comfort_mode comfort_mode = 0x01 if self.comfort_mode else 0 - return bytearray([ - power | prompt_tone, - mode | target_temperature, - fan_speed, - 0x00, 0x00, 0x00, - swing_mode, - boost_mode, - smart_eye | dry | aux_heating | eco_mode, - temp_fahrenheit | sleep_mode | boost_mode_1, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - natural_wind, - 0x00, 0x00, 0x00, - frost_protect, - comfort_mode - ]) + return bytearray( + [ + power | prompt_tone, + mode | target_temperature, + fan_speed, + 0x00, + 0x00, + 0x00, + swing_mode, + boost_mode, + smart_eye | dry | aux_heating | eco_mode, + temp_fahrenheit | sleep_mode | boost_mode_1, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + natural_wind, + 0x00, + 0x00, + 0x00, + frost_protect, + comfort_mode, + ] + ) class MessageNewProtocolSet(MessageACBase): @@ -299,7 +385,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0xB0) + body_type=0xB0, + ) self.indirect_wind = None self.prompt_tone = None self.breezeless = None @@ -316,29 +403,33 @@ def _body(self): payload.extend( NewProtocolMessageBody.pack( param=NewProtocolTags.breezeless, - value=bytearray([0x01 if self.breezeless else 0x00]) - )) + value=bytearray([0x01 if self.breezeless else 0x00]), + ) + ) if self.indirect_wind is not None: pack_count += 1 payload.extend( NewProtocolMessageBody.pack( param=NewProtocolTags.indirect_wind, - value=bytearray([0x02 if self.indirect_wind else 0x01]) - )) + value=bytearray([0x02 if self.indirect_wind else 0x01]), + ) + ) if self.prompt_tone is not None: pack_count += 1 payload.extend( NewProtocolMessageBody.pack( param=NewProtocolTags.prompt_tone, - value=bytearray([0x01 if self.prompt_tone else 0x00]) - )) + value=bytearray([0x01 if self.prompt_tone else 0x00]), + ) + ) if self.screen_display_alternate is not None: pack_count += 1 payload.extend( NewProtocolMessageBody.pack( param=NewProtocolTags.screen_display, - value=bytearray([0x64 if self.screen_display_alternate else 0x00]) - )) + value=bytearray([0x64 if self.screen_display_alternate else 0x00]), + ) + ) if self.fresh_air_1 is not None and len(self.fresh_air_1) == 2: pack_count += 1 fresh_air_power = 2 if self.fresh_air_1[0] > 0 else 1 @@ -346,13 +437,22 @@ def _body(self): payload.extend( NewProtocolMessageBody.pack( param=NewProtocolTags.fresh_air_1, - value=bytearray([ - fresh_air_power, - fresh_air_fan_speed, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - ]) - )) + value=bytearray( + [ + fresh_air_power, + fresh_air_fan_speed, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ), + ) + ) if self.fresh_air_2 is not None and len(self.fresh_air_2) == 2: pack_count += 1 fresh_air_power = 1 if self.fresh_air_2[0] > 0 else 0 @@ -360,12 +460,9 @@ def _body(self): payload.extend( NewProtocolMessageBody.pack( param=NewProtocolTags.fresh_air_2, - value=bytearray([ - fresh_air_power, - fresh_air_fan_speed, - 0xFF - ]) - )) + value=bytearray([fresh_air_power, fresh_air_fan_speed, 0xFF]), + ) + ) payload[0] = pack_count return payload @@ -374,9 +471,11 @@ class XA0MessageBody(MessageBody): def __init__(self, body): super().__init__(body) self.power = (body[1] & 0x1) > 0 - self.target_temperature = ((body[1] & 0x3E) >> 1) - 4 + 16.0 + (0.5 if body[1] & 0x40 > 0 else 0.0) - self.mode = (body[2] & 0xe0) >> 5 - self.fan_speed = body[3] & 0x7f + self.target_temperature = ( + ((body[1] & 0x3E) >> 1) - 4 + 16.0 + (0.5 if body[1] & 0x40 > 0 else 0.0) + ) + self.mode = (body[2] & 0xE0) >> 5 + self.fan_speed = body[3] & 0x7F self.swing_vertical = (body[7] & 0xC) > 0 self.swing_horizontal = (body[7] & 0x3) > 0 self.boost_mode = ((body[8] & 0x20) > 0) or ((body[10] & 0x2) > 0) @@ -417,13 +516,15 @@ def __init__(self, body, bt): super().__init__(body, bt) params = self.parse() if NewProtocolTags.indirect_wind in params: - self.indirect_wind = (params[NewProtocolTags.indirect_wind][0] == 0x02) + self.indirect_wind = params[NewProtocolTags.indirect_wind][0] == 0x02 if NewProtocolTags.indoor_humidity in params: self.indoor_humidity = params[NewProtocolTags.indoor_humidity][0] if NewProtocolTags.breezeless in params: - self.breezeless = (params[NewProtocolTags.breezeless][0] == 1) + self.breezeless = params[NewProtocolTags.breezeless][0] == 1 if NewProtocolTags.screen_display in params: - self.screen_display_alternate = (params[NewProtocolTags.screen_display][0] > 0) + self.screen_display_alternate = ( + params[NewProtocolTags.screen_display][0] > 0 + ) self.screen_display_new = True if NewProtocolTags.fresh_air_1 in params: self.fresh_air_1 = True @@ -441,8 +542,10 @@ class XC0MessageBody(MessageBody): def __init__(self, body): super().__init__(body) self.power = (body[1] & 0x1) > 0 - self.mode = (body[2] & 0xe0) >> 5 - self.target_temperature = (body[2] & 0x0F) + 16.0 + (0.5 if body[0x02] & 0x10 > 0 else 0.0) + self.mode = (body[2] & 0xE0) >> 5 + self.target_temperature = ( + (body[2] & 0x0F) + 16.0 + (0.5 if body[0x02] & 0x10 > 0 else 0.0) + ) self.fan_speed = body[3] & 0x7F self.swing_vertical = (body[7] & 0x0C) > 0 self.swing_horizontal = (body[7] & 0x03) > 0 @@ -481,16 +584,13 @@ def __init__(self, body, analysis_method=3): super().__init__(body) if body[3] == 0x44: self.total_energy_consumption = XC1MessageBody.parse_consumption( - analysis_method, - body[4], body[5], body[6], body[7] + analysis_method, body[4], body[5], body[6], body[7] ) self.current_energy_consumption = XC1MessageBody.parse_consumption( - analysis_method, - body[12], body[13], body[14], body[15] + analysis_method, body[12], body[13], body[14], body[15] ) self.realtime_power = XC1MessageBody.parse_power( - analysis_method, - body[16], body[17], body[18] + analysis_method, body[16], body[17], body[18] ) elif body[3] == 0x40: pass @@ -502,9 +602,14 @@ def parse_value(byte): @staticmethod def parse_power(analysis_method, byte1, byte2, byte3): if analysis_method == 1: - return float(XC1MessageBody.parse_value(byte1) * 10000 + - XC1MessageBody.parse_value(byte2) * 100 + - XC1MessageBody.parse_value(byte3)) / 10 + return ( + float( + XC1MessageBody.parse_value(byte1) * 10000 + + XC1MessageBody.parse_value(byte2) * 100 + + XC1MessageBody.parse_value(byte3) + ) + / 10 + ) elif analysis_method == 2: return float((byte1 << 16) + (byte2 << 8) + byte3) / 10 else: @@ -513,10 +618,15 @@ def parse_power(analysis_method, byte1, byte2, byte3): @staticmethod def parse_consumption(analysis_method, byte1, byte2, byte3, byte4): if analysis_method == 1: - return float(XC1MessageBody.parse_value(byte1) * 1000000 + - XC1MessageBody.parse_value(byte2) * 10000 + - XC1MessageBody.parse_value(byte3) * 100 + - XC1MessageBody.parse_value(byte4)) / 100 + return ( + float( + XC1MessageBody.parse_value(byte1) * 1000000 + + XC1MessageBody.parse_value(byte2) * 10000 + + XC1MessageBody.parse_value(byte3) * 100 + + XC1MessageBody.parse_value(byte4) + ) + / 100 + ) elif analysis_method == 2: return float((byte1 << 32) + (byte2 << 16) + (byte3 << 8) + byte4) / 10 else: @@ -542,22 +652,40 @@ def __init__(self, body): self.mode = 0 self.target_temperature = (subprotocol_body[6] - 30) / 2 self.fan_speed = subprotocol_body[7] - self.timer = (subprotocol_body[25] & 0x04) > 0 if subprotocol_body_len > 27 else False - self.eco_mode = (subprotocol_body[25] & 0x40) > 0 if subprotocol_body_len > 27 else False + self.timer = ( + (subprotocol_body[25] & 0x04) > 0 + if subprotocol_body_len > 27 + else False + ) + self.eco_mode = ( + (subprotocol_body[25] & 0x40) > 0 + if subprotocol_body_len > 27 + else False + ) elif data_type == 0x10: if subprotocol_body[8] & 0x80 == 0x80: - self.indoor_temperature = (0 - (~(subprotocol_body[7] + subprotocol_body[8] * 256) + 1) & 0xffff) / 100 + self.indoor_temperature = ( + 0 - (~(subprotocol_body[7] + subprotocol_body[8] * 256) + 1) + & 0xFFFF + ) / 100 else: - self.indoor_temperature = (subprotocol_body[7] + subprotocol_body[8] * 256) / 100 + self.indoor_temperature = ( + subprotocol_body[7] + subprotocol_body[8] * 256 + ) / 100 self.indoor_humidity = subprotocol_body[30] self.sn8_flag = subprotocol_body[80] == 0x31 elif data_type == 0x12: pass elif data_type == 0x30: if subprotocol_body[6] & 0x80 == 0x80: - self.outdoor_temperature = (0 - (~(subprotocol_body[5] + subprotocol_body[6] * 256) + 1) & 0xffff) / 100 + self.outdoor_temperature = ( + 0 - (~(subprotocol_body[5] + subprotocol_body[6] * 256) + 1) + & 0xFFFF + ) / 100 else: - self.outdoor_temperature = (subprotocol_body[5] + subprotocol_body[6] * 256) / 100 + self.outdoor_temperature = ( + subprotocol_body[5] + subprotocol_body[6] * 256 + ) / 100 elif data_type == 0x13 or data_type == 0x21: pass @@ -569,15 +697,25 @@ def __init__(self, message, power_analysis_method=3): self.set_body(XA0MessageBody(super().body)) elif self.message_type == MessageType.notify1 and self.body_type == 0xA1: self.set_body(XA1MessageBody(super().body)) - elif self.message_type in [MessageType.query, MessageType.set, MessageType.notify2] and \ - self.body_type in [0xB0, 0xB1, 0xB5]: + elif self.message_type in [ + MessageType.query, + MessageType.set, + MessageType.notify2, + ] and self.body_type in [0xB0, 0xB1, 0xB5]: self.set_body(XBXMessageBody(super().body, self.body_type)) - elif self.message_type in [MessageType.query, MessageType.set] and self.body_type == 0xC0: + elif ( + self.message_type in [MessageType.query, MessageType.set] + and self.body_type == 0xC0 + ): self.set_body(XC0MessageBody(super().body)) elif self.message_type == MessageType.query and self.body_type == 0xC1: self.set_body(XC1MessageBody(super().body, power_analysis_method)) - elif self.message_type in [MessageType.set, MessageType.query, MessageType.notify2] and \ - self.body_type == 0xBB and len(super().body) >= 21: + elif ( + self.message_type + in [MessageType.set, MessageType.query, MessageType.notify2] + and self.body_type == 0xBB + and len(super().body) >= 21 + ): self.used_subprotocol = True self.set_body(XBBMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/b0/device.py b/custom_components/midea_ac_lan/midea/devices/b0/device.py index c1f5f6ec..8de17bf5 100644 --- a/custom_components/midea_ac_lan/midea/devices/b0/device.py +++ b/custom_components/midea_ac_lan/midea/devices/b0/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery01, - MessageB0Response -) + +from .message import MessageB0Response, MessageQuery01 + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -24,22 +24,26 @@ class DeviceAttributes(StrEnum): class MideaB0Device(MiedaDevice): _status = { - 0x01: "Standby", 0x02: "Idle", 0x03: "Working", - 0x04: "Finished", 0x05: "Delay", 0x06: "Paused" + 0x01: "Standby", + 0x02: "Idle", + 0x03: "Working", + 0x04: "Finished", + 0x05: "Delay", + 0x06: "Paused", } def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -60,7 +64,8 @@ def __init__( DeviceAttributes.tank_ejected: False, DeviceAttributes.water_change_reminder: False, DeviceAttributes.water_shortage: False, - }) + }, + ) def build_query(self): return [MessageQuery01(self._protocol_version)] @@ -74,7 +79,9 @@ def process_message(self, msg): value = getattr(message, str(status)) if status == DeviceAttributes.status: if value in MideaB0Device._status.keys(): - self._attributes[DeviceAttributes.status] = MideaB0Device._status.get(value) + self._attributes[DeviceAttributes.status] = ( + MideaB0Device._status.get(value) + ) else: self._attributes[DeviceAttributes.status] = None else: diff --git a/custom_components/midea_ac_lan/midea/devices/b0/message.py b/custom_components/midea_ac_lan/midea/devices/b0/message.py index 8dc4d0ce..ebed3bdd 100644 --- a/custom_components/midea_ac_lan/midea/devices/b0/message.py +++ b/custom_components/midea_ac_lan/midea/devices/b0/message.py @@ -1,18 +1,13 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageB0Base(MessageRequest): - def __init__(self, protocol_version, message_type, body_type): + def __init__(self, protocol_version, message_type, body_type): super().__init__( device_type=0xB0, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x00) + body_type=0x00, + ) @property def _body(self): @@ -37,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -60,9 +57,11 @@ def __init__(self, body): if len(body) > 15: self.door = (body[32] & 0x02) > 0 self.status = body[31] - self.time_remaining = (0 if body[22] == 0xFF else body[22]) * 3600 + \ - (0 if body[23] == 0xFF else body[23]) * 60 + \ - (0 if body[24] == 0xFF else body[24]) + self.time_remaining = ( + (0 if body[22] == 0xFF else body[22]) * 3600 + + (0 if body[23] == 0xFF else body[23]) * 60 + + (0 if body[24] == 0xFF else body[24]) + ) self.current_temperature = (body[25] << 8) + (body[26]) if self.current_temperature == 0: self.current_temperature = (body[27] << 8) + body[28] diff --git a/custom_components/midea_ac_lan/midea/devices/b1/device.py b/custom_components/midea_ac_lan/midea/devices/b1/device.py index 4f62e6a9..9b78b67c 100644 --- a/custom_components/midea_ac_lan/midea/devices/b1/device.py +++ b/custom_components/midea_ac_lan/midea/devices/b1/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageB1Response -) + +from .message import MessageB1Response, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -24,22 +24,26 @@ class DeviceAttributes(StrEnum): class MideaB1Device(MiedaDevice): _status = { - 0x01: "Standby", 0x02: "Idle", 0x03: "Working", - 0x04: "Finished", 0x05: "Delay", 0x06: "Paused" + 0x01: "Standby", + 0x02: "Idle", + 0x03: "Working", + 0x04: "Finished", + 0x05: "Delay", + 0x06: "Paused", } def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -60,7 +64,8 @@ def __init__( DeviceAttributes.tank_ejected: False, DeviceAttributes.water_change_reminder: False, DeviceAttributes.water_shortage: False, - }) + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -74,7 +79,9 @@ def process_message(self, msg): value = getattr(message, str(status)) if status == DeviceAttributes.status: if value in MideaB1Device._status.keys(): - self._attributes[DeviceAttributes.status] = MideaB1Device._status.get(value) + self._attributes[DeviceAttributes.status] = ( + MideaB1Device._status.get(value) + ) else: self._attributes[DeviceAttributes.status] = None else: diff --git a/custom_components/midea_ac_lan/midea/devices/b1/message.py b/custom_components/midea_ac_lan/midea/devices/b1/message.py index 1810e1e8..d7d6fb63 100644 --- a/custom_components/midea_ac_lan/midea/devices/b1/message.py +++ b/custom_components/midea_ac_lan/midea/devices/b1/message.py @@ -1,18 +1,13 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageB1Base(MessageRequest): - def __init__(self, protocol_version, message_type, body_type): + def __init__(self, protocol_version, message_type, body_type): super().__init__( device_type=0xB1, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x00) + body_type=0x00, + ) @property def _body(self): @@ -37,9 +33,11 @@ def __init__(self, body): super().__init__(body) self.door = (body[16] & 0x02) > 0 self.status = body[1] - self.time_remaining = (0 if body[6] == 0xFF else body[6]) * 3600 + \ - (0 if body[7] == 0xFF else body[7]) * 60 + \ - (0 if body[8] == 0xFF else body[8]) + self.time_remaining = ( + (0 if body[6] == 0xFF else body[6]) * 3600 + + (0 if body[7] == 0xFF else body[7]) * 60 + + (0 if body[8] == 0xFF else body[8]) + ) self.current_temperature = body[19] self.tank_ejected = (body[16] & 0x04) > 0 self.water_shortage = (body[16] & 0x08) > 0 diff --git a/custom_components/midea_ac_lan/midea/devices/b3/device.py b/custom_components/midea_ac_lan/midea/devices/b3/device.py index 41fe63c9..8894dd19 100644 --- a/custom_components/midea_ac_lan/midea/devices/b3/device.py +++ b/custom_components/midea_ac_lan/midea/devices/b3/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageB3Response -) + +from .message import MessageB3Response, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -39,22 +39,25 @@ class DeviceAttributes(StrEnum): class MideaB2Device(MiedaDevice): _status = { - 0x00: "Off", 0x01: "Standby", 0x02: "Working", - 0x03: "Delay", 0x04: "Finished" + 0x00: "Off", + 0x01: "Standby", + 0x02: "Working", + 0x03: "Delay", + 0x04: "Finished", } def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -90,7 +93,8 @@ def __init__( DeviceAttributes.bottom_compartment_preheating: False, DeviceAttributes.bottom_compartment_cooling: False, DeviceAttributes.lock: False, - }) + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -105,7 +109,7 @@ def process_message(self, msg): if status in [ DeviceAttributes.top_compartment_status, DeviceAttributes.middle_compartment_status, - DeviceAttributes.bottom_compartment_status + DeviceAttributes.bottom_compartment_status, ]: if value in MideaB2Device._status.keys(): self._attributes[status] = MideaB2Device._status.get(value) diff --git a/custom_components/midea_ac_lan/midea/devices/b3/message.py b/custom_components/midea_ac_lan/midea/devices/b3/message.py index 8af1d9e6..c06e22a8 100644 --- a/custom_components/midea_ac_lan/midea/devices/b3/message.py +++ b/custom_components/midea_ac_lan/midea/devices/b3/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageB3Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xB3, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x31) + body_type=0x31, + ) @property def _body(self): @@ -39,25 +35,37 @@ def __init__(self, body): self.top_compartment_mode = body[2] self.top_compartment_temperature = body[3] self.top_compartment_remaining = ( - body[23] * 3600 if len(body) > 23 and body[23] != 0xFF else 0 + - body[4] * 60 if body[4] != 0xFF else 0 + - body[5] if body[5] != 0xFF else 0 + body[23] * 3600 + if len(body) > 23 and body[23] != 0xFF + else ( + 0 + body[4] * 60 + if body[4] != 0xFF + else 0 + body[5] if body[5] != 0xFF else 0 + ) ) self.bottom_compartment_status = body[6] self.bottom_compartment_mode = body[7] self.bottom_compartment_temperature = body[8] self.bottom_compartment_remaining = ( - body[24] * 3600 if len(body) > 24 and body[24] != 0xFF else 0 + - body[9] * 60 if body[9] != 0xFF else 0 + - body[10] if body[10] != 0xFF else 0 + body[24] * 3600 + if len(body) > 24 and body[24] != 0xFF + else ( + 0 + body[9] * 60 + if body[9] != 0xFF + else 0 + body[10] if body[10] != 0xFF else 0 + ) ) self.middle_compartment_status = body[17] self.middle_compartment_mode = body[18] self.middle_compartment_temperature = body[19] self.middle_compartment_remaining = ( - body[25] * 3600 if len(body) > 25 and body[25] != 0xFF else 0 + - body[20] * 60 if body[20] != 0xFF else 0 + - body[21] if body[21] != 0xFF else 0 + body[25] * 3600 + if len(body) > 25 and body[25] != 0xFF + else ( + 0 + body[20] * 60 + if body[20] != 0xFF + else 0 + body[21] if body[21] != 0xFF else 0 + ) ) self.lock = body[11] & 0x01 > 0 self.bottom_compartment_door = body[11] & 0x02 > 0 @@ -78,25 +86,37 @@ def __init__(self, body): self.top_compartment_mode = body[2] self.top_compartment_temperature = body[3] self.top_compartment_remaining = ( - body[17] * 3600 if len(body) > 17 and body[17] != 0xFF else 0 + - body[4] * 60 if body[4] != 0xFF else 0 + - body[5] if body[5] != 0xFF else 0 + body[17] * 3600 + if len(body) > 17 and body[17] != 0xFF + else ( + 0 + body[4] * 60 + if body[4] != 0xFF + else 0 + body[5] if body[5] != 0xFF else 0 + ) ) self.bottom_compartment_status = body[6] self.bottom_compartment_mode = body[7] self.bottom_compartment_temperature = body[8] self.bottom_compartment_remaining = ( - body[18] * 3600 if len(body) > 18 and body[18] != 0xFF else 0 + - body[9] * 60 if body[9] != 0xFF else 0 + - body[10] if body[10] != 0xFF else 0 + body[18] * 3600 + if len(body) > 18 and body[18] != 0xFF + else ( + 0 + body[9] * 60 + if body[9] != 0xFF + else 0 + body[10] if body[10] != 0xFF else 0 + ) ) self.middle_compartment_status = body[12] self.middle_compartment_mode = body[13] self.middle_compartment_temperature = body[14] self.middle_compartment_remaining = ( - body[19] * 3600 if len(body) > 19 and body[19] != 0xFF else 0 + - body[15] * 60 if body[15] != 0xFF else 0 + - body[16] if body[16] != 0xFF else 0 + body[19] * 3600 + if len(body) > 19 and body[19] != 0xFF + else ( + 0 + body[15] * 60 + if body[15] != 0xFF + else 0 + body[16] if body[16] != 0xFF else 0 + ) ) self.lock = body[11] & 0x01 > 0 @@ -108,30 +128,35 @@ def __init__(self, body): self.top_compartment_mode = body[6] self.top_compartment_temperature = body[7] self.top_compartment_remaining = ( - body[8] * 60 if body[8] != 0xFF else 0 + - body[9] if body[9] != 0xFF else 0 + body[8] * 60 if body[8] != 0xFF else 0 + body[9] if body[9] != 0xFF else 0 ) self.bottom_compartment_status = body[10] self.bottom_compartment_mode = body[11] self.bottom_compartment_temperature = body[12] self.bottom_compartment_remaining = ( - body[13] * 60 if body[13] != 0xFF else 0 + - body[14] if body[14] != 0xFF else 0 + body[13] * 60 + if body[13] != 0xFF + else 0 + body[14] if body[14] != 0xFF else 0 ) self.bottom_compartment_status = body[15] self.bottom_compartment_mode = body[16] self.bottom_compartment_temperature = body[17] self.bottom_compartment_remaining = ( - body[18] * 60 if body[18] != 0xFF else 0 + - body[19] if body[19] != 0xFF else 0 + body[18] * 60 + if body[18] != 0xFF + else 0 + body[19] if body[19] != 0xFF else 0 ) class MessageB3Response(MessageResponse): def __init__(self, message): super().__init__(message) - if (self.message_type == MessageType.query and self.body_type == 0x31 or - self.message_type == MessageType.notify1 and self.body_type == 0x41): + if ( + self.message_type == MessageType.query + and self.body_type == 0x31 + or self.message_type == MessageType.notify1 + and self.body_type == 0x41 + ): self.set_body(B3MessageBody31(super().body)) elif self.message_type == MessageType.set and self.body_type == 0x21: self.set_body(B3MessageBody21(super().body)) diff --git a/custom_components/midea_ac_lan/midea/devices/b4/device.py b/custom_components/midea_ac_lan/midea/devices/b4/device.py index 56e53d67..72824244 100644 --- a/custom_components/midea_ac_lan/midea/devices/b4/device.py +++ b/custom_components/midea_ac_lan/midea/devices/b4/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageB4Response -) + +from .message import MessageB4Response, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -24,22 +24,26 @@ class DeviceAttributes(StrEnum): class MideaB4Device(MiedaDevice): _status = { - 0x01: "Standby", 0x02: "Idle", 0x03: "Working", - 0x04: "Finished", 0x05: "Delay", 0x06: "Paused" + 0x01: "Standby", + 0x02: "Idle", + 0x03: "Working", + 0x04: "Finished", + 0x05: "Delay", + 0x06: "Paused", } def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -60,7 +64,8 @@ def __init__( DeviceAttributes.tank_ejected: False, DeviceAttributes.water_change_reminder: False, DeviceAttributes.water_shortage: False, - }) + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -74,7 +79,9 @@ def process_message(self, msg): value = getattr(message, str(status)) if status == DeviceAttributes.status: if value in MideaB4Device._status.keys(): - self._attributes[DeviceAttributes.status] = MideaB4Device._status.get(value) + self._attributes[DeviceAttributes.status] = ( + MideaB4Device._status.get(value) + ) else: self._attributes[DeviceAttributes.status] = None else: diff --git a/custom_components/midea_ac_lan/midea/devices/b4/message.py b/custom_components/midea_ac_lan/midea/devices/b4/message.py index 91f55059..07cc1ee9 100644 --- a/custom_components/midea_ac_lan/midea/devices/b4/message.py +++ b/custom_components/midea_ac_lan/midea/devices/b4/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageB4Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xB4, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -35,9 +31,11 @@ def _body(self): class B4MessageBody(MessageBody): def __init__(self, body): super().__init__(body) - self.time_remaining = (0 if body[22] == 0xFF else body[22]) * 3600 + \ - (0 if body[23] == 0xFF else body[23]) * 60 + \ - (0 if body[24] == 0xFF else body[24]) + self.time_remaining = ( + (0 if body[22] == 0xFF else body[22]) * 3600 + + (0 if body[23] == 0xFF else body[23]) * 60 + + (0 if body[24] == 0xFF else body[24]) + ) self.current_temperature = (body[25] << 8) + body[26] if self.current_temperature == 0: self.current_temperature = (body[27] << 8) + body[28] @@ -51,7 +49,11 @@ def __init__(self, body): class MessageB4Response(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.notify1, MessageType.query, MessageType.set]: + if self.message_type in [ + MessageType.notify1, + MessageType.query, + MessageType.set, + ]: if self.body_type == 0x01: self.set_body(B4MessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/b6/device.py b/custom_components/midea_ac_lan/midea/devices/b6/device.py index c9d68a1d..88b50c12 100644 --- a/custom_components/midea_ac_lan/midea/devices/b6/device.py +++ b/custom_components/midea_ac_lan/midea/devices/b6/device.py @@ -1,14 +1,13 @@ -import logging import json -from .message import ( - MessageQuery, - MessageB6Response, - MessageSet -) +import logging + +from .message import MessageB6Response, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -26,17 +25,17 @@ class DeviceAttributes(StrEnum): class MideaB6Device(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -56,11 +55,10 @@ def __init__( DeviceAttributes.fan_level: 0, DeviceAttributes.fan_speed: 0, DeviceAttributes.oilcup_full: False, - DeviceAttributes.cleaning_reminder: False - }) - self._default_speeds = { - 0: "Off", 1: "Level 1", 2: "Level 2" - } + DeviceAttributes.cleaning_reminder: False, + }, + ) + self._default_speeds = {0: "Off", 1: "Level 1", 2: "Level 2"} self._default_power_speed = 2 self._power_speed = self._default_power_speed self._speeds = self._default_speeds @@ -87,13 +85,21 @@ def process_message(self, msg): value = getattr(message, str(status)) if status == DeviceAttributes.fan_level: if value in self._speeds.keys(): - self._attributes[DeviceAttributes.mode] = self._speeds.get(value) - self._attributes[DeviceAttributes.fan_speed] = list(self._speeds.keys()).index(value) + self._attributes[DeviceAttributes.mode] = self._speeds.get( + value + ) + self._attributes[DeviceAttributes.fan_speed] = list( + self._speeds.keys() + ).index(value) else: self._attributes[DeviceAttributes.mode] = None self._attributes[DeviceAttributes.fan_speed] = 0 - new_status[DeviceAttributes.mode.value] = self._attributes[DeviceAttributes.mode] - new_status[DeviceAttributes.fan_speed.value] = self._attributes[DeviceAttributes.fan_speed] + new_status[DeviceAttributes.mode.value] = self._attributes[ + DeviceAttributes.mode + ] + new_status[DeviceAttributes.fan_speed.value] = self._attributes[ + DeviceAttributes.fan_speed + ] self._attributes[status] = getattr(message, str(status)) new_status[str(status)] = self._attributes[status] return new_status @@ -107,8 +113,9 @@ def set_attribute(self, attr, value): elif attr == DeviceAttributes.mode: if value in self._speeds.values(): message = MessageSet(self._protocol_version) - message.fan_level = \ - list(self._speeds.keys())[list(self._speeds.values()).index(value)] + message.fan_level = list(self._speeds.keys())[ + list(self._speeds.values()).index(value) + ] elif not value: message = MessageSet(self._protocol_version) message.power = False @@ -130,8 +137,9 @@ def turn_on(self, fan_speed=None, mode=None): else: message.fan_level = self._power_speed if mode is not None in self._speeds.values(): - message.fan_level = \ - list(self._speeds.keys())[list(self._speeds.values()).index(mode)] + message.fan_level = list(self._speeds.keys())[ + list(self._speeds.values()).index(mode) + ] self.build_send(message) def set_customize(self, customize): @@ -151,7 +159,9 @@ def set_customize(self, customize): keys = sorted(speeds.keys()) for k in keys: self._speeds[k] = speeds[k] - self.update_all({"speeds": self._speeds, "default_speed": self._power_speed}) + self.update_all( + {"speeds": self._speeds, "default_speed": self._power_speed} + ) except Exception as e: _LOGGER.error(f"[{self.device_id}] Set customize error: {repr(e)}") diff --git a/custom_components/midea_ac_lan/midea/devices/b6/message.py b/custom_components/midea_ac_lan/midea/devices/b6/message.py index 4ec8010c..53a1b78e 100644 --- a/custom_components/midea_ac_lan/midea/devices/b6/message.py +++ b/custom_components/midea_ac_lan/midea/devices/b6/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageB6Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xB6, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,7 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x11 if protocol_version == 2 else 0x31 + body_type=0x11 if protocol_version == 2 else 0x31, ) @property @@ -83,10 +78,9 @@ def _body(self): else: value2 = 0x02 value3 = self.fan_level - return bytearray([ - 0x01, light, value2, value3, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - ]) + return bytearray( + [0x01, light, value2, value3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] + ) else: value13 = 0xFF value14 = 0xFF @@ -113,10 +107,7 @@ def _body(self): value13 = 0x02 value14 = 0x02 value15 = 0x01 if self.light else 0x00 - return bytearray([ - 0x01, value13, value14, value15, value16, - 0xFF, 0xFF - ]) + return bytearray([0x01, value13, value14, value15, value16, 0xFF, 0xFF]) class B6FeedbackBody(MessageBody): @@ -157,7 +148,7 @@ class B6NewProtocolBody(MessageBody): def __init__(self, body): super().__init__(body) if body[1] == 0x01: - pack_bytes = body[3: 3 + body[2]] + pack_bytes = body[3 : 3 + body[2]] if pack_bytes[1] != 0xFF: self.power = True self.power = pack_bytes[1] not in [0x00, 0x01, 0x05, 0x07] @@ -189,9 +180,17 @@ def __init__(self, body): class MessageB6Response(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type == MessageType.set and self.body_type == 0x22 and super().body[1] == 0x01: + if ( + self.message_type == MessageType.set + and self.body_type == 0x22 + and super().body[1] == 0x01 + ): self.set_body(B6SpecialBody(super().body)) - elif self.message_type == MessageType.set and self.body_type == 0x11 and super().body[1] == 0x01: + elif ( + self.message_type == MessageType.set + and self.body_type == 0x11 + and super().body[1] == 0x01 + ): ############################# pass elif self.message_type == MessageType.query: diff --git a/custom_components/midea_ac_lan/midea/devices/bf/device.py b/custom_components/midea_ac_lan/midea/devices/bf/device.py index 225cba87..d8fe2070 100644 --- a/custom_components/midea_ac_lan/midea/devices/bf/device.py +++ b/custom_components/midea_ac_lan/midea/devices/bf/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageBFResponse -) + +from .message import MessageBFResponse, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -24,22 +24,26 @@ class DeviceAttributes(StrEnum): class MideaBFDevice(MiedaDevice): _status = { - 0x01: "PowerSave", 0x02: "Standby", 0x03: "Working", - 0x04: "Finished", 0x05: "Delay", 0x06: "Paused" + 0x01: "PowerSave", + 0x02: "Standby", + 0x03: "Working", + 0x04: "Finished", + 0x05: "Delay", + 0x06: "Paused", } def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -60,7 +64,8 @@ def __init__( DeviceAttributes.tank_ejected: None, DeviceAttributes.water_change_reminder: None, DeviceAttributes.water_shortage: None, - }) + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -74,7 +79,9 @@ def process_message(self, msg): value = getattr(message, str(status)) if status == DeviceAttributes.status: if value in MideaBFDevice._status.keys(): - self._attributes[DeviceAttributes.status] = MideaBFDevice._status.get(value) + self._attributes[DeviceAttributes.status] = ( + MideaBFDevice._status.get(value) + ) else: self._attributes[DeviceAttributes.status] = "Unknown" else: diff --git a/custom_components/midea_ac_lan/midea/devices/bf/message.py b/custom_components/midea_ac_lan/midea/devices/bf/message.py index e40da95b..358ce3eb 100644 --- a/custom_components/midea_ac_lan/midea/devices/bf/message.py +++ b/custom_components/midea_ac_lan/midea/devices/bf/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageBFBase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xBF, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -37,14 +33,17 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x02) + body_type=0x02, + ) self.power = None self.child_lock = None @property def _body(self): power = 0xFF if self.power is None else 0x11 if self.power else 0x01 - child_lock = 0xFF if self.child_lock is None else 0x01 if self.child_lock else 0x00 + child_lock = ( + 0xFF if self.child_lock is None else 0x01 if self.child_lock else 0x00 + ) return bytearray([power, child_lock] + [0xFF] * 7) @@ -52,9 +51,11 @@ class MessageBFBody(MessageBody): def __init__(self, body): super().__init__(body) self.status = body[31] - self.time_remaining = (0 if body[22] == 0xFF else body[22]) * 3600 + \ - (0 if body[23] == 0xFF else body[23]) * 60 + \ - (0 if body[24] == 0xFF else body[24]) + self.time_remaining = ( + (0 if body[22] == 0xFF else body[22]) * 3600 + + (0 if body[23] == 0xFF else body[23]) * 60 + + (0 if body[24] == 0xFF else body[24]) + ) cur_temperature = body[25] * 256 + body[26] if cur_temperature == 0: cur_temperature = body[27] * 256 + body[28] @@ -69,6 +70,10 @@ def __init__(self, body): class MessageBFResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.set, MessageType.notify1, MessageType.query] and self.body_type == 0x01: + if ( + self.message_type + in [MessageType.set, MessageType.notify1, MessageType.query] + and self.body_type == 0x01 + ): self.set_body(MessageBFBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/c2/device.py b/custom_components/midea_ac_lan/midea/devices/c2/device.py index 580f4b98..3e1a0444 100644 --- a/custom_components/midea_ac_lan/midea/devices/c2/device.py +++ b/custom_components/midea_ac_lan/midea/devices/c2/device.py @@ -1,15 +1,13 @@ -import logging import json -from .message import ( - MessageQuery, - MessageC2Response, - MessageSet, - MessagePower -) +import logging + +from .message import MessageC2Response, MessagePower, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -33,17 +31,17 @@ class DeviceAttributes(StrEnum): class MideaC2Device(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -69,8 +67,9 @@ def __init__( DeviceAttributes.seat_temp_level: 0, DeviceAttributes.water_temperature: None, DeviceAttributes.seat_temperature: None, - DeviceAttributes.filter_life: None - }) + DeviceAttributes.filter_life: None, + }, + ) self._max_dry_level = None self._max_water_temp_level = None self._max_seat_temp_level = None @@ -115,7 +114,7 @@ def set_attribute(self, attr, value): DeviceAttributes.foam_shield, DeviceAttributes.water_temp_level, DeviceAttributes.seat_temp_level, - DeviceAttributes.dry_level + DeviceAttributes.dry_level, ]: message = MessageSet(self._protocol_version) setattr(message, attr, value) @@ -137,10 +136,17 @@ def set_customize(self, customize): self._max_seat_temp_level = params.get("max_seat_temp_level") except Exception as e: _LOGGER.error(f"[{self.device_id}] Set customize error: {repr(e)}") - self.update_all({"dry_level": {"max_dry_level": self._max_dry_level}, - "water_temp_level": {"max_water_temp_level": self._max_water_temp_level}, - "seat_temp_level": {"max_seat_temp_level": self._max_seat_temp_level} - }) + self.update_all( + { + "dry_level": {"max_dry_level": self._max_dry_level}, + "water_temp_level": { + "max_water_temp_level": self._max_water_temp_level + }, + "seat_temp_level": { + "max_seat_temp_level": self._max_seat_temp_level + }, + } + ) class MideaAppliance(MideaC2Device): diff --git a/custom_components/midea_ac_lan/midea/devices/c2/message.py b/custom_components/midea_ac_lan/midea/devices/c2/message.py index 343fa1b3..4ab2c023 100644 --- a/custom_components/midea_ac_lan/midea/devices/c2/message.py +++ b/custom_components/midea_ac_lan/midea/devices/c2/message.py @@ -1,10 +1,6 @@ from enum import IntEnum -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) + +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class C2MessageEnum(IntEnum): @@ -12,8 +8,8 @@ class C2MessageEnum(IntEnum): child_lock = 0x10 foam_shield = 0x1F water_temp_level = 0x09 - seat_temp_level = 0x0a - dry_level = 0x0c + seat_temp_level = 0x0A + dry_level = 0x0C C2_MESSAGE_KEYS = { @@ -21,8 +17,22 @@ class C2MessageEnum(IntEnum): C2MessageEnum.sensor_light: {True: 0x01 << 1, False: 0x00}, C2MessageEnum.foam_shield: {True: 0x01 << 2, False: 0x00}, C2MessageEnum.dry_level: {0: 0x00, 1: 0x01 << 1, 2: 0x02 << 1, 3: 0x03 << 1}, - C2MessageEnum.seat_temp_level: {0: 0x00, 1: 0x01 << 3, 2: 0x02 << 3, 3: 0x03 << 3, 4: 0x04 << 3, 5: 0x05 << 3}, - C2MessageEnum.water_temp_level: {0: 0x00, 1: 0x01, 2: 0x02, 3: 0x03, 4: 0x04, 5: 0x05} + C2MessageEnum.seat_temp_level: { + 0: 0x00, + 1: 0x01 << 3, + 2: 0x02 << 3, + 3: 0x03 << 3, + 4: 0x04 << 3, + 5: 0x05 << 3, + }, + C2MessageEnum.water_temp_level: { + 0: 0x00, + 1: 0x01, + 2: 0x02, + 3: 0x03, + 4: 0x04, + 5: 0x05, + }, } @@ -32,7 +42,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xC2, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -45,7 +55,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -57,7 +68,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x00) + body_type=0x00, + ) self.power = False @property @@ -74,7 +86,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) @property def _body(self): @@ -86,7 +99,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x00) + body_type=0x00, + ) self.child_lock = None self.sensor_light = None @@ -127,9 +141,9 @@ def __init__(self, body): super().__init__(body) self.power = (body[2] & 0x01) > 0 self.seat_status = (body[3] & 0x01) > 0 - self.dry_level = ((body[6] & 0x7E) >> 1) - self.water_temp_level = (body[9] & 0x07) - self.seat_temp_level = ((body[9] & 0x38) >> 3) + self.dry_level = (body[6] & 0x7E) >> 1 + self.water_temp_level = body[9] & 0x07 + self.seat_temp_level = (body[9] & 0x38) >> 3 self.lid_status = (body[12] & 0x40) > 0 self.foam_shield = (body[13] & 0x80) > 0 self.sensor_light = (body[14] & 0x01) > 0 @@ -148,6 +162,10 @@ def __init__(self, body): class MessageC2Response(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.notify1, MessageType.query, MessageType.set]: + if self.message_type in [ + MessageType.notify1, + MessageType.query, + MessageType.set, + ]: self.set_body(C2MessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/c3/device.py b/custom_components/midea_ac_lan/midea/devices/c3/device.py index 90b41e5a..c513ec4c 100644 --- a/custom_components/midea_ac_lan/midea/devices/c3/device.py +++ b/custom_components/midea_ac_lan/midea/devices/c3/device.py @@ -1,15 +1,18 @@ import logging + from .message import ( + MessageC3Response, MessageQuery, - MessageSetSilent, + MessageSet, MessageSetECO, - MessageC3Response, - MessageSet + MessageSetSilent, ) + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -60,17 +63,17 @@ class DeviceAttributes(StrEnum): class MideaC3Device(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -123,8 +126,9 @@ def __init__( DeviceAttributes.status_ibh: None, DeviceAttributes.total_produced_energy: None, DeviceAttributes.outdoor_temperature: None, - DeviceAttributes.error_code: 0 - }) + DeviceAttributes.error_code: 0, + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -137,28 +141,48 @@ def process_message(self, msg): if hasattr(message, str(status)): self._attributes[status] = getattr(message, str(status)) new_status[str(status)] = getattr(message, str(status)) - if 'zone_temp_type' in new_status: + if "zone_temp_type" in new_status: for zone in [0, 1]: - if self._attributes[DeviceAttributes.zone_temp_type][zone]: # Water temp mode - self._attributes[DeviceAttributes.target_temperature][zone] = \ + if self._attributes[DeviceAttributes.zone_temp_type][ + zone + ]: # Water temp mode + self._attributes[DeviceAttributes.target_temperature][zone] = ( self._attributes[DeviceAttributes.zone_target_temp][zone] - if self._attributes[DeviceAttributes.mode_auto] == 2: # cooling mode - self._attributes[DeviceAttributes.temperature_max][zone] = \ - self._attributes[DeviceAttributes.zone_cooling_temp_max][zone] - self._attributes[DeviceAttributes.temperature_min][zone] = \ - self._attributes[DeviceAttributes.zone_cooling_temp_min][zone] + ) + if ( + self._attributes[DeviceAttributes.mode_auto] == 2 + ): # cooling mode + self._attributes[DeviceAttributes.temperature_max][zone] = ( + self._attributes[DeviceAttributes.zone_cooling_temp_max][ + zone + ] + ) + self._attributes[DeviceAttributes.temperature_min][zone] = ( + self._attributes[DeviceAttributes.zone_cooling_temp_min][ + zone + ] + ) elif self._attributes[DeviceAttributes.mode] == 3: # heating mode - self._attributes[DeviceAttributes.temperature_max][zone] = \ - self._attributes[DeviceAttributes.zone_heating_temp_max][zone] - self._attributes[DeviceAttributes.temperature_min][zone] = \ - self._attributes[DeviceAttributes.zone_heating_temp_min][zone] + self._attributes[DeviceAttributes.temperature_max][zone] = ( + self._attributes[DeviceAttributes.zone_heating_temp_max][ + zone + ] + ) + self._attributes[DeviceAttributes.temperature_min][zone] = ( + self._attributes[DeviceAttributes.zone_heating_temp_min][ + zone + ] + ) else: # Room temp mode - self._attributes[DeviceAttributes.target_temperature][zone] = \ + self._attributes[DeviceAttributes.target_temperature][zone] = ( self._attributes[DeviceAttributes.room_target_temp] - self._attributes[DeviceAttributes.temperature_max][zone] = \ + ) + self._attributes[DeviceAttributes.temperature_max][zone] = ( self._attributes[DeviceAttributes.room_temp_max] - self._attributes[DeviceAttributes.temperature_min][zone] = \ + ) + self._attributes[DeviceAttributes.temperature_min][zone] = ( self._attributes[DeviceAttributes.room_temp_min] + ) if self._attributes[DeviceAttributes.zone1_power]: if self._attributes[DeviceAttributes.zone_temp_type][zone]: self._attributes[DeviceAttributes.zone1_water_temp_mode] = True @@ -179,14 +203,18 @@ def process_message(self, msg): else: self._attributes[DeviceAttributes.zone2_water_temp_mode] = False self._attributes[DeviceAttributes.zone2_room_temp_mode] = False - new_status[DeviceAttributes.zone1_water_temp_mode.value] = \ - self._attributes[DeviceAttributes.zone1_water_temp_mode] - new_status[DeviceAttributes.zone2_water_temp_mode.value] = \ - self._attributes[DeviceAttributes.zone2_water_temp_mode] - new_status[DeviceAttributes.zone1_room_temp_mode.value] = \ - self._attributes[DeviceAttributes.zone1_room_temp_mode] - new_status[DeviceAttributes.zone2_room_temp_mode.value] = \ - self._attributes[DeviceAttributes.zone2_room_temp_mode] + new_status[DeviceAttributes.zone1_water_temp_mode.value] = self._attributes[ + DeviceAttributes.zone1_water_temp_mode + ] + new_status[DeviceAttributes.zone2_water_temp_mode.value] = self._attributes[ + DeviceAttributes.zone2_water_temp_mode + ] + new_status[DeviceAttributes.zone1_room_temp_mode.value] = self._attributes[ + DeviceAttributes.zone1_room_temp_mode + ] + new_status[DeviceAttributes.zone2_room_temp_mode.value] = self._attributes[ + DeviceAttributes.zone2_room_temp_mode + ] return new_status @@ -217,7 +245,7 @@ def set_attribute(self, attr, value): DeviceAttributes.disinfect, DeviceAttributes.fast_dhw, DeviceAttributes.dhw_target_temp, - DeviceAttributes.tbh + DeviceAttributes.tbh, ]: message = self.make_message_set() setattr(message, str(attr), value) diff --git a/custom_components/midea_ac_lan/midea/devices/c3/message.py b/custom_components/midea_ac_lan/midea/devices/c3/message.py index f9703572..cd2d4243 100644 --- a/custom_components/midea_ac_lan/midea/devices/c3/message.py +++ b/custom_components/midea_ac_lan/midea/devices/c3/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageC3Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xC3, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -37,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x01) + body_type=0x01, + ) self.zone1_power = False self.zone2_power = False self.dhw_power = False @@ -66,12 +63,17 @@ def _body(self): zone1_target_temp = int(self.zone_target_temp[0]) zone2_target_temp = int(self.zone_target_temp[1]) dhw_target_temp = int(self.dhw_target_temp) - return bytearray([ - zone1_power | zone2_power | dhw_power, - self.mode, zone1_target_temp, zone2_target_temp, - dhw_target_temp, room_target_temp, - zone1_curve | zone2_curve | disinfect | fast_dhw - ]) + return bytearray( + [ + zone1_power | zone2_power | dhw_power, + self.mode, + zone1_target_temp, + zone2_target_temp, + dhw_target_temp, + room_target_temp, + zone1_curve | zone2_curve | disinfect | fast_dhw, + ] + ) class MessageSetSilent(MessageC3Base): @@ -79,7 +81,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x05) + body_type=0x05, + ) self.silent_mode = False self.super_silent = False @@ -88,11 +91,9 @@ def _body(self): silent_mode = 0x01 if self.silent_mode else 0 super_silent = 0x02 if self.super_silent else 0 - return bytearray([ - silent_mode | super_silent, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - ]) + return bytearray( + [silent_mode | super_silent, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + ) class MessageSetECO(MessageC3Base): @@ -100,18 +101,15 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x07) + body_type=0x07, + ) self.eco_mode = False @property def _body(self): eco_mode = 0x01 if self.eco_mode else 0 - return bytearray([ - eco_mode, - 0x00, 0x00, 0x00, 0x00, - 0x00 - ]) + return bytearray([eco_mode, 0x00, 0x00, 0x00, 0x00, 0x00]) class C3MessageBody(MessageBody): @@ -127,34 +125,19 @@ def __init__(self, body, data_offset=0): self.fast_dhw = body[data_offset + 0] & 0x40 > 0 self.zone_temp_type = [ body[data_offset + 1] & 0x10 > 0, - body[data_offset + 1] & 0x20 > 0 + body[data_offset + 1] & 0x20 > 0, ] self.silent_mode = body[data_offset + 2] & 0x02 > 0 self.eco_mode = body[data_offset + 2] & 0x08 > 0 self.mode = body[data_offset + 3] self.mode_auto = body[data_offset + 4] - self.zone_target_temp = [ - body[data_offset + 5], - body[data_offset + 6] - ] + self.zone_target_temp = [body[data_offset + 5], body[data_offset + 6]] self.dhw_target_temp = body[data_offset + 7] self.room_target_temp = body[data_offset + 8] / 2 - self.zone_heating_temp_max = [ - body[data_offset + 9], - body[data_offset + 13] - ] - self.zone_heating_temp_min = [ - body[data_offset + 10], - body[data_offset + 14] - ] - self.zone_cooling_temp_max = [ - body[data_offset + 11], - body[data_offset + 15] - ] - self.zone_cooling_temp_min = [ - body[data_offset + 12], - body[data_offset + 16] - ] + self.zone_heating_temp_max = [body[data_offset + 9], body[data_offset + 13]] + self.zone_heating_temp_min = [body[data_offset + 10], body[data_offset + 14]] + self.zone_cooling_temp_max = [body[data_offset + 11], body[data_offset + 15]] + self.zone_cooling_temp_min = [body[data_offset + 12], body[data_offset + 16]] self.room_temp_max = body[data_offset + 17] / 2 self.room_temp_min = body[data_offset + 18] / 2 self.dhw_temp_max = body[data_offset + 19] @@ -173,24 +156,29 @@ def __init__(self, body, data_offset=0): self.status_heating = (status_byte & 0x01) > 0 self.total_energy_consumption = ( - (body[data_offset + 1] << 32) + - (body[data_offset + 2] << 16) + - (body[data_offset + 3] << 8) + - (body[data_offset + 4])) + (body[data_offset + 1] << 32) + + (body[data_offset + 2] << 16) + + (body[data_offset + 3] << 8) + + (body[data_offset + 4]) + ) self.total_produced_energy = ( - (body[data_offset + 5] << 32) + - (body[data_offset + 6] << 16) + - (body[data_offset + 7] << 8) + - (body[data_offset + 8])) + (body[data_offset + 5] << 32) + + (body[data_offset + 6] << 16) + + (body[data_offset + 7] << 8) + + (body[data_offset + 8]) + ) self.outdoor_temperature = int(body[data_offset + 9]) class MessageC3Response(MessageResponse): def __init__(self, message): super().__init__(message) - if (self.message_type in [MessageType.set, MessageType.notify1, MessageType.query] - and self.body_type == 0x01) or self.message_type == MessageType.notify2: + if ( + self.message_type + in [MessageType.set, MessageType.notify1, MessageType.query] + and self.body_type == 0x01 + ) or self.message_type == MessageType.notify2: self.set_body(C3MessageBody(super().body, data_offset=1)) elif self.message_type == MessageType.notify1 and self.body_type == 0x04: self.set_body(C3Notify1MessageBody(super().body, data_offset=1)) diff --git a/custom_components/midea_ac_lan/midea/devices/ca/device.py b/custom_components/midea_ac_lan/midea/devices/ca/device.py index 8d05ad69..f169b4ff 100644 --- a/custom_components/midea_ac_lan/midea/devices/ca/device.py +++ b/custom_components/midea_ac_lan/midea/devices/ca/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageCAResponse -) + +from .message import MessageCAResponse, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -35,17 +35,17 @@ class DeviceAttributes(StrEnum): class MideaCADevice(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -75,8 +75,9 @@ def __init__( DeviceAttributes.refrigerator_door: False, DeviceAttributes.freezer_door: False, DeviceAttributes.bar_door: False, - DeviceAttributes.flex_zone_door: False - }) + DeviceAttributes.flex_zone_door: False, + }, + ) self._modes = [""] def build_query(self): diff --git a/custom_components/midea_ac_lan/midea/devices/ca/message.py b/custom_components/midea_ac_lan/midea/devices/ca/message.py index b1284262..0611a875 100644 --- a/custom_components/midea_ac_lan/midea/devices/ca/message.py +++ b/custom_components/midea_ac_lan/midea/devices/ca/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageCABase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xCA, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x00) + body_type=0x00, + ) @property def _body(self): @@ -35,8 +31,8 @@ def _body(self): class CAGeneralMessageBody(MessageBody): def __init__(self, body): super().__init__(body) - self.refrigerator_setting_temp = (body[2] & 0x0f) - self.freezer_setting_temp = -12 - ((body[2] & 0xf0) >> 4) + self.refrigerator_setting_temp = body[2] & 0x0F + self.freezer_setting_temp = -12 - ((body[2] & 0xF0) >> 4) flex_zone_setting_temp = body[3] right_flex_zone_setting_temp = body[4] @@ -103,14 +99,23 @@ def __init__(self, body): class MessageCAResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if ((self.message_type in [MessageType.query, MessageType.set] and self.body_type == 0x00) or - (self.message_type == MessageType.notify1 and self.body_type == 0x02)) and len(super().body) > 20: + if ( + ( + self.message_type in [MessageType.query, MessageType.set] + and self.body_type == 0x00 + ) + or (self.message_type == MessageType.notify1 and self.body_type == 0x02) + ) and len(super().body) > 20: self.set_body(CAGeneralMessageBody(super().body)) - elif (self.message_type == MessageType.exception and self.body_type == 0x01) or \ - (self.message_type == 0x03 and self.body_type == 0x02): + elif ( + self.message_type == MessageType.exception and self.body_type == 0x01 + ) or (self.message_type == 0x03 and self.body_type == 0x02): self.set_body(CAExceptionMessageBody(super().body)) elif self.message_type == MessageType.notify1 and self.body_type == 0x00: self.set_body(CANotify00MessageBody(super().body)) - elif self.message_type in [MessageType.query, MessageType.notify1] and self.body_type == 0x01: + elif ( + self.message_type in [MessageType.query, MessageType.notify1] + and self.body_type == 0x01 + ): self.set_body(CANotify01MessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/cc/device.py b/custom_components/midea_ac_lan/midea/devices/cc/device.py index a82a5fbe..680a0ec9 100644 --- a/custom_components/midea_ac_lan/midea/devices/cc/device.py +++ b/custom_components/midea_ac_lan/midea/devices/cc/device.py @@ -1,13 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageSet, - MessageCCResponse -) + +from .message import MessageCCResponse, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -34,26 +33,29 @@ class DeviceAttributes(StrEnum): class MideaCCDevice(MiedaDevice): _fan_speeds_7level = { - 0x01: "Level 1", 0x02: "Level 2", 0x04: "Level 3", - 0x08: "Level 4", 0x10: "Level 5", 0x20: "Level 6", - 0x40: "Level 7", 0x80: "Auto", - } - _fan_speeds_3level = { - 0x01: "Low", 0x08: "Medium", 0x40: "High", 0x80: "Auto" + 0x01: "Level 1", + 0x02: "Level 2", + 0x04: "Level 3", + 0x08: "Level 4", + 0x10: "Level 5", + 0x20: "Level 6", + 0x40: "Level 7", + 0x80: "Auto", } + _fan_speeds_3level = {0x01: "Low", 0x08: "Medium", 0x40: "High", 0x80: "Auto"} def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -82,8 +84,9 @@ def __init__( DeviceAttributes.fan_speed_level: None, DeviceAttributes.indoor_temperature: None, DeviceAttributes.temperature_precision: 1, - DeviceAttributes.temp_fahrenheit: False - }) + DeviceAttributes.temp_fahrenheit: False, + }, + ) self._fan_speeds = None @property @@ -106,32 +109,46 @@ def process_message(self, msg): else: self._attributes[status] = getattr(message, str(status)) new_status[str(status)] = getattr(message, str(status)) - if fan_speed is not None and self._attributes[DeviceAttributes.fan_speed_level] is not None: + if ( + fan_speed is not None + and self._attributes[DeviceAttributes.fan_speed_level] is not None + ): if self._fan_speeds is None: if self._attributes[DeviceAttributes.fan_speed_level]: self._fan_speeds = MideaCCDevice._fan_speeds_3level else: self._fan_speeds = MideaCCDevice._fan_speeds_7level if fan_speed in self._fan_speeds.keys(): - self._attributes[DeviceAttributes.fan_speed] = self._fan_speeds.get(fan_speed) + self._attributes[DeviceAttributes.fan_speed] = self._fan_speeds.get( + fan_speed + ) else: self._attributes[DeviceAttributes.fan_speed] = None - new_status[DeviceAttributes.fan_speed.value] = self._attributes[DeviceAttributes.fan_speed] - aux_heating = \ - self._attributes[DeviceAttributes.aux_heat_status] == 1 or \ - self._attributes[DeviceAttributes.auto_aux_heat_running] + new_status[DeviceAttributes.fan_speed.value] = self._attributes[ + DeviceAttributes.fan_speed + ] + aux_heating = ( + self._attributes[DeviceAttributes.aux_heat_status] == 1 + or self._attributes[DeviceAttributes.auto_aux_heat_running] + ) if self._attributes[DeviceAttributes.aux_heating] != aux_heating: self._attributes[DeviceAttributes.aux_heating] = aux_heating - new_status[DeviceAttributes.aux_heating.value] = self._attributes[DeviceAttributes.aux_heating] + new_status[DeviceAttributes.aux_heating.value] = self._attributes[ + DeviceAttributes.aux_heating + ] return new_status def make_message_set(self): message = MessageSet(self._protocol_version) message.power = self._attributes[DeviceAttributes.power] message.mode = self._attributes[DeviceAttributes.mode] - message.target_temperature = self._attributes[DeviceAttributes.target_temperature] + message.target_temperature = self._attributes[ + DeviceAttributes.target_temperature + ] message.fan_speed = list(self._fan_speeds.keys())[ - list(self._fan_speeds.values()).index(self._attributes[DeviceAttributes.fan_speed]) + list(self._fan_speeds.values()).index( + self._attributes[DeviceAttributes.fan_speed] + ) ] message.eco_mode = self._attributes[DeviceAttributes.eco_mode] message.sleep_mode = self._attributes[DeviceAttributes.sleep_mode] @@ -150,11 +167,13 @@ def set_target_temperature(self, target_temperature, mode): def set_attribute(self, attr, value): # if nat a sensor - if attr not in [DeviceAttributes.indoor_temperature, - DeviceAttributes.temperature_precision, - DeviceAttributes.fan_speed_level, - DeviceAttributes.aux_heat_status, - DeviceAttributes.auto_aux_heat_running]: + if attr not in [ + DeviceAttributes.indoor_temperature, + DeviceAttributes.temperature_precision, + DeviceAttributes.fan_speed_level, + DeviceAttributes.aux_heat_status, + DeviceAttributes.auto_aux_heat_running, + ]: message = self.make_message_set() if attr == DeviceAttributes.fan_speed: if value in self._fan_speeds.values(): diff --git a/custom_components/midea_ac_lan/midea/devices/cc/message.py b/custom_components/midea_ac_lan/midea/devices/cc/message.py index 7aa35319..f94a7c34 100644 --- a/custom_components/midea_ac_lan/midea/devices/cc/message.py +++ b/custom_components/midea_ac_lan/midea/devices/cc/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageCCBase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xCC, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -37,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0xC3) + body_type=0xC3, + ) self.power = False self.mode = 4 self.fan_speed = 0x80 @@ -73,23 +70,38 @@ def _body(self): sleep_mode = 0x10 if self.sleep_mode else 0 night_light = 0x08 if self.night_light else 0 # Byte11 Dot of target_temperature - temperature_dot = int((self.target_temperature - temperature_integer) * 10) & 0xFF - return bytearray([ - power | mode, - fan_speed, - temperature_integer, - # timer - 0x00, 0x00, - eco_mode | ventilation | swing | aux_heating, - # non-stepless fan speed - 0xFF, - sleep_mode | night_light, - 0x00, 0x00, - temperature_dot, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - ]) + temperature_dot = ( + int((self.target_temperature - temperature_integer) * 10) & 0xFF + ) + return bytearray( + [ + power | mode, + fan_speed, + temperature_integer, + # timer + 0x00, + 0x00, + eco_mode | ventilation | swing | aux_heating, + # non-stepless fan speed + 0xFF, + sleep_mode | night_light, + 0x00, + 0x00, + temperature_dot, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class CCGeneralMessageBody(MessageBody): @@ -119,8 +131,13 @@ def __init__(self, body): class MessageCCResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if (self.message_type == MessageType.query and self.body_type == 0x01) or \ - (self.message_type in [MessageType.notify1, MessageType.notify2] and self.body_type == 0x01) or \ - (self.message_type == MessageType.set and self.body_type == 0xC3): + if ( + (self.message_type == MessageType.query and self.body_type == 0x01) + or ( + self.message_type in [MessageType.notify1, MessageType.notify2] + and self.body_type == 0x01 + ) + or (self.message_type == MessageType.set and self.body_type == 0xC3) + ): self.set_body(CCGeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/cd/device.py b/custom_components/midea_ac_lan/midea/devices/cd/device.py index 8de65e50..a124f735 100644 --- a/custom_components/midea_ac_lan/midea/devices/cd/device.py +++ b/custom_components/midea_ac_lan/midea/devices/cd/device.py @@ -1,14 +1,13 @@ -import logging import json -from .message import ( - MessageQuery, - MessageSet, - MessageCDResponse -) +import logging + +from .message import MessageCDResponse, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -31,17 +30,17 @@ class MideaCDDevice(MiedaDevice): _modes = ["Energy-save", "Standard", "Dual", "Smart"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -64,8 +63,9 @@ def __init__( DeviceAttributes.outdoor_temperature: None, DeviceAttributes.condenser_temperature: None, DeviceAttributes.compressor_temperature: None, - DeviceAttributes.compressor_status: None - }) + DeviceAttributes.compressor_status: None, + }, + ) self._fields = {} self._temperature_step = None self._default_temperature_step = 1 @@ -99,12 +99,20 @@ def process_message(self, msg): return new_status def set_attribute(self, attr, value): - if attr in [DeviceAttributes.mode, DeviceAttributes.power, DeviceAttributes.target_temperature]: + if attr in [ + DeviceAttributes.mode, + DeviceAttributes.power, + DeviceAttributes.target_temperature, + ]: message = MessageSet(self._protocol_version) message.fields = self._fields - message.mode = MideaCDDevice._modes.index(self._attributes[DeviceAttributes.mode]) + message.mode = MideaCDDevice._modes.index( + self._attributes[DeviceAttributes.mode] + ) message.power = self._attributes[DeviceAttributes.power] - message.target_temperature = self._attributes[DeviceAttributes.target_temperature] + message.target_temperature = self._attributes[ + DeviceAttributes.target_temperature + ] if attr == DeviceAttributes.mode: if value in MideaCDDevice._modes: setattr(message, str(attr), MideaCDDevice._modes.index(value)) diff --git a/custom_components/midea_ac_lan/midea/devices/cd/message.py b/custom_components/midea_ac_lan/midea/devices/cd/message.py index 40c4ba0c..454cfe00 100644 --- a/custom_components/midea_ac_lan/midea/devices/cd/message.py +++ b/custom_components/midea_ac_lan/midea/devices/cd/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageCDBase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xCD, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -37,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x01) + body_type=0x01, + ) self.power = False self.target_temperature = 0 self.aux_heating = False @@ -53,13 +50,18 @@ def _body(self): power = 0x01 if self.power else 0x00 mode = self.mode + 1 target_temperature = round(self.target_temperature * 2 + 30) - return bytearray([ - 0x01, power, mode, target_temperature, - self.read_field("trValue"), - self.read_field("openPTC"), - self.read_field("ptcTemp"), - 0 # self.read_field("byte8") - ]) + return bytearray( + [ + 0x01, + power, + mode, + target_temperature, + self.read_field("trValue"), + self.read_field("openPTC"), + self.read_field("ptcTemp"), + 0, # self.read_field("byte8") + ] + ) class CDGeneralMessageBody(MessageBody): diff --git a/custom_components/midea_ac_lan/midea/devices/ce/device.py b/custom_components/midea_ac_lan/midea/devices/ce/device.py index f040e27d..c8ac6ecf 100644 --- a/custom_components/midea_ac_lan/midea/devices/ce/device.py +++ b/custom_components/midea_ac_lan/midea/devices/ce/device.py @@ -1,14 +1,13 @@ -import logging import json -from .message import ( - MessageQuery, - MessageCEResponse, - MessageSet -) +import logging + +from .message import MessageCEResponse, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -36,22 +35,20 @@ class DeviceAttributes(StrEnum): class MideaCEDevice(MiedaDevice): - _modes = [ - "Normal", "Sleep mode", "ECO mode" - ] + _modes = ["Normal", "Sleep mode", "ECO mode"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -82,8 +79,9 @@ def __init__( DeviceAttributes.powerful_purify: False, DeviceAttributes.filter_cleaning_reminder: False, DeviceAttributes.filter_change_reminder: False, - DeviceAttributes.error_code: 0 - }) + DeviceAttributes.error_code: 0, + }, + ) self._default_speed_count = 7 self._speed_count = self._default_speed_count self.set_customize(customize) @@ -114,7 +112,9 @@ def process_message(self, msg): self._attributes[DeviceAttributes.mode] = "ECO mode" else: self._attributes[DeviceAttributes.mode] = "None" - new_status[DeviceAttributes.mode.value] = self._attributes[DeviceAttributes.mode] + new_status[DeviceAttributes.mode.value] = self._attributes[ + DeviceAttributes.mode + ] return new_status def make_message_set(self): diff --git a/custom_components/midea_ac_lan/midea/devices/ce/message.py b/custom_components/midea_ac_lan/midea/devices/ce/message.py index dfcb6c9e..0f6afe3f 100644 --- a/custom_components/midea_ac_lan/midea/devices/ce/message.py +++ b/custom_components/midea_ac_lan/midea/devices/ce/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageFABase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xCE, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -37,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x01) + body_type=0x01, + ) self.power = False self.fan_speed = 0 @@ -59,14 +56,16 @@ def _body(self): powerful_purify = 0x10 if self.powerful_purify else 0x00 scheduled = 0x01 if self.scheduled else 0x00 child_lock = 0x7F if self.child_lock else 0x00 - return bytearray([ - power | 0x01, - self.fan_speed, - link_to_ac | sleep_mode | eco_mode | aux_heating | powerful_purify, - scheduled, - 0x00, - child_lock - ]) + return bytearray( + [ + power | 0x01, + self.fan_speed, + link_to_ac | sleep_mode | eco_mode | aux_heating | powerful_purify, + scheduled, + 0x00, + child_lock, + ] + ) class CEGeneralMessageBody(MessageBody): @@ -126,8 +125,10 @@ def __init__(self, body): class MessageCEResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if (self.message_type in [MessageType.query, MessageType.set] and self.body_type == 0x01) or \ - (self.message_type == MessageType.notify1 and self.body_type == 0x02): + if ( + self.message_type in [MessageType.query, MessageType.set] + and self.body_type == 0x01 + ) or (self.message_type == MessageType.notify1 and self.body_type == 0x02): self.set_body(CEGeneralMessageBody(super().body)) elif self.message_type == MessageType.notify1 and self.body_type == 0x01: self.set_body(CENotifyMessageBody(super().body)) diff --git a/custom_components/midea_ac_lan/midea/devices/cf/device.py b/custom_components/midea_ac_lan/midea/devices/cf/device.py index 273e3972..40b5a9d5 100644 --- a/custom_components/midea_ac_lan/midea/devices/cf/device.py +++ b/custom_components/midea_ac_lan/midea/devices/cf/device.py @@ -1,13 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageCFResponse, - MessageSet -) + +from .message import MessageCFResponse, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -25,17 +24,17 @@ class DeviceAttributes(StrEnum): class MideaCFDevice(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -55,8 +54,9 @@ def __init__( DeviceAttributes.aux_heating: False, DeviceAttributes.current_temperature: 0, DeviceAttributes.max_temperature: 55, - DeviceAttributes.min_temperature: 5 - }) + DeviceAttributes.min_temperature: 5, + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] diff --git a/custom_components/midea_ac_lan/midea/devices/cf/message.py b/custom_components/midea_ac_lan/midea/devices/cf/message.py index 49bf150c..0bf0fb3f 100644 --- a/custom_components/midea_ac_lan/midea/devices/cf/message.py +++ b/custom_components/midea_ac_lan/midea/devices/cf/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageCFBase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xCF, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -37,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x01) + body_type=0x01, + ) self.power = False self.mode = 0 # 1 自动 2 制冷 3 制热 self.target_temperature = None @@ -47,11 +44,15 @@ def __init__(self, protocol_version): def _body(self): power = 0x01 if self.power else 0x00 mode = self.mode - target_temperature = 0xFF if self.target_temperature is None else (int(self.target_temperature) & 0xFF) - aux_heating = 0xFF if self.aux_heating is None else (0x01 if self.aux_heating else 0x00) - return bytearray([ - power, mode, target_temperature, aux_heating - ]) + target_temperature = ( + 0xFF + if self.target_temperature is None + else (int(self.target_temperature) & 0xFF) + ) + aux_heating = ( + 0xFF if self.aux_heating is None else (0x01 if self.aux_heating else 0x00) + ) + return bytearray([power, mode, target_temperature, aux_heating]) class CFMessageBody(MessageBody): @@ -77,7 +78,10 @@ def __init__(self, body, data_offset=0): class MessageCFResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.query, MessageType.set] and self.body_type == 0x01: + if ( + self.message_type in [MessageType.query, MessageType.set] + and self.body_type == 0x01 + ): self.set_body(CFMessageBody(super().body, data_offset=1)) elif self.message_type in [MessageType.notify1, MessageType.notify2]: self.set_body(CFMessageBody(super().body, data_offset=0)) diff --git a/custom_components/midea_ac_lan/midea/devices/da/device.py b/custom_components/midea_ac_lan/midea/devices/da/device.py index f213424f..f5506e40 100644 --- a/custom_components/midea_ac_lan/midea/devices/da/device.py +++ b/custom_components/midea_ac_lan/midea/devices/da/device.py @@ -1,14 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessagePower, - MessageStart, - MessageDAResponse -) + +from .message import MessageDAResponse, MessagePower, MessageQuery, MessageStart + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -36,17 +34,17 @@ class DeviceAttributes(StrEnum): class MideaDADevice(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -76,8 +74,9 @@ def __init__( DeviceAttributes.wash_level: None, DeviceAttributes.wash_strength: None, DeviceAttributes.softener: None, - DeviceAttributes.detergent: None - }) + DeviceAttributes.detergent: None, + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -86,17 +85,47 @@ def process_message(self, msg): message = MessageDAResponse(msg) _LOGGER.debug(f"[{self.device_id}] Received: {message}") new_status = {} - progress = ["Idle", "Spin", "Rinse", "Wash", - "Weight", "Unknown", "Dry", "Soak"] - program = ["Standard", "Fast", "Blanket", "Wool", - "embathe", "Memory", "Child", "Down Jacket", - "Stir", "Mute", "Bucket Self Clean", "Air Dry"] + progress = ["Idle", "Spin", "Rinse", "Wash", "Weight", "Unknown", "Dry", "Soak"] + program = [ + "Standard", + "Fast", + "Blanket", + "Wool", + "embathe", + "Memory", + "Child", + "Down Jacket", + "Stir", + "Mute", + "Bucket Self Clean", + "Air Dry", + ] speed = ["-", "Low", "Medium", "High"] strength = ["-", "Week", "Medium", "Strong"] - detergent = ["No", "Less", "Medium", "More", "4", - "5", "6", "7", "8", "Insufficient"] - softener = ["No", "Intelligent", "Programed", "3", "4", - "5", "6", "7", "8", "Insufficient"] + detergent = [ + "No", + "Less", + "Medium", + "More", + "4", + "5", + "6", + "7", + "8", + "Insufficient", + ] + softener = [ + "No", + "Intelligent", + "Programed", + "3", + "4", + "5", + "6", + "7", + "8", + "Insufficient", + ] for status in self._attributes.keys(): if hasattr(message, str(status)): if status == DeviceAttributes.progress: diff --git a/custom_components/midea_ac_lan/midea/devices/da/message.py b/custom_components/midea_ac_lan/midea/devices/da/message.py index 9baf761a..5578d46b 100644 --- a/custom_components/midea_ac_lan/midea/devices/da/message.py +++ b/custom_components/midea_ac_lan/midea/devices/da/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageDABase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xDA, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x03) + body_type=0x03, + ) @property def _body(self): @@ -37,15 +33,14 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) self.power = False @property def _body(self): power = 0x01 if self.power else 0x00 - return bytearray([ - power, 0xFF - ]) + return bytearray([power, 0xFF]) class MessageStart(MessageDABase): @@ -53,21 +48,18 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) self.start = False self.washing_data = bytearray([]) @property def _body(self): if self.start: - return bytearray([ - 0xFF, 0x01 - ]) + self.washing_data + return bytearray([0xFF, 0x01]) + self.washing_data else: # Stop - return bytearray([ - 0xFF, 0x00 - ]) + return bytearray([0xFF, 0x00]) class DAGeneralMessageBody(MessageBody): @@ -79,14 +71,14 @@ def __init__(self, body): self.program = body[4] self.wash_time = body[9] self.soak_time = body[12] - self.dehydration_time = (body[10] & 0xf0) >> 4 - self.dehydration_speed = (body[6] & 0xf0) >> 4 - self.rinse_count = body[10] & 0xf - self.rinse_level = (body[5] & 0xf0) >> 4 - self.wash_level = body[5] & 0xf - self.wash_strength = body[6] & 0xf - self.softener = (body[8] & 0xf0) >> 4 - self.detergent = body[8] & 0x0f + self.dehydration_time = (body[10] & 0xF0) >> 4 + self.dehydration_speed = (body[6] & 0xF0) >> 4 + self.rinse_count = body[10] & 0xF + self.rinse_level = (body[5] & 0xF0) >> 4 + self.wash_level = body[5] & 0xF + self.wash_strength = body[6] & 0xF + self.softener = (body[8] & 0xF0) >> 4 + self.detergent = body[8] & 0x0F self.washing_data = body[3:15] self.progress = 0 for i in range(1, 7): @@ -102,7 +94,8 @@ def __init__(self, body): class MessageDAResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.query, MessageType.set] or \ - (self.message_type == MessageType.notify1 and self.body_type == 0x04): + if self.message_type in [MessageType.query, MessageType.set] or ( + self.message_type == MessageType.notify1 and self.body_type == 0x04 + ): self.set_body(DAGeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/db/device.py b/custom_components/midea_ac_lan/midea/devices/db/device.py index 0ef737c0..fe1c02e4 100644 --- a/custom_components/midea_ac_lan/midea/devices/db/device.py +++ b/custom_components/midea_ac_lan/midea/devices/db/device.py @@ -1,14 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessagePower, - MessageStart, - MessageDBResponse -) + +from .message import MessageDBResponse, MessagePower, MessageQuery, MessageStart + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -24,17 +22,17 @@ class DeviceAttributes(StrEnum): class MideaDBDevice(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -52,8 +50,9 @@ def __init__( DeviceAttributes.start: False, DeviceAttributes.washing_data: bytearray([]), DeviceAttributes.progress: "Unknown", - DeviceAttributes.time_remaining: None - }) + DeviceAttributes.time_remaining: None, + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -62,8 +61,17 @@ def process_message(self, msg): message = MessageDBResponse(msg) _LOGGER.debug(f"[{self.device_id}] Received: {message}") new_status = {} - progress = ["Idle", "Spin", "Rinse", "Wash", "Pre-wash", - "Dry", "Weight", "Hi-speed Spin", "Unknown"] + progress = [ + "Idle", + "Spin", + "Rinse", + "Wash", + "Pre-wash", + "Dry", + "Weight", + "Hi-speed Spin", + "Unknown", + ] for status in self._attributes.keys(): if hasattr(message, str(status)): if status == DeviceAttributes.progress: diff --git a/custom_components/midea_ac_lan/midea/devices/db/message.py b/custom_components/midea_ac_lan/midea/devices/db/message.py index 580e5542..7a70ca55 100644 --- a/custom_components/midea_ac_lan/midea/devices/db/message.py +++ b/custom_components/midea_ac_lan/midea/devices/db/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageDBBase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xDB, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x03) + body_type=0x03, + ) @property def _body(self): @@ -37,20 +33,38 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) self.power = False @property def _body(self): power = 0x01 if self.power else 0x00 - return bytearray([ - power, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF - ]) + return bytearray( + [ + power, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + ] + ) class MessageStart(MessageDBBase): @@ -58,21 +72,18 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) self.start = False self.washing_data = bytearray([]) @property def _body(self): - if self.start: # Pause - return bytearray([ - 0xFF, 0x01 - ]) + self.washing_data + if self.start: # Pause + return bytearray([0xFF, 0x01]) + self.washing_data else: # Pause - return bytearray([ - 0xFF, 0x00 - ]) + return bytearray([0xFF, 0x00]) class DBGeneralMessageBody(MessageBody): @@ -95,7 +106,8 @@ def __init__(self, body): class MessageDBResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.query, MessageType.set] or \ - (self.message_type == MessageType.notify1 and self.body_type == 0x04): + if self.message_type in [MessageType.query, MessageType.set] or ( + self.message_type == MessageType.notify1 and self.body_type == 0x04 + ): self.set_body(DBGeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/dc/device.py b/custom_components/midea_ac_lan/midea/devices/dc/device.py index b78c121e..7e6d659f 100644 --- a/custom_components/midea_ac_lan/midea/devices/dc/device.py +++ b/custom_components/midea_ac_lan/midea/devices/dc/device.py @@ -1,14 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessagePower, - MessageStart, - MessageDCResponse -) + +from .message import MessageDCResponse, MessagePower, MessageQuery, MessageStart + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -24,17 +22,17 @@ class DeviceAttributes(StrEnum): class MideaDADevice(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -52,8 +50,9 @@ def __init__( DeviceAttributes.start: False, DeviceAttributes.washing_data: bytearray([]), DeviceAttributes.progress: "Unknown", - DeviceAttributes.time_remaining: None - }) + DeviceAttributes.time_remaining: None, + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -62,8 +61,16 @@ def process_message(self, msg): message = MessageDCResponse(msg) _LOGGER.debug(f"[{self.device_id}] Received: {message}") new_status = {} - progress = ["Prog0", "Prog1", "Prog2", "Prog3", - "Prog4", "Prog5", "Prog6", "Prog7"] + progress = [ + "Prog0", + "Prog1", + "Prog2", + "Prog3", + "Prog4", + "Prog5", + "Prog6", + "Prog7", + ] for status in self._attributes.keys(): if hasattr(message, str(status)): if status == DeviceAttributes.progress: diff --git a/custom_components/midea_ac_lan/midea/devices/dc/message.py b/custom_components/midea_ac_lan/midea/devices/dc/message.py index b9e66a7a..3be5d3dd 100644 --- a/custom_components/midea_ac_lan/midea/devices/dc/message.py +++ b/custom_components/midea_ac_lan/midea/devices/dc/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageDCBase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xDC, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x03) + body_type=0x03, + ) @property def _body(self): @@ -37,15 +33,14 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) self.power = False @property def _body(self): power = 0x01 if self.power else 0x00 - return bytearray([ - power, 0xFF - ]) + return bytearray([power, 0xFF]) class MessageStart(MessageDCBase): @@ -53,21 +48,18 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) self.start = False self.washing_data = bytearray([]) @property def _body(self): if self.start: - return bytearray([ - 0xFF, 0x01 - ]) + self.washing_data + return bytearray([0xFF, 0x01]) + self.washing_data else: # Stop - return bytearray([ - 0xFF, 0x00 - ]) + return bytearray([0xFF, 0x00]) class DCGeneralMessageBody(MessageBody): @@ -90,7 +82,8 @@ def __init__(self, body): class MessageDCResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.query, MessageType.set] or \ - (self.message_type == MessageType.notify1 and self.body_type == 0x04): + if self.message_type in [MessageType.query, MessageType.set] or ( + self.message_type == MessageType.notify1 and self.body_type == 0x04 + ): self.set_body(DCGeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/e1/device.py b/custom_components/midea_ac_lan/midea/devices/e1/device.py index afc3d4c4..43c72ffa 100644 --- a/custom_components/midea_ac_lan/midea/devices/e1/device.py +++ b/custom_components/midea_ac_lan/midea/devices/e1/device.py @@ -1,15 +1,18 @@ import logging + from .message import ( - MessageQuery, + MessageE1Response, + MessageLock, MessagePower, + MessageQuery, MessageStorage, - MessageLock, - MessageE1Response ) + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -44,17 +47,17 @@ class DeviceAttributes(StrEnum): class MideaE1Device(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -91,31 +94,32 @@ def __init__( DeviceAttributes.error_code: None, DeviceAttributes.softwater: 0, DeviceAttributes.wrong_operation: None, - DeviceAttributes.bright: 0 - }) + DeviceAttributes.bright: 0, + }, + ) self._modes = { - 0x0: "Neutral Gear", # BYTE_MODE_NEUTRAL_GEAR - 0x1: "Auto", # BYTE_MODE_AUTO_WASH - 0x2: "Heavy", # BYTE_MODE_STRONG_WASH - 0x3: "Normal", # BYTE_MODE_STANDARD_WASH - 0x4: "Energy Saving", # BYTE_MODE_ECO_WASH - 0x5: "Delicate", # BYTE_MODE_GLASS_WASH - 0x6: "Hour", # BYTE_MODE_HOUR_WASH - 0x7: "Quick", # BYTE_MODE_FAST_WASH - 0x8: "Rinse", # BYTE_MODE_SOAK_WASH - 0x9: "90min", # BYTE_MODE_90MIN_WASH - 0xA: "Self Clean", # BYTE_MODE_SELF_CLEAN - 0xB: "Fruit Wash", # BYTE_MODE_FRUIT_WASH - 0xC: "Self Define", # BYTE_MODE_SELF_DEFINE - 0xD: "Germ", # BYTE_MODE_GERM ??? - 0xE: "Bowl Wash", # BYTE_MODE_BOWL_WASH - 0xF: "Kill Germ", # BYTE_MODE_KILL_GERM - 0x10: "Sea Food Wash", # BYTE_MODE_SEA_FOOD_WASH - 0x12: "Hot Pot Wash", # BYTE_MODE_HOT_POT_WASH - 0x13: "Quiet", # BYTE_MODE_QUIET_NIGHT_WASH - 0x14: "Less Wash", # BYTE_MODE_LESS_WASH - 0x16: "Oil Net Wash", # BYTE_MODE_OIL_NET_WASH - 0x19: "Cloud Wash" # BYTE_MODE_CLOUD_WASH + 0x0: "Neutral Gear", # BYTE_MODE_NEUTRAL_GEAR + 0x1: "Auto", # BYTE_MODE_AUTO_WASH + 0x2: "Heavy", # BYTE_MODE_STRONG_WASH + 0x3: "Normal", # BYTE_MODE_STANDARD_WASH + 0x4: "Energy Saving", # BYTE_MODE_ECO_WASH + 0x5: "Delicate", # BYTE_MODE_GLASS_WASH + 0x6: "Hour", # BYTE_MODE_HOUR_WASH + 0x7: "Quick", # BYTE_MODE_FAST_WASH + 0x8: "Rinse", # BYTE_MODE_SOAK_WASH + 0x9: "90min", # BYTE_MODE_90MIN_WASH + 0xA: "Self Clean", # BYTE_MODE_SELF_CLEAN + 0xB: "Fruit Wash", # BYTE_MODE_FRUIT_WASH + 0xC: "Self Define", # BYTE_MODE_SELF_DEFINE + 0xD: "Germ", # BYTE_MODE_GERM ??? + 0xE: "Bowl Wash", # BYTE_MODE_BOWL_WASH + 0xF: "Kill Germ", # BYTE_MODE_KILL_GERM + 0x10: "Sea Food Wash", # BYTE_MODE_SEA_FOOD_WASH + 0x12: "Hot Pot Wash", # BYTE_MODE_HOT_POT_WASH + 0x13: "Quiet", # BYTE_MODE_QUIET_NIGHT_WASH + 0x14: "Less Wash", # BYTE_MODE_LESS_WASH + 0x16: "Oil Net Wash", # BYTE_MODE_OIL_NET_WASH + 0x19: "Cloud Wash", # BYTE_MODE_CLOUD_WASH } self._status = ["Off", "Idle", "Delay", "Running", "Error"] self._progress = ["Idle", "Pre-wash", "Wash", "Rinse", "Dry", "Complete"] diff --git a/custom_components/midea_ac_lan/midea/devices/e1/message.py b/custom_components/midea_ac_lan/midea/devices/e1/message.py index 1147e648..530572e8 100644 --- a/custom_components/midea_ac_lan/midea/devices/e1/message.py +++ b/custom_components/midea_ac_lan/midea/devices/e1/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageE1Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xE1, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,16 +20,14 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x08) + body_type=0x08, + ) self.power = False @property def _body(self): power = 0x01 if self.power else 0x00 - return bytearray([ - power, - 0x00, 0x00, 0x00 - ]) + return bytearray([power, 0x00, 0x00, 0x00]) class MessageLock(MessageE1Base): @@ -42,7 +35,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x83) + body_type=0x83, + ) self.lock = False @property @@ -56,14 +50,18 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x81) + body_type=0x81, + ) self.storage = False @property def _body(self): storage = 0x01 if self.storage else 0x00 - return bytearray([0x00, 0x00, 0x00, storage]) + \ - bytearray([0xff] * 6) + bytearray([0x00] * 27) + return ( + bytearray([0x00, 0x00, 0x00, storage]) + + bytearray([0xFF] * 6) + + bytearray([0x00] * 27) + ) class MessageQuery(MessageE1Base): @@ -71,7 +69,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x00) + body_type=0x00, + ) @property def _body(self): @@ -85,9 +84,9 @@ def __init__(self, body): self.status = body[1] self.mode = body[2] self.additional = body[3] - self.door = (body[5] & 0x01) == 0 # 0 - open, 1 - close - self.rinse_aid = (body[5] & 0x02) > 0 # 0 - enough, 1 - shortage - self.salt = (body[5] & 0x04) > 0 # 0 - enough, 1 - shortage + self.door = (body[5] & 0x01) == 0 # 0 - open, 1 - close + self.rinse_aid = (body[5] & 0x02) > 0 # 0 - enough, 1 - shortage + self.salt = (body[5] & 0x04) > 0 # 0 - enough, 1 - shortage start_pause = (body[5] & 0x08) > 0 if start_pause: self.start = True @@ -115,7 +114,9 @@ def __init__(self, body): class MessageE1Response(MessageResponse): def __init__(self, message): super().__init__(message) - if (self.message_type == MessageType.set and 0 <= self.body_type <= 7) or \ - (self.message_type in [MessageType.query, MessageType.notify1] and self.body_type == 0): + if (self.message_type == MessageType.set and 0 <= self.body_type <= 7) or ( + self.message_type in [MessageType.query, MessageType.notify1] + and self.body_type == 0 + ): self.set_body(E1GeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/e2/device.py b/custom_components/midea_ac_lan/midea/devices/e2/device.py index fc8ca315..b261a44f 100644 --- a/custom_components/midea_ac_lan/midea/devices/e2/device.py +++ b/custom_components/midea_ac_lan/midea/devices/e2/device.py @@ -1,16 +1,19 @@ -import logging import json +import logging + from .message import ( - MessageQuery, - MessageSet, MessageE2Response, + MessageNewProtocolSet, MessagePower, - MessageNewProtocolSet + MessageQuery, + MessageSet, ) + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -32,17 +35,17 @@ class DeviceAttributes(StrEnum): class MideaE2Device(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -66,8 +69,9 @@ def __init__( DeviceAttributes.variable_heating: False, DeviceAttributes.heating_time_remaining: 0, DeviceAttributes.water_consumption: None, - DeviceAttributes.heating_power: None - }) + DeviceAttributes.heating_power: None, + }, + ) self._default_old_protocol = "auto" self._old_protocol = self._default_old_protocol self.set_customize(customize) @@ -91,15 +95,21 @@ def process_message(self, msg): def make_message_set(self): message = MessageSet(self._protocol_version) message.protection = self._attributes[DeviceAttributes.protection] - message.whole_tank_heating = self._attributes[DeviceAttributes.whole_tank_heating] - message.target_temperature = self._attributes[DeviceAttributes.target_temperature] + message.whole_tank_heating = self._attributes[ + DeviceAttributes.whole_tank_heating + ] + message.target_temperature = self._attributes[ + DeviceAttributes.target_temperature + ] message.variable_heating = self._attributes[DeviceAttributes.variable_heating] return message def set_attribute(self, attr, value): - if attr not in [DeviceAttributes.heating, - DeviceAttributes.keep_warm, - DeviceAttributes.current_temperature]: + if attr not in [ + DeviceAttributes.heating, + DeviceAttributes.keep_warm, + DeviceAttributes.current_temperature, + ]: if self._old_protocol is not None and self._old_protocol != "auto": old_protocol = self._old_protocol else: diff --git a/custom_components/midea_ac_lan/midea/devices/e2/message.py b/custom_components/midea_ac_lan/midea/devices/e2/message.py index 18d80ecc..a70c6cc9 100644 --- a/custom_components/midea_ac_lan/midea/devices/e2/message.py +++ b/custom_components/midea_ac_lan/midea/devices/e2/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageE2Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xE2, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -37,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) self.power = False @property @@ -54,7 +51,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x14) + body_type=0x14, + ) self.target_temperature = None self.variable_heating = None self.whole_tank_heating = None @@ -80,7 +78,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x04) + body_type=0x04, + ) self.target_temperature = 0 self.variable_heating = False self.whole_tank_heating = False @@ -95,18 +94,28 @@ def _body(self): target_temperature = self.target_temperature & 0xFF # Byte 9 variable_heating variable_heating = 0x10 if self.variable_heating else 0x00 - return bytearray([ - 0x01, - 0x00, - 0x80, - whole_tank_heating | protection, - target_temperature, - 0x00, 0x00, 0x00, - variable_heating, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00 - ]) + return bytearray( + [ + 0x01, + 0x00, + 0x80, + whole_tank_heating | protection, + target_temperature, + 0x00, + 0x00, + 0x00, + variable_heating, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class E2GeneralMessageBody(MessageBody): @@ -130,7 +139,12 @@ def __init__(self, body): class MessageE2Response(MessageResponse): def __init__(self, message): super().__init__(message) - if (self.message_type in [MessageType.query, MessageType.notify1] and self.body_type == 0x01) or \ - (self.message_type == MessageType.set and self.body_type in [0x01, 0x02, 0x04, 0x14]): + if ( + self.message_type in [MessageType.query, MessageType.notify1] + and self.body_type == 0x01 + ) or ( + self.message_type == MessageType.set + and self.body_type in [0x01, 0x02, 0x04, 0x14] + ): self.set_body(E2GeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/e3/device.py b/custom_components/midea_ac_lan/midea/devices/e3/device.py index 69b92557..250076d1 100644 --- a/custom_components/midea_ac_lan/midea/devices/e3/device.py +++ b/custom_components/midea_ac_lan/midea/devices/e3/device.py @@ -1,16 +1,19 @@ -import logging import json +import logging + from .message import ( - MessageQuery, - MessageSet, + MessageE3Response, MessageNewProtocolSet, MessagePower, - MessageE3Response + MessageQuery, + MessageSet, ) + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -29,17 +32,17 @@ class DeviceAttributes(StrEnum): class MideaE3Device(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -61,10 +64,9 @@ def __init__( DeviceAttributes.smart_volume: False, DeviceAttributes.current_temperature: None, DeviceAttributes.target_temperature: 40, - }) - self._old_subtypes = [ - 32, 33, 34, 35, 36, 37, 40, 43, 48, 49, 80 - ] + }, + ) + self._old_subtypes = [32, 33, 34, 35, 36, 37, 40, 43, 48, 49, 80] self._precision_halves = None self._default_precision_halves = False self.set_customize(customize) @@ -82,9 +84,10 @@ def process_message(self, msg): new_status = {} for status in self._attributes.keys(): if hasattr(message, str(status)): - if self._precision_halves and status in \ - [DeviceAttributes.current_temperature, - DeviceAttributes.target_temperature]: + if self._precision_halves and status in [ + DeviceAttributes.current_temperature, + DeviceAttributes.target_temperature, + ]: self._attributes[status] = getattr(message, str(status)) / 2 else: self._attributes[status] = getattr(message, str(status)) @@ -98,13 +101,17 @@ def make_message_set(self): message.protection = self._attributes[DeviceAttributes.protection] message.zero_clod_pulse = self._attributes[DeviceAttributes.zero_cold_pulse] message.smart_volume = self._attributes[DeviceAttributes.smart_volume] - message.target_temperature = self._attributes[DeviceAttributes.target_temperature] + message.target_temperature = self._attributes[ + DeviceAttributes.target_temperature + ] return message def set_attribute(self, attr, value): - if attr not in [DeviceAttributes.burning_state, - DeviceAttributes.current_temperature, - DeviceAttributes.protection]: + if attr not in [ + DeviceAttributes.burning_state, + DeviceAttributes.current_temperature, + DeviceAttributes.protection, + ]: if self._precision_halves and attr == DeviceAttributes.target_temperature: value = int(value * 2) if attr == DeviceAttributes.power: diff --git a/custom_components/midea_ac_lan/midea/devices/e3/message.py b/custom_components/midea_ac_lan/midea/devices/e3/message.py index 2b86ff07..4b6952a2 100644 --- a/custom_components/midea_ac_lan/midea/devices/e3/message.py +++ b/custom_components/midea_ac_lan/midea/devices/e3/message.py @@ -1,17 +1,11 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) - +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType NEW_PROTOCOL_PARAMS = { "zero_cold_water": 0x03, # "zero_cold_master": 0x12, "zero_cold_pulse": 0x04, "smart_volume": 0x07, - "target_temperature": 0x08 + "target_temperature": 0x08, } @@ -21,7 +15,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xE3, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -34,7 +28,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): @@ -46,7 +41,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x02) + body_type=0x02, + ) self.power = False @property @@ -63,7 +59,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x04) + body_type=0x04, + ) self.target_temperature = 0 self.zero_cold_water = False @@ -83,17 +80,24 @@ def _body(self): # Byte 5 target_temperature target_temperature = self.target_temperature & 0xFF - return bytearray([ - 0x01, - zero_cold_water | 0x02, - protection | zero_cold_pulse | smart_volume, - 0x00, - target_temperature, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, - - ]) + return bytearray( + [ + 0x01, + zero_cold_water | 0x02, + protection | zero_cold_pulse | smart_volume, + 0x00, + target_temperature, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class MessageNewProtocolSet(MessageE3Base): @@ -101,7 +105,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x14) + body_type=0x14, + ) self.key = None self.value = None @@ -112,14 +117,29 @@ def _body(self): value = self.value else: value = 0x01 if self.value else 0x00 - return bytearray([ - key, value, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00 - ]) + return bytearray( + [ + key, + value, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class E3GeneralMessageBody(MessageBody): @@ -138,8 +158,16 @@ def __init__(self, body): class MessageE3Response(MessageResponse): def __init__(self, message): super().__init__(message) - if (self.message_type == MessageType.query and self.body_type == 0x01) or \ - (self.message_type == MessageType.set and self.body_type in [0x01, 0x02, 0x04, 0x14]) or \ - (self.message_type == MessageType.notify1 and self.body_type in [0x00, 0x01]): + if ( + (self.message_type == MessageType.query and self.body_type == 0x01) + or ( + self.message_type == MessageType.set + and self.body_type in [0x01, 0x02, 0x04, 0x14] + ) + or ( + self.message_type == MessageType.notify1 + and self.body_type in [0x00, 0x01] + ) + ): self.set_body(E3GeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/e6/device.py b/custom_components/midea_ac_lan/midea/devices/e6/device.py index 951125df..82368b40 100644 --- a/custom_components/midea_ac_lan/midea/devices/e6/device.py +++ b/custom_components/midea_ac_lan/midea/devices/e6/device.py @@ -1,13 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageSet, - MessageE6Response -) + +from .message import MessageE6Response, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -28,17 +27,17 @@ class DeviceAttributes(StrEnum): class MideaE6Device(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -61,8 +60,9 @@ def __init__( DeviceAttributes.heating_temperature: 50, DeviceAttributes.bathing_temperature: 40, DeviceAttributes.heating_leaving_temperature: None, - DeviceAttributes.bathing_leaving_temperature: None - }) + DeviceAttributes.bathing_leaving_temperature: None, + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -78,10 +78,12 @@ def process_message(self, msg): return new_status def set_attribute(self, attr, value): - if attr in [DeviceAttributes.main_power, - DeviceAttributes.heating_power, - DeviceAttributes.heating_temperature, - DeviceAttributes.bathing_temperature]: + if attr in [ + DeviceAttributes.main_power, + DeviceAttributes.heating_power, + DeviceAttributes.heating_temperature, + DeviceAttributes.bathing_temperature, + ]: message = MessageSet(self._protocol_version) setattr(message, str(attr), value) self.build_send(message) diff --git a/custom_components/midea_ac_lan/midea/devices/e6/message.py b/custom_components/midea_ac_lan/midea/devices/e6/message.py index 79738af4..230e2d5d 100644 --- a/custom_components/midea_ac_lan/midea/devices/e6/message.py +++ b/custom_components/midea_ac_lan/midea/devices/e6/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageE6Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type): device_type=0xE6, protocol_version=protocol_version, message_type=message_type, - body_type=None + body_type=None, ) @property @@ -27,8 +22,7 @@ def _body(self): class MessageQuery(MessageE6Base): def __init__(self, protocol_version): super().__init__( - protocol_version=protocol_version, - message_type=MessageType.query + protocol_version=protocol_version, message_type=MessageType.query ) @property @@ -39,8 +33,7 @@ def _body(self): class MessageSet(MessageE6Base): def __init__(self, protocol_version): super().__init__( - protocol_version=protocol_version, - message_type=MessageType.set + protocol_version=protocol_version, message_type=MessageType.set ) self.main_power = None self.heating_temperature = None @@ -71,14 +64,8 @@ def __init__(self, body): self.heating_working = (body[2] & 0x10) > 0 self.bathing_working = (body[2] & 0x20) > 0 self.heating_power = (body[4] & 0x01) > 0 - self.min_temperature = [ - body[16], - body[11] - ] - self.max_temperature = [ - body[15], - body[10] - ] + self.min_temperature = [body[16], body[11]] + self.max_temperature = [body[15], body[10]] self.heating_temperature = body[17] self.bathing_temperature = body[12] self.heating_leaving_temperature = body[14] diff --git a/custom_components/midea_ac_lan/midea/devices/e8/device.py b/custom_components/midea_ac_lan/midea/devices/e8/device.py index abaaada3..813941a2 100644 --- a/custom_components/midea_ac_lan/midea/devices/e8/device.py +++ b/custom_components/midea_ac_lan/midea/devices/e8/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageE8Response -) + +from .message import MessageE8Response, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -25,22 +25,26 @@ class DeviceAttributes(StrEnum): class MideaE8Device(MiedaDevice): _status = { - 0x00: "Standby", 0x01: "Delay", 0x02: "Working", - 0x03: "Paused", 0x04: "Keep-Warming", 0xFF: "Error" + 0x00: "Standby", + 0x01: "Delay", + 0x02: "Working", + 0x03: "Paused", + 0x04: "Keep-Warming", + 0xFF: "Error", } def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -62,7 +66,8 @@ def __init__( DeviceAttributes.current_temperature: None, DeviceAttributes.finished: None, DeviceAttributes.water_shortage: None, - }) + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -76,7 +81,9 @@ def process_message(self, msg): value = getattr(message, str(status)) if status == DeviceAttributes.status: if value in MideaE8Device._status.keys(): - self._attributes[DeviceAttributes.status] = MideaE8Device._status.get(value) + self._attributes[DeviceAttributes.status] = ( + MideaE8Device._status.get(value) + ) else: self._attributes[DeviceAttributes.status] = None else: diff --git a/custom_components/midea_ac_lan/midea/devices/e8/message.py b/custom_components/midea_ac_lan/midea/devices/e8/message.py index 9c45c688..6fe81409 100644 --- a/custom_components/midea_ac_lan/midea/devices/e8/message.py +++ b/custom_components/midea_ac_lan/midea/devices/e8/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageE8Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xE8, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0xAA) + body_type=0xAA, + ) @property def _body(self): @@ -50,7 +46,10 @@ def __init__(self, message): super().__init__(message) if len(super().body) > 6: sub_cmd = super().body[6] - if ((self.message_type == MessageType.set and sub_cmd in [0x02, 0x04, 0x06]) or - self.message_type in [MessageType.query, MessageType.notify1] and sub_cmd == 2): + if ( + (self.message_type == MessageType.set and sub_cmd in [0x02, 0x04, 0x06]) + or self.message_type in [MessageType.query, MessageType.notify1] + and sub_cmd == 2 + ): self.set_body(E8MessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/ea/device.py b/custom_components/midea_ac_lan/midea/devices/ea/device.py index aa4fbc45..722d12d8 100644 --- a/custom_components/midea_ac_lan/midea/devices/ea/device.py +++ b/custom_components/midea_ac_lan/midea/devices/ea/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageEAResponse -) + +from .message import MessageEAResponse, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -24,43 +24,122 @@ class DeviceAttributes(StrEnum): class MideaEADevice(MiedaDevice): - _mode_list = [ - "smart", "reserve", "cook_rice", "fast_cook_rice", "standard_cook_rice", - "gruel", "cook_congee", "stew_soup", "stewing", "heat_rice", "make_cake", - "yoghourt", "soup_rice", "coarse_rice", "five_ceeals_rice", "eight_treasures_rice", - "crispy_rice", "shelled_rice", "eight_treasures_congee", "infant_congee", - "older_rice", "rice_soup", "rice_paste", "egg_custard", "warm_milk", - "hot_spring_egg", "millet_congee", "firewood_rice", "few_rice", - "red_potato", "corn", "quick_freeze_bun", "steam_ribs", "steam_egg", - "coarse_congee", "steep_rice", "appetizing_congee", "corn_congee", - "sprout_rice", "luscious_rice", "luscious_boiled", "fast_rice", "fast_boil", - "bean_rice_congee", "fast_congee", "baby_congee", "cook_soup", "congee_coup", - "steam_corn", "steam_red_potato", "boil_congee", "delicious_steam", "boil_egg", - "rice_wine", "fruit_vegetable_paste", "vegetable_porridge", "pork_porridge", - "fragrant_rice", "assorte_rice", "steame_fish", "baby_rice", "essence_rice", - "fragrant_dense_congee", "one_two_cook", "original_steame", "hot_fast_rice", - "online_celebrity_rice", "sushi_rice", "stone_bowl_rice", "no_water_treat", - "keep_fresh", "low_sugar_rice", "black_buckwheat_rice", "resveratrol_rice", - "yellow_wheat_rice", "green_buckwheat_rice", "roughage_rice", "millet_mixed_rice", - "iron_pan_rice", "olla_pan_rice", "vegetable_rice", "baby_side", "regimen_congee", - "earthen_pot_congee", "regimen_soup", "pottery_jar_soup", "canton_soup", - "nutrition_stew", "northeast_stew", "uncap_boil", "trichromatic_coarse_grain", - "four_color_vegetables", "egg", "chop", - ] + ["unknown"] * 98 + ["clean"] + ["unknown"] * 5 + ["keep_warm"] + _mode_list = ( + [ + "smart", + "reserve", + "cook_rice", + "fast_cook_rice", + "standard_cook_rice", + "gruel", + "cook_congee", + "stew_soup", + "stewing", + "heat_rice", + "make_cake", + "yoghourt", + "soup_rice", + "coarse_rice", + "five_ceeals_rice", + "eight_treasures_rice", + "crispy_rice", + "shelled_rice", + "eight_treasures_congee", + "infant_congee", + "older_rice", + "rice_soup", + "rice_paste", + "egg_custard", + "warm_milk", + "hot_spring_egg", + "millet_congee", + "firewood_rice", + "few_rice", + "red_potato", + "corn", + "quick_freeze_bun", + "steam_ribs", + "steam_egg", + "coarse_congee", + "steep_rice", + "appetizing_congee", + "corn_congee", + "sprout_rice", + "luscious_rice", + "luscious_boiled", + "fast_rice", + "fast_boil", + "bean_rice_congee", + "fast_congee", + "baby_congee", + "cook_soup", + "congee_coup", + "steam_corn", + "steam_red_potato", + "boil_congee", + "delicious_steam", + "boil_egg", + "rice_wine", + "fruit_vegetable_paste", + "vegetable_porridge", + "pork_porridge", + "fragrant_rice", + "assorte_rice", + "steame_fish", + "baby_rice", + "essence_rice", + "fragrant_dense_congee", + "one_two_cook", + "original_steame", + "hot_fast_rice", + "online_celebrity_rice", + "sushi_rice", + "stone_bowl_rice", + "no_water_treat", + "keep_fresh", + "low_sugar_rice", + "black_buckwheat_rice", + "resveratrol_rice", + "yellow_wheat_rice", + "green_buckwheat_rice", + "roughage_rice", + "millet_mixed_rice", + "iron_pan_rice", + "olla_pan_rice", + "vegetable_rice", + "baby_side", + "regimen_congee", + "earthen_pot_congee", + "regimen_soup", + "pottery_jar_soup", + "canton_soup", + "nutrition_stew", + "northeast_stew", + "uncap_boil", + "trichromatic_coarse_grain", + "four_color_vegetables", + "egg", + "chop", + ] + + ["unknown"] * 98 + + ["clean"] + + ["unknown"] * 5 + + ["keep_warm"] + ) _progress = ["Idle", "Delay", "Cooking", "Keep-warm"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -81,8 +160,9 @@ def __init__( DeviceAttributes.top_temperature: None, DeviceAttributes.bottom_temperature: None, DeviceAttributes.keep_warm_time: None, - DeviceAttributes.progress: "Unknown" - }) + DeviceAttributes.progress: "Unknown", + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] diff --git a/custom_components/midea_ac_lan/midea/devices/ea/message.py b/custom_components/midea_ac_lan/midea/devices/ea/message.py index 50ac934a..49347767 100644 --- a/custom_components/midea_ac_lan/midea/devices/ea/message.py +++ b/custom_components/midea_ac_lan/midea/devices/ea/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageEABase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xEA, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,13 +20,12 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=None) + body_type=None, + ) @property def body(self): - return bytearray([ - 0xAA, 0x55, 0x01, 0x03, 0x00 - ]) + return bytearray([0xAA, 0x55, 0x01, 0x03, 0x00]) @property def _body(self): @@ -100,16 +94,22 @@ def __init__(self, message): if self.message_type == MessageType.set and super().body[5] == 0x16: # 381 self.set_body(EABody1(super().body)) elif self.message_type == MessageType.query: - if super().body[6] == 0x52 and super().body[7] == 0xc3: # 404 + if super().body[6] == 0x52 and super().body[7] == 0xC3: # 404 self.set_body(EABody2(super().body)) - elif super().body[5] == 0x3d: # 420 + elif super().body[5] == 0x3D: # 420 self.set_body(EABody1(super().body)) - elif self.message_type == MessageType.notify1 and super().body[5] == 0x3d: # 463 + elif ( + self.message_type == MessageType.notify1 and super().body[5] == 0x3D + ): # 463 self.set_body(EABody1(super().body)) else: - if (self.message_type == MessageType.set and super().body[3] == 0x02) or \ - (self.message_type == MessageType.query and super().body[3] == 0x03) or \ - (self.message_type == MessageType.notify1 and super().body[3] == 0x04): # 351 + if ( + (self.message_type == MessageType.set and super().body[3] == 0x02) + or (self.message_type == MessageType.query and super().body[3] == 0x03) + or ( + self.message_type == MessageType.notify1 and super().body[3] == 0x04 + ) + ): # 351 self.set_body(EABody3(super().body)) elif self.message_type == MessageType.notify1 and super().body[3] == 0x06: self.mode = super().body[4] + (super().body[5] << 8) diff --git a/custom_components/midea_ac_lan/midea/devices/ec/device.py b/custom_components/midea_ac_lan/midea/devices/ec/device.py index c6eed5b1..a87e9955 100644 --- a/custom_components/midea_ac_lan/midea/devices/ec/device.py +++ b/custom_components/midea_ac_lan/midea/devices/ec/device.py @@ -1,12 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageECResponse -) + +from .message import MessageECResponse, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -24,45 +24,134 @@ class DeviceAttributes(StrEnum): class MideaECDevice(MiedaDevice): - _mode_list = [ - "smart", "reserve", "cook_rice", "fast_cook_rice", "standard_cook_rice", - "gruel", "cook_congee", "stew_soup", "stewing", "heat_rice", "make_cake", - "yoghourt", "soup_rice", "coarse_rice", "five_ceeals_rice", "eight_treasures_rice", - "crispy_rice", "shelled_rice", "eight_treasures_congee", "infant_congee", - "older_rice", "rice_soup", "rice_paste", "egg_custard", "warm_milk", - "hot_spring_egg", "millet_congee", "firewood_rice", "few_rice", - "red_potato", "corn", "quick_freeze_bun", "steam_ribs", "steam_egg", - "coarse_congee", "steep_rice", "appetizing_congee", "corn_congee", - "sprout_rice", "luscious_rice", "luscious_boiled", "fast_rice", "fast_boil", - "bean_rice_congee", "fast_congee", "baby_congee", "cook_soup", "congee_coup", - "steam_corn", "steam_red_potato", "boil_congee", "delicious_steam", "boil_egg", - "rice_wine", "fruit_vegetable_paste", "vegetable_porridge", "pork_porridge", - "fragrant_rice", "assorte_rice", "steame_fish", "baby_rice", "essence_rice", - "fragrant_dense_congee", "one_two_cook", "original_steame", "hot_fast_rice", - "online_celebrity_rice", "sushi_rice", "stone_bowl_rice", "no_water_treat", - "keep_fresh", "low_sugar_rice", "black_buckwheat_rice", "resveratrol_rice", - "yellow_wheat_rice", "green_buckwheat_rice", "roughage_rice", "millet_mixed_rice", - "iron_pan_rice", "olla_pan_rice", "vegetable_rice", "baby_side", "regimen_congee", - "earthen_pot_congee", "regimen_soup", "pottery_jar_soup", "canton_soup", - "nutrition_stew", "northeast_stew", "uncap_boil", "trichromatic_coarse_grain", - "four_color_vegetables", "egg", "chop", - ] + ["unknown"] * 98 + ["clean"] + ["unknown"] * 5 + ["keep_warm", "diy"] - _progress = ["Idle", "Cooking", "Delay", "Keep-warm", - "Lid-open", "Relieving", "Keep-pressure", - "Relieving", "Cooking", "Relieving", "Lid-open"] + _mode_list = ( + [ + "smart", + "reserve", + "cook_rice", + "fast_cook_rice", + "standard_cook_rice", + "gruel", + "cook_congee", + "stew_soup", + "stewing", + "heat_rice", + "make_cake", + "yoghourt", + "soup_rice", + "coarse_rice", + "five_ceeals_rice", + "eight_treasures_rice", + "crispy_rice", + "shelled_rice", + "eight_treasures_congee", + "infant_congee", + "older_rice", + "rice_soup", + "rice_paste", + "egg_custard", + "warm_milk", + "hot_spring_egg", + "millet_congee", + "firewood_rice", + "few_rice", + "red_potato", + "corn", + "quick_freeze_bun", + "steam_ribs", + "steam_egg", + "coarse_congee", + "steep_rice", + "appetizing_congee", + "corn_congee", + "sprout_rice", + "luscious_rice", + "luscious_boiled", + "fast_rice", + "fast_boil", + "bean_rice_congee", + "fast_congee", + "baby_congee", + "cook_soup", + "congee_coup", + "steam_corn", + "steam_red_potato", + "boil_congee", + "delicious_steam", + "boil_egg", + "rice_wine", + "fruit_vegetable_paste", + "vegetable_porridge", + "pork_porridge", + "fragrant_rice", + "assorte_rice", + "steame_fish", + "baby_rice", + "essence_rice", + "fragrant_dense_congee", + "one_two_cook", + "original_steame", + "hot_fast_rice", + "online_celebrity_rice", + "sushi_rice", + "stone_bowl_rice", + "no_water_treat", + "keep_fresh", + "low_sugar_rice", + "black_buckwheat_rice", + "resveratrol_rice", + "yellow_wheat_rice", + "green_buckwheat_rice", + "roughage_rice", + "millet_mixed_rice", + "iron_pan_rice", + "olla_pan_rice", + "vegetable_rice", + "baby_side", + "regimen_congee", + "earthen_pot_congee", + "regimen_soup", + "pottery_jar_soup", + "canton_soup", + "nutrition_stew", + "northeast_stew", + "uncap_boil", + "trichromatic_coarse_grain", + "four_color_vegetables", + "egg", + "chop", + ] + + ["unknown"] * 98 + + ["clean"] + + ["unknown"] * 5 + + ["keep_warm", "diy"] + ) + _progress = [ + "Idle", + "Cooking", + "Delay", + "Keep-warm", + "Lid-open", + "Relieving", + "Keep-pressure", + "Relieving", + "Cooking", + "Relieving", + "Lid-open", + ] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -83,8 +172,9 @@ def __init__( DeviceAttributes.bottom_temperature: None, DeviceAttributes.keep_warm_time: None, DeviceAttributes.progress: "Unknown", - DeviceAttributes.with_pressure: None - }) + DeviceAttributes.with_pressure: None, + }, + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -98,7 +188,9 @@ def process_message(self, msg): value = getattr(message, str(status)) if status == DeviceAttributes.progress: if value < len(MideaECDevice._progress): - self._attributes[status] = MideaECDevice._progress[getattr(message, str(status))] + self._attributes[status] = MideaECDevice._progress[ + getattr(message, str(status)) + ] else: self._attributes[status] = "Unknown" elif status == DeviceAttributes.mode: diff --git a/custom_components/midea_ac_lan/midea/devices/ec/message.py b/custom_components/midea_ac_lan/midea/devices/ec/message.py index 5911670c..00c4aefe 100644 --- a/custom_components/midea_ac_lan/midea/devices/ec/message.py +++ b/custom_components/midea_ac_lan/midea/devices/ec/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageECBase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xEC, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,16 +20,12 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=None) + body_type=None, + ) @property def body(self): - return bytearray([ - 0xAA, 0x55, - 0x01, 0x03, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00 - ]) + return bytearray([0xAA, 0x55, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) @property def _body(self): @@ -63,7 +54,7 @@ def __init__(self, body): self.keep_warm_time = body[19] * 60 + body[20] self.top_temperature = body[48] self.bottom_temperature = body[49] - self.with_pressure = (body[33] > 0) + self.with_pressure = body[33] > 0 class MessageECResponse(MessageResponse): @@ -71,10 +62,12 @@ def __init__(self, message): super().__init__(message) if self.message_type == MessageType.notify1 and super().body[3] == 0x01: self.set_body(ECBodyNew(super().body)) - elif (self.message_type == MessageType.set and super().body[3] == 0x02) or \ - (self.message_type == MessageType.query and super().body[3] == 0x03) or \ - (self.message_type == MessageType.notify1 and super().body[3] == 0x04) or \ - (self.message_type == MessageType.notify1 and super().body[3] == 0x3d): + elif ( + (self.message_type == MessageType.set and super().body[3] == 0x02) + or (self.message_type == MessageType.query and super().body[3] == 0x03) + or (self.message_type == MessageType.notify1 and super().body[3] == 0x04) + or (self.message_type == MessageType.notify1 and super().body[3] == 0x3D) + ): self.set_body(ECGeneralMessageBody(super().body)) elif self.message_type == MessageType.notify1 and super().body[3] == 0x06: self.mode = super().body[4] + (super().body[5] << 8) diff --git a/custom_components/midea_ac_lan/midea/devices/ed/device.py b/custom_components/midea_ac_lan/midea/devices/ed/device.py index 3052f861..a25872e5 100644 --- a/custom_components/midea_ac_lan/midea/devices/ed/device.py +++ b/custom_components/midea_ac_lan/midea/devices/ed/device.py @@ -1,14 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageEDResponse, - MessageNewSet, - MessageOldSet -) + +from .message import MessageEDResponse, MessageNewSet, MessageOldSet, MessageQuery + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -31,17 +29,17 @@ class DeviceAttributes(StrEnum): class MideaEDDevice(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -65,17 +63,16 @@ def __init__( DeviceAttributes.life1: None, DeviceAttributes.life2: None, DeviceAttributes.life3: None, - DeviceAttributes.child_lock: False - }) + DeviceAttributes.child_lock: False, + }, + ) self._device_class = 0 def _use_new_set(self): - return True # if (self.sub_type > 342 or self.sub_type == 340) else False + return True # if (self.sub_type > 342 or self.sub_type == 340) else False def build_query(self): - return [ - MessageQuery(self._protocol_version, self._device_class) - ] + return [MessageQuery(self._protocol_version, self._device_class)] def process_message(self, msg): message = MessageEDResponse(msg) @@ -92,10 +89,7 @@ def process_message(self, msg): def set_attribute(self, attr, value): message = None if self._use_new_set(): - if attr in [ - DeviceAttributes.power, - DeviceAttributes.child_lock - ]: + if attr in [DeviceAttributes.power, DeviceAttributes.child_lock]: message = MessageNewSet(self._protocol_version) else: if attr in []: diff --git a/custom_components/midea_ac_lan/midea/devices/ed/message.py b/custom_components/midea_ac_lan/midea/devices/ed/message.py index 30a5a53b..e843d8af 100644 --- a/custom_components/midea_ac_lan/midea/devices/ed/message.py +++ b/custom_components/midea_ac_lan/midea/devices/ed/message.py @@ -1,10 +1,6 @@ from enum import IntEnum -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) + +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class NewSetTags(IntEnum): @@ -15,7 +11,9 @@ class NewSetTags(IntEnum): class EDNewSetParamPack: @staticmethod def pack(param, value, addition=0): - return bytearray([param & 0xFF, param >> 8, value, addition & 0xFF, addition >> 8]) + return bytearray( + [param & 0xFF, param >> 8, value, addition & 0xFF, addition >> 8] + ) class MessageEDBase(MessageRequest): @@ -24,7 +22,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xED, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -37,7 +35,8 @@ def __init__(self, protocol_version, device_class): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=device_class) + body_type=device_class, + ) @property def _body(self): @@ -49,7 +48,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x15) + body_type=0x15, + ) self.power = None self.lock = None @@ -61,16 +61,14 @@ def _body(self): pack_count += 1 payload.extend( EDNewSetParamPack.pack( - param=NewSetTags.power, # power - value=0x01 if self.power else 0x00 + param=NewSetTags.power, value=0x01 if self.power else 0x00 # power ) ) if self.lock is not None: pack_count += 1 payload.extend( EDNewSetParamPack.pack( - param=NewSetTags.lock, # lock - value=0x01 if self.lock else 0x00 + param=NewSetTags.lock, value=0x01 if self.lock else 0x00 # lock ) ) payload[1] = pack_count @@ -82,7 +80,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=None) + body_type=None, + ) @property def body(self): @@ -157,10 +156,17 @@ def __init__(self, body): self.child_lock = (body[data_offset + 5] & 0x01) > 0 self.power = (body[data_offset + 6] & 0x01) > 0 elif attr == 0x011: - self.water_consumption = float((body[data_offset + 3] + - (body[data_offset + 4] << 8) + - (body[data_offset + 5] << 16) + - (body[data_offset + 6] << 24))) / 1000 + self.water_consumption = ( + float( + ( + body[data_offset + 3] + + (body[data_offset + 4] << 8) + + (body[data_offset + 5] << 16) + + (body[data_offset + 6] << 24) + ) + ) + / 1000 + ) elif attr == 0x013: self.in_tds = body[data_offset + 3] + (body[data_offset + 4] << 8) self.out_tds = body[data_offset + 5] + (body[data_offset + 6] << 8) diff --git a/custom_components/midea_ac_lan/midea/devices/fa/device.py b/custom_components/midea_ac_lan/midea/devices/fa/device.py index 58dea3e9..38772c93 100644 --- a/custom_components/midea_ac_lan/midea/devices/fa/device.py +++ b/custom_components/midea_ac_lan/midea/devices/fa/device.py @@ -1,14 +1,13 @@ -import logging import json -from .message import ( - MessageQuery, - MessageFAResponse, - MessageSet -) +import logging + +from .message import MessageFAResponse, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -26,32 +25,45 @@ class DeviceAttributes(StrEnum): class MideaFADevice(MiedaDevice): - _oscillation_angles = [ - "Off", "30", "60", "90", "120", "180", "360" - ] - _tilting_angles = [ - "Off", "30", "60", "90", "120", "180", "360", "+60", "-60", "40" - ] + _oscillation_angles = ["Off", "30", "60", "90", "120", "180", "360"] + _tilting_angles = ["Off", "30", "60", "90", "120", "180", "360", "+60", "-60", "40"] _oscillation_modes = [ - "Off", "Oscillation", "Tilting", "Curve-W", "Curve-8", "Reserved", "Both" + "Off", + "Oscillation", + "Tilting", + "Curve-W", + "Curve-8", + "Reserved", + "Both", ] _modes = [ - "Normal", "Natural", "Sleep", "Comfort", "Silent", "Baby", - "Induction", "Circulation", "Strong", "Soft", "Customize", "Warm", "Smart" + "Normal", + "Natural", + "Sleep", + "Comfort", + "Silent", + "Baby", + "Induction", + "Circulation", + "Strong", + "Soft", + "Customize", + "Warm", + "Smart", ] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -73,7 +85,8 @@ def __init__( DeviceAttributes.oscillation_angle: None, DeviceAttributes.tilting_angle: None, DeviceAttributes.oscillation_mode: None, - }) + }, + ) self._default_speed_count = 3 self._speed_count = self._default_speed_count self.set_customize(customize) @@ -110,7 +123,9 @@ def process_message(self, msg): value = getattr(message, str(status)) if status == DeviceAttributes.oscillation_angle: if value < len(MideaFADevice._oscillation_angles): - self._attributes[status] = MideaFADevice._oscillation_angles[value] + self._attributes[status] = MideaFADevice._oscillation_angles[ + value + ] else: self._attributes[status] = None elif status == DeviceAttributes.tilting_angle: @@ -120,7 +135,9 @@ def process_message(self, msg): self._attributes[status] = None elif status == DeviceAttributes.oscillation_mode: if value < len(MideaFADevice._oscillation_modes): - self._attributes[status] = MideaFADevice._oscillation_modes[value] + self._attributes[status] = MideaFADevice._oscillation_modes[ + value + ] else: self._attributes[status] = None elif status == DeviceAttributes.mode: @@ -132,7 +149,10 @@ def process_message(self, msg): self._attributes[status] = value if not value: self._attributes[DeviceAttributes.fan_speed] = 0 - elif status == DeviceAttributes.fan_speed and not self._attributes[DeviceAttributes.power]: + elif ( + status == DeviceAttributes.fan_speed + and not self._attributes[DeviceAttributes.power] + ): self._attributes[status] = 0 else: self._attributes[status] = value @@ -148,20 +168,28 @@ def set_oscillation(self, attr, value): if value: message.oscillation_angle = 3 # 90 message.oscillation_mode = 1 # Oscillation - elif attr == DeviceAttributes.oscillation_mode and \ - (value in MideaFADevice._oscillation_modes or not value): + elif attr == DeviceAttributes.oscillation_mode and ( + value in MideaFADevice._oscillation_modes or not value + ): message = MessageSet(self._protocol_version, self.subtype) if value == "Off" or not value: message.oscillate = False else: message.oscillate = True - message.oscillation_mode = MideaFADevice._oscillation_modes.index(value) + message.oscillation_mode = MideaFADevice._oscillation_modes.index( + value + ) if value == "Oscillation": - if self._attributes[DeviceAttributes.oscillation_angle] == "Off": + if ( + self._attributes[DeviceAttributes.oscillation_angle] + == "Off" + ): message.oscillation_angle = 3 # 90 else: - message.oscillation_angle = MideaFADevice._oscillation_angles.index( - self._attributes[DeviceAttributes.oscillation_angle] + message.oscillation_angle = ( + MideaFADevice._oscillation_angles.index( + self._attributes[DeviceAttributes.oscillation_angle] + ) ) elif value == "Tilting": if self._attributes[DeviceAttributes.tilting_angle] == "Off": @@ -171,11 +199,16 @@ def set_oscillation(self, attr, value): self._attributes[DeviceAttributes.tilting_angle] ) else: - if self._attributes[DeviceAttributes.oscillation_angle] == "Off": + if ( + self._attributes[DeviceAttributes.oscillation_angle] + == "Off" + ): message.oscillation_angle = 3 # 90 else: - message.oscillation_angle = MideaFADevice._oscillation_angles.index( - self._attributes[DeviceAttributes.oscillation_angle] + message.oscillation_angle = ( + MideaFADevice._oscillation_angles.index( + self._attributes[DeviceAttributes.oscillation_angle] + ) ) if self._attributes[DeviceAttributes.tilting_angle] == "Off": message.tilting_angle = 3 # 90 @@ -183,8 +216,9 @@ def set_oscillation(self, attr, value): message.tilting_angle = MideaFADevice._tilting_angles.index( self._attributes[DeviceAttributes.tilting_angle] ) - elif attr == DeviceAttributes.oscillation_angle and \ - (value in MideaFADevice._oscillation_angles or not value): + elif attr == DeviceAttributes.oscillation_angle and ( + value in MideaFADevice._oscillation_angles or not value + ): message = MessageSet(self._protocol_version, self.subtype) if value == "Off" or not value: if self._attributes[DeviceAttributes.tilting_angle] == "Off": @@ -196,17 +230,22 @@ def set_oscillation(self, attr, value): self._attributes[DeviceAttributes.tilting_angle] ) else: - message.oscillation_angle = MideaFADevice._oscillation_angles.index(value) + message.oscillation_angle = MideaFADevice._oscillation_angles.index( + value + ) message.oscillate = True if self._attributes[DeviceAttributes.tilting_angle] == "Off": message.oscillation_mode = 1 - elif self._attributes[DeviceAttributes.oscillation_mode] == "Tilting": + elif ( + self._attributes[DeviceAttributes.oscillation_mode] == "Tilting" + ): message.oscillation_mode = 6 message.tilting_angle = MideaFADevice._tilting_angles.index( self._attributes[DeviceAttributes.tilting_angle] ) - elif attr == DeviceAttributes.tilting_angle and \ - (value in MideaFADevice._tilting_angles or not value): + elif attr == DeviceAttributes.tilting_angle and ( + value in MideaFADevice._tilting_angles or not value + ): message = MessageSet(self._protocol_version, self.subtype) if value == "Off" or not value: if self._attributes[DeviceAttributes.oscillation_angle] == "Off": @@ -214,18 +253,25 @@ def set_oscillation(self, attr, value): else: message.oscillate = True message.oscillation_mode = 1 - message.oscillation_angle = MideaFADevice._oscillation_angles.index( - self._attributes[DeviceAttributes.oscillation_angle] + message.oscillation_angle = ( + MideaFADevice._oscillation_angles.index( + self._attributes[DeviceAttributes.oscillation_angle] + ) ) else: message.tilting_angle = MideaFADevice._tilting_angles.index(value) message.oscillate = True if self._attributes[DeviceAttributes.oscillation_angle] == "Off": message.oscillation_mode = 2 - elif self._attributes[DeviceAttributes.oscillation_mode] == "Oscillation": + elif ( + self._attributes[DeviceAttributes.oscillation_mode] + == "Oscillation" + ): message.oscillation_mode = 6 - message.oscillation_angle = MideaFADevice._oscillation_angles.index( - self._attributes[DeviceAttributes.oscillation_angle] + message.oscillation_angle = ( + MideaFADevice._oscillation_angles.index( + self._attributes[DeviceAttributes.oscillation_angle] + ) ) return message @@ -235,11 +281,14 @@ def set_attribute(self, attr, value): DeviceAttributes.oscillate, DeviceAttributes.oscillation_mode, DeviceAttributes.oscillation_angle, - DeviceAttributes.tilting_angle + DeviceAttributes.tilting_angle, ]: message = self.set_oscillation(attr, value) - elif attr == DeviceAttributes.fan_speed and value > 0 and \ - not self._attributes[DeviceAttributes.power]: + elif ( + attr == DeviceAttributes.fan_speed + and value > 0 + and not self._attributes[DeviceAttributes.power] + ): message = MessageSet(self._protocol_version, self.subtype) message.fan_speed = value message.power = True diff --git a/custom_components/midea_ac_lan/midea/devices/fa/message.py b/custom_components/midea_ac_lan/midea/devices/fa/message.py index 4153e040..6e11a307 100644 --- a/custom_components/midea_ac_lan/midea/devices/fa/message.py +++ b/custom_components/midea_ac_lan/midea/devices/fa/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageFABase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xFA, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=None) + body_type=None, + ) @property def body(self): @@ -41,7 +37,8 @@ def __init__(self, protocol_version, subtype): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x00) + body_type=0x00, + ) self._subtype = subtype self.power = None self.lock = None @@ -55,31 +52,84 @@ def __init__(self, protocol_version, subtype): @property def _body(self): if 1 <= self._subtype <= 10 or self._subtype == 161: - _body_return = bytearray([ - 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00 - ]) + _body_return = bytearray( + [ + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) if self._subtype != 10: _body_return[13] = 0xFF else: - _body_return = bytearray([ - 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00 - ]) + _body_return = bytearray( + [ + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) if self.power is not None: if self.power: _body_return[3] = 1 @@ -100,9 +150,13 @@ def _body(self): else: _body_return[7] = 0 if self.oscillation_angle is not None: - _body_return[7] = 1 | _body_return[7] | ((self.oscillation_angle << 4) & 0x70) + _body_return[7] = ( + 1 | _body_return[7] | ((self.oscillation_angle << 4) & 0x70) + ) if self.oscillation_mode is not None: - _body_return[7] = 1 | _body_return[7] | ((self.oscillation_mode << 1) & 0x0E) + _body_return[7] = ( + 1 | _body_return[7] | ((self.oscillation_mode << 1) & 0x0E) + ) if self.tilting_angle is not None and len(_body_return) > 24: _body_return[24] = self.tilting_angle return _body_return @@ -117,7 +171,7 @@ def __init__(self, body): else: self.child_lock = False self.power = (body[4] & 0x01) > 0 - mode = ((body[4] & 0x1E) >> 1) + mode = (body[4] & 0x1E) >> 1 if mode > 0: self.mode = mode - 1 fan_speed = body[5] @@ -134,6 +188,10 @@ def __init__(self, body): class MessageFAResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.query, MessageType.set, MessageType.notify1]: + if self.message_type in [ + MessageType.query, + MessageType.set, + MessageType.notify1, + ]: self.set_body(FAGeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/fb/device.py b/custom_components/midea_ac_lan/midea/devices/fb/device.py index c7b614ab..c5ae1887 100644 --- a/custom_components/midea_ac_lan/midea/devices/fb/device.py +++ b/custom_components/midea_ac_lan/midea/devices/fb/device.py @@ -1,13 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageFBResponse, - MessageSet -) + +from .message import MessageFBResponse, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -23,22 +22,30 @@ class DeviceAttributes(StrEnum): class MideaFBDevice(MiedaDevice): - _modes = {0x01: "Auto", 0x02: "ECO", 0x03: "Sleep", - 0x04: "Anti-freezing", 0x05: "Comfort", 0x06: "Constant-temperature", - 0x07: "Normal", 0x08: "Fast-heating", 0x10: "Standby"} + _modes = { + 0x01: "Auto", + 0x02: "ECO", + 0x03: "Sleep", + 0x04: "Anti-freezing", + 0x05: "Comfort", + 0x06: "Constant-temperature", + 0x07: "Normal", + 0x08: "Fast-heating", + 0x10: "Standby", + } def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -58,7 +65,8 @@ def __init__( DeviceAttributes.target_temperature: None, DeviceAttributes.current_temperature: None, DeviceAttributes.child_lock: False, - }) + }, + ) @property def modes(self): diff --git a/custom_components/midea_ac_lan/midea/devices/fb/message.py b/custom_components/midea_ac_lan/midea/devices/fb/message.py index 2fe89860..57e613d9 100644 --- a/custom_components/midea_ac_lan/midea/devices/fb/message.py +++ b/custom_components/midea_ac_lan/midea/devices/fb/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageFBBase(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xFB, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=None) + body_type=None, + ) @property def body(self): @@ -41,7 +37,8 @@ def __init__(self, protocol_version, subtype): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x00) + body_type=0x00, + ) self._subtype = subtype self.power = None self.mode = None @@ -53,24 +50,52 @@ def __init__(self, protocol_version, subtype): def body(self): power = 0 if self.power is None else (0x01 if self.power else 0x02) mode = 0 if self.mode is None else self.mode - heating_level = 0 if self.heating_level is None else \ - (int(self.heating_level if 1 <= self.heating_level <= 10 else 0) & 0xFF) - target_temperature = 0 if self.target_temperature is None else \ - (int((self.target_temperature + 41) if -40 <= self.target_temperature <= 50 else - (0x80 if self.target_temperature in [0x80, 87] else 0)) & 0xFF) - child_lock = 0xFF if self.child_lock is None else (0x01 if self.child_lock else 0x00) - _return_body = bytearray([ - power, - 0x00, 0x00, 0x00, - mode, - heating_level, - target_temperature, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - child_lock, - 0x00 - ]) + heating_level = ( + 0 + if self.heating_level is None + else ( + int(self.heating_level if 1 <= self.heating_level <= 10 else 0) & 0xFF + ) + ) + target_temperature = ( + 0 + if self.target_temperature is None + else ( + int( + (self.target_temperature + 41) + if -40 <= self.target_temperature <= 50 + else (0x80 if self.target_temperature in [0x80, 87] else 0) + ) + & 0xFF + ) + ) + child_lock = ( + 0xFF if self.child_lock is None else (0x01 if self.child_lock else 0x00) + ) + _return_body = bytearray( + [ + power, + 0x00, + 0x00, + 0x00, + mode, + heating_level, + target_temperature, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + child_lock, + 0x00, + ] + ) if self._subtype > 5: _return_body += bytearray([0x00, 0x00, 0x00]) return _return_body @@ -100,6 +125,10 @@ def __init__(self, body): class MessageFBResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.query, MessageType.set, MessageType.notify1]: + if self.message_type in [ + MessageType.query, + MessageType.set, + MessageType.notify1, + ]: self.set_body(FBGeneralMessageBody(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/fc/device.py b/custom_components/midea_ac_lan/midea/devices/fc/device.py index e4282516..fad4f23d 100644 --- a/custom_components/midea_ac_lan/midea/devices/fc/device.py +++ b/custom_components/midea_ac_lan/midea/devices/fc/device.py @@ -1,14 +1,13 @@ -import logging import json -from .message import ( - MessageQuery, - MessageFCResponse, - MessageSet -) +import logging + +from .message import MessageFCResponse, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -33,28 +32,29 @@ class DeviceAttributes(StrEnum): class MideaFCDevice(MiedaDevice): _modes = { - 0x00: "Standby", 0x10: "Auto", 0x20: "Manual", 0x30: "Sleep", 0x40: "Fast", 0x50: "Smoke" - } - _speeds = { - 1: "Auto", 4: "Standby", 39: "Low", 59: "Medium", 80: "High" - } - _screen_displays = { - 0: "Bright", 6: "Dim", 7: "Off" + 0x00: "Standby", + 0x10: "Auto", + 0x20: "Manual", + 0x30: "Sleep", + 0x40: "Fast", + 0x50: "Smoke", } + _speeds = {1: "Auto", 4: "Standby", 39: "Low", 59: "Medium", 80: "High"} + _screen_displays = {0: "Bright", 6: "Dim", 7: "Off"} _detect_modes = ["Off", "PM 2.5", "Methanal"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -82,7 +82,8 @@ def __init__( DeviceAttributes.prompt_tone: True, DeviceAttributes.filter1_life: None, DeviceAttributes.filter2_life: None, - }) + }, + ) self._standby_detect_default = [40, 20] self._standby_detect = self._standby_detect_default @@ -126,7 +127,9 @@ def process_message(self, msg): self._attributes[status] = None elif status == DeviceAttributes.screen_display: if value in MideaFCDevice._screen_displays.keys(): - self._attributes[status] = MideaFCDevice._screen_displays.get(value) + self._attributes[status] = MideaFCDevice._screen_displays.get( + value + ) else: self._attributes[status] = None elif status == DeviceAttributes.detect_mode: @@ -148,20 +151,40 @@ def make_message_set(self): message.anion = self._attributes[DeviceAttributes.anion] message.standby = self._attributes[DeviceAttributes.standby] message.screen_display = self._attributes[DeviceAttributes.screen_display] - message.detect_mode = 0 if self._attributes[DeviceAttributes.detect_mode] is None else \ - MideaFCDevice._detect_modes.index(self._attributes[DeviceAttributes.detect_mode]) - message.mode = 0x10 if self._attributes[DeviceAttributes.mode] is None else \ - list(MideaFCDevice._modes.keys())[list(MideaFCDevice._modes.values()).index( - self._attributes[DeviceAttributes.mode] - )] - message.fan_speed = 39 if self._attributes[DeviceAttributes.fan_speed] is None else \ - list(MideaFCDevice._speeds.keys())[list(MideaFCDevice._speeds.values()).index( - self._attributes[DeviceAttributes.fan_speed] - )] - message.screen_display = 0 if self._attributes[DeviceAttributes.screen_display] is None else \ - list(MideaFCDevice._screen_displays.keys())[list(MideaFCDevice._screen_displays.values()).index( - self._attributes[DeviceAttributes.screen_display] - )] + message.detect_mode = ( + 0 + if self._attributes[DeviceAttributes.detect_mode] is None + else MideaFCDevice._detect_modes.index( + self._attributes[DeviceAttributes.detect_mode] + ) + ) + message.mode = ( + 0x10 + if self._attributes[DeviceAttributes.mode] is None + else list(MideaFCDevice._modes.keys())[ + list(MideaFCDevice._modes.values()).index( + self._attributes[DeviceAttributes.mode] + ) + ] + ) + message.fan_speed = ( + 39 + if self._attributes[DeviceAttributes.fan_speed] is None + else list(MideaFCDevice._speeds.keys())[ + list(MideaFCDevice._speeds.values()).index( + self._attributes[DeviceAttributes.fan_speed] + ) + ] + ) + message.screen_display = ( + 0 + if self._attributes[DeviceAttributes.screen_display] is None + else list(MideaFCDevice._screen_displays.keys())[ + list(MideaFCDevice._screen_displays.values()).index( + self._attributes[DeviceAttributes.screen_display] + ) + ] + ) message.standby_detect = self._standby_detect return message @@ -183,9 +206,9 @@ def set_attribute(self, attr, value): ] elif attr == DeviceAttributes.screen_display: if value in MideaFCDevice._screen_displays.values(): - message.screen_display = list(MideaFCDevice._screen_displays.keys())[ - list(MideaFCDevice._screen_displays.values()).index(value) - ] + message.screen_display = list( + MideaFCDevice._screen_displays.keys() + )[list(MideaFCDevice._screen_displays.values()).index(value)] elif not value: message.screen_display = 7 elif attr == DeviceAttributes.detect_mode: diff --git a/custom_components/midea_ac_lan/midea/devices/fc/message.py b/custom_components/midea_ac_lan/midea/devices/fc/message.py index febbfbaa..db391f8e 100644 --- a/custom_components/midea_ac_lan/midea/devices/fc/message.py +++ b/custom_components/midea_ac_lan/midea/devices/fc/message.py @@ -1,10 +1,5 @@ from ...core.crc8 import calculate -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageFCBase(MessageRequest): @@ -15,7 +10,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xFC, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) MessageFCBase._message_serial += 1 if MessageFCBase._message_serial >= 254: @@ -38,17 +33,34 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x41) + body_type=0x41, + ) @property def _body(self): - return bytearray([ - 0x00, 0x00, 0xFF, 0x03, - 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 - ]) + return bytearray( + [ + 0x00, + 0x00, + 0xFF, + 0x03, + 0x00, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class MessageSet(MessageFCBase): @@ -56,7 +68,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x48) + body_type=0x48, + ) self.power = False self.mode = 0 self.fan_speed = 0 @@ -91,16 +104,30 @@ def _body(self): standby = 0x08 standby_detect_high = 0 standby_detect_low = 0 - return bytearray([ - power | prompt_tone | detect | 0x02, - self.mode, - self.fan_speed, - 0x00, 0x00, 0x00, 0x00, - child_lock, self.screen_display, anion, - 0x00, 0x00, 0x00, detect_mode, - standby, standby_detect_high, standby_detect_low, - 0x00, 0x00, 0x00, - ]) + return bytearray( + [ + power | prompt_tone | detect | 0x02, + self.mode, + self.fan_speed, + 0x00, + 0x00, + 0x00, + 0x00, + child_lock, + self.screen_display, + anion, + 0x00, + 0x00, + 0x00, + detect_mode, + standby, + standby_detect_high, + standby_detect_low, + 0x00, + 0x00, + 0x00, + ] + ) class FCGeneralMessageBody(MessageBody): @@ -171,8 +198,11 @@ def __init__(self, message): if self.body_type in [0xB0, 0xB1]: pass else: - if self.message_type in [MessageType.query, MessageType.set, MessageType.notify1] and \ - self.body_type == 0xC8: + if ( + self.message_type + in [MessageType.query, MessageType.set, MessageType.notify1] + and self.body_type == 0xC8 + ): self.set_body(FCGeneralMessageBody(super().body)) elif self.message_type == MessageType.notify1 and self.body_type == 0xA0: self.set_body(FCNotifyMessageBody(super().body)) diff --git a/custom_components/midea_ac_lan/midea/devices/fd/device.py b/custom_components/midea_ac_lan/midea/devices/fd/device.py index b3f010ff..82dfc063 100644 --- a/custom_components/midea_ac_lan/midea/devices/fd/device.py +++ b/custom_components/midea_ac_lan/midea/devices/fd/device.py @@ -1,13 +1,12 @@ import logging -from .message import ( - MessageQuery, - MessageFDResponse, - MessageSet -) + +from .message import MessageFDResponse, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -28,31 +27,45 @@ class DeviceAttributes(StrEnum): class MideaFDDevice(MiedaDevice): _modes = [ - "Manual", "Auto", "Continuous", "Living-Room", "Bed-Room", "Kitchen", "Sleep" + "Manual", + "Auto", + "Continuous", + "Living-Room", + "Bed-Room", + "Kitchen", + "Sleep", ] _speeds_old = { - 1: "Lowest", 40: "Low", 60: "Medium", 80: "High", 102: "Auto", 127: "Off" + 1: "Lowest", + 40: "Low", + 60: "Medium", + 80: "High", + 102: "Auto", + 127: "Off", } _speeds_new = { - 1: "Lowest", 39: "Low", 59: "Medium", 80: "High", 101: "Auto", 127: "Off" - } - _screen_displays = { - 0: "Bright", 6: "Dim", 7: "Off" + 1: "Lowest", + 39: "Low", + 59: "Medium", + 80: "High", + 101: "Auto", + 127: "Off", } + _screen_displays = {0: "Bright", 6: "Dim", 7: "Off"} _detect_modes = ["Off", "PM 2.5", "Methanal"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -76,7 +89,8 @@ def __init__( DeviceAttributes.mode: None, DeviceAttributes.screen_display: None, DeviceAttributes.disinfect: None, - }) + }, + ) if self.subtype > 5: self._speeds = MideaFDDevice._speeds_new else: @@ -121,7 +135,9 @@ def process_message(self, msg): self._attributes[status] = None elif status == DeviceAttributes.screen_display: if value in MideaFDDevice._screen_displays.keys(): - self._attributes[status] = MideaFDDevice._screen_displays.get(value) + self._attributes[status] = MideaFDDevice._screen_displays.get( + value + ) else: self._attributes[status] = None else: @@ -136,17 +152,29 @@ def make_message_set(self): message.screen_display = self._attributes[DeviceAttributes.screen_display] message.disinfect = self._attributes[DeviceAttributes.disinfect] if self._attributes[DeviceAttributes.mode] in MideaFDDevice._modes: - message.mode = MideaFDDevice._modes.index(self._attributes[DeviceAttributes.mode]) + 1 + message.mode = ( + MideaFDDevice._modes.index(self._attributes[DeviceAttributes.mode]) + 1 + ) else: message.mode = 1 - message.fan_speed = 40 if self._attributes[DeviceAttributes.fan_speed] is None else \ - list(self._speeds.keys())[list(self._speeds.values()).index( - self._attributes[DeviceAttributes.fan_speed] - )] - message.screen_display = 0 if self._attributes[DeviceAttributes.screen_display] is None else \ - list(MideaFDDevice._screen_displays.keys())[list(MideaFDDevice._screen_displays.values()).index( - self._attributes[DeviceAttributes.screen_display] - )] + message.fan_speed = ( + 40 + if self._attributes[DeviceAttributes.fan_speed] is None + else list(self._speeds.keys())[ + list(self._speeds.values()).index( + self._attributes[DeviceAttributes.fan_speed] + ) + ] + ) + message.screen_display = ( + 0 + if self._attributes[DeviceAttributes.screen_display] is None + else list(MideaFDDevice._screen_displays.keys())[ + list(MideaFDDevice._screen_displays.values()).index( + self._attributes[DeviceAttributes.screen_display] + ) + ] + ) return message def set_attribute(self, attr, value): @@ -165,9 +193,9 @@ def set_attribute(self, attr, value): ] elif attr == DeviceAttributes.screen_display: if value in MideaFDDevice._screen_displays.values(): - message.screen_display = list(MideaFDDevice._screen_displays.keys())[ - list(MideaFDDevice._screen_displays.values()).index(value) - ] + message.screen_display = list( + MideaFDDevice._screen_displays.keys() + )[list(MideaFDDevice._screen_displays.values()).index(value)] elif not value: message.screen_display = 7 else: diff --git a/custom_components/midea_ac_lan/midea/devices/fd/message.py b/custom_components/midea_ac_lan/midea/devices/fd/message.py index d5402bff..39aa6ec1 100644 --- a/custom_components/midea_ac_lan/midea/devices/fd/message.py +++ b/custom_components/midea_ac_lan/midea/devices/fd/message.py @@ -1,10 +1,5 @@ from ...core.crc8 import calculate -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class MessageFDBase(MessageRequest): @@ -15,7 +10,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0xFD, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) MessageFDBase._message_serial += 1 if MessageFDBase._message_serial >= 254: @@ -38,17 +33,34 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x41) + body_type=0x41, + ) @property def _body(self): - return bytearray([ - 0x81, 0x00, 0xFF, 0x03, - 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 - ]) + return bytearray( + [ + 0x81, + 0x00, + 0xFF, + 0x03, + 0x00, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class MessageSet(MessageFDBase): @@ -56,7 +68,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x48) + body_type=0x48, + ) self.power = False self.fan_speed = 0 self.target_humidity = 50 @@ -70,20 +83,31 @@ def _body(self): power = 0x01 if self.power else 0x00 prompt_tone = 0x40 if self.prompt_tone else 0x00 disinfect = 0 if self.disinfect is None else (1 if self.disinfect else 2) - return bytearray([ - power | prompt_tone | 0x02, - 0x00, - self.fan_speed, - 0x00, 0x00, 0x00, - self.target_humidity, - 0x00, - self.screen_display, - self.mode, - 0x00, 0x00, 0x00, 0x00, - disinfect, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00 - ]) + return bytearray( + [ + power | prompt_tone | 0x02, + 0x00, + self.fan_speed, + 0x00, + 0x00, + 0x00, + self.target_humidity, + 0x00, + self.screen_display, + self.mode, + 0x00, + 0x00, + 0x00, + 0x00, + disinfect, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + ) class FDC8MessageBody(MessageBody): @@ -127,7 +151,11 @@ def __init__(self, body): class MessageFDResponse(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.query, MessageType.set, MessageType.notify1]: + if self.message_type in [ + MessageType.query, + MessageType.set, + MessageType.notify1, + ]: if self.body_type in [0xB0, 0xB1]: pass elif self.body_type == 0xA0: @@ -135,5 +163,9 @@ def __init__(self, message): elif self.body_type == 0xC8: self.set_body(FDC8MessageBody(super().body)) self.set_attr() - if hasattr(self, "fan_speed") and self.fan_speed is not None and self.fan_speed < 5: + if ( + hasattr(self, "fan_speed") + and self.fan_speed is not None + and self.fan_speed < 5 + ): self.fan_speed = 1 diff --git a/custom_components/midea_ac_lan/midea/devices/x13/device.py b/custom_components/midea_ac_lan/midea/devices/x13/device.py index d719ad1f..cc019d20 100644 --- a/custom_components/midea_ac_lan/midea/devices/x13/device.py +++ b/custom_components/midea_ac_lan/midea/devices/x13/device.py @@ -1,14 +1,13 @@ -import logging import json -from .message import ( - MessageQuery, - MessageSet, - Message13Response -) +import logging + +from .message import Message13Response, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -26,17 +25,17 @@ class Midea13Device(MiedaDevice): _effects = ["Manual", "Living", "Reading", "Mildly", "Cinema", "Night"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -54,8 +53,9 @@ def __init__( DeviceAttributes.color_temperature: None, DeviceAttributes.rgb_color: None, DeviceAttributes.effect: None, - DeviceAttributes.power: False - }) + DeviceAttributes.power: False, + }, + ) self._color_temp_range = None self._default_color_temp_range = [2700, 6500] self.set_customize(customize) @@ -69,12 +69,17 @@ def color_temp_range(self): return self._color_temp_range def kelvin_to_midea(self, kelvin): - return round((kelvin - self._color_temp_range[0]) / - (self._color_temp_range[1] - self._color_temp_range[0]) * 255) + return round( + (kelvin - self._color_temp_range[0]) + / (self._color_temp_range[1] - self._color_temp_range[0]) + * 255 + ) def midea_to_kelvin(self, midea): - return round((self._color_temp_range[1] - self._color_temp_range[0]) / 255 * midea) + \ - self._color_temp_range[0] + return ( + round((self._color_temp_range[1] - self._color_temp_range[0]) / 255 * midea) + + self._color_temp_range[0] + ) def build_query(self): return [MessageQuery(self._protocol_version)] @@ -101,10 +106,12 @@ def process_message(self, msg): return new_status def set_attribute(self, attr, value): - if attr in [DeviceAttributes.brightness, - DeviceAttributes.color_temperature, - DeviceAttributes.effect, - DeviceAttributes.power]: + if attr in [ + DeviceAttributes.brightness, + DeviceAttributes.color_temperature, + DeviceAttributes.effect, + DeviceAttributes.power, + ]: message = MessageSet(self._protocol_version) if attr == DeviceAttributes.effect and value in self._effects: setattr(message, str(attr), Midea13Device._effects.index(value)) diff --git a/custom_components/midea_ac_lan/midea/devices/x13/message.py b/custom_components/midea_ac_lan/midea/devices/x13/message.py index ce8e3699..fb208a11 100644 --- a/custom_components/midea_ac_lan/midea/devices/x13/message.py +++ b/custom_components/midea_ac_lan/midea/devices/x13/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class Message13Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0x13, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,13 +20,12 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x24) + body_type=0x24, + ) @property def _body(self): - return bytearray([ - 0x00, 0x00, 0x00, 0x00 - ]) + return bytearray([0x00, 0x00, 0x00, 0x00]) class MessageSet(Message13Base): @@ -39,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x00) + body_type=0x00, + ) self.brightness = None self.color_temperature = None self.effect = None @@ -71,11 +66,11 @@ def __init__(self, body): self.effect = self.read_byte(body, 3) - 1 if self.effect > 5: self.effect = 1 - ''' + """ self.rgb_color = [self.read_byte(body, 5), self.read_byte(body, 6), self.read_byte(body, 7)] - ''' + """ self.power = self.read_byte(body, 8) > 0 @@ -88,7 +83,7 @@ def __init__(self, body): class Message13Response(MessageResponse): def __init__(self, message): super().__init__(message) - if self.body_type == 0xa4: + if self.body_type == 0xA4: self.set_body(MessageMainLightBody(super().body)) elif self.message_type == MessageType.set and self.body_type > 0x80: self.set_body(MessageMainLightResponseBody(super().body)) diff --git a/custom_components/midea_ac_lan/midea/devices/x26/device.py b/custom_components/midea_ac_lan/midea/devices/x26/device.py index 2230154e..b01c67b5 100644 --- a/custom_components/midea_ac_lan/midea/devices/x26/device.py +++ b/custom_components/midea_ac_lan/midea/devices/x26/device.py @@ -1,14 +1,13 @@ import logging import math -from .message import ( - MessageQuery, - MessageSet, - Message26Response -) + +from .message import Message26Response, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -29,17 +28,17 @@ class Midea26Device(MiedaDevice): _directions = ["60", "70", "80", "90", "100", "110", "120", "Oscillate"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -59,8 +58,9 @@ def __init__( DeviceAttributes.direction: None, DeviceAttributes.current_humidity: None, DeviceAttributes.current_radar: None, - DeviceAttributes.current_temperature: None - }) + DeviceAttributes.current_temperature: None, + }, + ) self._fields = {} @staticmethod @@ -68,8 +68,11 @@ def _convert_to_midea_direction(direction): if direction == "Oscillate": result = 0xFD else: - result = Midea26Device._directions.index(direction) * 10 + 60 \ - if direction in Midea26Device._directions else 0xFD + result = ( + Midea26Device._directions.index(direction) * 10 + 60 + if direction in Midea26Device._directions + else 0xFD + ) return result @staticmethod @@ -111,21 +114,23 @@ def process_message(self, msg): return new_status def set_attribute(self, attr, value): - if attr in [DeviceAttributes.main_light, - DeviceAttributes.night_light, - DeviceAttributes.mode, - DeviceAttributes.direction - ]: + if attr in [ + DeviceAttributes.main_light, + DeviceAttributes.night_light, + DeviceAttributes.mode, + DeviceAttributes.direction, + ]: message = MessageSet(self._protocol_version) message.fields = self._fields message.main_light = self._attributes[DeviceAttributes.main_light] message.night_light = self._attributes[DeviceAttributes.night_light] - message.mode = Midea26Device._modes.index(self._attributes[DeviceAttributes.mode]) - message.direction = self._convert_to_midea_direction(self._attributes[DeviceAttributes.direction]) - if attr in [ - DeviceAttributes.main_light, - DeviceAttributes.night_light - ]: + message.mode = Midea26Device._modes.index( + self._attributes[DeviceAttributes.mode] + ) + message.direction = self._convert_to_midea_direction( + self._attributes[DeviceAttributes.direction] + ) + if attr in [DeviceAttributes.main_light, DeviceAttributes.night_light]: message.main_light = False message.night_light = False setattr(message, str(attr), value) diff --git a/custom_components/midea_ac_lan/midea/devices/x26/message.py b/custom_components/midea_ac_lan/midea/devices/x26/message.py index f0afd96c..51026d3a 100644 --- a/custom_components/midea_ac_lan/midea/devices/x26/message.py +++ b/custom_components/midea_ac_lan/midea/devices/x26/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class Message26Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0x26, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,12 +20,12 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): - return bytearray([ - ]) + return bytearray([]) class MessageSet(Message26Base): @@ -38,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x01) + body_type=0x01, + ) self.fields = {} self.main_light = False self.night_light = False @@ -51,47 +47,53 @@ def read_field(self, field): @property def _body(self): - return bytearray([ - 1 if self.main_light else 0, - self.read_field("MAIN_LIGHT_BRIGHTNESS"), - 1 if self.night_light else 0, - self.read_field("NIGHT_LIGHT_BRIGHTNESS"), - self.read_field("RADAR_INDUCTION_ENABLE"), - self.read_field("RADAR_INDUCTION_CLOSING_TIME"), - self.read_field("LIGHT_INTENSITY_THRESHOLD"), - self.read_field("RADAR_SENSITIVITY"), - 1 if self.mode == 1 or self.mode == 2 else 0, - 0 if not (self.mode == 1 or self.mode == 2) else 55 if self.mode == 1 else 30, - self.read_field("HEATING_SPEED"), - self.direction, - 1 if self.mode == 3 else 0, - self.read_field("BATH_HEATING_TIME"), - self.read_field("BATH_TEMPERATURE"), - self.read_field("BATH_SPEED"), - self.direction, - 1 if self.mode == 5 else 0, - self.read_field("VENTILATION_SPEED"), - self.direction, - 1 if self.mode == 6 else 0, - self.read_field("DRYING_TIME"), - self.read_field("DRYING_TEMPERATURE"), - self.read_field("DRYING_SPEED"), - self.direction, - 1 if self.mode == 4 else 0, - self.read_field("BLOWING_SPEED"), - self.direction, - self.read_field("DELAY_ENABLE"), - self.read_field("DELAY_TIME"), - self.read_field("SOFT_WIND_ENABLE"), - self.read_field("SOFT_WIND_TIME"), - self.read_field("SOFT_WIND_TEMPERATURE"), - self.read_field("SOFT_WIND_SPEED"), - self.read_field("SOFT_WIND_DIRECTION"), - self.read_field("WINDLESS_ENABLE"), - self.read_field("ANION_ENABLE"), - self.read_field("SMELLY_ENABLE"), - self.read_field("SMELLY_THRESHOLD") - ]) + return bytearray( + [ + 1 if self.main_light else 0, + self.read_field("MAIN_LIGHT_BRIGHTNESS"), + 1 if self.night_light else 0, + self.read_field("NIGHT_LIGHT_BRIGHTNESS"), + self.read_field("RADAR_INDUCTION_ENABLE"), + self.read_field("RADAR_INDUCTION_CLOSING_TIME"), + self.read_field("LIGHT_INTENSITY_THRESHOLD"), + self.read_field("RADAR_SENSITIVITY"), + 1 if self.mode == 1 or self.mode == 2 else 0, + ( + 0 + if not (self.mode == 1 or self.mode == 2) + else 55 if self.mode == 1 else 30 + ), + self.read_field("HEATING_SPEED"), + self.direction, + 1 if self.mode == 3 else 0, + self.read_field("BATH_HEATING_TIME"), + self.read_field("BATH_TEMPERATURE"), + self.read_field("BATH_SPEED"), + self.direction, + 1 if self.mode == 5 else 0, + self.read_field("VENTILATION_SPEED"), + self.direction, + 1 if self.mode == 6 else 0, + self.read_field("DRYING_TIME"), + self.read_field("DRYING_TEMPERATURE"), + self.read_field("DRYING_SPEED"), + self.direction, + 1 if self.mode == 4 else 0, + self.read_field("BLOWING_SPEED"), + self.direction, + self.read_field("DELAY_ENABLE"), + self.read_field("DELAY_TIME"), + self.read_field("SOFT_WIND_ENABLE"), + self.read_field("SOFT_WIND_TIME"), + self.read_field("SOFT_WIND_TEMPERATURE"), + self.read_field("SOFT_WIND_SPEED"), + self.read_field("SOFT_WIND_DIRECTION"), + self.read_field("WINDLESS_ENABLE"), + self.read_field("ANION_ENABLE"), + self.read_field("SMELLY_ENABLE"), + self.read_field("SMELLY_THRESHOLD"), + ] + ) class Message26Body(MessageBody): @@ -168,6 +170,10 @@ def __init__(self, body): class Message26Response(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.set, MessageType.notify1, MessageType.query] and self.body_type == 0x01: + if ( + self.message_type + in [MessageType.set, MessageType.notify1, MessageType.query] + and self.body_type == 0x01 + ): self.set_body(Message26Body(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/x34/device.py b/custom_components/midea_ac_lan/midea/devices/x34/device.py index a1cb1ef7..76e8f303 100644 --- a/custom_components/midea_ac_lan/midea/devices/x34/device.py +++ b/custom_components/midea_ac_lan/midea/devices/x34/device.py @@ -1,15 +1,18 @@ import logging + from .message import ( - MessageQuery, + Message34Response, + MessageLock, MessagePower, + MessageQuery, MessageStorage, - MessageLock, - Message34Response ) + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -44,17 +47,17 @@ class DeviceAttributes(StrEnum): class Midea34Device(MiedaDevice): def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -91,31 +94,32 @@ def __init__( DeviceAttributes.error_code: None, DeviceAttributes.softwater: 0, DeviceAttributes.wrong_operation: None, - DeviceAttributes.bright: 0 - }) + DeviceAttributes.bright: 0, + }, + ) self._modes = { - 0x0: "Neutral Gear", # BYTE_MODE_NEUTRAL_GEAR - 0x1: "Auto", # BYTE_MODE_AUTO_WASH - 0x2: "Heavy", # BYTE_MODE_STRONG_WASH - 0x3: "Normal", # BYTE_MODE_STANDARD_WASH - 0x4: "Energy Saving", # BYTE_MODE_ECO_WASH - 0x5: "Delicate", # BYTE_MODE_GLASS_WASH - 0x6: "Hour", # BYTE_MODE_HOUR_WASH - 0x7: "Quick", # BYTE_MODE_FAST_WASH - 0x8: "Rinse", # BYTE_MODE_SOAK_WASH - 0x9: "90min", # BYTE_MODE_90MIN_WASH - 0xA: "Self Clean", # BYTE_MODE_SELF_CLEAN - 0xB: "Fruit Wash", # BYTE_MODE_FRUIT_WASH - 0xC: "Self Define", # BYTE_MODE_SELF_DEFINE - 0xD: "Germ", # BYTE_MODE_GERM ??? - 0xE: "Bowl Wash", # BYTE_MODE_BOWL_WASH - 0xF: "Kill Germ", # BYTE_MODE_KILL_GERM - 0x10: "Sea Food Wash", # BYTE_MODE_SEA_FOOD_WASH - 0x12: "Hot Pot Wash", # BYTE_MODE_HOT_POT_WASH - 0x13: "Quiet", # BYTE_MODE_QUIET_NIGHT_WASH - 0x14: "Less Wash", # BYTE_MODE_LESS_WASH - 0x16: "Oil Net Wash", # BYTE_MODE_OIL_NET_WASH - 0x19: "Cloud Wash" # BYTE_MODE_CLOUD_WASH + 0x0: "Neutral Gear", # BYTE_MODE_NEUTRAL_GEAR + 0x1: "Auto", # BYTE_MODE_AUTO_WASH + 0x2: "Heavy", # BYTE_MODE_STRONG_WASH + 0x3: "Normal", # BYTE_MODE_STANDARD_WASH + 0x4: "Energy Saving", # BYTE_MODE_ECO_WASH + 0x5: "Delicate", # BYTE_MODE_GLASS_WASH + 0x6: "Hour", # BYTE_MODE_HOUR_WASH + 0x7: "Quick", # BYTE_MODE_FAST_WASH + 0x8: "Rinse", # BYTE_MODE_SOAK_WASH + 0x9: "90min", # BYTE_MODE_90MIN_WASH + 0xA: "Self Clean", # BYTE_MODE_SELF_CLEAN + 0xB: "Fruit Wash", # BYTE_MODE_FRUIT_WASH + 0xC: "Self Define", # BYTE_MODE_SELF_DEFINE + 0xD: "Germ", # BYTE_MODE_GERM ??? + 0xE: "Bowl Wash", # BYTE_MODE_BOWL_WASH + 0xF: "Kill Germ", # BYTE_MODE_KILL_GERM + 0x10: "Sea Food Wash", # BYTE_MODE_SEA_FOOD_WASH + 0x12: "Hot Pot Wash", # BYTE_MODE_HOT_POT_WASH + 0x13: "Quiet", # BYTE_MODE_QUIET_NIGHT_WASH + 0x14: "Less Wash", # BYTE_MODE_LESS_WASH + 0x16: "Oil Net Wash", # BYTE_MODE_OIL_NET_WASH + 0x19: "Cloud Wash", # BYTE_MODE_CLOUD_WASH } self._status = ["Off", "Idle", "Delay", "Running", "Error"] self._progress = ["Idle", "Pre-wash", "Wash", "Rinse", "Dry", "Complete"] diff --git a/custom_components/midea_ac_lan/midea/devices/x34/message.py b/custom_components/midea_ac_lan/midea/devices/x34/message.py index 729280f1..c107acd2 100644 --- a/custom_components/midea_ac_lan/midea/devices/x34/message.py +++ b/custom_components/midea_ac_lan/midea/devices/x34/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody, -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class Message34Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0x34, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,7 +20,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x00) + body_type=0x00, + ) @property def _body(self): @@ -37,16 +33,14 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x08) + body_type=0x08, + ) self.power = False @property def _body(self): power = 0x01 if self.power else 0x00 - return bytearray([ - power, - 0x00, 0x00, 0x00 - ]) + return bytearray([power, 0x00, 0x00, 0x00]) class MessageLock(Message34Base): @@ -54,7 +48,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x83) + body_type=0x83, + ) self.lock = False @property @@ -68,14 +63,18 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x81) + body_type=0x81, + ) self.storage = False @property def _body(self): storage = 0x01 if self.storage else 0x00 - return bytearray([0x00, 0x00, 0x00, storage]) + \ - bytearray([0xff] * 6) + bytearray([0x00] * 27) + return ( + bytearray([0x00, 0x00, 0x00, storage]) + + bytearray([0xFF] * 6) + + bytearray([0x00] * 27) + ) class Message34Body(MessageBody): @@ -85,9 +84,9 @@ def __init__(self, body): self.status = body[1] self.mode = body[2] self.additional = body[3] - self.door = (body[5] & 0x01) == 0 # 0 - open, 1 - close - self.rinse_aid = (body[5] & 0x02) > 0 # 0 - enough, 1 - shortage - self.salt = (body[5] & 0x04) > 0 # 0 - enough, 1 - shortage + self.door = (body[5] & 0x01) == 0 # 0 - open, 1 - close + self.rinse_aid = (body[5] & 0x02) > 0 # 0 - enough, 1 - shortage + self.salt = (body[5] & 0x04) > 0 # 0 - enough, 1 - shortage start_pause = (body[5] & 0x08) > 0 if start_pause: self.start = True @@ -115,7 +114,9 @@ def __init__(self, body): class Message34Response(MessageResponse): def __init__(self, message): super().__init__(message) - if (self.message_type == MessageType.set and 0 <= self.body_type <= 7) or \ - (self.message_type in [MessageType.query, MessageType.notify1] and self.body_type == 0): + if (self.message_type == MessageType.set and 0 <= self.body_type <= 7) or ( + self.message_type in [MessageType.query, MessageType.notify1] + and self.body_type == 0 + ): self.set_body(Message34Body(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea/devices/x40/device.py b/custom_components/midea_ac_lan/midea/devices/x40/device.py index f7d33fd9..6115e00c 100644 --- a/custom_components/midea_ac_lan/midea/devices/x40/device.py +++ b/custom_components/midea_ac_lan/midea/devices/x40/device.py @@ -1,14 +1,13 @@ import logging import math -from .message import ( - MessageQuery, - MessageSet, - Message40Response -) + +from .message import Message40Response, MessageQuery, MessageSet + try: from enum import StrEnum except ImportError: from ...backports.myenum import StrEnum + from ...core.device import MiedaDevice _LOGGER = logging.getLogger(__name__) @@ -27,17 +26,17 @@ class Midea40Device(MiedaDevice): _directions = ["60", "70", "80", "90", "100", "Oscillate"] def __init__( - self, - name: str, - device_id: int, - ip_address: str, - port: int, - token: str, - key: str, - protocol: int, - model: str, - subtype: int, - customize: str + self, + name: str, + device_id: int, + ip_address: str, + port: int, + token: str, + key: str, + protocol: int, + model: str, + subtype: int, + customize: str, ): super().__init__( name=name, @@ -56,8 +55,9 @@ def __init__( DeviceAttributes.direction: False, DeviceAttributes.ventilation: False, DeviceAttributes.smelly_sensor: False, - DeviceAttributes.current_temperature: None - }) + DeviceAttributes.current_temperature: None, + }, + ) self._fields = {} @property @@ -69,8 +69,11 @@ def _convert_to_midea_direction(direction): if direction == "Oscillate": result = 0xFD else: - result = Midea40Device._directions.index(direction) * 10 + 60 \ - if direction in Midea40Device._directions else 0xFD + result = ( + Midea40Device._directions.index(direction) * 10 + 60 + if direction in Midea40Device._directions + else 0xFD + ) return result @staticmethod @@ -102,18 +105,22 @@ def process_message(self, msg): return new_status def set_attribute(self, attr, value): - if attr in [DeviceAttributes.light, - DeviceAttributes.fan_speed, - DeviceAttributes.direction, - DeviceAttributes.ventilation, - DeviceAttributes.smelly_sensor]: + if attr in [ + DeviceAttributes.light, + DeviceAttributes.fan_speed, + DeviceAttributes.direction, + DeviceAttributes.ventilation, + DeviceAttributes.smelly_sensor, + ]: message = MessageSet(self._protocol_version) message.fields = self._fields message.light = self._attributes[DeviceAttributes.light] message.ventilation = self._attributes[DeviceAttributes.ventilation] message.smelly_sensor = self._attributes[DeviceAttributes.smelly_sensor] message.fan_speed = self._attributes[DeviceAttributes.fan_speed] - message.direction = self._convert_to_midea_direction(self._attributes[DeviceAttributes.direction]) + message.direction = self._convert_to_midea_direction( + self._attributes[DeviceAttributes.direction] + ) if attr == DeviceAttributes.direction: message.direction = self._convert_to_midea_direction(value) elif attr == DeviceAttributes.ventilation and message.fan_speed == 2: diff --git a/custom_components/midea_ac_lan/midea/devices/x40/message.py b/custom_components/midea_ac_lan/midea/devices/x40/message.py index 71baa781..fba44d2e 100644 --- a/custom_components/midea_ac_lan/midea/devices/x40/message.py +++ b/custom_components/midea_ac_lan/midea/devices/x40/message.py @@ -1,9 +1,4 @@ -from ...core.message import ( - MessageType, - MessageRequest, - MessageResponse, - MessageBody -) +from ...core.message import MessageBody, MessageRequest, MessageResponse, MessageType class Message40Base(MessageRequest): @@ -12,7 +7,7 @@ def __init__(self, protocol_version, message_type, body_type): device_type=0x40, protocol_version=protocol_version, message_type=message_type, - body_type=body_type + body_type=body_type, ) @property @@ -25,12 +20,12 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.query, - body_type=0x01) + body_type=0x01, + ) @property def _body(self): - return bytearray([ - ]) + return bytearray([]) class MessageSet(Message40Base): @@ -38,7 +33,8 @@ def __init__(self, protocol_version): super().__init__( protocol_version=protocol_version, message_type=MessageType.set, - body_type=0x01) + body_type=0x01, + ) self.fields = {} self.light = False self.fan_speed = 0 @@ -58,47 +54,49 @@ def _body(self): ventilation = 1 if self.ventilation else 0 direction = self.direction smelly_sensor = 1 if self.smelly_sensor else 0 - return bytearray([ - light, - self.read_field("MAIN_LIGHT_BRIGHTNESS"), - self.read_field("NIGHT_LIGHT_ENABLE"), - self.read_field("NIGHT_LIGHT_BRIGHTNESS"), - self.read_field("RADAR_INDUCTION_ENABLE"), - self.read_field("RADAR_INDUCTION_CLOSING_TIME"), - self.read_field("LIGHT_INTENSITY_THRESHOLD"), - self.read_field("RADAR_SENSITIVITY"), - self.read_field("HEATING_ENABLE"), - self.read_field("HEATING_TEMPERATURE"), - self.read_field("HEATING_SPEED"), - self.read_field("HEATING_DIRECTION"), - self.read_field("BATH_ENABLE"), - self.read_field("BATH_HEATING_TIME"), - self.read_field("BATH_TEMPERATURE"), - self.read_field("BATH_SPEED"), - self.read_field("BATH_DIRECTION"), - ventilation, - self.read_field("VENTILATION_SPEED"), - self.read_field("VENTILATION_DIRECTION"), - self.read_field("DRYING_ENABLE"), - self.read_field("DRYING_TIME"), - self.read_field("DRYING_TEMPERATURE"), - self.read_field("DRYING_SPEED"), - self.read_field("DRYING_DIRECTION"), - blow, - fan_speed, - direction, - self.read_field("DELAY_ENABLE"), - self.read_field("DELAY_TIME"), - self.read_field("SOFT_WIND_ENABLE"), - self.read_field("SOFT_WIND_TIME"), - self.read_field("SOFT_WIND_TEMPERATURE"), - self.read_field("SOFT_WIND_SPEED"), - self.read_field("SOFT_WIND_DIRECTION"), - self.read_field("WINDLESS_ENABLE"), - self.read_field("ANION_ENABLE"), - smelly_sensor, - self.read_field("SMELLY_THRESHOLD") - ]) + return bytearray( + [ + light, + self.read_field("MAIN_LIGHT_BRIGHTNESS"), + self.read_field("NIGHT_LIGHT_ENABLE"), + self.read_field("NIGHT_LIGHT_BRIGHTNESS"), + self.read_field("RADAR_INDUCTION_ENABLE"), + self.read_field("RADAR_INDUCTION_CLOSING_TIME"), + self.read_field("LIGHT_INTENSITY_THRESHOLD"), + self.read_field("RADAR_SENSITIVITY"), + self.read_field("HEATING_ENABLE"), + self.read_field("HEATING_TEMPERATURE"), + self.read_field("HEATING_SPEED"), + self.read_field("HEATING_DIRECTION"), + self.read_field("BATH_ENABLE"), + self.read_field("BATH_HEATING_TIME"), + self.read_field("BATH_TEMPERATURE"), + self.read_field("BATH_SPEED"), + self.read_field("BATH_DIRECTION"), + ventilation, + self.read_field("VENTILATION_SPEED"), + self.read_field("VENTILATION_DIRECTION"), + self.read_field("DRYING_ENABLE"), + self.read_field("DRYING_TIME"), + self.read_field("DRYING_TEMPERATURE"), + self.read_field("DRYING_SPEED"), + self.read_field("DRYING_DIRECTION"), + blow, + fan_speed, + direction, + self.read_field("DELAY_ENABLE"), + self.read_field("DELAY_TIME"), + self.read_field("SOFT_WIND_ENABLE"), + self.read_field("SOFT_WIND_TIME"), + self.read_field("SOFT_WIND_TEMPERATURE"), + self.read_field("SOFT_WIND_SPEED"), + self.read_field("SOFT_WIND_DIRECTION"), + self.read_field("WINDLESS_ENABLE"), + self.read_field("ANION_ENABLE"), + smelly_sensor, + self.read_field("SMELLY_THRESHOLD"), + ] + ) class Message40Body(MessageBody): @@ -157,6 +155,10 @@ def __init__(self, body): class Message40Response(MessageResponse): def __init__(self, message): super().__init__(message) - if self.message_type in [MessageType.set, MessageType.notify1, MessageType.query] and self.body_type == 0x01: + if ( + self.message_type + in [MessageType.set, MessageType.notify1, MessageType.query] + and self.body_type == 0x01 + ): self.set_body(Message40Body(super().body)) self.set_attr() diff --git a/custom_components/midea_ac_lan/midea_devices.py b/custom_components/midea_ac_lan/midea_devices.py index fb46ab95..5ad32bd2 100644 --- a/custom_components/midea_ac_lan/midea_devices.py +++ b/custom_components/midea_ac_lan/midea_devices.py @@ -1,19 +1,17 @@ +from homeassistant.components.binary_sensor import BinarySensorDeviceClass +from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + CONCENTRATION_PARTS_PER_MILLION, + PERCENTAGE, Platform, - UnitOfTime, - UnitOfTemperature, + UnitOfEnergy, UnitOfPower, - PERCENTAGE, + UnitOfTemperature, + UnitOfTime, UnitOfVolume, - UnitOfEnergy, - CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - CONCENTRATION_PARTS_PER_MILLION ) -from homeassistant.components.binary_sensor import BinarySensorDeviceClass -from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass -from .midea.devices.x26.device import DeviceAttributes as X26Attributes -from .midea.devices.x34.device import DeviceAttributes as X34Attributes -from .midea.devices.x40.device import DeviceAttributes as X40Attributes + from .midea.devices.a1.device import DeviceAttributes as A1Attributes from .midea.devices.ac.device import DeviceAttributes as ACAttributes from .midea.devices.b0.device import DeviceAttributes as B0Attributes @@ -44,18 +42,16 @@ from .midea.devices.fb.device import DeviceAttributes as FBAttributes from .midea.devices.fc.device import DeviceAttributes as FCAttributes from .midea.devices.fd.device import DeviceAttributes as FDAttributes - +from .midea.devices.x26.device import DeviceAttributes as X26Attributes +from .midea.devices.x34.device import DeviceAttributes as X34Attributes +from .midea.devices.x40.device import DeviceAttributes as X40Attributes MIDEA_DEVICES = { 0x13: { "name": "Light", "entities": { - "light": { - "type": Platform.LIGHT, - "icon": "mdi:lightbulb", - "default": True - } - } + "light": {"type": Platform.LIGHT, "icon": "mdi:lightbulb", "default": True} + }, }, 0x26: { "name": "Bathroom Master", @@ -65,43 +61,43 @@ "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, X26Attributes.current_humidity: { "type": Platform.SENSOR, "name": "Current Humidity", "device_class": SensorDeviceClass.HUMIDITY, "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, X26Attributes.current_radar: { "type": Platform.BINARY_SENSOR, "name": "Occupancy Status", - "device_class": BinarySensorDeviceClass.MOVING + "device_class": BinarySensorDeviceClass.MOVING, }, X26Attributes.main_light: { "type": Platform.SWITCH, "name": "Main Light", - "icon": "mdi:lightbulb" + "icon": "mdi:lightbulb", }, X26Attributes.night_light: { "type": Platform.SWITCH, "name": "Night Light", - "icon": "mdi:lightbulb" + "icon": "mdi:lightbulb", }, X26Attributes.mode: { "type": Platform.SELECT, "name": "Mode", "options": "preset_modes", - "icon": "mdi:fan" + "icon": "mdi:fan", }, X26Attributes.direction: { "type": Platform.SELECT, "name": "Direction", "options": "directions", - "icon": "mdi:arrow-split-vertical" - } - } + "icon": "mdi:arrow-split-vertical", + }, + }, }, 0x34: { "name": "Sink Dishwasher", @@ -110,81 +106,78 @@ "type": Platform.BINARY_SENSOR, "name": "Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, X34Attributes.rinse_aid: { "type": Platform.BINARY_SENSOR, "name": "Rinse Aid Shortage", "icon": "mdi:bottle-tonic", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, X34Attributes.salt: { "type": Platform.BINARY_SENSOR, "name": "Salt Shortage", "icon": "mdi:drag", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, X34Attributes.humidity: { "type": Platform.SENSOR, "name": "Humidity", "device_class": SensorDeviceClass.HUMIDITY, "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, X34Attributes.progress: { "type": Platform.SENSOR, "name": "Progress", - "icon": "mdi:rotate-360" + "icon": "mdi:rotate-360", }, X34Attributes.status: { "type": Platform.SENSOR, "name": "Status", - "icon": "mdi:information" + "icon": "mdi:information", }, X34Attributes.storage_remaining: { "type": Platform.SENSOR, "name": "Storage Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.HOURS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, X34Attributes.temperature: { "type": Platform.SENSOR, "name": "Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, X34Attributes.time_remaining: { "type": Platform.SENSOR, "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT - }, - X34Attributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" + "state_class": SensorStateClass.MEASUREMENT, }, + X34Attributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, X34Attributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, X34Attributes.storage: { "type": Platform.SWITCH, "name": "Storage", - "icon": "mdi:repeat-variant" + "icon": "mdi:repeat-variant", }, X34Attributes.mode: { "type": Platform.SENSOR, "name": "Working Mode", - "icon": "mdi:dishwasher" + "icon": "mdi:dishwasher", }, X34Attributes.error_code: { "type": Platform.SENSOR, "name": "Error Code", - "icon": "mdi:alert-box" + "icon": "mdi:alert-box", }, X34Attributes.softwater: { "type": Platform.SENSOR, @@ -194,47 +187,43 @@ X34Attributes.bright: { "type": Platform.SENSOR, "name": "Bright Level", - "icon": "mdi:star-four-points" - } - } + "icon": "mdi:star-four-points", + }, + }, }, 0x40: { "name": "Integrated Ceiling Fan", "entities": { - "fan": { - "type": Platform.FAN, - "icon": "mdi:fan", - "default": True - }, + "fan": {"type": Platform.FAN, "icon": "mdi:fan", "default": True}, X40Attributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, X40Attributes.light: { "type": Platform.SWITCH, "name": "Light", - "icon": "mdi:lightbulb" + "icon": "mdi:lightbulb", }, X40Attributes.ventilation: { "type": Platform.SWITCH, "name": "Ventilation", - "icon": "mdi:air-filter" + "icon": "mdi:air-filter", }, X40Attributes.smelly_sensor: { "type": Platform.SWITCH, "name": "Smelly Sensor", - "icon": "mdi:scent" + "icon": "mdi:scent", }, X40Attributes.direction: { "type": Platform.SELECT, "name": "Direction", "options": "directions", - "icon": "mdi:arrow-split-vertical" - } - } + "icon": "mdi:arrow-split-vertical", + }, + }, }, 0xA1: { "name": "Dehumidifier", @@ -242,72 +231,69 @@ "humidifier": { "type": Platform.HUMIDIFIER, "icon": "mdi:air-humidifier", - "default": True - }, - A1Attributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" + "default": True, }, + A1Attributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, A1Attributes.anion: { "type": Platform.SWITCH, "name": "Anion", - "icon": "mdi:vanish" + "icon": "mdi:vanish", }, A1Attributes.prompt_tone: { "type": Platform.SWITCH, "name": "Prompt Tone", - "icon": "mdi:bell" + "icon": "mdi:bell", }, A1Attributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, A1Attributes.swing: { "type": Platform.SWITCH, "name": "swing", - "icon": "mdi:pan-horizontal" + "icon": "mdi:pan-horizontal", }, A1Attributes.fan_speed: { "type": Platform.SELECT, "name": "Fan Speed", "options": "fan_speeds", - "icon": "mdi:fan" + "icon": "mdi:fan", }, A1Attributes.water_level_set: { "type": Platform.SELECT, "name": "Water Level Setting", "options": "water_level_sets", - "icon": "mdi:cup-water" + "icon": "mdi:cup-water", }, A1Attributes.current_humidity: { "type": Platform.SENSOR, "name": "Current Humidity", "device_class": SensorDeviceClass.HUMIDITY, "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, A1Attributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, A1Attributes.tank: { "type": Platform.SENSOR, "name": "Tank", "icon": "mdi:cup-water", "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, A1Attributes.tank_full: { "type": Platform.BINARY_SENSOR, "name": "Tank status", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM - } - } + "device_class": BinarySensorDeviceClass.PROBLEM, + }, + }, }, 0xAC: { "name": "Air Conditioner", @@ -315,147 +301,143 @@ "climate": { "type": Platform.CLIMATE, "icon": "mdi:air-conditioner", - "default": True - }, - "fresh_air": { - "type": Platform.FAN, - "icon": "mdi:fan", - "name": "Fresh Air" + "default": True, }, + "fresh_air": {"type": Platform.FAN, "icon": "mdi:fan", "name": "Fresh Air"}, ACAttributes.aux_heating: { "type": Platform.SWITCH, "name": "Aux Heating", - "icon": "mdi:heat-wave" + "icon": "mdi:heat-wave", }, ACAttributes.boost_mode: { "type": Platform.SWITCH, "name": "Boost Mode", - "icon": "mdi:turbine" + "icon": "mdi:turbine", }, ACAttributes.breezeless: { "type": Platform.SWITCH, "name": "Breezeless", - "icon": "mdi:tailwind" + "icon": "mdi:tailwind", }, ACAttributes.comfort_mode: { "type": Platform.SWITCH, "name": "Comfort Mode", - "icon": "mdi:alpha-c-circle" + "icon": "mdi:alpha-c-circle", }, ACAttributes.dry: { "type": Platform.SWITCH, "name": "Dry", - "icon": "mdi:air-filter" + "icon": "mdi:air-filter", }, ACAttributes.eco_mode: { "type": Platform.SWITCH, "name": "ECO Mode", - "icon": "mdi:leaf-circle" + "icon": "mdi:leaf-circle", }, ACAttributes.frost_protect: { "type": Platform.SWITCH, "name": "Frost Protect", - "icon": "mdi:snowflake-alert" + "icon": "mdi:snowflake-alert", }, ACAttributes.indirect_wind: { "type": Platform.SWITCH, "name": "Indirect Wind", - "icon": "mdi:tailwind" + "icon": "mdi:tailwind", }, ACAttributes.natural_wind: { "type": Platform.SWITCH, "name": "Natural Wind", - "icon": "mdi:tailwind" + "icon": "mdi:tailwind", }, ACAttributes.prompt_tone: { "type": Platform.SWITCH, "name": "Prompt Tone", - "icon": "mdi:bell" + "icon": "mdi:bell", }, ACAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, ACAttributes.screen_display: { "type": Platform.SWITCH, "name": "Screen Display", - "icon": "mdi:television-ambient-light" + "icon": "mdi:television-ambient-light", }, ACAttributes.screen_display_alternate: { "type": Platform.SWITCH, "name": "Screen Display Alternate", - "icon": "mdi:television-ambient-light" + "icon": "mdi:television-ambient-light", }, ACAttributes.sleep_mode: { "type": Platform.SWITCH, "name": "Sleep Mode", - "icon": "mdi:power-sleep" + "icon": "mdi:power-sleep", }, ACAttributes.smart_eye: { "type": Platform.SWITCH, "name": "Smart Eye", - "icon": "mdi:eye" + "icon": "mdi:eye", }, ACAttributes.swing_horizontal: { "type": Platform.SWITCH, "name": "Swing Horizontal", - "icon": "mdi:arrow-split-vertical" + "icon": "mdi:arrow-split-vertical", }, ACAttributes.swing_vertical: { "type": Platform.SWITCH, "name": "Swing Vertical", - "icon": "mdi:arrow-split-horizontal" + "icon": "mdi:arrow-split-horizontal", }, ACAttributes.full_dust: { "type": Platform.BINARY_SENSOR, "name": "Full of Dust", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, ACAttributes.indoor_humidity: { "type": Platform.SENSOR, "name": "Indoor Humidity", "device_class": SensorDeviceClass.HUMIDITY, "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, ACAttributes.indoor_temperature: { "type": Platform.SENSOR, "name": "Indoor Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, ACAttributes.outdoor_temperature: { "type": Platform.SENSOR, "name": "Outdoor Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, ACAttributes.total_energy_consumption: { "type": Platform.SENSOR, "name": "Total Energy Consumption", "device_class": SensorDeviceClass.ENERGY, "unit": UnitOfEnergy.KILO_WATT_HOUR, - "state_class": SensorStateClass.TOTAL_INCREASING + "state_class": SensorStateClass.TOTAL_INCREASING, }, ACAttributes.current_energy_consumption: { "type": Platform.SENSOR, "name": "Current Energy Consumption", "device_class": SensorDeviceClass.ENERGY, "unit": UnitOfEnergy.KILO_WATT_HOUR, - "state_class": SensorStateClass.TOTAL_INCREASING + "state_class": SensorStateClass.TOTAL_INCREASING, }, ACAttributes.realtime_power: { "type": Platform.SENSOR, "name": "Realtime Power", "device_class": SensorDeviceClass.POWER, "unit": UnitOfPower.WATT, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, 0xB0: { "name": "Microwave Oven", @@ -464,32 +446,32 @@ "type": Platform.BINARY_SENSOR, "name": "Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, B0Attributes.tank_ejected: { "type": Platform.BINARY_SENSOR, "name": "Tank Ejected", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B0Attributes.water_change_reminder: { "type": Platform.BINARY_SENSOR, "name": "Water Change Reminder", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B0Attributes.water_shortage: { "type": Platform.BINARY_SENSOR, "name": "Water Shortage", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B0Attributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, B0Attributes.status: { "type": Platform.SENSOR, @@ -501,9 +483,9 @@ "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, 0xB1: { "name": "Electric Oven", @@ -512,32 +494,32 @@ "type": Platform.BINARY_SENSOR, "name": "Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, B1Attributes.tank_ejected: { "type": Platform.BINARY_SENSOR, "name": "Tank ejected", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B1Attributes.water_change_reminder: { "type": Platform.BINARY_SENSOR, "name": "Water Change Reminder", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B1Attributes.water_shortage: { "type": Platform.BINARY_SENSOR, "name": "Water Shortage", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B1Attributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, B1Attributes.status: { "type": Platform.SENSOR, @@ -549,9 +531,9 @@ "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, 0xB3: { "name": "Dish Sterilizer", @@ -613,58 +595,58 @@ B3Attributes.top_compartment_status: { "type": Platform.SENSOR, "name": "Top Compartment Status", - "icon": "mdi:information" + "icon": "mdi:information", }, B3Attributes.top_compartment_temperature: { "type": Platform.SENSOR, "name": "Top Compartment Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, B3Attributes.top_compartment_remaining: { "type": Platform.SENSOR, "name": "Top Compartment Remaining", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, B3Attributes.middle_compartment_status: { "type": Platform.SENSOR, "name": "Middle Compartment Status", - "icon": "mdi:information" + "icon": "mdi:information", }, B3Attributes.middle_compartment_temperature: { "type": Platform.SENSOR, "name": "Middle Compartment Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, B3Attributes.middle_compartment_remaining: { "type": Platform.SENSOR, "name": "Middle Compartment Remaining", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, B3Attributes.bottom_compartment_status: { "type": Platform.SENSOR, "name": "Bottom Compartment Status", - "icon": "mdi:information" + "icon": "mdi:information", }, B3Attributes.bottom_compartment_temperature: { "type": Platform.SENSOR, "name": "Bottom Compartment Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, B3Attributes.bottom_compartment_remaining: { "type": Platform.SENSOR, "name": "Bottom Compartment Remaining", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, 0xB4: { "name": "Toaster", @@ -673,32 +655,32 @@ "type": Platform.BINARY_SENSOR, "name": "Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, B4Attributes.tank_ejected: { "type": Platform.BINARY_SENSOR, "name": "Tank ejected", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B4Attributes.water_change_reminder: { "type": Platform.BINARY_SENSOR, "name": "Water Change Reminder", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B4Attributes.water_shortage: { "type": Platform.BINARY_SENSOR, "name": "Water Shortage", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B4Attributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, B4Attributes.status: { "type": Platform.SENSOR, @@ -710,47 +692,43 @@ "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, 0xB6: { "name": "Range Hood", "entities": { - "fan": { - "type": Platform.FAN, - "icon": "mdi:fan", - "default": True - }, + "fan": {"type": Platform.FAN, "icon": "mdi:fan", "default": True}, B6Attributes.light: { "type": Platform.SWITCH, "name": "Light", - "icon": "mdi:lightbulb" + "icon": "mdi:lightbulb", }, B6Attributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, B6Attributes.cleaning_reminder: { "type": Platform.BINARY_SENSOR, "name": "Cleaning Reminder", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B6Attributes.oilcup_full: { "type": Platform.BINARY_SENSOR, "name": "Oil-cup Full", "icon": "mdi:cup", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, B6Attributes.fan_level: { "type": Platform.SENSOR, "name": "Fan level", "icon": "mdi:fan", - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, - } + }, }, 0xBF: { "name": "Microwave Steam Oven", @@ -759,32 +737,32 @@ "type": Platform.BINARY_SENSOR, "name": "Tank ejected", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, BFAttributes.water_change_reminder: { "type": Platform.BINARY_SENSOR, "name": "Water Change Reminder", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, BFAttributes.door: { "type": Platform.BINARY_SENSOR, "name": "Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, BFAttributes.water_shortage: { "type": Platform.BINARY_SENSOR, "name": "Water Shortage", "icon": "mdi:cup-water", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, BFAttributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, BFAttributes.status: { "type": Platform.SENSOR, @@ -796,9 +774,9 @@ "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, 0xC2: { "name": "Toilet", @@ -806,58 +784,55 @@ C2Attributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, C2Attributes.sensor_light: { "type": Platform.SWITCH, "name": "Sensor Light", - "icon": "mdi:lightbulb" + "icon": "mdi:lightbulb", }, C2Attributes.foam_shield: { "type": Platform.SWITCH, "name": "Foam Shield", "icon": "mdi:chart-bubble", }, - C2Attributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" - }, + C2Attributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, C2Attributes.seat_status: { "type": Platform.BINARY_SENSOR, "name": "Seat Status", - "icon": "mdi:seat-legroom-normal" + "icon": "mdi:seat-legroom-normal", }, C2Attributes.lid_status: { "type": Platform.BINARY_SENSOR, "name": "Lid Status", - "icon": "mdi:toilet" + "icon": "mdi:toilet", }, C2Attributes.light_status: { "type": Platform.BINARY_SENSOR, "name": "Light Status", "icon": "mdi:lightbulb", - "device_class": BinarySensorDeviceClass.LIGHT + "device_class": BinarySensorDeviceClass.LIGHT, }, C2Attributes.water_temperature: { "type": Platform.SENSOR, "name": "Water Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, C2Attributes.seat_temperature: { "type": Platform.SENSOR, "name": "Seat Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, C2Attributes.filter_life: { "type": Platform.SENSOR, "name": "Filter Life", "icon": "mdi:toilet", "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, C2Attributes.dry_level: { "type": Platform.NUMBER, @@ -865,7 +840,7 @@ "icon": "mdi:fire", "max": "max_dry_level", "min": 0, - "step": 1 + "step": 1, }, C2Attributes.water_temp_level: { "type": Platform.NUMBER, @@ -873,7 +848,7 @@ "icon": "mdi:fire", "max": "max_water_temp_level", "min": 0, - "step": 1 + "step": 1, }, C2Attributes.seat_temp_level: { "type": Platform.NUMBER, @@ -881,9 +856,9 @@ "icon": "mdi:fire", "max": "max_seat_temp_level", "min": 0, - "step": 1 - } - } + "step": 1, + }, + }, }, 0xC3: { "name": "Heat Pump Wi-Fi Controller", @@ -893,70 +868,70 @@ "icon": "mdi:air-conditioner", "name": "Zone1 Thermostat", "zone": 0, - "default": True + "default": True, }, "climate_zone2": { "type": Platform.CLIMATE, "icon": "mdi:air-conditioner", "name": "Zone2 Thermostat", "zone": 1, - "default": True + "default": True, }, "water_heater": { "type": Platform.WATER_HEATER, "icon": "mdi:heat-pump", "name": "Domestic hot water", - "default": True + "default": True, }, C3Attributes.disinfect: { "type": Platform.SWITCH, "name": "Disinfect", - "icon": "mdi:water-plus-outline" + "icon": "mdi:water-plus-outline", }, C3Attributes.dhw_power: { "type": Platform.SWITCH, "name": "DHW Power", - "icon": "mdi:power" + "icon": "mdi:power", }, C3Attributes.eco_mode: { "type": Platform.SWITCH, "name": "ECO Mode", - "icon": "mdi:leaf-circle" + "icon": "mdi:leaf-circle", }, C3Attributes.fast_dhw: { "type": Platform.SWITCH, "name": "Fast DHW", - "icon": "mdi:rotate-orbit" + "icon": "mdi:rotate-orbit", }, C3Attributes.silent_mode: { "type": Platform.SWITCH, "name": "Silent Mode", - "icon": "mdi:fan-remove" + "icon": "mdi:fan-remove", }, C3Attributes.tbh: { "type": Platform.SWITCH, "name": "TBH", - "icon": "mdi:water-boiler" + "icon": "mdi:water-boiler", }, C3Attributes.zone1_curve: { "type": Platform.SWITCH, "name": "Zone1 Curve", - "icon": "mdi:chart-bell-curve-cumulative" + "icon": "mdi:chart-bell-curve-cumulative", }, C3Attributes.zone2_curve: { "type": Platform.SWITCH, "name": "Zone2 Curve", - "icon": "mdi:chart-bell-curve-cumulative" + "icon": "mdi:chart-bell-curve-cumulative", }, C3Attributes.zone1_power: { "type": Platform.SWITCH, "name": "Zone1 Power", - "icon": "mdi:power" + "icon": "mdi:power", }, C3Attributes.zone2_power: { "type": Platform.SWITCH, "name": "Zone2 Power", - "icon": "mdi:power" + "icon": "mdi:power", }, C3Attributes.zone1_water_temp_mode: { "type": Platform.BINARY_SENSOR, @@ -985,14 +960,14 @@ C3Attributes.error_code: { "type": Platform.SENSOR, "name": "Error Code", - "icon": "mdi:alpha-e-circle" + "icon": "mdi:alpha-e-circle", }, C3Attributes.tank_actual_temperature: { "type": Platform.SENSOR, "name": "Tank Actual Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, C3Attributes.status_dhw: { "type": Platform.BINARY_SENSOR, @@ -1023,23 +998,23 @@ "name": "Total energy consumption", "device_class": SensorDeviceClass.ENERGY, "unit": UnitOfEnergy.KILO_WATT_HOUR, - "state_class": SensorStateClass.TOTAL_INCREASING + "state_class": SensorStateClass.TOTAL_INCREASING, }, C3Attributes.total_produced_energy: { "type": Platform.SENSOR, "name": "Total produced energy", "device_class": SensorDeviceClass.ENERGY, "unit": UnitOfEnergy.KILO_WATT_HOUR, - "state_class": SensorStateClass.TOTAL_INCREASING + "state_class": SensorStateClass.TOTAL_INCREASING, }, C3Attributes.outdoor_temperature: { "type": Platform.SENSOR, "name": "Outdoor Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, 0xCA: { "name": "Refrigerator", @@ -1048,112 +1023,112 @@ "type": Platform.BINARY_SENSOR, "name": "Bar Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, CAAttributes.bar_door_overtime: { "type": Platform.BINARY_SENSOR, "name": "Bar Door Overtime", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, CAAttributes.flex_zone_door: { "type": Platform.BINARY_SENSOR, "name": "Flex Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, CAAttributes.flex_zone_door_overtime: { "type": Platform.BINARY_SENSOR, "name": "Flex Zone Door", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, CAAttributes.freezer_door: { "type": Platform.BINARY_SENSOR, "name": "Freezer Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, CAAttributes.freezer_door_overtime: { "type": Platform.BINARY_SENSOR, "name": "Freezer Door Overtime", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, CAAttributes.refrigerator_door: { "type": Platform.BINARY_SENSOR, "name": "Refrigerator Door", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, CAAttributes.refrigerator_door_overtime: { "type": Platform.BINARY_SENSOR, "name": "Refrigerator Door Overtime", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, CAAttributes.flex_zone_actual_temp: { "type": Platform.SENSOR, "name": "Flex Zone Actual Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CAAttributes.flex_zone_setting_temp: { "type": Platform.SENSOR, "name": "Flex Zone Setting Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CAAttributes.freezer_actual_temp: { "type": Platform.SENSOR, "name": "Freezer Actual Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CAAttributes.freezer_setting_temp: { "type": Platform.SENSOR, "name": "Freezer Setting Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CAAttributes.energy_consumption: { "type": Platform.SENSOR, "name": "Energy Consumption", "device_class": SensorDeviceClass.ENERGY, "unit": UnitOfEnergy.KILO_WATT_HOUR, - "state_class": SensorStateClass.TOTAL_INCREASING + "state_class": SensorStateClass.TOTAL_INCREASING, }, CAAttributes.refrigerator_actual_temp: { "type": Platform.SENSOR, "name": "Refrigerator Actual Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CAAttributes.refrigerator_setting_temp: { "type": Platform.SENSOR, "name": "Refrigerator Setting Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CAAttributes.right_flex_zone_actual_temp: { "type": Platform.SENSOR, "name": "Right Flex Zone Actual Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CAAttributes.right_flex_zone_setting_temp: { "type": Platform.SENSOR, "name": "Right Flex Zone Setting Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, }, }, @@ -1163,46 +1138,46 @@ "climate": { "type": Platform.CLIMATE, "icon": "hass:air-conditioner", - "default": True + "default": True, }, CCAttributes.aux_heating: { "type": Platform.SWITCH, "name": "Aux Heating", - "icon": "mdi:heat-wave" + "icon": "mdi:heat-wave", }, CCAttributes.eco_mode: { "type": Platform.SWITCH, "name": "ECO Mode", - "icon": "mdi:leaf-circle" + "icon": "mdi:leaf-circle", }, CCAttributes.night_light: { "type": Platform.SWITCH, "name": "Night Light", - "icon": "mdi:lightbulb" + "icon": "mdi:lightbulb", }, CCAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, CCAttributes.sleep_mode: { "type": Platform.SWITCH, "name": "Sleep Mode", - "icon": "mdi:power-sleep" + "icon": "mdi:power-sleep", }, CCAttributes.swing: { "type": Platform.SWITCH, "name": "Swing", - "icon": "mdi:arrow-split-horizontal" + "icon": "mdi:arrow-split-horizontal", }, CCAttributes.indoor_temperature: { "type": Platform.SENSOR, "name": "Indoor Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, - } + }, }, 0xCD: { "name": "Heat Pump Water Heater", @@ -1210,132 +1185,125 @@ "water_heater": { "type": Platform.WATER_HEATER, "icon": "mdi:heat-pump", - "default": True + "default": True, }, CDAttributes.compressor_status: { "type": Platform.BINARY_SENSOR, "name": "Compressor Status", "icon": "mdi:drag", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, CDAttributes.compressor_temperature: { "type": Platform.SENSOR, "name": "Compressor Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CDAttributes.condenser_temperature: { "type": Platform.SENSOR, "name": "Condenser Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CDAttributes.outdoor_temperature: { "type": Platform.SENSOR, "name": "Outdoor Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CDAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" - } - } + "icon": "mdi:power", + }, + }, }, 0xCE: { "name": "Fresh Air Appliance", "entities": { - "fan": { - "type": Platform.FAN, - "icon": "mdi:fan", - "default": True - }, + "fan": {"type": Platform.FAN, "icon": "mdi:fan", "default": True}, CEAttributes.filter_cleaning_reminder: { "type": Platform.BINARY_SENSOR, "name": "Filter Cleaning Reminder", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, CEAttributes.filter_change_reminder: { "type": Platform.BINARY_SENSOR, "name": "Filter Change Reminder", "icon": "mdi:alert-circle", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, CEAttributes.current_humidity: { "type": Platform.SENSOR, "name": "Current Humidity", "device_class": SensorDeviceClass.HUMIDITY, "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CEAttributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CEAttributes.co2: { "type": Platform.SENSOR, "name": "Carbon Dioxide", "device_class": SensorDeviceClass.CO2, "unit": CONCENTRATION_PARTS_PER_MILLION, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CEAttributes.hcho: { "type": Platform.SENSOR, "name": "Methanal", "icon": "mdi:molecule", "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, CEAttributes.pm25: { "type": Platform.SENSOR, "name": "PM 2.5", "device_class": SensorDeviceClass.PM25, "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - "state_class": SensorStateClass.MEASUREMENT - }, - CEAttributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" + "state_class": SensorStateClass.MEASUREMENT, }, + CEAttributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, CEAttributes.aux_heating: { "type": Platform.SWITCH, "name": "Aux Heating", - "icon": "mdi:heat-wave" + "icon": "mdi:heat-wave", }, CEAttributes.eco_mode: { "type": Platform.SWITCH, "name": "ECO Mode", - "icon": "mdi:leaf-circle" + "icon": "mdi:leaf-circle", }, CEAttributes.link_to_ac: { "type": Platform.SWITCH, "name": "Link to AC", - "icon": "mdi:link" + "icon": "mdi:link", }, CEAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, CEAttributes.powerful_purify: { "type": Platform.SWITCH, "name": "Powerful Purification", - "icon": "mdi:turbine" + "icon": "mdi:turbine", }, CEAttributes.sleep_mode: { "type": Platform.SWITCH, "name": "Sleep Mode", - "icon": "mdi:power-sleep" + "icon": "mdi:power-sleep", }, - } + }, }, 0xCF: { "name": "Heat Pump", @@ -1343,26 +1311,26 @@ "climate": { "type": Platform.CLIMATE, "icon": "hass:air-conditioner", - "default": True + "default": True, }, CFAttributes.aux_heating: { "type": Platform.SWITCH, "name": "Aux Heating", - "icon": "mdi:heat-wave" + "icon": "mdi:heat-wave", }, CFAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, CFAttributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, - } + }, }, 0xDA: { "name": "Top Load Washer", @@ -1372,90 +1340,90 @@ "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, DAAttributes.wash_time: { "type": Platform.SENSOR, "name": "wash time", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, DAAttributes.soak_time: { "type": Platform.SENSOR, "name": "soak time", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, DAAttributes.dehydration_time: { "type": Platform.SENSOR, "name": "dehydration time", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, DAAttributes.dehydration_speed: { "type": Platform.SENSOR, "name": "dehydration speed", - "icon": "mdi:speedometer" + "icon": "mdi:speedometer", }, DAAttributes.error_code: { "type": Platform.SENSOR, "name": "error code", - "icon": "mdi:washing-machine-alert" + "icon": "mdi:washing-machine-alert", }, DAAttributes.rinse_count: { "type": Platform.SENSOR, "name": "rinse count", - "icon": "mdi:water-sync" + "icon": "mdi:water-sync", }, DAAttributes.rinse_level: { "type": Platform.SENSOR, "name": "rinse level", - "icon": "mdi:hydraulic-oil-level" + "icon": "mdi:hydraulic-oil-level", }, DAAttributes.wash_level: { "type": Platform.SENSOR, "name": "rinse count", - "icon": "mdi:hydraulic-oil-level" + "icon": "mdi:hydraulic-oil-level", }, DAAttributes.wash_strength: { "type": Platform.SENSOR, "name": "wash strength", - "icon": "mdi:network-strength-4-cog" + "icon": "mdi:network-strength-4-cog", }, DAAttributes.softener: { "type": Platform.SENSOR, "name": "softener", - "icon": "mdi:tshirt-crew" + "icon": "mdi:tshirt-crew", }, DAAttributes.detergent: { "type": Platform.SENSOR, "name": "detergent", - "icon": "mdi:spray-bottle" + "icon": "mdi:spray-bottle", }, DAAttributes.program: { "type": Platform.SENSOR, "name": "Program", - "icon": "mdi:progress-wrench" + "icon": "mdi:progress-wrench", }, DAAttributes.progress: { "type": Platform.SENSOR, "name": "Progress", - "icon": "mdi:rotate-360" + "icon": "mdi:rotate-360", }, DAAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, DAAttributes.start: { "type": Platform.SWITCH, "name": "Start", - "icon": "mdi:motion-play-outline" + "icon": "mdi:motion-play-outline", }, - } + }, }, 0xDB: { "name": "Front Load Washer", @@ -1465,24 +1433,24 @@ "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, DBAttributes.progress: { "type": Platform.SENSOR, "name": "Progress", - "icon": "mdi:rotate-360" + "icon": "mdi:rotate-360", }, DBAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, DBAttributes.start: { "type": Platform.SWITCH, "name": "Start", - "icon": "mdi:motion-play-outline" + "icon": "mdi:motion-play-outline", }, - } + }, }, 0xDC: { "name": "Clothes Dryer", @@ -1492,24 +1460,24 @@ "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, DCAttributes.progress: { "type": Platform.SENSOR, "name": "Progress", - "icon": "mdi:rotate-360" + "icon": "mdi:rotate-360", }, DCAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, DCAttributes.start: { "type": Platform.SWITCH, "name": "Start", - "icon": "mdi:motion-play-outline" + "icon": "mdi:motion-play-outline", }, - } + }, }, 0xE1: { "name": "Dishwasher", @@ -1518,81 +1486,78 @@ "type": Platform.BINARY_SENSOR, "name": "Door", "icon": "mdi:box-shadow", - "device_class": BinarySensorDeviceClass.DOOR + "device_class": BinarySensorDeviceClass.DOOR, }, E1Attributes.rinse_aid: { "type": Platform.BINARY_SENSOR, "name": "Rinse Aid Shortage", "icon": "mdi:bottle-tonic", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, E1Attributes.salt: { "type": Platform.BINARY_SENSOR, "name": "Salt Shortage", "icon": "mdi:drag", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, E1Attributes.humidity: { "type": Platform.SENSOR, "name": "Humidity", "device_class": SensorDeviceClass.HUMIDITY, "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E1Attributes.progress: { "type": Platform.SENSOR, "name": "Progress", - "icon": "mdi:rotate-360" + "icon": "mdi:rotate-360", }, E1Attributes.status: { "type": Platform.SENSOR, "name": "Status", - "icon": "mdi:information" + "icon": "mdi:information", }, E1Attributes.storage_remaining: { "type": Platform.SENSOR, "name": "Storage Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.HOURS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E1Attributes.temperature: { "type": Platform.SENSOR, "name": "Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E1Attributes.time_remaining: { "type": Platform.SENSOR, "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT - }, - E1Attributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" + "state_class": SensorStateClass.MEASUREMENT, }, + E1Attributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, E1Attributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, E1Attributes.storage: { "type": Platform.SWITCH, "name": "Storage", - "icon": "mdi:repeat-variant" + "icon": "mdi:repeat-variant", }, E1Attributes.mode: { "type": Platform.SENSOR, "name": "Working Mode", - "icon": "mdi:dishwasher" + "icon": "mdi:dishwasher", }, E1Attributes.error_code: { "type": Platform.SENSOR, "name": "Error Code", - "icon": "mdi:alert-box" + "icon": "mdi:alert-box", }, E1Attributes.softwater: { "type": Platform.SENSOR, @@ -1602,9 +1567,9 @@ E1Attributes.bright: { "type": Platform.SENSOR, "name": "Bright Level", - "icon": "mdi:star-four-points" - } - } + "icon": "mdi:star-four-points", + }, + }, }, 0xE2: { "name": "Electric Water Heater", @@ -1612,70 +1577,70 @@ "water_heater": { "type": Platform.WATER_HEATER, "icon": "mdi:meter-electric-outline", - "default": True + "default": True, }, E2Attributes.heating: { "type": Platform.BINARY_SENSOR, "name": "Heating", "icon": "mdi:heat-wave", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, E2Attributes.keep_warm: { "type": Platform.BINARY_SENSOR, "name": "Keep Warm", "icon": "mdi:menu", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, E2Attributes.protection: { "type": Platform.BINARY_SENSOR, "name": "Protection", "icon": "mdi:shield-check", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, E2Attributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E2Attributes.heating_time_remaining: { "type": Platform.SENSOR, "name": "Heating Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E2Attributes.heating_power: { "type": Platform.SENSOR, "name": "Heating Power", "device_class": SensorDeviceClass.POWER, "unit": UnitOfPower.WATT, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E2Attributes.water_consumption: { "type": Platform.SENSOR, "name": "Water Consumption", "icon": "mdi:water", "unit": UnitOfVolume.LITERS, - "state_class": SensorStateClass.TOTAL_INCREASING + "state_class": SensorStateClass.TOTAL_INCREASING, }, E2Attributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, E2Attributes.variable_heating: { "type": Platform.SWITCH, "name": "Variable Heating", - "icon": "mdi:waves" + "icon": "mdi:waves", }, E2Attributes.whole_tank_heating: { "type": Platform.SWITCH, "name": "Whole Tank Heating", - "icon": "mdi:restore" - } - } + "icon": "mdi:restore", + }, + }, }, 0xE3: { "name": "Gas Water Heater", @@ -1683,48 +1648,48 @@ "water_heater": { "type": Platform.WATER_HEATER, "icon": "mdi:meter-gas", - "default": True + "default": True, }, E3Attributes.burning_state: { "type": Platform.BINARY_SENSOR, "name": "Burning State", "icon": "mdi:fire", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, E3Attributes.protection: { "type": Platform.BINARY_SENSOR, "name": "Protection", "icon": "mdi:shield-check", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, E3Attributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E3Attributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, E3Attributes.smart_volume: { "type": Platform.SWITCH, "name": "Smart Volume", - "icon": "mdi:recycle" + "icon": "mdi:recycle", }, E3Attributes.zero_cold_water: { "type": Platform.SWITCH, "name": "Zero Cold Water", - "icon": "mdi:restore" + "icon": "mdi:restore", }, E3Attributes.zero_cold_pulse: { "type": Platform.SWITCH, "name": "Zero Cold Water (Pulse)", - "icon": "mdi:restore-alert" + "icon": "mdi:restore-alert", }, - } + }, }, 0xE6: { "name": "Gas Boilers", @@ -1734,52 +1699,52 @@ "icon": "mdi:meter-gas", "name": "Heating", "use": 0, - "default": True + "default": True, }, "water_heater_bathing": { "type": Platform.WATER_HEATER, "icon": "mdi:meter-gas", "name": "Bathing", "use": 1, - "default": True + "default": True, }, E6Attributes.heating_working: { "type": Platform.BINARY_SENSOR, "name": "Heating Working Status", "icon": "mdi:fire", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, E6Attributes.bathing_working: { "type": Platform.BINARY_SENSOR, "name": "Bathing Working Status", "icon": "mdi:fire", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, E6Attributes.heating_leaving_temperature: { "type": Platform.SENSOR, "name": "Heating Leaving Water Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E6Attributes.bathing_leaving_temperature: { "type": Platform.SENSOR, "name": "Bathing Leaving Water Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E6Attributes.main_power: { "type": Platform.SWITCH, "name": "Main Power", - "icon": "mdi:power" + "icon": "mdi:power", }, E6Attributes.heating_power: { "type": Platform.SWITCH, "name": "Heating Power", - "icon": "mdi:heating-coil" - } - } + "icon": "mdi:heating-coil", + }, + }, }, 0xE8: { "name": "Electric Slow Cooker", @@ -1793,50 +1758,49 @@ "type": Platform.BINARY_SENSOR, "name": "Water Shortage", "icon": "mdi:drag", - "device_class": BinarySensorDeviceClass.PROBLEM + "device_class": BinarySensorDeviceClass.PROBLEM, }, E8Attributes.status: { "type": Platform.SENSOR, "name": "Status", - "icon": "mdi:information" + "icon": "mdi:information", }, E8Attributes.time_remaining: { "type": Platform.SENSOR, "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E8Attributes.keep_warm_remaining: { "type": Platform.SENSOR, "name": "Keep Warm Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E8Attributes.working_time: { "type": Platform.SENSOR, "name": "Working Time", "icon": "mdi:progress-clock", "unit": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E8Attributes.target_temperature: { "type": Platform.SENSOR, "name": "Target Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, E8Attributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, - - } + }, }, 0xEA: { "name": "Electric Rice Cooker", @@ -1845,53 +1809,53 @@ "type": Platform.BINARY_SENSOR, "name": "Cooking", "icon": "mdi:fire", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, EAAttributes.keep_warm: { "type": Platform.BINARY_SENSOR, "name": "Keep Warm", "icon": "mdi:menu", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, EAAttributes.bottom_temperature: { "type": Platform.SENSOR, "name": "Bottom Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EAAttributes.keep_warm_time: { "type": Platform.SENSOR, "name": "Keep Warm Time", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EAAttributes.mode: { "type": Platform.SENSOR, "name": "Mode", - "icon": "mdi:orbit" + "icon": "mdi:orbit", }, EAAttributes.progress: { "type": Platform.SENSOR, "name": "Progress", - "icon": "mdi:rotate-360" + "icon": "mdi:rotate-360", }, EAAttributes.time_remaining: { "type": Platform.SENSOR, "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EAAttributes.top_temperature: { "type": Platform.SENSOR, "name": "Top Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, - } + }, }, 0xEC: { "name": "Electric Pressure Cooker", @@ -1900,172 +1864,162 @@ "type": Platform.BINARY_SENSOR, "name": "Cooking", "icon": "mdi:fire", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, ECAttributes.with_pressure: { "type": Platform.BINARY_SENSOR, "name": "With Pressure", "icon": "mdi:information", - "device_class": BinarySensorDeviceClass.RUNNING + "device_class": BinarySensorDeviceClass.RUNNING, }, ECAttributes.bottom_temperature: { "type": Platform.SENSOR, "name": "Bottom Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, ECAttributes.keep_warm_time: { "type": Platform.SENSOR, "name": "Keep Warm Time", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, ECAttributes.mode: { "type": Platform.SENSOR, "name": "Mode", - "icon": "mdi:orbit" + "icon": "mdi:orbit", }, ECAttributes.progress: { "type": Platform.SENSOR, "name": "Progress", - "icon": "mdi:rotate-360" + "icon": "mdi:rotate-360", }, ECAttributes.time_remaining: { "type": Platform.SENSOR, "name": "Time Remaining", "icon": "mdi:progress-clock", "unit": UnitOfTime.MINUTES, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, ECAttributes.top_temperature: { "type": Platform.SENSOR, "name": "Top Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, - } + }, }, 0xED: { "name": "Water Drinking Appliance", "entities": { - EDAttributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" - }, + EDAttributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, EDAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, EDAttributes.filter1: { "type": Platform.SENSOR, "name": "Filter1 Available Days", "icon": "mdi:air-filter", "unit": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EDAttributes.filter2: { "type": Platform.SENSOR, "name": "Filter2 Available Days", "icon": "mdi:air-filter", "unit": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EDAttributes.filter3: { "type": Platform.SENSOR, "name": "Filter3 Available Days", "icon": "mdi:air-filter", "unit": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EDAttributes.life1: { "type": Platform.SENSOR, "name": "Filter1 Life Level", "icon": "mdi:percent", "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EDAttributes.life2: { "type": Platform.SENSOR, "name": "Filter2 Life Level", "icon": "mdi:percent", "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EDAttributes.life3: { "type": Platform.SENSOR, "name": "Filter3 Life Level", "icon": "mdi:percent", "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EDAttributes.in_tds: { "type": Platform.SENSOR, "name": "In TDS", "icon": "mdi:water", "unit": CONCENTRATION_PARTS_PER_MILLION, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EDAttributes.out_tds: { "type": Platform.SENSOR, "name": "Out TDS", "icon": "mdi:water-plus", "unit": CONCENTRATION_PARTS_PER_MILLION, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, EDAttributes.water_consumption: { "type": Platform.SENSOR, "name": "Water Consumption", "icon": "mdi:water-pump", "unit": UnitOfVolume.LITERS, - "state_class": SensorStateClass.TOTAL_INCREASING - } - } + "state_class": SensorStateClass.TOTAL_INCREASING, + }, + }, }, 0xFA: { "name": "Fan", "entities": { - "fan": { - "type": Platform.FAN, - "icon": "mdi:fan", - "default": True - }, + "fan": {"type": Platform.FAN, "icon": "mdi:fan", "default": True}, FAAttributes.oscillation_mode: { "type": Platform.SELECT, "name": "Oscillation Mode", "options": "oscillation_modes", - "icon": "mdi:swap-horizontal-variant" + "icon": "mdi:swap-horizontal-variant", }, FAAttributes.oscillation_angle: { "type": Platform.SELECT, "name": "Oscillation Angle", "options": "oscillation_angles", - "icon": "mdi:pan-horizontal" + "icon": "mdi:pan-horizontal", }, FAAttributes.tilting_angle: { "type": Platform.SELECT, "name": "Tilting Angle", "options": "tilting_angles", - "icon": "mdi:pan-vertical" - }, - FAAttributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" + "icon": "mdi:pan-vertical", }, + FAAttributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, FAAttributes.oscillate: { "type": Platform.SWITCH, "name": "Oscillate", - "icon": "mdi:swap-horizontal-bold" + "icon": "mdi:swap-horizontal-bold", }, FAAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, - } + }, }, 0xFB: { "name": "Electric Heater", @@ -2073,121 +2027,115 @@ "climate": { "type": Platform.CLIMATE, "icon": "mdi:air-conditioner", - "default": True - }, - FBAttributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" + "default": True, }, + FBAttributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, FBAttributes.heating_level: { "type": Platform.NUMBER, "name": "Heating Level", "icon": "mdi:fire", "max": 10, "min": 1, - "step": 1 + "step": 1, }, FBAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, FBAttributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, - } + }, }, 0xFC: { "name": "Air Purifier", "entities": { - FCAttributes.child_lock: { - "type": Platform.LOCK, - "name": "Child Lock" - }, + FCAttributes.child_lock: {"type": Platform.LOCK, "name": "Child Lock"}, FCAttributes.anion: { "type": Platform.SWITCH, "name": "Anion", - "icon": "mdi:vanish" + "icon": "mdi:vanish", }, FCAttributes.prompt_tone: { "type": Platform.SWITCH, "name": "Prompt Tone", - "icon": "mdi:bell" + "icon": "mdi:bell", }, FCAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, FCAttributes.standby: { "type": Platform.SWITCH, "name": "Standby", - "icon": "mdi:smoke-detector-variant" + "icon": "mdi:smoke-detector-variant", }, FCAttributes.detect_mode: { "type": Platform.SELECT, "name": "Detect Mode", "options": "detect_modes", - "icon": "mdi:smoke-detector-variant" + "icon": "mdi:smoke-detector-variant", }, FCAttributes.mode: { "type": Platform.SELECT, "name": "Mode", "options": "modes", - "icon": "mdi:rotate-360" + "icon": "mdi:rotate-360", }, FCAttributes.fan_speed: { "type": Platform.SELECT, "name": "Fan Speed", "options": "fan_speeds", - "icon": "mdi:fan" + "icon": "mdi:fan", }, FCAttributes.screen_display: { "type": Platform.SELECT, "name": "Screen Display", "options": "screen_displays", - "icon": "mdi:television-ambient-light" + "icon": "mdi:television-ambient-light", }, FCAttributes.pm25: { "type": Platform.SENSOR, "name": "PM 2.5", "device_class": SensorDeviceClass.PM25, "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, FCAttributes.tvoc: { "type": Platform.SENSOR, "name": "TVOC", "icon": "mdi:heat-wave", "unit": CONCENTRATION_PARTS_PER_MILLION, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, FCAttributes.hcho: { "type": Platform.SENSOR, "name": "Methanal", "icon": "mdi:molecule", "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, FCAttributes.filter1_life: { "type": Platform.SENSOR, "name": "Filter1 Life Level", "icon": "mdi:air-filter", "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, FCAttributes.filter2_life: { "type": Platform.SENSOR, "name": "Filter2 Life Level", "icon": "mdi:air-filter", "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, 0xFD: { "name": "Humidifier", @@ -2195,49 +2143,49 @@ Platform.HUMIDIFIER: { "type": Platform.HUMIDIFIER, "icon": "mdi:air-humidifier", - "default": True + "default": True, }, FDAttributes.disinfect: { "type": Platform.SWITCH, "name": "Disinfect", - "icon": "mdi:water-plus-outline" + "icon": "mdi:water-plus-outline", }, FDAttributes.prompt_tone: { "type": Platform.SWITCH, "name": "Prompt Tone", - "icon": "mdi:bell" + "icon": "mdi:bell", }, FDAttributes.power: { "type": Platform.SWITCH, "name": "Power", - "icon": "mdi:power" + "icon": "mdi:power", }, FDAttributes.fan_speed: { "type": Platform.SELECT, "name": "Fan Speed", "options": "fan_speeds", - "icon": "mdi:fan" + "icon": "mdi:fan", }, FDAttributes.screen_display: { "type": Platform.SELECT, "name": "Screen Display", "options": "screen_displays", - "icon": "mdi:television-ambient-light" + "icon": "mdi:television-ambient-light", }, FDAttributes.current_humidity: { "type": Platform.SENSOR, "name": "Current Humidity", "device_class": SensorDeviceClass.HUMIDITY, "unit": PERCENTAGE, - "state_class": SensorStateClass.MEASUREMENT + "state_class": SensorStateClass.MEASUREMENT, }, FDAttributes.current_temperature: { "type": Platform.SENSOR, "name": "Current Temperature", "device_class": SensorDeviceClass.TEMPERATURE, "unit": UnitOfTemperature.CELSIUS, - "state_class": SensorStateClass.MEASUREMENT - } - } + "state_class": SensorStateClass.MEASUREMENT, + }, + }, }, } diff --git a/custom_components/midea_ac_lan/midea_entity.py b/custom_components/midea_ac_lan/midea_entity.py index d418b928..678c13e7 100644 --- a/custom_components/midea_ac_lan/midea_entity.py +++ b/custom_components/midea_ac_lan/midea_entity.py @@ -1,8 +1,10 @@ +import logging + from homeassistant.helpers.entity import Entity + from .const import DOMAIN from .midea_devices import MIDEA_DEVICES -import logging _LOGGER = logging.getLogger(__name__) @@ -25,10 +27,10 @@ def device_info(self): return { "manufacturer": "Midea", "model": f"{MIDEA_DEVICES[self._device.device_type]['name']} " - f"{self._device.model}" - f" ({self._device.subtype})", + f"{self._device.model}" + f" ({self._device.subtype})", "identifiers": {(DOMAIN, self._device.device_id)}, - "name": self._device_name + "name": self._device_name, } @property @@ -41,8 +43,11 @@ def should_poll(self): @property def name(self): - return f"{self._device_name} {self._config.get('name')}" if "name" in self._config \ + return ( + f"{self._device_name} {self._config.get('name')}" + if "name" in self._config else self._device_name + ) @property def available(self): @@ -57,4 +62,6 @@ def update_state(self, status): try: self.schedule_update_ha_state() except Exception as e: - _LOGGER.debug(f"Entity {self.entity_id} update_state {repr(e)}, status = {status}") + _LOGGER.debug( + f"Entity {self.entity_id} update_state {repr(e)}, status = {status}" + ) diff --git a/custom_components/midea_ac_lan/number.py b/custom_components/midea_ac_lan/number.py index 987e78eb..dbe8dd19 100644 --- a/custom_components/midea_ac_lan/number.py +++ b/custom_components/midea_ac_lan/number.py @@ -1,23 +1,15 @@ -from .midea_entity import MideaEntity -from .midea_devices import MIDEA_DEVICES from homeassistant.components.number import NumberEntity -from homeassistant.const import ( - Platform, - CONF_DEVICE_ID, - CONF_SWITCHES -) -from .const import ( - DOMAIN, - DEVICES, -) +from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform + +from .const import DEVICES, DOMAIN +from .midea_devices import MIDEA_DEVICES +from .midea_entity import MideaEntity async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) numbers = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): if config["type"] == Platform.NUMBER and entity_key in extra_switches: @@ -35,24 +27,39 @@ def __init__(self, device, entity_key: str): @property def native_min_value(self): - return self._min_value if isinstance(self._min_value, int) else \ - self._device.get_attribute(attr=self._min_value) \ - if self._device.get_attribute(attr=self._min_value) else \ - getattr(self._device, self._min_value) + return ( + self._min_value + if isinstance(self._min_value, int) + else ( + self._device.get_attribute(attr=self._min_value) + if self._device.get_attribute(attr=self._min_value) + else getattr(self._device, self._min_value) + ) + ) @property def native_max_value(self): - return self._max_value if isinstance(self._max_value, int) else \ - self._device.get_attribute(attr=self._max_value) \ - if self._device.get_attribute(attr=self._max_value) else \ - getattr(self._device, self._max_value) + return ( + self._max_value + if isinstance(self._max_value, int) + else ( + self._device.get_attribute(attr=self._max_value) + if self._device.get_attribute(attr=self._max_value) + else getattr(self._device, self._max_value) + ) + ) @property def native_step(self): - return self._step_value if isinstance(self._step_value, int) else \ - self._device.get_attribute(attr=self._step_value) \ - if self._device.get_attribute(attr=self._step_value) else \ - getattr(self._device, self._step_value) + return ( + self._step_value + if isinstance(self._step_value, int) + else ( + self._device.get_attribute(attr=self._step_value) + if self._device.get_attribute(attr=self._step_value) + else getattr(self._device, self._step_value) + ) + ) @property def native_value(self): diff --git a/custom_components/midea_ac_lan/select.py b/custom_components/midea_ac_lan/select.py index 3e44b022..67cf1aec 100644 --- a/custom_components/midea_ac_lan/select.py +++ b/custom_components/midea_ac_lan/select.py @@ -1,23 +1,15 @@ -from .midea_entity import MideaEntity -from .midea_devices import MIDEA_DEVICES from homeassistant.components.select import SelectEntity -from homeassistant.const import ( - Platform, - CONF_DEVICE_ID, - CONF_SWITCHES -) -from .const import ( - DOMAIN, - DEVICES, -) +from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform + +from .const import DEVICES, DOMAIN +from .midea_devices import MIDEA_DEVICES +from .midea_entity import MideaEntity async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) selects = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): if config["type"] == Platform.SELECT and entity_key in extra_switches: diff --git a/custom_components/midea_ac_lan/sensor.py b/custom_components/midea_ac_lan/sensor.py index 956bcb90..78221898 100644 --- a/custom_components/midea_ac_lan/sensor.py +++ b/custom_components/midea_ac_lan/sensor.py @@ -1,23 +1,15 @@ -from .midea_entity import MideaEntity -from .midea_devices import MIDEA_DEVICES from homeassistant.components.sensor import SensorEntity -from homeassistant.const import ( - Platform, - CONF_DEVICE_ID, - CONF_SENSORS -) -from .const import ( - DOMAIN, - DEVICES -) +from homeassistant.const import CONF_DEVICE_ID, CONF_SENSORS, Platform + +from .const import DEVICES, DOMAIN +from .midea_devices import MIDEA_DEVICES +from .midea_entity import MideaEntity async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_sensors = config_entry.options.get( - CONF_SENSORS, [] - ) + extra_sensors = config_entry.options.get(CONF_SENSORS, []) sensors = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): if config["type"] == Platform.SENSOR and entity_key in extra_sensors: diff --git a/custom_components/midea_ac_lan/switch.py b/custom_components/midea_ac_lan/switch.py index 6c91cadd..c6a24cff 100644 --- a/custom_components/midea_ac_lan/switch.py +++ b/custom_components/midea_ac_lan/switch.py @@ -1,23 +1,15 @@ -from .midea_entity import MideaEntity -from .midea_devices import MIDEA_DEVICES +from homeassistant.const import CONF_DEVICE_ID, CONF_SWITCHES, Platform from homeassistant.helpers.entity import ToggleEntity -from homeassistant.const import ( - Platform, - CONF_DEVICE_ID, - CONF_SWITCHES -) -from .const import ( - DOMAIN, - DEVICES, -) + +from .const import DEVICES, DOMAIN +from .midea_devices import MIDEA_DEVICES +from .midea_entity import MideaEntity async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) switches = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): if config["type"] == Platform.SWITCH and entity_key in extra_switches: diff --git a/custom_components/midea_ac_lan/water_heater.py b/custom_components/midea_ac_lan/water_heater.py index 83fb04db..20d3959c 100644 --- a/custom_components/midea_ac_lan/water_heater.py +++ b/custom_components/midea_ac_lan/water_heater.py @@ -1,31 +1,29 @@ import functools as ft +import logging from homeassistant.components.water_heater import ( WaterHeaterEntity, WaterHeaterEntityFeature, ) from homeassistant.const import ( - Platform, - UnitOfTemperature, - PRECISION_WHOLE, - PRECISION_HALVES, ATTR_TEMPERATURE, CONF_DEVICE_ID, CONF_SWITCHES, - STATE_ON, + PRECISION_HALVES, + PRECISION_WHOLE, STATE_OFF, + STATE_ON, + Platform, + UnitOfTemperature, ) -from .const import ( - DOMAIN, - DEVICES -) -from .midea.devices.e6.device import DeviceAttributes as E6Attributes + +from .const import DEVICES, DOMAIN from .midea.devices.c3.device import DeviceAttributes as C3Attributes from .midea.devices.cd.device import DeviceAttributes as CDAttributes +from .midea.devices.e6.device import DeviceAttributes as E6Attributes from .midea_devices import MIDEA_DEVICES from .midea_entity import MideaEntity -import logging _LOGGER = logging.getLogger(__name__) E2_TEMPERATURE_MAX = 75 @@ -37,12 +35,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) - extra_switches = config_entry.options.get( - CONF_SWITCHES, [] - ) + extra_switches = config_entry.options.get(CONF_SWITCHES, []) devs = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): - if config["type"] == Platform.WATER_HEATER and (config.get("default") or entity_key in extra_switches): + if config["type"] == Platform.WATER_HEATER and ( + config.get("default") or entity_key in extra_switches + ): if device.device_type == 0xE2: devs.append(MideaE2WaterHeater(device, entity_key)) elif device.device_type == 0xE3: @@ -98,7 +96,11 @@ def temperature_unit(self): @property def current_operation(self): - return self._device.get_attribute("mode") if self._device.get_attribute("power") else STATE_OFF + return ( + self._device.get_attribute("mode") + if self._device.get_attribute("power") + else STATE_OFF + ) @property def current_temperature(self): @@ -137,7 +139,9 @@ def update_state(self, status): try: self.schedule_update_ha_state() except Exception as e: - _LOGGER.debug(f"Entity {self.entity_id} update_state {repr(e)}, status = {status}") + _LOGGER.debug( + f"Entity {self.entity_id} update_state {repr(e)}, status = {status}" + ) class MideaE2WaterHeater(MideaWaterHeater): @@ -176,7 +180,11 @@ def __init__(self, device, entity_key): @property def state(self): - return STATE_ON if self._device.get_attribute(C3Attributes.dhw_power) else STATE_OFF + return ( + STATE_ON + if self._device.get_attribute(C3Attributes.dhw_power) + else STATE_OFF + ) @property def current_temperature(self): @@ -225,20 +233,28 @@ def __init__(self, device, entity_key, use): super().__init__(device, entity_key) self._use = use self._power_attr = MideaE6WaterHeater._powers[self._use] - self._current_temperature_attr = MideaE6WaterHeater._current_temperatures[self._use] - self._target_temperature_attr = MideaE6WaterHeater._target_temperatures[self._use] + self._current_temperature_attr = MideaE6WaterHeater._current_temperatures[ + self._use + ] + self._target_temperature_attr = MideaE6WaterHeater._target_temperatures[ + self._use + ] @property def state(self): if self._use == 0: # for heating - return STATE_ON if \ - self._device.get_attribute(E6Attributes.main_power) and \ - self._device.get_attribute(E6Attributes.heating_power) \ + return ( + STATE_ON + if self._device.get_attribute(E6Attributes.main_power) + and self._device.get_attribute(E6Attributes.heating_power) else STATE_OFF + ) else: # for bathing - return STATE_ON if \ - self._device.get_attribute(E6Attributes.main_power) \ + return ( + STATE_ON + if self._device.get_attribute(E6Attributes.main_power) else STATE_OFF + ) @property def current_temperature(self): @@ -275,8 +291,10 @@ def __init__(self, device, entity_key): @property def supported_features(self): - return WaterHeaterEntityFeature.TARGET_TEMPERATURE | \ - WaterHeaterEntityFeature.OPERATION_MODE + return ( + WaterHeaterEntityFeature.TARGET_TEMPERATURE + | WaterHeaterEntityFeature.OPERATION_MODE + ) @property def min_temp(self):