diff --git a/custom_components/smartthinq_sensors/binary_sensor.py b/custom_components/smartthinq_sensors/binary_sensor.py index c04d2926..a5295fac 100644 --- a/custom_components/smartthinq_sensors/binary_sensor.py +++ b/custom_components/smartthinq_sensors/binary_sensor.py @@ -83,13 +83,6 @@ class ThinQBinarySensorEntityDescription(BinarySensorEntityDescription): icon_on="mdi:account-lock", entity_registry_enabled_default=False, ), - ThinQBinarySensorEntityDescription( - key=WashDeviceFeatures.DOORCLOSE, - name="Door close", - icon="mdi:alpha-o-box-outline", - icon_on="mdi:alpha-c-box", - entity_registry_enabled_default=False, - ), ThinQBinarySensorEntityDescription( key=WashDeviceFeatures.DOORLOCK, name="Door lock", diff --git a/custom_components/smartthinq_sensors/wideq/const.py b/custom_components/smartthinq_sensors/wideq/const.py index 3445c178..df479f35 100644 --- a/custom_components/smartthinq_sensors/wideq/const.py +++ b/custom_components/smartthinq_sensors/wideq/const.py @@ -121,7 +121,6 @@ class WashDeviceFeatures(StrEnum): DELAYSTART = "delay_start" DETERGENT = "detergent" DETERGENTLOW = "detergent_low" - DOORCLOSE = "door_close" DOORLOCK = "door_lock" DOOROPEN = "door_open" DRYLEVEL = "dry_level" diff --git a/custom_components/smartthinq_sensors/wideq/device.py b/custom_components/smartthinq_sensors/wideq/device.py index d7b47284..71820dde 100644 --- a/custom_components/smartthinq_sensors/wideq/device.py +++ b/custom_components/smartthinq_sensors/wideq/device.py @@ -1030,20 +1030,27 @@ def lookup_bit_enum(self, key): # exception because doorlock bit # is not inside the model enum - if key == "DoorLock" and ret_val is None: - if str_val == "1": + door_locks = {"DoorLock": "1", "doorLock": "DOORLOCK_ON"} + if ret_val is None and key in door_locks: + if self.is_info_v2 and not str_val: + return None + if str_val == door_locks[key]: return LABEL_BIT_ON return LABEL_BIT_OFF return ret_val - def lookup_bit(self, key): + def lookup_bit(self, key, invert=False): """Lookup bit value for a specific key of type enum.""" enum_val = self.lookup_bit_enum(key) if enum_val is None: return None - bit_val = LOCAL_LANG_PACK.get(enum_val, StateOptions.OFF) - if bit_val == StateOptions.ON: + bit_val = LOCAL_LANG_PACK.get(enum_val) + if not bit_val: + return StateOptions.OFF + if not invert: + return bit_val + if bit_val == StateOptions.OFF: return StateOptions.ON return StateOptions.OFF diff --git a/custom_components/smartthinq_sensors/wideq/devices/washerDryer.py b/custom_components/smartthinq_sensors/wideq/devices/washerDryer.py index c4c93902..2e15d890 100644 --- a/custom_components/smartthinq_sensors/wideq/devices/washerDryer.py +++ b/custom_components/smartthinq_sensors/wideq/devices/washerDryer.py @@ -42,7 +42,7 @@ WashDeviceFeatures.DAMPDRYBEEP: ["DampDryBeep", "dampDryBeep"], WashDeviceFeatures.DETERGENT: ["DetergentStatus", "ezDetergentState"], WashDeviceFeatures.DETERGENTLOW: ["DetergentRemaining", "detergentRemaining"], - WashDeviceFeatures.DOORCLOSE: ["DoorClose", "doorClose"], + WashDeviceFeatures.DOOROPEN: ["DoorClose", "doorClose"], WashDeviceFeatures.DOORLOCK: ["DoorLock", "doorLock"], WashDeviceFeatures.HANDIRON: ["HandIron", "handIron"], WashDeviceFeatures.MEDICRINSE: ["MedicRinse", "medicRinse"], @@ -57,6 +57,8 @@ WashDeviceFeatures.TURBOWASH: ["TurboWash", "turboWash"], } +INVERTED_BITS = [WashDeviceFeatures.DOOROPEN] + _LOGGER = logging.getLogger(__name__) @@ -651,7 +653,8 @@ def _update_bit_features(self): """Update features related to bit status.""" index = 1 if self.is_info_v2 else 0 for feature, keys in BIT_FEATURES.items(): - status = self.lookup_bit(self._getkeys(keys[index])) + invert = feature in INVERTED_BITS + status = self.lookup_bit(self._getkeys(keys[index]), invert) self._update_feature(feature, status, False) def _update_features(self): diff --git a/custom_components/smartthinq_sensors/wideq/model_info.py b/custom_components/smartthinq_sensors/wideq/model_info.py index c2978124..a9eaa4e0 100644 --- a/custom_components/smartthinq_sensors/wideq/model_info.py +++ b/custom_components/smartthinq_sensors/wideq/model_info.py @@ -6,6 +6,7 @@ from collections import namedtuple from copy import deepcopy import json +import logging from numbers import Number from .const import BIT_OFF, BIT_ON @@ -18,6 +19,8 @@ TYPE_REFERENCE = "reference" TYPE_STRING = "string" +_LOGGER = logging.getLogger(__name__) + EnumValue = namedtuple("EnumValue", ["options"]) RangeValue = namedtuple("RangeValue", ["min", "max", "step"]) @@ -178,6 +181,11 @@ def decode_monitor(self, data): def decode_snapshot(self, data, key): """Decode status data.""" + @property + def monitor_type(self) -> str | None: + """Return used monitor type.""" + return None + class ModelInfoV1(ModelInfo): """A description of a device model's capabilities for type V1.""" @@ -190,6 +198,7 @@ def is_valid_model_data(model_data: dict) -> bool: def __init__(self, data): """Initialize the class.""" super().__init__(data) + self._monitor_type = None self._bit_keys = {} @property @@ -344,15 +353,27 @@ def get_control_cmd(self, cmd_key, ctrl_key=None): control["cmd"] = ctrl_key return control + @property + def monitor_type(self) -> str | None: + """Return used monitor type.""" + if self._monitor_type is None: + self._monitor_type = self._data["Monitoring"]["type"] + return self._monitor_type + @property def byte_monitor_data(self): """Check that type of monitoring is BINARY(BYTE).""" - return self._data["Monitoring"]["type"] == "BINARY(BYTE)" + return self.monitor_type == "BINARY(BYTE)" @property def hex_monitor_data(self): """Check that type of monitoring is BINARY(HEX).""" - return self._data["Monitoring"]["type"] == "BINARY(HEX)" + return self.monitor_type == "BINARY(HEX)" + + @property + def xml_monitor_data(self): + """Check that type of monitoring is XML.""" + return self.monitor_type == "XML" def decode_monitor_byte(self, data): """Decode binary byte encoded status data.""" @@ -388,9 +409,23 @@ def decode_monitor_hex(self, data): return decoded @staticmethod - def decode_monitor_json(data): + def decode_monitor_xml(data): + """Decode a xml that encodes status data.""" + _LOGGER.warning("Received XML data from device: %s", data) + return None + + @staticmethod + def decode_monitor_json(data, mon_type): """Decode a bytestring that encodes JSON status data.""" - return json.loads(data.decode("utf8")) + try: + return json.loads(data.decode("utf8")) + except json.JSONDecodeError: + _LOGGER.warning( + "Received data with invalid format from device. Type: %s - Data: %s", + mon_type, + data, + ) + return None def decode_monitor(self, data): """Decode status data.""" @@ -399,7 +434,9 @@ def decode_monitor(self, data): return self.decode_monitor_byte(data) if self.hex_monitor_data: return self.decode_monitor_hex(data) - return self.decode_monitor_json(data) + if self.xml_monitor_data: + return self.decode_monitor_xml(data) + return self.decode_monitor_json(data, self.monitor_type) @staticmethod def _get_current_temp_key(key: str, data): @@ -421,7 +458,7 @@ def _get_current_temp_key(key: str, data): def decode_snapshot(self, data, key): """Decode status data.""" - if self._data["Monitoring"]["type"] != "THINQ2": + if self.monitor_type != "THINQ2": return {} if key and key not in data: