diff --git a/README.md b/README.md index 594e3032..cc70fef2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![](https://img.shields.io/badge/COMMUNITY-FORUM-success?style=for-the-badge)](https://community.home-assistant.io) # LG ThinQ Devices integration for HomeAssistant -A Homeassistant custom component to monitor LG Air Conditioner, Washer, Dryer, DishWasher, Refrigerator, Styler and Range +A Homeassistant custom component to monitor and control LG Air Conditioner, Washer, Dryer, DishWasher, Refrigerator, Styler and Range using ThinQ API based on [WideQ project][wideq].
**Important Version note:** diff --git a/custom_components/smartthinq_sensors/const.py b/custom_components/smartthinq_sensors/const.py index 16520dd2..726902d4 100644 --- a/custom_components/smartthinq_sensors/const.py +++ b/custom_components/smartthinq_sensors/const.py @@ -2,7 +2,7 @@ Support to interface with LGE ThinQ Devices. """ -__version__ = "0.8.10" +__version__ = "0.8.11" PROJECT_URL = "https://github.com/ollo69/ha-smartthinq-sensors/" ISSUE_URL = "{}issues".format(PROJECT_URL) diff --git a/custom_components/smartthinq_sensors/manifest.json b/custom_components/smartthinq_sensors/manifest.json index ca0daf4c..bc0bbad7 100644 --- a/custom_components/smartthinq_sensors/manifest.json +++ b/custom_components/smartthinq_sensors/manifest.json @@ -8,5 +8,5 @@ "requirements": ["pycountry>=20.7.3"], "config_flow": true, "iot_class": "cloud_polling", - "version": "0.8.10" + "version": "0.8.11" } diff --git a/custom_components/smartthinq_sensors/sensor.py b/custom_components/smartthinq_sensors/sensor.py index d37dbe26..5071797b 100644 --- a/custom_components/smartthinq_sensors/sensor.py +++ b/custom_components/smartthinq_sensors/sensor.py @@ -32,7 +32,9 @@ FEAT_COOKTOP_CENTER_STATE, FEAT_COOKTOP_RIGHT_FRONT_STATE, FEAT_COOKTOP_RIGHT_REAR_STATE, + FEAT_OVEN_LOWER_CURRENT_TEMP, FEAT_OVEN_LOWER_STATE, + FEAT_OVEN_UPPER_CURRENT_TEMP, FEAT_OVEN_UPPER_STATE, ) @@ -147,6 +149,12 @@ ATTR_VALUE_FN: lambda x: x._power_state, ATTR_ENABLED: True, }, + ATTR_CURRENT_COURSE: { + ATTR_MEASUREMENT_NAME: "Current Course", + ATTR_ICON: "mdi:pin-outline", + ATTR_VALUE_FN: lambda x: x._current_course, + ATTR_ENABLED: True, + }, FEAT_RUN_STATE: { ATTR_MEASUREMENT_NAME: "Run State", ATTR_ICON: DEFAULT_ICON, @@ -159,51 +167,51 @@ ATTR_VALUE_FEAT: FEAT_PROCESS_STATE, ATTR_ENABLED: True, }, - FEAT_PRE_STATE: { - ATTR_MEASUREMENT_NAME: "Pre State", - ATTR_ICON: DEFAULT_ICON, - ATTR_VALUE_FEAT: FEAT_PRE_STATE, - }, - FEAT_ERROR_MSG: { - ATTR_MEASUREMENT_NAME: "Error Message", - ATTR_ICON: "mdi:alert-circle-outline", - ATTR_VALUE_FEAT: FEAT_ERROR_MSG, - }, - FEAT_TUBCLEAN_COUNT: { - ATTR_MEASUREMENT_NAME: "Tube Clean Counter", - ATTR_ICON: DEFAULT_ICON, - ATTR_VALUE_FEAT: FEAT_TUBCLEAN_COUNT, - }, FEAT_SPINSPEED: { ATTR_MEASUREMENT_NAME: "Spin Speed", ATTR_ICON: "mdi:rotate-3d", ATTR_VALUE_FEAT: FEAT_SPINSPEED, + ATTR_ENABLED: True, }, FEAT_WATERTEMP: { ATTR_MEASUREMENT_NAME: "Water Temp", ATTR_ICON: "mdi:thermometer-lines", ATTR_VALUE_FEAT: FEAT_WATERTEMP, + ATTR_ENABLED: True, }, FEAT_TEMPCONTROL: { ATTR_MEASUREMENT_NAME: "Temp Control", ATTR_ICON: "mdi:thermometer-lines", ATTR_VALUE_FEAT: FEAT_TEMPCONTROL, + ATTR_ENABLED: True, }, FEAT_DRYLEVEL: { ATTR_MEASUREMENT_NAME: "Dry Level", ATTR_ICON: "mdi:tumble-dryer", ATTR_VALUE_FEAT: FEAT_DRYLEVEL, + ATTR_ENABLED: True, + }, + FEAT_ERROR_MSG: { + ATTR_MEASUREMENT_NAME: "Error Message", + ATTR_ICON: "mdi:alert-circle-outline", + ATTR_VALUE_FEAT: FEAT_ERROR_MSG, + ATTR_ENABLED: True, + }, + FEAT_PRE_STATE: { + ATTR_MEASUREMENT_NAME: "Pre State", + ATTR_ICON: DEFAULT_ICON, + ATTR_VALUE_FEAT: FEAT_PRE_STATE, + }, + FEAT_TUBCLEAN_COUNT: { + ATTR_MEASUREMENT_NAME: "Tube Clean Counter", + ATTR_ICON: DEFAULT_ICON, + ATTR_VALUE_FEAT: FEAT_TUBCLEAN_COUNT, }, FEAT_HALFLOAD: { ATTR_MEASUREMENT_NAME: "Half Load", ATTR_ICON: "mdi:circle-half-full", ATTR_VALUE_FEAT: FEAT_HALFLOAD, }, - ATTR_CURRENT_COURSE: { - ATTR_MEASUREMENT_NAME: "Current Course", - ATTR_ICON: "mdi:pin-outline", - ATTR_VALUE_FN: lambda x: x._current_course, - }, ATTR_INITIAL_TIME: { ATTR_MEASUREMENT_NAME: "Initial Time", ATTR_ICON: "mdi:clock-outline", @@ -394,6 +402,13 @@ ATTR_VALUE_FN: lambda x: x._oven_lower_target_temp, ATTR_ENABLED: True, }, + FEAT_OVEN_LOWER_CURRENT_TEMP: { + ATTR_MEASUREMENT_NAME: "Oven Lower Current Temperature", + ATTR_UNIT_FN: lambda x: x._oven_temp_unit, + ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, + ATTR_VALUE_FEAT: FEAT_OVEN_LOWER_CURRENT_TEMP, + ATTR_ENABLED: True, + }, ATTR_OVEN_UPPER_TARGET_TEMP: { ATTR_MEASUREMENT_NAME: "Oven Upper Target Temperature", ATTR_UNIT_FN: lambda x: x._oven_temp_unit, @@ -401,6 +416,13 @@ ATTR_VALUE_FN: lambda x: x._oven_upper_target_temp, ATTR_ENABLED: True, }, + FEAT_OVEN_UPPER_CURRENT_TEMP: { + ATTR_MEASUREMENT_NAME: "Oven Upper Current Temperature", + ATTR_UNIT_FN: lambda x: x._oven_temp_unit, + ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, + ATTR_VALUE_FEAT: FEAT_OVEN_UPPER_CURRENT_TEMP, + ATTR_ENABLED: True, + }, } RANGE_BINARY_SENSORS = { diff --git a/custom_components/smartthinq_sensors/wideq/__init__.py b/custom_components/smartthinq_sensors/wideq/__init__.py index 140c69e2..3108e3e1 100644 --- a/custom_components/smartthinq_sensors/wideq/__init__.py +++ b/custom_components/smartthinq_sensors/wideq/__init__.py @@ -66,9 +66,12 @@ FEAT_COOKTOP_CENTER_STATE = "cooktop_center_state" FEAT_COOKTOP_RIGHT_FRONT_STATE = "cooktop_right_front_state" FEAT_COOKTOP_RIGHT_REAR_STATE = "cooktop_right_rear_state" +FEAT_OVEN_LOWER_CURRENT_TEMP = "oven_lower_current_temp" FEAT_OVEN_LOWER_STATE = "oven_lower_state" +FEAT_OVEN_UPPER_CURRENT_TEMP = "oven_upper_current_temp" FEAT_OVEN_UPPER_STATE = "oven_upper_state" + # request ciphers settings CIPHERS = ":HIGH:!DH:!aNULL" diff --git a/custom_components/smartthinq_sensors/wideq/device.py b/custom_components/smartthinq_sensors/wideq/device.py index fa9d7501..a3eb7add 100644 --- a/custom_components/smartthinq_sensors/wideq/device.py +++ b/custom_components/smartthinq_sensors/wideq/device.py @@ -367,7 +367,12 @@ def value(self, name): elif d["type"] == "String": pass else: - assert False, "unsupported value type {}".format(d["type"]) + _LOGGER.error( + "ModelInfo: unsupported value type (%s) - value: %s", + d["type"], + d, + ) + return None def default(self, name): """Get the default value, if it exists, for a given value. @@ -537,6 +542,23 @@ def decode_monitor(self, data): else: return self.decode_monitor_json(data) + @staticmethod + def _get_current_temp_key(key: str, data): + """Special case for oven current temperature, that in protocol + is represented with a suffix "F" or "C" depending from the unit + """ + if key.count("CurrentTemperature") == 0: + return key + new_key = key[:-1] + if not new_key.endswith("CurrentTemperature"): + return key + unit_key = f"{new_key}Unit" + if unit_key not in data: + return key + if data[unit_key][0] == key[-1]: + return f"{new_key}Value" + return key + def decode_snapshot(self, data, key): """Decode status data.""" decoded = {} @@ -545,6 +567,7 @@ def decode_snapshot(self, data, key): info = data.get(key) if not info: return decoded + protocol = self._data["Monitoring"]["protocol"] if isinstance(protocol, list): for elem in protocol: @@ -552,18 +575,21 @@ def decode_snapshot(self, data, key): key = elem["value"] value = data for ident in elem["superSet"].split("."): - if value is not None: - value = value.get(ident) + if value is None: + break + pr_key = self._get_current_temp_key(ident, value) + value = value.get(pr_key) if value is not None: if isinstance(value, Number): value = int(value) decoded[key] = str(value) - else: - for data_key, value_key in protocol.items(): - value = info.get(data_key, "") - if value is not None and isinstance(value, Number): - value = int(value) - decoded[value_key] = str(value) + return decoded + + for data_key, value_key in protocol.items(): + value = info.get(data_key, "") + if value is not None and isinstance(value, Number): + value = int(value) + decoded[value_key] = str(value) return decoded @@ -628,12 +654,17 @@ def value(self, data): # return ReferenceValue( # self.data[ref] # ) - # elif d['dataType'] == 'Boolean': - # return EnumValue({'0': 'False', '1' : 'True'}) + elif data_type in ("Boolean", "boolean"): + return {"0": {"label": LABEL_BIT_OFF}, "1": {"label": LABEL_BIT_ON}} # elif d['dataType'] == 'String': # pass else: - assert False, "unsupported value type {}".format(data_type) + _LOGGER.error( + "ModelInfoV2: unsupported value type (%s) - value: %s", + data_type, + data, + ) + return None def default(self, name): """Get the default value, if it exists, for a given value. diff --git a/custom_components/smartthinq_sensors/wideq/range.py b/custom_components/smartthinq_sensors/wideq/range.py index 192607b9..909ac19d 100644 --- a/custom_components/smartthinq_sensors/wideq/range.py +++ b/custom_components/smartthinq_sensors/wideq/range.py @@ -9,7 +9,9 @@ FEAT_COOKTOP_CENTER_STATE, FEAT_COOKTOP_RIGHT_FRONT_STATE, FEAT_COOKTOP_RIGHT_REAR_STATE, + FEAT_OVEN_LOWER_CURRENT_TEMP, FEAT_OVEN_LOWER_STATE, + FEAT_OVEN_UPPER_CURRENT_TEMP, FEAT_OVEN_UPPER_STATE, ) @@ -200,6 +202,34 @@ def oven_upper_target_temp(self): return None return self._data.get(key) + @property + def oven_lower_current_temp(self): + unit = self.oven_temp_unit + if unit == UNIT_TEMP_FAHRENHEIT: + key = "LowerCookTemp_F" + elif unit == UNIT_TEMP_CELSIUS: + key = "LowerCookTemp_C" + else: + return None + status = self._data.get(key) + return self._update_feature( + FEAT_OVEN_LOWER_CURRENT_TEMP, status, False + ) + + @property + def oven_upper_current_temp(self): + unit = self.oven_temp_unit + if unit == UNIT_TEMP_FAHRENHEIT: + key = "UpperCookTemp_F" + elif unit == UNIT_TEMP_CELSIUS: + key = "UpperCookTemp_C" + else: + return None + status = self._data.get(key) + return self._update_feature( + FEAT_OVEN_UPPER_CURRENT_TEMP, status, False + ) + def _update_features(self): result = [ self.cooktop_left_front_state, @@ -208,6 +238,8 @@ def _update_features(self): self.cooktop_right_front_state, self.cooktop_right_rear_state, self.oven_lower_state, + self.oven_lower_current_temp, self.oven_upper_state, + self.oven_upper_current_temp, ] return diff --git a/info.md b/info.md index 394cdce6..28af55bc 100644 --- a/info.md +++ b/info.md @@ -1,5 +1,5 @@ # LG ThinQ Devices integration for HomeAssistant -A Homeassistant custom component to monitor LG Air Conditioner, Washer, Dryer, DishWasher, Refrigerator, Styler and Range +A Homeassistant custom component to monitor and control LG Air Conditioner, Washer, Dryer, DishWasher, Refrigerator, Styler and Range using ThinQ API based on [WideQ project][wideq].
**Important Version note:**