diff --git a/custom_components/connectlife/data_dictionaries/025-1wj105050v0w.yaml b/custom_components/connectlife/data_dictionaries/025-1wj105050v0w.yaml new file mode 100644 index 0000000..1dc6bf7 --- /dev/null +++ b/custom_components/connectlife/data_dictionaries/025-1wj105050v0w.yaml @@ -0,0 +1,175 @@ +#WashingMachine +properties: + - property: Child_lock + icon: mdi:lock + switch: + - property: ApplicationPermissions + icon: mdi:remote + sensor: + device_class: enum + options: + 2: "off" + 3: "on" + - property: Current_program_phase + icon: mdi:state-machine + sensor: + device_class: enum + options: + 0: not_available + 1: weigh + 2: prewash + 3: wash + 4: rinse + 7: spin-dry + 8: drying + 10: finished + read_only: true + + - property: Detergent_display + icon: mdi:alpha-d-circle-outline + sensor: + device_class: enum + options: + 0: "off" + 1: "on" + - property: Detergent_state + icon: mdi:alpha-d-circle-outline + binary_sensor: + read_only: true + device_class: problem + options: + 0: false + 1: true + - property: Softer_display + icon: mdi:alpha-s-circle-outline + sensor: + device_class: enum + options: + 0: "off" + 1: "on" + - property: Softener_state + icon: mdi:alpha-s-circle-outline + binary_sensor: + read_only: true + device_class: problem + options: + 0: false + 1: true + - property: Door_status + icon: mdi:door-closed + sensor: + device_class: enum + options: + 1: "open" + 3: "closed" + + - property: machine_status + icon: mdi:washing-machine + sensor: + device_class: enum + options: + 0: "off" + 1: "standby" + 2: "running" + 3: "pause" + + - property: Selected_program_ID + icon: mdi:tshirt-crew + select: + options: + 1: "cotton_dry" + 2: "synthetic_dry" + 4: "refresh" + 5: "anti_allergy" + 6: "drum_cleaning" + 7: "cotton" + 8: "synthetic" + 9: "eco_40_60" + 10: "wool" + 11: "fast15" + 12: "mix" + 14: "spin-dry" + 16: "baby" + 20: "rinse_spin" + 21: "delicates" + 22: "clean_dry_60" + 24: "shirts" + 41: "power49" + 42: "auto" + 44: "bed_linen" + 45: "jeans" + + - property: Selected_program_remaining_time_in_minutes + icon: mdi:timer-sand + sensor: + device_class: duration + unit: min + read_only: true + + - property: Selected_program_total_time_in_minutes + icon: mdi:timer-outline + sensor: + device_class: duration + unit: min + read_only: true + + - property: Energy_estimate + icon: mdi:flash + sensor: + device_class: energy + unit: Wh + read_only: true + state_class: total + + - property: Electricit_consumption_int + icon: mdi:flash + sensor: + device_class: energy + unit: Wh + read_only: true + state_class: total_increasing + + - property: Water_consumption_int + icon: mdi:water + sensor: + device_class: water + unit: L + read_only: true + state_class: total_increasing + + - property: temperature + icon: mdi:thermometer + select: + options: + 0: "cold" + 2: "20" + 3: "30" + 4: "40" + 6: "60" + 9: "90" + + - property: Spin_speed_rpm + icon: mdi:rotate-right + select: + options: + 14: "1400" + 12: "1200" + 10: "1000" + 8: "800" + 6: "600" + 0: "none" + + - property: mute + icon: mdi:volume-variant-off + switch: + + - property: Prewash + icon: mdi:numeric-1-circle-outline + switch: + + - property: Steam + icon: mdi:heat-wave + switch: + - property: Extra_Rinse + icon: mdi:water-sync + switch: diff --git a/custom_components/connectlife/data_dictionaries/properties-schema.json b/custom_components/connectlife/data_dictionaries/properties-schema.json index f0c7823..65d9ced 100644 --- a/custom_components/connectlife/data_dictionaries/properties-schema.json +++ b/custom_components/connectlife/data_dictionaries/properties-schema.json @@ -1,21 +1,24 @@ { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "#/$defs/ConnectLifeDataDictionary", - "$defs": { + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/ConnectLifeDataDictionary", + "definitions": { "ConnectLifeDataDictionary": { "type": "object", "additionalProperties": false, "properties": { "climate": { - "$ref": "#/$defs/ClimateDevice" + "$ref": "#/definitions/ClimateDevice" }, "properties": { - "type": ["array", "null"], + "type": "array", "items": { - "$ref": "#/$defs/Property" + "$ref": "#/definitions/Property" } } }, + "required": [ + "properties" + ], "title": "ConnectLife Data Dictionary" }, "ClimateDevice": { @@ -25,7 +28,7 @@ "presets": { "type": "array", "items": { - "$ref": "#/$defs/Preset" + "$ref": "#/definitions/Preset" }, "description": "List of presets" } @@ -52,29 +55,33 @@ "description": "Property name", "type": "string" }, + "combine_with" : { + "type": "string", + "description": "Optional second property to combine with the primary property, e.g., for combining integer and decimal parts." + } "binary_sensor": { - "$ref": "#/$defs/BinarySensor" + "$ref": "#/definitions/BinarySensor" }, "climate": { - "$ref": "#/$defs/Climate" + "$ref": "#/definitions/Climate" }, "humidifier": { - "$ref": "#/$defs/Humidifier" + "$ref": "#/definitions/Humidifier" }, "number": { - "$ref": "#/$defs/Number" + "$ref": "#/definitions/Number" }, "select": { - "$ref": "#/$defs/Select" + "$ref": "#/definitions/Select" }, "sensor": { - "$ref": "#/$defs/Sensor" + "$ref": "#/definitions/Sensor" }, "switch": { - "$ref": "#/$defs/Switch" + "$ref": "#/definitions/Switch" }, "water_heater": { - "$ref": "#/$defs/WaterHeater" + "$ref": "#/definitions/WaterHeater" }, "icon": { "type": "string", @@ -115,16 +122,16 @@ "description": "Defines a property for an appliance." }, "BinarySensor": { - "type": ["object", "null"], + "type": "object", "additionalProperties": false, "properties": { "device_class": { - "$ref": "#/$defs/BinarySensorDeviceClass" + "$ref": "#/definitions/BinarySensorDeviceClass" }, "options": { "type": "object", "additionalProperties": { - "type": "boolean" + "type": "string" }, "description": "Map of integer to boolean. By default, 0 and 1 is mapped to off (as 0 often implies that the sensor is not available), and 1 to true." } @@ -152,16 +159,16 @@ "additionalProperties": false, "properties": { "target": { - "$ref": "#/$defs/ClimateTarget" + "$ref": "#/definitions/ClimateTarget" }, "unknown_value": { - "$ref": "#/$defs/UnknownValue" + "$ref": "#/definitions/UnknownValue" }, "max_value": { - "$ref": "#/$defs/IntegerOrTemperature" + "$ref": "#/definitions/IntegerOrTemperature" }, "min_value": { - "$ref": "#/$defs/IntegerOrTemperature" + "$ref": "#/definitions/IntegerOrTemperature" }, "options": { "type": "object", @@ -177,7 +184,7 @@ "additionalProperties": false, "properties": { "target": { - "$ref": "#/$defs/HumidifierTarget" + "$ref": "#/definitions/HumidifierTarget" }, "min_value": { "type": "integer" @@ -186,7 +193,7 @@ "type": "integer" }, "device_class": { - "$ref": "#/$defs/HumidifierDeviceClass" + "$ref": "#/definitions/HumidifierDeviceClass" }, "options": { "type": "object", @@ -202,7 +209,7 @@ "additionalProperties": false, "properties": { "device_class": { - "$ref": "#/$defs/NumberDeviceClass" + "$ref": "#/definitions/NumberDeviceClass" }, "unit": { "type": "string", @@ -243,7 +250,7 @@ "description": "Map of integer to string." }, "command": { - "$ref": "#/$defs/Command" + "$ref": "#/definitions/Command" } }, "required": [ @@ -251,11 +258,11 @@ ] }, "Sensor": { - "type": ["object", "null" ], + "type": "object", "additionalProperties": false, "properties": { "device_class": { - "$ref": "#/$defs/SensorDeviceClass" + "$ref": "#/definitions/SensorDeviceClass" }, "read_only": { "type": "boolean", @@ -272,7 +279,7 @@ ] }, "unknown_value": { - "$ref": "#/$defs/UnknownValue" + "$ref": "#/definitions/UnknownValue" }, "options": { "type": "object", @@ -282,7 +289,7 @@ "description": "Map of integer to string. Required if device_class is set to enum." }, "state_class": { - "$ref": "#/$defs/SensorStateClass" + "$ref": "#/definitions/SensorStateClass" }, "multiplier": { "type": "number", @@ -291,29 +298,29 @@ "0.1", "10" ], - "default": 1 + "default": "1" } } }, "Switch": { - "type": ["object", "null"], + "type": "object", "additionalProperties": false, "properties": { "off": { "type": "integer", "description": "Off value", - "default": 0 + "default": "0" }, "on": { "type": "integer", "description": "On value", - "default": 1 + "default": "1" }, "device_class": { - "$ref": "#/$defs/SwitchDeviceClass" + "$ref": "#/definitions/SwitchDeviceClass" }, "command": { - "$ref": "#/$defs/Command" + "$ref": "#/definitions/Command" } }, "title": "BinarySensor" @@ -323,23 +330,23 @@ "additionalProperties": false, "properties": { "target": { - "$ref": "#/$defs/WaterHeaterTarget" + "$ref": "#/definitions/WaterHeaterTarget" }, "unknown_value": { - "$ref": "#/$defs/UnknownValue" + "$ref": "#/definitions/UnknownValue" }, "max_value": { - "$ref": "#/$defs/IntegerOrTemperature" + "$ref": "#/definitions/IntegerOrTemperature" }, "min_value": { - "$ref": "#/$defs/IntegerOrTemperature" + "$ref": "#/definitions/IntegerOrTemperature" }, "options": { "type": "object", "additionalProperties": { - "type": ["string", "boolean"] + "type": "string" }, - "description": "Map of integer to string or boolean. Required for targets current_operation (string), state (string), temperature_unit (string), and is_away_mode_on (boolean)." + "description": "Map of integer to string. Required for targets current_operation, state, and temperature_unit." } } }, @@ -387,7 +394,6 @@ "hvac_action", "hvac_mode", "is_on", - "swing_horizontal_mode", "swing_mode", "target_humidity", "target_temperature", diff --git a/custom_components/connectlife/dictionaries.py b/custom_components/connectlife/dictionaries.py index 5146d2b..21ad14b 100644 --- a/custom_components/connectlife/dictionaries.py +++ b/custom_components/connectlife/dictionaries.py @@ -317,6 +317,7 @@ def __init__(self, name: str, water_heater: dict | None): class Property: name: str icon: str | None + combine_with: str | None hide: bool disable: bool unavailable: int | None @@ -343,6 +344,7 @@ def __init__(self, entry: dict): if ENTITY_CATEGORY in entry else None ) + self.combine_with = entry.get("combine_with") if Platform.BINARY_SENSOR in entry: self.binary_sensor = BinarySensor(self.name, entry[Platform.BINARY_SENSOR]) @@ -384,26 +386,15 @@ def get_dictionary(cls, appliance: ConnectLifeAppliance) -> Dictionary: return cls.dictionaries[key] climate: dict[[list[dict[str, int]]]] | None = None properties = defaultdict(lambda: Property({PROPERTY: "default", HIDE: True})) - try: - data = pkgutil.get_data(__name__, f"data_dictionaries/{appliance.device_type_code}.yaml") - parsed = yaml.safe_load(data) - # TODO: Support default climate section - if parsed is not None and PROPERTIES in parsed and parsed[PROPERTIES] is not None: - for prop in parsed[PROPERTIES]: - properties[prop[PROPERTY]] = Property(prop) - except FileNotFoundError: - pass try: data = pkgutil.get_data(__name__, f"data_dictionaries/{key}.yaml") parsed = yaml.safe_load(data) - if parsed is not None: - if Platform.CLIMATE in parsed: - climate = ( - parsed[Platform.CLIMATE] if Platform.CLIMATE in parsed else None - ) - if PROPERTIES in parsed and parsed[PROPERTIES] is not None: - for prop in parsed[PROPERTIES]: - properties[prop[PROPERTY]] = Property(prop) + if Platform.CLIMATE in parsed: + climate = ( + parsed[Platform.CLIMATE] if Platform.CLIMATE in parsed else None + ) + for prop in parsed[PROPERTIES]: + properties[prop[PROPERTY]] = Property(prop) except FileNotFoundError: _LOGGER.warning( "No data dictionary found for %s (%s)", appliance.device_nickname, key diff --git a/custom_components/connectlife/sensor.py b/custom_components/connectlife/sensor.py index f3b42a9..60f1fb4 100644 --- a/custom_components/connectlife/sensor.py +++ b/custom_components/connectlife/sensor.py @@ -76,6 +76,8 @@ def __init__( self.multiplier = dd_entry.sensor.multiplier self.unknown_value = dd_entry.sensor.unknown_value + self.combine_with = getattr(dd_entry, "combine_with", None) + device_class = dd_entry.sensor.device_class options = None if device_class == SensorDeviceClass.ENUM: @@ -116,6 +118,23 @@ def __init__( def update_state(self): if self.status in self.coordinator.data[self.device_id].status_list: value = self.coordinator.data[self.device_id].status_list[self.status] + + if self.combine_with: + other_status = self.combine_with + other_value = self.coordinator.data[self.device_id].status_list.get(other_status) + if isinstance(value, int) and isinstance(other_value, int): + value = value + (other_value / 100) + else: + _LOGGER.warning( + "Cannot combine %s with %s for %s (%s), unexpected types: %s and %s", + self.status, + other_status, + self.nickname, + self.device_id, + type(value), + type(other_value), + ) + if self.device_class == SensorDeviceClass.ENUM: if value in self.options_map: value = self.options_map[value] @@ -127,12 +146,14 @@ def update_state(self): self.nickname, ) value = None + if value == self.unknown_value: self._attr_native_value = None else: if self.multiplier is not None: value *= self.multiplier self._attr_native_value = value + self._attr_available = self.coordinator.data[self.device_id].offline_state == 1 async def async_set_value(self, value: int) -> None: diff --git a/custom_components/connectlife/translations/en.json b/custom_components/connectlife/translations/en.json index e56db35..4e664ad 100644 --- a/custom_components/connectlife/translations/en.json +++ b/custom_components/connectlife/translations/en.json @@ -400,57 +400,9 @@ "existing_sr_mode": { "name": "Existing SR mode" }, - "f_e_arkgrille": { - "name": "Cabinet grille protection alarm" - }, "f_e_filterclean": { "name": "Filter clean" }, - "f_e_incoiltemp": { - "name": "Indoor coil temperature sensor failure" - }, - "f_e_incom": { - "name": "Indoor/outdoor communication failure" - }, - "f_e_indisplay": { - "name": "Communication failure between indoor control panel and display panel" - }, - "f_e_ineeprom": { - "name": "Indoor control board EEPROM error" - }, - "f_e_inele": { - "name": "Communication failure between indoor control panel and indoor power panel" - }, - "f_e_infanmotor": { - "name": "Indoor fan motor abnormal operation failure" - }, - "f_e_inhumidity": { - "name": "Indoor humidity sensor failure" - }, - "f_e_inkeys": { - "name": "Communication failure between indoor control panel and keypad" - }, - "f_e_intemp": { - "name": "Indoor temperature sensor failure" - }, - "f_e_invzero": { - "name": "Indoor voltage zero-crossing detection fault" - }, - "f_e_inwifi": { - "name": "Communication failure between WIFI control panel and indoor control panel" - }, - "f_e_outcoiltemp": { - "name": "Outdoor coil temperature sensor failure" - }, - "f_e_outeeprom": { - "name": "Outdoor EEPROM error" - }, - "f_e_outgastemp": { - "name": "Exhaust temperature sensor failure" - }, - "f_e_outtemp": { - "name": "Outdoor ambient temperature sensor failure" - }, "f_e_pump": { "name": "Pump" }, @@ -835,9 +787,6 @@ "step3_status": { "name": "Step 3 status" }, - "t_beep": { - "name": "Beep" - }, "tab_setting_status": { "name": "Tab setting" }, @@ -894,9 +843,6 @@ } }, "swing_mode": { - "state": {} - }, - "swing_horizontal_mode": { "state": { "both_sides": "Both sides", "forward": "Forward", @@ -937,8 +883,8 @@ }, "appliance_status": { "state": { - "idle": "Idle", - "running": "Running" + "idle": "Idle", + "running": "Running" } }, "applicationpermissions": { @@ -1004,9 +950,6 @@ "running": "Running" } }, - "current_temperature": { - "name": "Current temperature" - }, "daily_energy_consumption": { "name": "Daily energy consumption" }, @@ -1022,9 +965,6 @@ "delay_start_remaining_time": { "name": "Delay start remaining time" }, - "delay_start_remaining_time_in_minutes": { - "name": "Delay start remaining time in minutes" - }, "delay_start_set_time": { "name": "Delay start set time" }, @@ -1034,13 +974,6 @@ "delaystart_delayend_duration_inminutes": { "name": "Delay start duration" }, - "delaystart_delayend_mode_status": { - "name": "Delay start status", - "state": { - "off": "Off", - "on": "On" - } - }, "delaystart_delayend_remaining_timein_minutes": { "name": "Delay start remaining time" }, @@ -1081,9 +1014,8 @@ "state": { "not_available": "Not available", "open": "Open", - "closed": "Closed", - "closed_when_running": "Closed when running", - "other": "Other" + "other": "Other", + "closed": "Closed" } }, "electricit_consumption_decimal": { @@ -1196,6 +1128,7 @@ "state": { "off": "Off", "standby": "Standby", + "pause": "Paused", "running": "Running" } }, @@ -1301,9 +1234,6 @@ "stopped": "Stopped" } }, - "selected_program_duration_in_minutes": { - "name": "Selected program duration" - }, "selected_program_id": { "name": "Selected program", "state": { @@ -1349,7 +1279,10 @@ "shirts": "Shirts", "drum_cleaning": "Drum Cleaning", "spinning_draining": "Spinning & Draining", - "rinsing_softening": "Rinsing & Softening" + "rinsing_softening": "Rinsing & Softening", + "jeans": "Jeans", + "bedding": "Bedding", + "mix": "Mix" } }, "selected_program_mode": { @@ -1641,14 +1574,6 @@ "spintime_index": { "name": "Spin time index" }, - "status": { - "name": "Device status", - "state": { - "off": "Off", - "standby": "Standby", - "running": "Running" - } - }, "step1_duration": { "name": "Step1 duration" }, @@ -1884,6 +1809,30 @@ }, "zone_number": { "name": "Number of zones" + }, + "status": { + "name": "Device status", + "state": { + "off": "Off", + "standby": "Standby", + "running": "Running" + } + }, + "door_status": { + "name": "Door status", + "state": { + "not_available": "Not Available", + "open": "Open", + "closed": "Closed", + "closed_when_running": "Closed when running" + } + }, + "delaystart_delayend_mode_status": { + "name": "Delay start status", + "state": { + "off": "Off", + "on": "On" + } } }, "switch": { @@ -1932,6 +1881,9 @@ "prewash": { "name": "Prewash" }, + "extrarinse": { + "name": "Extra rinse" + }, "save_mode": { "name": "Save mode" }, @@ -2007,6 +1959,12 @@ "extrarinsenum": { "name": "Extra rinse num" }, + "lightbrightness": { + "name": "Light brightness" + }, + "lightcolortemperature": { + "name": "Light colort emperature" + }, "motorlevel": { "name": "Motor level" }, @@ -2036,7 +1994,9 @@ "spin-dry": "Spin-dry", "rinse_spin": "Rinse spin", "clean_dry_60": "Clean dry 60", - "power49": "Power49" + "power49": "Power49", + "mix": "Mix", + "jeans": "Jeans" } }, "spin_speed_rpm": { @@ -2083,12 +2043,6 @@ "follow": "Follow", "not_follow": "Not Follow" } - }, - "temperature": { - "name": "Temperature", - "state": { - "cold": "Cold" - } } }, "water_heater": { @@ -2108,12 +2062,6 @@ "freeze_temperature": { "name": "Freezeer temperature" }, - "lightbrightness": { - "name": "Light brightness" - }, - "lightcolortemperature": { - "name": "Light color temperature" - }, "refrigerator_max_temperature": { "name": "Refrigerator max temperature" },