diff --git a/custom_components/luxtronik/__init__.py b/custom_components/luxtronik/__init__.py index 561db6f..060e4ec 100644 --- a/custom_components/luxtronik/__init__.py +++ b/custom_components/luxtronik/__init__.py @@ -23,7 +23,8 @@ PLATFORMS, SERVICE_WRITE, SERVICE_WRITE_SCHEMA, - SensorKey as SK, + Parameter_SensorKey as LP, + Calculation_SensorKey as LC, ) from .coordinator import LuxtronikCoordinator @@ -127,13 +128,13 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> prefix = None ent_reg = None - def _up(ident: str, new_id: SK, platform: P = P.SENSOR) -> None: + def _up(ident: str, new_id: LC | LP, platform: P = P.SENSOR) -> None: nonlocal prefix, ent_reg if prefix is None or ent_reg is None: prefix = config_entry.data[CONF_HA_SENSOR_PREFIX] ent_reg = async_get(hass) entity_id = f"{platform}.{prefix}_{ident}" - new_ident = f"{platform}.{prefix}_{new_id}" + new_ident = f"{platform}.{prefix}_{new_id.name.lower()}" try: ent_reg.async_update_entity( entity_id, new_entity_id=new_ident, new_unique_id=new_ident @@ -160,119 +161,119 @@ def _up(ident: str, new_id: SK, platform: P = P.SENSOR) -> None: hass.config_entries.async_update_entry(config_entry, data=new_data) if config_entry.version == 5: - _up("heat_amount_domestic_water", SK.DHW_HEAT_AMOUNT) - _up("domestic_water_energy_input", SK.DHW_ENERGY_INPUT) - _up("domestic_water_temperature", SK.DHW_TEMPERATURE) - _up("operation_hours_domestic_water", SK.DHW_OPERATION_HOURS) - _up("domestic_water_target_temperature", SK.DHW_TARGET_TEMPERATURE, P.NUMBER) - _up("domestic_water_hysteresis", SK.DHW_HYSTERESIS, P.NUMBER) + _up("heat_amount_domestic_water", LC.DHW_HEAT_AMOUNT) + _up("domestic_water_energy_input", LP.DHW_ENERGY_INPUT) + _up("domestic_water_temperature", LC.DHW_TEMPERATURE) + _up("operation_hours_domestic_water", LC.DHW_OPERATION_HOURS) + _up("domestic_water_target_temperature", LP.DHW_TARGET_TEMPERATURE, P.NUMBER) + _up("domestic_water_hysteresis", LP.DHW_HYSTERESIS, P.NUMBER) _up( "domestic_water_thermal_desinfection_target", - SK.DHW_THERMAL_DESINFECTION_TARGET, + LP.DHW_THERMAL_DESINFECTION_TARGET, P.NUMBER, ) _up( "domestic_water_recirculation_pump", - SK.DHW_RECIRCULATION_PUMP, + LC.DHW_RECIRCULATION_PUMP, P.BINARY_SENSOR, ) _up( "domestic_water_circulation_pump", - SK.DHW_CIRCULATION_PUMP, + LC.DHW_CIRCULATION_PUMP, P.BINARY_SENSOR, ) - _up("domestic_water_charging_pump", SK.DHW_CHARGING_PUMP, P.BINARY_SENSOR) + _up("domestic_water_charging_pump", LP.DHW_CHARGING_PUMP, P.BINARY_SENSOR) # [sensor] - _up("pump_frequency", SK.PUMP_FREQUENCY, P.SENSOR) - _up("room_thermostat_temperature", SK.ROOM_THERMOSTAT_TEMPERATURE, P.SENSOR) + _up("pump_frequency", LC.PUMP_FREQUENCY, P.SENSOR) + _up("room_thermostat_temperature", LC.ROOM_THERMOSTAT_TEMPERATURE, P.SENSOR) _up( "room_thermostat_temperature_target", - SK.ROOM_THERMOSTAT_TEMPERATURE_TARGET, + LC.ROOM_THERMOSTAT_TEMPERATURE_TARGET, P.SENSOR, ) # [binary sensor] - _up("evu_unlocked", SK.EVU_UNLOCKED, P.BINARY_SENSOR) - _up("compressor", SK.COMPRESSOR, P.BINARY_SENSOR) - _up("pump_flow", SK.PUMP_FLOW, P.BINARY_SENSOR) - _up("compressor_heater", SK.COMPRESSOR_HEATER, P.BINARY_SENSOR) - _up("defrost_valve", SK.DEFROST_VALVE, P.BINARY_SENSOR) - _up("additional_heat_generator", SK.ADDITIONAL_HEAT_GENERATOR, P.BINARY_SENSOR) - _up("disturbance_output", SK.DISTURBANCE_OUTPUT, P.BINARY_SENSOR) - _up("circulation_pump_heating", SK.CIRCULATION_PUMP_HEATING, P.BINARY_SENSOR) + _up("evu_unlocked", LC.EVU_UNLOCKED, P.BINARY_SENSOR) + _up("compressor", LC.COMPRESSOR, P.BINARY_SENSOR) + _up("pump_flow", LC.PUMP_FLOW, P.BINARY_SENSOR) + _up("compressor_heater", LC.COMPRESSOR_HEATER, P.BINARY_SENSOR) + _up("defrost_valve", LC.DEFROST_VALVE, P.BINARY_SENSOR) + _up("additional_heat_generator", LC.ADDITIONAL_HEAT_GENERATOR, P.BINARY_SENSOR) + _up("disturbance_output", LC.DISTURBANCE_OUTPUT, P.BINARY_SENSOR) + _up("circulation_pump_heating", LC.CIRCULATION_PUMP_HEATING, P.BINARY_SENSOR) _up( "additional_circulation_pump", - SK.ADDITIONAL_CIRCULATION_PUMP, + LC.ADDITIONAL_CIRCULATION_PUMP, P.BINARY_SENSOR, ) - _up("approval_cooling", SK.APPROVAL_COOLING, P.BINARY_SENSOR) + _up("approval_cooling", LC.APPROVAL_COOLING, P.BINARY_SENSOR) # [number] - _up("release_second_heat_generator", SK.RELEASE_SECOND_HEAT_GENERATOR, P.NUMBER) + _up("release_second_heat_generator", LP.RELEASE_SECOND_HEAT_GENERATOR, P.NUMBER) _up( "release_time_second_heat_generator", - SK.RELEASE_TIME_SECOND_HEAT_GENERATOR, + LP.RELEASE_TIME_SECOND_HEAT_GENERATOR, P.NUMBER, ) - _up("heating_target_correction", SK.HEATING_TARGET_CORRECTION, P.NUMBER) - _up("pump_optimization_time", SK.PUMP_OPTIMIZATION_TIME, P.NUMBER) - _up("heating_threshold_temperature", SK.HEATING_THRESHOLD_TEMPERATURE, P.NUMBER) + _up("heating_target_correction", LP.HEATING_TARGET_CORRECTION, P.NUMBER) + _up("pump_optimization_time", LP.PUMP_OPTIMIZATION_TIME, P.NUMBER) + _up("heating_threshold_temperature", LP.HEATING_THRESHOLD_TEMPERATURE, P.NUMBER) _up( "heating_min_flow_out_temperature", - SK.HEATING_MIN_FLOW_OUT_TEMPERATURE, + LP.HEATING_MIN_FLOW_OUT_TEMPERATURE, P.NUMBER, ) _up( "heating_circuit_curve1_temperature", - SK.HEATING_CIRCUIT_CURVE1_TEMPERATURE, + LP.HEATING_CIRCUIT_CURVE1_TEMPERATURE, P.NUMBER, ) _up( "heating_circuit_curve2_temperature", - SK.HEATING_CIRCUIT_CURVE2_TEMPERATURE, + LP.HEATING_CIRCUIT_CURVE2_TEMPERATURE, P.NUMBER, ) _up( "heating_circuit_curve_night_temperature", - SK.HEATING_CIRCUIT_CURVE_NIGHT_TEMPERATURE, + LP.HEATING_CIRCUIT_CURVE_NIGHT_TEMPERATURE, P.NUMBER, ) _up( "heating_night_lowering_to_temperature", - SK.HEATING_NIGHT_LOWERING_TO_TEMPERATURE, + LP.HEATING_NIGHT_LOWERING_TO_TEMPERATURE, P.NUMBER, ) - _up("heating_hysteresis", SK.HEATING_HYSTERESIS, P.NUMBER) + _up("heating_hysteresis", LP.HEATING_HYSTERESIS, P.NUMBER) _up( "heating_max_flow_out_increase_temperature", - SK.HEATING_MAX_FLOW_OUT_INCREASE_TEMPERATURE, + LP.HEATING_MAX_FLOW_OUT_INCREASE_TEMPERATURE, P.NUMBER, ) _up( "heating_maximum_circulation_pump_speed", - SK.HEATING_MAXIMUM_CIRCULATION_PUMP_SPEED, + LP.HEATING_MAXIMUM_CIRCULATION_PUMP_SPEED, P.NUMBER, ) _up( "heating_room_temperature_impact_factor", - SK.HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, + LP.HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, P.NUMBER, ) # [switch] - _up("remote_maintenance", SK.REMOTE_MAINTENANCE, P.SWITCH) - _up("efficiency_pump", SK.EFFICIENCY_PUMP, P.SWITCH) - _up("pump_heat_control", SK.PUMP_HEAT_CONTROL, P.SWITCH) - _up("heating", SK.HEATING, P.SWITCH) - _up("pump_optimization", SK.PUMP_OPTIMIZATION, P.SWITCH) - _up("heating_threshold", SK.HEATING_THRESHOLD, P.SWITCH) - _up("domestic_water", SK.DOMESTIC_WATER, P.SWITCH) - _up("cooling", SK.COOLING, P.SWITCH) + _up("remote_maintenance", LP.REMOTE_MAINTENANCE, P.SWITCH) + _up("efficiency_pump", LP.EFFICIENCY_PUMP, P.SWITCH) + _up("pump_heat_control", LP.PUMP_HEAT_CONTROL, P.SWITCH) + _up("heating", LP.HEATING, P.SWITCH) + _up("pump_optimization", LP.PUMP_OPTIMIZATION, P.SWITCH) + _up("heating_threshold", LP.HEATING_THRESHOLD, P.SWITCH) + _up("domestic_water", LP.DOMESTIC_WATER, P.SWITCH) + _up("cooling", LP.COOLING, P.SWITCH) # [climate] - _up("heating", SK.HEATING, P.CLIMATE) - _up("cooling", SK.COOLING, P.CLIMATE) + _up("heating", LP.HEATING, P.CLIMATE) + _up("cooling", LP.COOLING, P.CLIMATE) new_data = {**config_entry.data} config_entry.version = 6 @@ -282,10 +283,10 @@ def _up(ident: str, new_id: SK, platform: P = P.SENSOR) -> None: if config_entry.version == 6: _up( - "cooling_threshold_temperature", SK.COOLING_OUTDOOR_TEMP_THRESHOLD, P.NUMBER + "cooling_threshold_temperature", LP.COOLING_OUTDOOR_TEMP_THRESHOLD, P.NUMBER ) - _up("cooling_start_delay_hours", SK.COOLING_START_DELAY_HOURS, P.NUMBER) - _up("cooling_stop_delay_hours", SK.COOLING_STOP_DELAY_HOURS, P.NUMBER) + _up("cooling_start_delay_hours", LP.COOLING_START_DELAY_HOURS, P.NUMBER) + _up("cooling_stop_delay_hours", LP.COOLING_STOP_DELAY_HOURS, P.NUMBER) new_data = {**config_entry.data} config_entry.version = 7 diff --git a/custom_components/luxtronik/base.py b/custom_components/luxtronik/base.py index e0cb5bf..516743e 100644 --- a/custom_components/luxtronik/base.py +++ b/custom_components/luxtronik/base.py @@ -6,7 +6,8 @@ from typing import Any from homeassistant.backports.enum import StrEnum -from homeassistant.components.sensor import RestoreSensor +from homeassistant.components.sensor import RestoreSensor, SensorEntityDescription +from homeassistant.components.sensor.const import SensorDeviceClass, SensorStateClass from homeassistant.components.water_heater import STATE_HEAT_PUMP from homeassistant.const import STATE_OFF, UnitOfTemperature, UnitOfTime from homeassistant.core import callback @@ -18,12 +19,16 @@ from .common import get_sensor_data from .const import ( + UNIT_DEVICE_CLASS_MAP, + DOMAIN, + UNIT_ICON_MAP, + UNIT_STATE_CLASS_MAP, DeviceKey, LOGGER, - LuxCalculation as LC, + Calculation_SensorKey as LC, LuxMode, LuxOperationMode, - LuxParameter as LP, + Parameter_SensorKey as LP, SensorAttrFormat, SensorAttrKey as SA, ) @@ -55,8 +60,9 @@ def __init__( """Init LuxtronikEntity.""" super().__init__(coordinator=coordinator) self._device_info_ident = device_info_ident + self.enrich_description(description) self._attr_extra_state_attributes = { - SA.LUXTRONIK_KEY: f"{description.luxtronik_key.name[1:5]} {description.luxtronik_key.value}" + SA.LUXTRONIK_KEY: f"{str(description.luxtronik_key.__class__)[7:].split('_')[0]} {description.luxtronik_key.value}" } for field in description.__dataclass_fields__: if field.startswith("luxtronik_key_"): @@ -70,7 +76,7 @@ def __init__( else: self._attr_extra_state_attributes[field] = value if description.translation_key is None: - description.translation_key = description.key.value + description.translation_key = description.key if description.entity_registry_enabled_default: description.entity_registry_enabled_default = coordinator.entity_visible( description @@ -79,7 +85,7 @@ def __init__( self._attr_device_info = coordinator.get_device(device_info_ident) translation_key = ( - description.key.value + description.key if description.translation_key_name is None else description.translation_key_name ) @@ -87,6 +93,12 @@ def __init__( description.has_entity_name = True self._attr_state = self._get_value(description.luxtronik_key) + def enrich_description(self, d: LuxtronikEntityDescription) -> None: + d.key = d.luxtronik_key.name.lower() + d.icon = d.icon or UNIT_ICON_MAP.get(d.native_unit_of_measurement) + d.state_class = d.state_class or UNIT_STATE_CLASS_MAP.get(d.native_unit_of_measurement) + d.device_class = d.device_class or UNIT_DEVICE_CLASS_MAP.get(d.native_unit_of_measurement) + async def async_added_to_hass(self) -> None: """When entity is added to hass.""" await super().async_added_to_hass() @@ -118,6 +130,11 @@ async def async_added_to_hass(self) -> None: async_dispatcher_connect( self.hass, data_updated, self._schedule_immediate_update ) + except AttributeError as err: + LOGGER.warning( + "Could not restore latest data (async_added_to_hass)", + exc_info=err, + ) except Exception as err: LOGGER.error( "Could not restore latest data (async_added_to_hass)", @@ -138,7 +155,10 @@ def _handle_coordinator_update(self) -> None: # Ensure timezone: time_zone = dt_util.get_time_zone(self.hass.config.time_zone) value = value.replace(tzinfo=time_zone) + elif isinstance(descr, SensorEntityDescription): + value = descr.options[value] + last_state = self._attr_state self._attr_state = value # Calc icon: @@ -164,16 +184,34 @@ def _handle_coordinator_update(self) -> None: ): self.next_update = utcnow() + descr.update_interval super()._handle_coordinator_update() + if last_state != self._attr_state: + self.handle_value_change(self._attr_state, last_state) + + def handle_value_change(self, value, last_value): + self.fire_event(self.entity_description.event_id_on_change, value, last_value) + + def fire_event(self, event: str | None, value, last_value): + if event is None: + return + _event = f"{DOMAIN}_{event}" + data = { + "unique_id": self.unique_id, + "value": value, + "last_value": last_value, + } + self.hass.bus.fire(_event, data) def _enrich_extra_attributes(self) -> None: for attr in self.entity_description.extra_attributes: if attr.format is None and ( attr.luxtronik_key is None or attr.luxtronik_key == LP.UNSET ): - continue - self._attr_extra_state_attributes[attr.key.value] = self.formatted_data( - attr - ) + if attr.default_value is not None: + self._attr_extra_state_attributes[attr.key.value] = "_" + else: + self._attr_extra_state_attributes[attr.key.value] = self.formatted_data( + attr + ) @callback def _schedule_immediate_update(self): @@ -199,14 +237,14 @@ def formatted_data(self, attr: LuxtronikEntityAttributeDescription) -> str: return f"{value/10:.1f} {UnitOfTemperature.CELSIUS}" if attr.format == SensorAttrFormat.SWITCH_GAP: flow_out_target = float( - self._get_value(LC.C0012_FLOW_OUT_TEMPERATURE_TARGET) + self._get_value(LC.FLOW_OUT_TEMPERATURE_TARGET) ) flow_out = float(value) - hyst = float(self._get_value(LP.P0088_HEATING_HYSTERESIS)) * 0.1 + hyst = float(self._get_value(LP.HEATING_HYSTERESIS)) * 0.1 - if self._get_value(LC.C0080_STATUS) == LuxOperationMode.heating: + if self._get_value(LC.STATUS) == LuxOperationMode.heating: return f"{flow_out + hyst - flow_out_target:.1f} {UnitOfTemperature.KELVIN}" - if self._get_value(LP.P0003_MODE_HEATING) != LuxMode.off: + if self._get_value(LP.MODE_HEATING) != LuxMode.off: return f"{flow_out - hyst - flow_out_target:.1f} {UnitOfTemperature.KELVIN}" return "" diff --git a/custom_components/luxtronik/binary_sensor.py b/custom_components/luxtronik/binary_sensor.py index 179ad42..23493a4 100644 --- a/custom_components/luxtronik/binary_sensor.py +++ b/custom_components/luxtronik/binary_sensor.py @@ -62,7 +62,7 @@ def __init__( self.entity_id = ENTITY_ID_FORMAT.format(f"{prefix}_{description.key}") self._attr_unique_id = self.entity_id self._sensor_data = get_sensor_data( - coordinator.data, description.luxtronik_key.value + coordinator.data, description.luxtronik_key ) hass.bus.async_listen(f"{DOMAIN}_data_update", self._data_update) @@ -85,7 +85,7 @@ def _handle_coordinator_update( if data is None: return self._attr_state = get_sensor_data( - data, self.entity_description.luxtronik_key.value + data, self.entity_description.luxtronik_key ) if ( self.entity_description.on_state is True @@ -100,3 +100,12 @@ def _handle_coordinator_update( and self._attr_state in self.entity_description.on_states # noqa: W503 ) super()._handle_coordinator_update() + + def handle_value_change(self, value, last_value): + super().handle_value_change(value, last_value) + if value == True: + self.fire_event(self.entity_description.event_id_on_true, value, last_value) + elif value == False: + self.fire_event( + self.entity_description.event_id_on_false, value, last_value + ) diff --git a/custom_components/luxtronik/binary_sensor_entities_predefined.py b/custom_components/luxtronik/binary_sensor_entities_predefined.py index f729985..85201e0 100644 --- a/custom_components/luxtronik/binary_sensor_entities_predefined.py +++ b/custom_components/luxtronik/binary_sensor_entities_predefined.py @@ -1,130 +1,115 @@ """Luxtronik binary sensors definitions.""" from homeassistant.components.binary_sensor import BinarySensorDeviceClass -from .const import DeviceKey, LuxCalculation as LC, LuxVisibility as LV, SensorKey +from .const import DeviceKey, Calculation_SensorKey as LC, Visibility_SensorKey as LV from .model import LuxtronikBinarySensorEntityDescription BINARY_SENSORS: list[LuxtronikBinarySensorEntityDescription] = [ # region Main heatpump LuxtronikBinarySensorEntityDescription( - key=SensorKey.EVU_UNLOCKED, - luxtronik_key=LC.C0031_EVU_UNLOCKED, + luxtronik_key=LC.EVU_UNLOCKED, icon="mdi:lock", device_class=BinarySensorDeviceClass.LOCK, - visibility=LV.V0121_EVU_LOCKED, + visibility=LV.EVU_LOCKED, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.COMPRESSOR, - luxtronik_key=LC.C0044_COMPRESSOR, + luxtronik_key=LC.COMPRESSOR, icon="mdi:arrow-collapse-all", device_class=BinarySensorDeviceClass.RUNNING, + event_id_on_true="IMPULSE_START", + event_id_on_false="IMPULSE_END", ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.COMPRESSOR2, - luxtronik_key=LC.C0045_COMPRESSOR2, + luxtronik_key=LC.COMPRESSOR2, icon="mdi:arrow-collapse-all", device_class=BinarySensorDeviceClass.RUNNING, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.PUMP_FLOW, - luxtronik_key=LC.C0043_PUMP_FLOW, + luxtronik_key=LC.PUMP_FLOW, icon="mdi:pump", device_class=BinarySensorDeviceClass.RUNNING, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.COMPRESSOR_HEATER, - luxtronik_key=LC.C0182_COMPRESSOR_HEATER, + luxtronik_key=LC.COMPRESSOR_HEATER, icon="mdi:heat-wave", device_class=BinarySensorDeviceClass.RUNNING, - visibility=LV.V0290_COMPRESSOR_HEATING, + visibility=LV.COMPRESSOR_HEATING, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.DEFROST_VALVE, - luxtronik_key=LC.C0037_DEFROST_VALVE, + luxtronik_key=LC.DEFROST_VALVE, icon_by_state={True: "mdi:valve-open", False: "mdi:valve-closed"}, device_class=BinarySensorDeviceClass.OPENING, - visibility=LV.V0049_DEFROST_VALVE, + visibility=LV.DEFROST_VALVE, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.ADDITIONAL_HEAT_GENERATOR, - luxtronik_key=LC.C0048_ADDITIONAL_HEAT_GENERATOR, + luxtronik_key=LC.ADDITIONAL_HEAT_GENERATOR, icon="mdi:patio-heater", device_class=BinarySensorDeviceClass.RUNNING, - visibility=LV.V0061_SECOND_HEAT_GENERATOR, + visibility=LV.SECOND_HEAT_GENERATOR, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.DISTURBANCE_OUTPUT, - luxtronik_key=LC.C0049_DISTURBANCE_OUTPUT, + luxtronik_key=LC.DISTURBANCE_OUTPUT, device_class=BinarySensorDeviceClass.PROBLEM, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.DEFROST_END_FLOW_OKAY, - luxtronik_key=LC.C0029_DEFROST_END_FLOW_OKAY, - visibility=LV.V0041_DEFROST_END_FLOW_OKAY, + luxtronik_key=LC.DEFROST_END_FLOW_OKAY, + visibility=LV.DEFROST_END_FLOW_OKAY, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.MOTOR_PROTECTION, - luxtronik_key=LC.C0034_MOTOR_PROTECTION, - visibility=LV.V0045_MOTOR_PROTECTION, + luxtronik_key=LC.MOTOR_PROTECTION, + visibility=LV.MOTOR_PROTECTION, ), # endregion Main heatpump # region Heating LuxtronikBinarySensorEntityDescription( - key=SensorKey.CIRCULATION_PUMP_HEATING, - luxtronik_key=LC.C0039_CIRCULATION_PUMP_HEATING, + luxtronik_key=LC.CIRCULATION_PUMP_HEATING, device_key=DeviceKey.heating, icon="mdi:car-turbocharger", device_class=BinarySensorDeviceClass.RUNNING, - visibility=LV.V0052_CIRCULATION_PUMP_HEATING, + visibility=LV.CIRCULATION_PUMP_HEATING, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.ADDITIONAL_CIRCULATION_PUMP, - luxtronik_key=LC.C0047_ADDITIONAL_CIRCULATION_PUMP, + luxtronik_key=LC.ADDITIONAL_CIRCULATION_PUMP, device_key=DeviceKey.heating, icon="mdi:pump", device_class=BinarySensorDeviceClass.RUNNING, - visibility=LV.V0060_ADDITIONAL_CIRCULATION_PUMP, + visibility=LV.ADDITIONAL_CIRCULATION_PUMP, ), # endregion Heating # region Domestic water LuxtronikBinarySensorEntityDescription( - key=SensorKey.DHW_RECIRCULATION_PUMP, - luxtronik_key=LC.C0038_DHW_RECIRCULATION_PUMP, + luxtronik_key=LC.DHW_RECIRCULATION_PUMP, device_key=DeviceKey.domestic_water, icon="mdi:pump", device_class=BinarySensorDeviceClass.RUNNING, - visibility=LV.V0050_DHW_RECIRCULATION_PUMP, + visibility=LV.DHW_RECIRCULATION_PUMP, ), # Special case: Same underlying sensor with different ha sensor! LuxtronikBinarySensorEntityDescription( - key=SensorKey.DHW_CIRCULATION_PUMP, - luxtronik_key=LC.C0046_DHW_CIRCULATION_PUMP, + luxtronik_key=LC.DHW_CIRCULATION_PUMP, device_key=DeviceKey.domestic_water, icon="mdi:pump", device_class=BinarySensorDeviceClass.RUNNING, - visibility=LV.V0059_DHW_CIRCULATION_PUMP, + visibility=LV.DHW_CIRCULATION_PUMP, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.DHW_CHARGING_PUMP, - luxtronik_key=LC.C0046_DHW_CIRCULATION_PUMP, + luxtronik_key=LC.DHW_CIRCULATION_PUMP, device_key=DeviceKey.domestic_water, icon="mdi:pump", device_class=BinarySensorDeviceClass.RUNNING, - visibility=LV.V0059A_DHW_CHARGING_PUMP, + visibility=LV.DHW_CHARGING_PUMP, ), LuxtronikBinarySensorEntityDescription( - key=SensorKey.SOLAR_PUMP, - luxtronik_key=LC.C0052_SOLAR_PUMP, + luxtronik_key=LC.SOLAR_PUMP, device_key=DeviceKey.domestic_water, icon="mdi:pump", device_class=BinarySensorDeviceClass.RUNNING, - visibility=LV.V0250_SOLAR, + visibility=LV.SOLAR, ), # endregion Domestic water # region Cooling LuxtronikBinarySensorEntityDescription( - key=SensorKey.APPROVAL_COOLING, - luxtronik_key=LC.C0146_APPROVAL_COOLING, + luxtronik_key=LC.APPROVAL_COOLING, device_key=DeviceKey.cooling, icon="mdi:lock", device_class=BinarySensorDeviceClass.LOCK, diff --git a/custom_components/luxtronik/climate.py b/custom_components/luxtronik/climate.py index d124e6d..d7785e6 100644 --- a/custom_components/luxtronik/climate.py +++ b/custom_components/luxtronik/climate.py @@ -30,6 +30,9 @@ from .base import LuxtronikEntity from .common import get_sensor_data, state_as_number_or_none from .const import ( + Calculation_SensorKey as LC, + Parameter_SensorKey as LP, + Visibility_SensorKey as LV, CONF_COORDINATOR, CONF_HA_SENSOR_INDOOR_TEMPERATURE, CONF_HA_SENSOR_PREFIX, @@ -37,12 +40,8 @@ LUX_STATE_ICON_MAP, LUX_STATE_ICON_MAP_COOL, DeviceKey, - LuxCalculation, LuxMode, LuxOperationMode, - LuxParameter, - LuxVisibility, - SensorKey, ) from .coordinator import LuxtronikCoordinator, LuxtronikCoordinatorData from .model import LuxtronikClimateDescription @@ -106,19 +105,19 @@ supported_features=ClimateEntityFeature.AUX_HEAT | ClimateEntityFeature.PRESET_MODE # noqa: W503 | ClimateEntityFeature.TARGET_TEMPERATURE, # noqa: W503 - luxtronik_key=LuxParameter.P0003_MODE_HEATING, - # luxtronik_key_current_temperature=LuxCalculation.C0227_ROOM_THERMOSTAT_TEMPERATURE, - # luxtronik_key_target_temperature=LuxCalculation.C0228_ROOM_THERMOSTAT_TEMPERATURE_TARGET, - # luxtronik_key_has_target_temperature=LuxParameter - luxtronik_key_current_action=LuxCalculation.C0080_STATUS, + luxtronik_key=LP.MODE_HEATING, + # luxtronik_key_current_temperature=LC.C0227_ROOM_THERMOSTAT_TEMPERATURE, + # luxtronik_key_target_temperature=LC.C0228_ROOM_THERMOSTAT_TEMPERATURE_TARGET, + # luxtronik_key_has_target_temperature=LP + luxtronik_key_current_action=LC.STATUS, luxtronik_action_active=LuxOperationMode.heating.value, - # luxtronik_key_target_temperature_high=LuxParameter, - # luxtronik_key_target_temperature_low=LuxParameter, - luxtronik_key_correction_factor=LuxParameter.P0980_HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, - luxtronik_key_correction_target=LuxParameter.P0001_HEATING_TARGET_CORRECTION, + # luxtronik_key_target_temperature_high=LP, + # luxtronik_key_target_temperature_low=LP, + luxtronik_key_correction_factor=LP.HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, + luxtronik_key_correction_target=LP.HEATING_TARGET_CORRECTION, icon_by_state=LUX_STATE_ICON_MAP, temperature_unit=UnitOfTemperature.CELSIUS, - visibility=LuxVisibility.V0023_FLOW_IN_TEMPERATURE, + visibility=LV.FLOW_IN_TEMPERATURE, device_key=DeviceKey.heating, ), LuxtronikClimateDescription( @@ -128,19 +127,19 @@ hvac_action_mapping=HVAC_ACTION_MAPPING_COOL, preset_modes=[PRESET_NONE], supported_features=ClimateEntityFeature.TARGET_TEMPERATURE, - luxtronik_key=LuxParameter.P0108_MODE_COOLING, - # luxtronik_key_current_temperature=LuxCalculation.C0227_ROOM_THERMOSTAT_TEMPERATURE, - luxtronik_key_target_temperature=LuxParameter.P0110_COOLING_OUTDOOR_TEMP_THRESHOLD, - # luxtronik_key_has_target_temperature=LuxParameter - luxtronik_key_current_action=LuxCalculation.C0080_STATUS, + luxtronik_key=LP.MODE_COOLING, + # luxtronik_key_current_temperature=LC.C0227_ROOM_THERMOSTAT_TEMPERATURE, + luxtronik_key_target_temperature=LP.COOLING_OUTDOOR_TEMP_THRESHOLD, + # luxtronik_key_has_target_temperature=LP + luxtronik_key_current_action=LC.STATUS, luxtronik_action_active=LuxOperationMode.cooling.value, - # luxtronik_key_target_temperature_high=LuxParameter, - # luxtronik_key_target_temperature_low=LuxParameter, - # luxtronik_key_correction_factor=LuxParameter.P0980_HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, - # luxtronik_key_correction_target=LuxParameter.P0001_HEATING_TARGET_CORRECTION, + # luxtronik_key_target_temperature_high=LP, + # luxtronik_key_target_temperature_low=LP, + # luxtronik_key_correction_factor=LP.P0980_HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, + # luxtronik_key_correction_target=LP.P0001_HEATING_TARGET_CORRECTION, icon_by_state=LUX_STATE_ICON_MAP_COOL, temperature_unit=UnitOfTemperature.CELSIUS, - visibility=LuxVisibility.V0005_COOLING, + visibility=LV.COOLING, device_key=DeviceKey.cooling, ), ] @@ -214,7 +213,7 @@ def __init__( description=description, device_info_ident=description.device_key, ) - if description.luxtronik_key_current_temperature == LuxCalculation.UNSET: + if description.luxtronik_key_current_temperature == LC.UNSET: description.luxtronik_key_current_temperature = entry.data.get( CONF_HA_SENSOR_INDOOR_TEMPERATURE ) @@ -227,7 +226,7 @@ def __init__( self._attr_supported_features = description.supported_features self._sensor_data = get_sensor_data( - coordinator.data, description.luxtronik_key.value + coordinator.data, description.luxtronik_key ) async def _data_update(self, event): @@ -241,7 +240,7 @@ def _handle_coordinator_update( data = self.coordinator.data if data is None else data if data is None: return - mode = get_sensor_data(data, self.entity_description.luxtronik_key.value) + mode = get_sensor_data(data, self.entity_description.luxtronik_key) self._attr_hvac_mode = ( None if mode is None else self.entity_description.hvac_mode_mapping[mode] ) @@ -263,10 +262,10 @@ def _handle_coordinator_update( if isinstance(key, str): temp = self.hass.states.get(key) self._attr_current_temperature = state_as_number_or_none(temp, 0.0) - elif key != LuxCalculation.UNSET: + elif key != LC.UNSET: self._attr_current_temperature = get_sensor_data(data, key) key_tar = self.entity_description.luxtronik_key_target_temperature - if key_tar != LuxParameter.UNSET: + if key_tar != LP.UNSET: self._attr_target_temperature = get_sensor_data(data, key_tar) correction_factor = get_sensor_data( data, self.entity_description.luxtronik_key_correction_factor.value, False diff --git a/custom_components/luxtronik/common.py b/custom_components/luxtronik/common.py index 6e22cb5..3a37eda 100644 --- a/custom_components/luxtronik/common.py +++ b/custom_components/luxtronik/common.py @@ -16,11 +16,11 @@ CONF_PARAMETERS, CONF_VISIBILITIES, LOGGER, - LuxCalculation as LC, + Calculation_SensorKey as LC, LuxOperationMode, - LuxParameter as LP, + Parameter_SensorKey as LP, LuxStatus1Option, - LuxVisibility as LV, + Visibility_SensorKey as LV, ) from .model import LuxtronikCoordinatorData @@ -29,35 +29,43 @@ def get_sensor_data( sensors: LuxtronikCoordinatorData, - luxtronik_key: str | LP | LC | LV, + luxtronik_key: LP | LC | LV | int, warn_unset=True, ) -> Any: """Get sensor data.""" - if luxtronik_key is None or "." not in luxtronik_key: + # if luxtronik_key is None or "." not in luxtronik_key: + if luxtronik_key is None or luxtronik_key.value == -1: if warn_unset: LOGGER.warning( - "Function get_sensor_data luxtronik_key %s is None", luxtronik_key + "Function get_sensor_data luxtronik_key %s is None/UNSET", luxtronik_key ) return None - elif "{" in luxtronik_key: - return None - key = luxtronik_key.split(".") - group: str = key[0] - sensor_id: str = key[1] + # elif "{" in luxtronik_key: + # return None + # key = luxtronik_key.split(".") + # group: str = key[0] + # sensor_id: str = key[1] if sensors is None: return None - if group == CONF_PARAMETERS: - sensor = sensors.parameters.get(sensor_id) - elif group == CONF_CALCULATIONS: - sensor = sensors.calculations.get(sensor_id) - elif group == CONF_VISIBILITIES: - sensor = sensors.visibilities.get(sensor_id) + sensor = None + if isinstance(luxtronik_key, LP): # group == CONF_PARAMETERS: + # value = sensors.parameters[luxtronik_key.value] + sensor = sensors.parameters.get(luxtronik_key.value) + elif isinstance(luxtronik_key, LC): # group == CONF_CALCULATIONS: + # value = sensors.calculations[luxtronik_key.value] + sensor = sensors.calculations.get(luxtronik_key.value) + elif isinstance(luxtronik_key, LV): # group == CONF_VISIBILITIES: + # value = sensors.visibilities[luxtronik_key.value] + sensor = sensors.visibilities.get(luxtronik_key.value) + elif isinstance(sensor_id, int): + # sensor = self.client.calculations[sensor_id] + sensor = sensors.calculations.get(luxtronik_key.value) else: raise NotImplementedError if sensor is None: LOGGER.warning( "Get_sensor %s returns None", - sensor_id, + luxtronik_key, ) return None @@ -67,30 +75,30 @@ def get_sensor_data( def correct_key_value( value: Any, sensors: LuxtronikCoordinatorData | None, - sensor_id: str | LP | LC | LV, + sensor_id: LP | LC | LV | int, ) -> Any: """Handle special value corrections.""" if ( - sensor_id == LC.C0080_STATUS + sensor_id == LC.STATUS and value == LuxOperationMode.heating - and not get_sensor_data(sensors, LC.C0044_COMPRESSOR) - and not get_sensor_data(sensors, LC.C0048_ADDITIONAL_HEAT_GENERATOR) + and not get_sensor_data(sensors, LC.COMPRESSOR) + and not get_sensor_data(sensors, LC.ADDITIONAL_HEAT_GENERATOR) ): return LuxOperationMode.no_request # region Workaround Luxtronik Bug: Line 1 shows 'heatpump coming' on shutdown! if ( - sensor_id == LC.C0117_STATUS_LINE_1 + sensor_id == LC.STATUS_LINE_1 and value == LuxStatus1Option.heatpump_coming - and int(get_sensor_data(sensors, LC.C0072_TIMER_SCB_ON)) < 10 - and int(get_sensor_data(sensors, LC.C0071_TIMER_SCB_OFF)) > 0 + and int(get_sensor_data(sensors, LC.TIMER_SCB_ON)) < 10 + and int(get_sensor_data(sensors, LC.TIMER_SCB_OFF)) > 0 ): return LuxStatus1Option.heatpump_shutdown # endregion Workaround Luxtronik Bug: Line 1 shows 'heatpump coming' on shutdown! # region Workaround Luxtronik Bug: Line 1 shows 'pump forerun' on CompressorHeater! if ( - sensor_id == LC.C0117_STATUS_LINE_1 + sensor_id == LC.STATUS_LINE_1 and value == LuxStatus1Option.pump_forerun - and bool(get_sensor_data(sensors, LC.C0182_COMPRESSOR_HEATER)) + and bool(get_sensor_data(sensors, LC.COMPRESSOR_HEATER)) ): return LuxStatus1Option.compressor_heater # endregion Workaround Luxtronik Bug: Line 1 shows 'pump forerun' on CompressorHeater! diff --git a/custom_components/luxtronik/const.py b/custom_components/luxtronik/const.py index 506c508..e1a1199 100644 --- a/custom_components/luxtronik/const.py +++ b/custom_components/luxtronik/const.py @@ -8,9 +8,10 @@ from enum import StrEnum import voluptuous as vol +from homeassistant.components.sensor.const import SensorDeviceClass, SensorStateClass import homeassistant.helpers.config_validation as cv -from homeassistant.const import Platform +from homeassistant.const import Platform, UnitOfElectricPotential, UnitOfEnergy, UnitOfFrequency, UnitOfPower, UnitOfPressure, UnitOfTemperature, UnitOfTime from homeassistant.helpers.typing import StateType # endregion Imports @@ -117,67 +118,65 @@ class FirmwareVersionMinor(Enum): PRESET_SECOND_HEATSOURCE: Final = "second_heatsource" -class LuxOperationMode(StrEnum): +class LuxOperationMode(Enum): """Lux Operation modes heating, hot water etc.""" - heating: Final = "heating" # 0 - domestic_water: Final = "hot water" # 1 - swimming_pool_solar: Final = "swimming pool/solar" # 2 - evu: Final = "evu" # 3 - defrost: Final = "defrost" # 4 - no_request: Final = "no request" # 5 - heating_external_source: Final = "heating external source" # 6 - cooling: Final = "cooling" # 7 + heating: Final = 0 + domestic_water: Final = 1 # "hot water" + swimming_pool_solar: Final = 2 # "swimming pool/solar" + evu: Final = 3 + defrost: Final = 4 + no_request: Final = 5 + heating_external_source: Final = 6 + cooling: Final = 7 -class LuxMode(StrEnum): +class LuxMode(Enum): """Luxmodes off etc.""" - off: Final = "Off" - automatic: Final = "Automatic" - second_heatsource: Final = "Second heatsource" - party: Final = "Party" - holidays: Final = "Holidays" + automatic: Final = 0 + second_heatsource: Final = 1 + party: Final = 2 + holidays: Final = 3 + off: Final = 4 -class LuxStatus1Option(StrEnum): +class LuxStatus1Option(Enum): """LuxStatus1 option defrost etc.""" - heatpump_running: Final = "heatpump running" - heatpump_idle: Final = "heatpump idle" - heatpump_coming: Final = "heatpump coming" - heatpump_shutdown: Final = "heatpump shutdown" - errorcode_slot_zero: Final = "errorcode slot 0" - defrost: Final = "defrost" - witing_on_LIN_connection: Final = "witing on LIN connection" - compressor_heating_up: Final = "compressor heating up" - pump_forerun: Final = "pump forerun" - compressor_heater: Final = "compressor heater" + heatpump_running: Final = 0 + heatpump_idle: Final = 1 + heatpump_coming: Final = 2 + heatpump_shutdown: Final = 101 + errorcode_slot_zero: Final = 3 # "errorcode slot 0" + defrost: Final = 4 + witing_on_LIN_connection: Final = 5 # "witing on LIN connection" + compressor_heating_up: Final = 6 + pump_forerun: Final = 7 + compressor_heater: Final = 102 -class LuxStatus3Option(StrEnum): +class LuxStatus3Option(Enum): """LuxStatus3 option heating etc.""" - unknown: Final = ("unknown",) - none: Final = ("none",) - heating: Final = ("heating",) - no_request: Final = ("no request",) - grid_switch_on_delay: Final = ("grid switch on delay",) - cycle_lock: Final = ("cycle lock",) - lock_time: Final = ("lock time",) - domestic_water: Final = ("domestic water",) - info_bake_out_program: Final = ("info bake out program",) - defrost: Final = ("defrost",) - pump_forerun: Final = ("pump forerun",) - thermal_desinfection: Final = ("thermal desinfection",) - cooling: Final = ("cooling",) - swimming_pool_solar: Final = ("swimming pool/solar",) - heating_external_energy_source: Final = ("heating external energy source",) - domestic_water_external_energy_source: Final = ( - "domestic water external energy source", - ) - flow_monitoring: Final = ("flow monitoring",) - second_heat_generator_1_active: Final = ("second heat generator 1 active",) + unknown: Final = 101 + none: Final = 102 + heating: Final = 0 + no_request: Final = 1 + grid_switch_on_delay: Final = 2 + cycle_lock: Final = 3 + lock_time: Final = 4 + domestic_water: Final = 5 + info_bake_out_program: Final = 6 + defrost: Final = 7 + pump_forerun: Final = 8 + thermal_desinfection: Final = 9 + cooling: Final = 10 + swimming_pool_solar: Final = 12 # "swimming pool/solar" + heating_external_energy_source: Final = 13 + domestic_water_external_energy_source: Final = 14 + flow_monitoring: Final = 16 + second_heat_generator_1_active: Final = 17 class LuxMkTypes(Enum): @@ -251,92 +250,197 @@ class LuxSwitchoffReason(Enum): LUX_MODELS_OTHER = ["CB", "CI", "CN", "CS"] # endregion Lux Definitions -# region Lux parameters +# region Mappings +HEATPUMP_CODE_TYPE_MAP: Final[dict[int, str]] = { + 0: "ERC", + 1: "SW1", + 2: "SW2", + 3: "WW1", + 4: "WW2", + 5: "L1I", + 6: "L2I", + 7: "L1A", + 8: "L2A", + 9: "KSW", + 10: "KLW", + 11: "SWC", + 12: "LWC", + 13: "L2G", + 14: "WZS", + 15: "L1I407", + 16: "L2I407", + 17: "L1A407", + 18: "L2A407", + 19: "L2G407", + 20: "LWC407", + 21: "L1AREV", + 22: "L2AREV", + 23: "WWC1", + 24: "WWC2", + 25: "L2G404", + 26: "WZW", + 27: "L1S", + 28: "L1H", + 29: "L2H", + 30: "WZWD", + 31: "ERC", + 40: "WWB_20", + 41: "LD5", + 42: "LD7", + 43: "SW 37_45", + 44: "SW 58_69", + 45: "SW 29_56", + 46: "LD5 (230V)", + 47: "LD7 (230 V)", + 48: "LD9", + 49: "LD5 REV", + 50: "LD7 REV", + 51: "LD5 REV 230V", + 52: "LD7 REV 230V", + 53: "LD9 REV 230V", + 54: "SW 291", + 55: "LW SEC", + 56: "HMD 2", + 57: "MSW 4", + 58: "MSW 6", + 59: "MSW 8", + 60: "MSW 10", + 61: "MSW 12", + 62: "MSW 14", + 63: "MSW 17", + 64: "MSW 19", + 65: "MSW 23", + 66: "MSW 26", + 67: "MSW 30", + 68: "MSW 4S", + 69: "MSW 6S", + 70: "MSW 8S", + 71: "MSW 10S", + 72: "MSW 13S", + 73: "MSW 16S", + 74: "MSW2-6S", + 75: "MSW4-16", +} +UNIT_STATE_CLASS_MAP: Final[dict[UnitOfTemperature|UnitOfPressure|UnitOfEnergy|UnitOfElectricPotential|UnitOfPower|UnitOfFrequency, SensorStateClass]] = { + UnitOfTemperature.CELSIUS: SensorStateClass.MEASUREMENT, + UnitOfElectricPotential.VOLT: SensorStateClass.MEASUREMENT, + UnitOfPressure.BAR: SensorStateClass.MEASUREMENT, + UnitOfEnergy.KILO_WATT_HOUR: SensorStateClass.TOTAL_INCREASING, + UnitOfPower.WATT: SensorStateClass.MEASUREMENT, + UnitOfFrequency.HERTZ: SensorStateClass.MEASUREMENT, +} +UNIT_DEVICE_CLASS_MAP: Final[dict[UnitOfTemperature|UnitOfTime|UnitOfElectricPotential|UnitOfPressure|UnitOfEnergy|UnitOfPower|UnitOfFrequency, SensorDeviceClass]] = { + UnitOfTemperature.CELSIUS: SensorDeviceClass.TEMPERATURE, + UnitOfTemperature.KELVIN: SensorDeviceClass.TEMPERATURE, + UnitOfTime.MINUTES: SensorDeviceClass.DURATION, + UnitOfElectricPotential.VOLT: SensorDeviceClass.VOLTAGE, + UnitOfPressure.BAR: SensorDeviceClass.PRESSURE, + UnitOfEnergy.KILO_WATT_HOUR: SensorDeviceClass.ENERGY, + UnitOfPower.WATT: SensorDeviceClass.POWER, + UnitOfFrequency.HERTZ: SensorDeviceClass.FREQUENCY, +} +UNIT_ICON_MAP: Final[dict[UnitOfTemperature|UnitOfEnergy, str]] = { + UnitOfTemperature.CELSIUS: 'mdi:thermometer', + UnitOfTemperature.KELVIN: 'mdi:thermometer', + UnitOfEnergy.KILO_WATT_HOUR: 'mdi:lightning-bolt-circle', +} +UNIT_FACTOR_MAP: Final[dict[UnitOfTemperature|UnitOfTime|UnitOfElectricPotential|UnitOfEnergy, float]] = { + UnitOfTemperature.CELSIUS: 0.1, + UnitOfTemperature.KELVIN: 0.1, + UnitOfTime.MINUTES: 1, + UnitOfElectricPotential.VOLT: 0.01, + UnitOfEnergy.KILO_WATT_HOUR: 0.1, +} +# endregion Mappings -class LuxParameter(StrEnum): - """Luxtronik parameter ids.""" +# region Lux parameters - UNSET: Final = "UNSET" - P0001_HEATING_TARGET_CORRECTION: Final = "parameters.ID_Einst_WK_akt" - P0002_DHW_TARGET_TEMPERATURE: Final = "parameters.ID_Einst_BWS_akt" - P0003_MODE_HEATING: Final = "parameters.ID_Ba_Hz_akt" - P0004_MODE_DHW: Final = "parameters.ID_Ba_Bw_akt" - P0011_HEATING_CIRCUIT_CURVE1_TEMPERATURE: Final = "parameters.ID_Einst_HzHwHKE_akt" - P0012_HEATING_CIRCUIT_CURVE2_TEMPERATURE: Final = "parameters.ID_Einst_HzHKRANH_akt" - P0013_HEATING_CIRCUIT_CURVE_NIGHT_TEMPERATURE: Final = ( - "parameters.ID_Einst_HzHKRABS_akt" - ) + +# region Keys +class Parameter_SensorKey(Enum): + UNSET: Final = -1 + HEATING_TARGET_CORRECTION: Final = 1 # ID_Einst_WK_akt + DHW_TARGET_TEMPERATURE: Final = 2 # ID_Einst_BWS_akt + MODE_HEATING: Final = 3 # ID_Ba_Hz_akt + MODE_DHW: Final = 4 # ID_Ba_Bw_akt + HEATING_CIRCUIT_CURVE1_TEMPERATURE: Final = 11 # ID_Einst_HzHwHKE_akt + HEATING_CIRCUIT_CURVE2_TEMPERATURE: Final = 12 # ID_Einst_HzHKRANH_akt + HEATING_CIRCUIT_CURVE_NIGHT_TEMPERATURE: Final = 13 # ID_Einst_HzHKRABS_akt # luxtronik*_heating_circuit2_curve* - P0014_HEATING_CIRCUIT2_CURVE1_TEMPERATURE: Final = ( - "parameters.ID_Einst_HzMK1E_akt" # 260 - ) - P0015_HEATING_CIRCUIT2_CURVE2_TEMPERATURE: Final = ( - "parameters.ID_Einst_HzMK1ANH_akt" # 290 - ) - P0016_HEATING_CIRCUIT2_CURVE_NIGHT_TEMPERATURE: Final = ( - "parameters.ID_Einst_HzMK1ABS_akt" # 0 - ) + HEATING_CIRCUIT2_CURVE1_TEMPERATURE: Final = 14 # ID_Einst_HzMK1E_akt 260 + HEATING_CIRCUIT2_CURVE2_TEMPERATURE: Final = 15 # ID_Einst_HzMK1ANH_akt 290 + HEATING_CIRCUIT2_CURVE_NIGHT_TEMPERATURE: Final = 16 # ID_Einst_HzMK1ABS_akt 0 # P0036_SECOND_HEAT_GENERATOR: Final = "parameters.ID_Einst_ZWE1Art_akt" # = 1 --> Heating and domestic water - Is second heat generator activated 1=electrical heater - P0042_MIXING_CIRCUIT1_TYPE: Final = "parameters.ID_Einst_MK1Typ_akt" - P0047_DHW_THERMAL_DESINFECTION_TARGET: Final = "parameters.ID_Einst_LGST_akt" - P0049_PUMP_OPTIMIZATION: Final = "parameters.ID_Einst_Popt_akt" - # P0033_ROOM_THERMOSTAT_TYPE: Final = "parameters.ID_Einst_RFVEinb_akt" # != 0 --> Has_Room_Temp - P0074_DHW_HYSTERESIS: Final = "parameters.ID_Einst_BWS_Hyst_akt" - P0085_DHW_CHARGING_PUMP: Final = "parameters.ID_Einst_BWZIP_akt" # has_domestic_water_circulation_pump int() != 1 - P0088_HEATING_HYSTERESIS: Final = "parameters.ID_Einst_HRHyst_akt" - P0089_HEATING_MAX_FLOW_OUT_INCREASE_TEMPERATURE: Final = ( - "parameters.ID_Einst_TRErhmax_akt" + MIXING_CIRCUIT1_TYPE: Final = 42 # ID_Einst_MK1Typ_akt + AIR_DEFROST_TEMPERATURE: Final = ( + 44 # ID_Einst_TLAbt_akt" # "temp. air defrost" 7.0 C° ) - P0090_RELEASE_SECOND_HEAT_GENERATOR: Final = "parameters.ID_Einst_ZWEFreig_akt" + DHW_THERMAL_DESINFECTION_TARGET: Final = 47 # ID_Einst_LGST_akt + PUMP_OPTIMIZATION: Final = 49 # ID_Einst_Popt_akt + # P0033_ROOM_THERMOSTAT_TYPE: Final = "parameters.ID_Einst_RFVEinb_akt" # != 0 --> Has_Room_Temp + DHW_HYSTERESIS: Final = 74 # ID_Einst_BWS_Hyst_akt + DHW_CHARGING_PUMP: Final = ( + 85 # ID_Einst_BWZIP_akt # has_domestic_water_circulation_pump int() != 1 + ) + # "Return temperature limit" / "Rückl.-Begr." 50 35-70 C° step 1 -> Setting the maximum return setpoint temperatures in heating mode. + HEATING_RETURN_TEMPERATURE_LIMIT: Final = 87 # ID_Einst_TRBegr_akt + HEATING_HYSTERESIS: Final = 88 # ID_Einst_HRHyst_akt + HEATING_MAX_FLOW_OUT_INCREASE_TEMPERATURE: Final = 89 # ID_Einst_TRErhmax_akt + RELEASE_SECOND_HEAT_GENERATOR: Final = 90 # ID_Einst_ZWEFreig_akt + # P0091_ "max. outdoor temp." 35 20-45 + # P0092 "min. outdoor temp." -20-10 + AIR_DEFROST_STOP_TEMPERATURE: Final = 98 # ID_Einst_TAbtEnd_akt # MODE_COOLING: Automatic or Off - P0108_MODE_COOLING: Final = "parameters.ID_Einst_BA_Kuehl_akt" - P0110_COOLING_OUTDOOR_TEMP_THRESHOLD: Final = "parameters.ID_Einst_KuehlFreig_akt" - P0111_HEATING_NIGHT_LOWERING_TO_TEMPERATURE: Final = ( - "parameters.ID_Einst_TAbsMin_akt" - ) - P0122_SOLAR_PUMP_ON_DIFFERENCE_TEMPERATURE: Final = ( - "parameters.ID_Einst_TDC_Ein_akt" - ) - P0123_SOLAR_PUMP_OFF_DIFFERENCE_TEMPERATURE: Final = ( - "parameters.ID_Einst_TDC_Aus_akt" - ) - P0130_MIXING_CIRCUIT2_TYPE: Final = "parameters.ID_Einst_MK2Typ_akt" - P0132_COOLING_TARGET_TEMPERATURE_MK1: Final = "parameters.ID_Sollwert_KuCft1_akt" - P0133_COOLING_TARGET_TEMPERATURE_MK2: Final = "parameters.ID_Sollwert_KuCft2_akt" - P0780_MIXING_CIRCUIT3_TYPE: Final = "parameters.ID_Einst_MK3Typ_akt" - P0149_FLOW_IN_TEMPERATURE_MAX_ALLOWED: Final = "parameters.ID_Einst_TVLmax_akt" - P0289_SOLAR_PUMP_OFF_MAX_DIFFERENCE_TEMPERATURE_BOILER: Final = ( - "parameters.ID_Einst_TDC_Max_akt" - ) - P0699_HEATING_THRESHOLD: Final = "parameters.ID_Einst_Heizgrenze" - P0700_HEATING_THRESHOLD_TEMPERATURE: Final = "parameters.ID_Einst_Heizgrenze_Temp" - P0716_0720_SWITCHOFF_REASON: Final = "parameters.ID_Switchoff_file_{ID}_0" # e.g. ID_Switchoff_file_0_0 - ID_Switchoff_file_4_0 - P0721_0725_SWITCHOFF_TIMESTAMP: Final = "parameters.ID_Switchoff_file_{ID}_1" # e.g. ID_Switchoff_file_0_1 - ID_Switchoff_file_4_1 + MODE_COOLING: Final = 108 # ID_Einst_BA_Kuehl_akt + COOLING_OUTDOOR_TEMP_THRESHOLD: Final = 110 # ID_Einst_KuehlFreig_akt + HEATING_NIGHT_LOWERING_TO_TEMPERATURE: Final = 111 # ID_Einst_TAbsMin_akt + SOLAR_PUMP_ON_DIFFERENCE_TEMPERATURE: Final = 122 # ID_Einst_TDC_Ein_akt + SOLAR_PUMP_OFF_DIFFERENCE_TEMPERATURE: Final = 123 # ID_Einst_TDC_Aus_akt + SOLAR_PUMP_OFF_MAX_DIFFERENCE_TEMPERATURE_BOILER: Final = ( + 124 # ID_Einst_TDC_Max_akt + ) + # P0125_HEATING_EXTERNAL_ENERGY_SOURCE TEE heating External energy source 10k 1.0-15.0 0.5 + # P0126_DHW_EXTERNAL_ENERGY_SOURCE TEE DHW External energy source 5k 1.0-15.0 0.5 + MIXING_CIRCUIT2_TYPE: Final = 130 # ID_Einst_MK2Typ_akt + COOLING_TARGET_TEMPERATURE_MK1: Final = 132 # ID_Sollwert_KuCft1_akt + COOLING_TARGET_TEMPERATURE_MK2: Final = 133 # ID_Sollwert_KuCft2_akt + FLOW_IN_TEMPERATURE_MAX_ALLOWED: Final = 149 # ID_Einst_TVLmax_akt + HEATING_THRESHOLD: Final = 699 # ID_Einst_Heizgrenze + HEATING_THRESHOLD_TEMPERATURE: Final = 700 # ID_Einst_Heizgrenze_Temp + # P0716_0720_SWITCHOFF_REASON: Final = "parameters.ID_Switchoff_file_{ID}_0" # e.g. ID_Switchoff_file_0_0 - ID_Switchoff_file_4_0 + SWITCHOFF_REASON: Final = 716 # ID_Switchoff_file_{ID}_0" # e.g. ID_Switchoff_file_0_0 - ID_Switchoff_file_4_0 + SWITCHOFF_REASON_2: Final = 717 + SWITCHOFF_REASON_3: Final = 718 + SWITCHOFF_REASON_4: Final = 719 + SWITCHOFF_REASON_5: Final = 720 + # P0721_0725_SWITCHOFF_TIMESTAMP: Final = "parameters.ID_Switchoff_file_{ID}_1" # e.g. ID_Switchoff_file_0_1 - ID_Switchoff_file_4_1 + SWITCHOFF_TIMESTAMP: Final = 721 # ID_Switchoff_file_{ID}_1 # e.g. ID_Switchoff_file_0_1 - ID_Switchoff_file_4_1 + SWITCHOFF_TIMESTAMP_2: Final = 722 + SWITCHOFF_TIMESTAMP_3: Final = 723 + SWITCHOFF_TIMESTAMP_4: Final = 724 + SWITCHOFF_TIMESTAMP_5: Final = 725 # luxtronik*_heating_circuit3_curve* - P0774_HEATING_CIRCUIT3_CURVE1_TEMPERATURE: Final = ( - "parameters.ID_Einst_HzMK3E_akt" # 270 - ) - P0775_HEATING_CIRCUIT3_CURVE2_TEMPERATURE: Final = ( - "parameters.ID_Einst_HzMK3ANH_akt" # 290 - ) - P0776_HEATING_CIRCUIT3_CURVE_NIGHT_TEMPERATURE: Final = ( - "parameters.ID_Einst_HzMK3ABS_akt" # 0 - ) - P0850_COOLING_START_DELAY_HOURS: Final = "parameters.ID_Einst_Kuhl_Zeit_Ein_akt" - P0851_COOLING_STOP_DELAY_HOURS: Final = "parameters.ID_Einst_Kuhl_Zeit_Aus_akt" - P0860_REMOTE_MAINTENANCE: Final = "parameters.ID_Einst_Fernwartung_akt" - P0864_PUMP_OPTIMIZATION_TIME: Final = "parameters.ID_Einst_Popt_Nachlauf_akt" - P0867_EFFICIENCY_PUMP_NOMINAL: Final = ( - "parameters.ID_Einst_Effizienzpumpe_Nominal_akt" - ) - P0868_EFFICIENCY_PUMP_MINIMAL: Final = ( - "parameters.ID_Einst_Effizienzpumpe_Minimal_akt" - ) - P0869_EFFICIENCY_PUMP: Final = "parameters.ID_Einst_Effizienzpumpe_akt" + HEATING_CIRCUIT3_CURVE1_TEMPERATURE: Final = 774 # ID_Einst_HzMK3E_akt # 270 + HEATING_CIRCUIT3_CURVE2_TEMPERATURE: Final = 775 # ID_Einst_HzMK3ANH_akt # 290 + HEATING_CIRCUIT3_CURVE_NIGHT_TEMPERATURE: Final = 776 # ID_Einst_HzMK3ABS_akt # 0 + MIXING_CIRCUIT3_TYPE: Final = 780 # ID_Einst_MK3Typ_akt + COOLING_START_DELAY_HOURS: Final = 850 # ID_Einst_Kuhl_Zeit_Ein_akt + COOLING_STOP_DELAY_HOURS: Final = 851 # ID_Einst_Kuhl_Zeit_Aus_akt + REMOTE_MAINTENANCE: Final = 860 # ID_Einst_Fernwartung_akt + # "min OT flow max": Heat source temperature-dependent adjustment of the flow temperature. The outside temperature, up to which the flow max. + # temperature with the heat pump may be increased, is adjusted here. Below this outside temperature, the actual VL maximum + # temperature of the heat pump will fall linearally to the value “low limit of applic.“. + # P0862_ "min OT flow max" -2C° -20-5 1 + # "flow operation limit": Heat source temperature-dependent adjustment of the flow temperature. Here, the maximum forward flow temperature of the heat pump is set at an outside temperature of -20°C. + # P0863_ "flow operation limit" 58C° 35-75 1 + PUMP_OPTIMIZATION_TIME: Final = 864 # ID_Einst_Popt_Nachlauf_akt + EFFICIENCY_PUMP_NOMINAL: Final = 867 # ID_Einst_Effizienzpumpe_Nominal_akt + EFFICIENCY_PUMP_MINIMAL: Final = 868 # ID_Einst_Effizienzpumpe_Minimal_akt + EFFICIENCY_PUMP: Final = 869 # ID_Einst_Effizienzpumpe_akt # P0870_AMOUNT_COUNTER_ACTIVE: Final = "parameters.ID_Einst_Waermemenge_akt" - P0874_SERIAL_NUMBER: Final = "parameters.ID_WP_SerienNummer_DATUM" - P0875_SERIAL_NUMBER_MODEL: Final = "parameters.ID_WP_SerienNummer_HEX" + SERIAL_NUMBER: Final = 874 # ID_WP_SerienNummer_DATUM + SERIAL_NUMBER_MODEL: Final = 875 # ID_WP_SerienNummer_HEX # "852 ID_Waermemenge_Seit ": "2566896", # "853 ID_Waermemenge_WQ ": "0", @@ -346,360 +450,182 @@ class LuxParameter(StrEnum): # "879 ID_Waermemenge_SW ": "0", # "880 ID_Waermemenge_Datum ": "1483648906", <-- Unix timestamp! 5.1.2017 - P0882_SOLAR_OPERATION_HOURS: Final = "parameters.ID_BSTD_Solar" - P0883_SOLAR_PUMP_MAX_TEMPERATURE_COLLECTOR: Final = ( - "parameters.ID_Einst_TDC_Koll_Max_akt" - ) + SOLAR_OPERATION_HOURS: Final = 882 # ID_BSTD_Solar + SOLAR_PUMP_MAX_TEMPERATURE_COLLECTOR: Final = 883 # ID_Einst_TDC_Koll_Max_akt # P0894_VENTILATION_MODE: Final = "parameters.ID_Einst_BA_Lueftung_akt" # "Automatic", "Party", "Holidays", "Off" - P0966_COOLING_TARGET_TEMPERATURE_MK3: Final = "parameters.ID_Sollwert_KuCft3_akt" - P0979_HEATING_MIN_FLOW_OUT_TEMPERATURE: Final = ( - "parameters.ID_Einst_Minimale_Ruecklaufsolltemperatur" - ) - P0980_HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR: Final = ( - "parameters.ID_RBE_Einflussfaktor_RT_akt" - ) - P0992_RELEASE_TIME_SECOND_HEAT_GENERATOR: Final = ( - "parameters.ID_Einst_Freigabe_Zeit_ZWE" - ) - P1032_HEATING_MAXIMUM_CIRCULATION_PUMP_SPEED: Final = ( - "parameters.ID_Einst_P155_PumpHeat_Max" - ) - P1033_PUMP_HEAT_CONTROL: Final = "parameters.ID_Einst_P155_PumpHeatCtrl" - P1059_ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER: Final = ( - "parameters.ID_Waermemenge_ZWE" - ) + COOLING_TARGET_TEMPERATURE_MK3: Final = 966 # ID_Sollwert_KuCft3_akt + # P0973_ "DHW temp. max." 65C° 30-65 0.5 + HEATING_MIN_FLOW_OUT_TEMPERATURE: Final = ( + 979 # ID_Einst_Minimale_Ruecklaufsolltemperatur + ) + HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR: Final = 980 # ID_RBE_Einflussfaktor_RT_akt + RELEASE_TIME_SECOND_HEAT_GENERATOR: Final = 992 # ID_Einst_Freigabe_Zeit_ZWE + HEATING_MAXIMUM_CIRCULATION_PUMP_SPEED: Final = 1032 # ID_Einst_P155_PumpHeat_Max + PUMP_HEAT_CONTROL: Final = 1033 # ID_Einst_P155_PumpHeatCtrl + ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER: Final = 1059 # ID_Waermemenge_ZWE # "1060 ID_Waermemenge_Reset ": "535051", # "1061 ID_Waermemenge_Reset_2 ": "0", - P1087_SILENT_MODE: Final = "parameters.Unknown_Parameter_1087" # Silent mode On/Off - P1119_LAST_DEFROST_TIMESTAMP: Final = ( - "parameters.Unknown_Parameter_1119" # 1685073431 -> 26.5.23 05:57 + SILENT_MODE: Final = 1087 # Unknown_Parameter_1087 # Silent mode On/Off + LAST_DEFROST_TIMESTAMP: Final = ( + 1119 # Unknown_Parameter_1119 # 1685073431 -> 26.5.23 05:57 ) - P1136_HEAT_ENERGY_INPUT: Final = "parameters.Unknown_Parameter_1136" - P1137_DHW_ENERGY_INPUT: Final = "parameters.Unknown_Parameter_1137" + HEAT_ENERGY_INPUT: Final = 1136 # Unknown_Parameter_1136 + DHW_ENERGY_INPUT: Final = 1137 # Unknown_Parameter_1137 # ? P1138_SWIMMING_POOL_ENERGY_INPUT: Final = "parameters.Unknown_Parameter_1138" --> # ? P1139_COOLING_ENERGY_INPUT: Final = "parameters.Unknown_Parameter_1139" # ? P1140_SECOND_HEAT_SOURCE_DHW_ENERGY_INPUT: Final = "parameters.Unknown_Parameter_1140" -# endregion Lux parameters - -LUX_PARAMETER_MK_SENSORS: Final = [ - LuxParameter.P0042_MIXING_CIRCUIT1_TYPE, - LuxParameter.P0130_MIXING_CIRCUIT2_TYPE, - LuxParameter.P0780_MIXING_CIRCUIT3_TYPE, -] - - -# region Lux calculations -class LuxCalculation(StrEnum): - """Luxtronik calculation ids.""" - - UNSET: Final = "UNSET" - C0010_FLOW_IN_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TVL" - C0011_FLOW_OUT_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TRL" - C0012_FLOW_OUT_TEMPERATURE_TARGET: Final = "calculations.ID_WEB_Sollwert_TRL_HZ" - C0013_FLOW_OUT_TEMPERATURE_EXTERNAL: Final = ( - "calculations.ID_WEB_Temperatur_TRL_ext" - ) - C0014_HOT_GAS_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_THG" - C0015_OUTDOOR_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TA" - C0016_OUTDOOR_TEMPERATURE_AVERAGE: Final = "calculations.ID_WEB_Mitteltemperatur" - C0017_DHW_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TBW" - C0020_HEAT_SOURCE_OUTPUT_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TWA" - C0026_SOLAR_COLLECTOR_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TSK" - C0027_SOLAR_BUFFER_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TSS" - C0029_DEFROST_END_FLOW_OKAY: Final = "calculations.ID_WEB_ASDin" - C0031_EVU_UNLOCKED: Final = "calculations.ID_WEB_EVUin" +class Calculation_SensorKey(Enum): + UNSET: Final = -1 + FLOW_IN_TEMPERATURE: Final = 10 # ID_WEB_Temperatur_TVL + FLOW_OUT_TEMPERATURE: Final = 11 # ID_WEB_Temperatur_TRL + FLOW_OUT_TEMPERATURE_TARGET: Final = 12 # ID_WEB_Sollwert_TRL_HZ + FLOW_OUT_TEMPERATURE_EXTERNAL: Final = 13 # ID_WEB_Temperatur_TRL_ext + HOT_GAS_TEMPERATURE: Final = 14 # ID_WEB_Temperatur_THG + OUTDOOR_TEMPERATURE: Final = 15 # ID_WEB_Temperatur_TA + OUTDOOR_TEMPERATURE_AVERAGE: Final = 16 # ID_WEB_Mitteltemperatur + DHW_TEMPERATURE: Final = 17 # ID_WEB_Temperatur_TBW + HEAT_SOURCE_INPUT_TEMPERATURE: Final = 19 # ID_WEB_Temperatur_TWE + HEAT_SOURCE_OUTPUT_TEMPERATURE: Final = 20 # ID_WEB_Temperatur_TWA + SOLAR_COLLECTOR_TEMPERATURE: Final = 26 # ID_WEB_Temperatur_TSK + SOLAR_BUFFER_TEMPERATURE: Final = 27 # ID_WEB_Temperatur_TSS + DEFROST_END_FLOW_OKAY: Final = 29 # ID_WEB_ASDin + EVU_UNLOCKED: Final = 31 # ID_WEB_EVUin # C0032_HIGH_PRESSURE_OKAY: Final = "calculations.ID_WEB_HDin" # True/False -> Hochdruck OK - C0034_MOTOR_PROTECTION: Final = "calculations.ID_WEB_MOTin" - C0037_DEFROST_VALVE: Final = "calculations.ID_WEB_AVout" - C0038_DHW_RECIRCULATION_PUMP: Final = "calculations.ID_WEB_BUPout" - C0039_CIRCULATION_PUMP_HEATING: Final = "calculations.ID_WEB_HUPout" + MOTOR_PROTECTION: Final = 34 # ID_WEB_MOTin + DEFROST_VALVE: Final = 37 # ID_WEB_AVout + DHW_RECIRCULATION_PUMP: Final = 38 # ID_WEB_BUPout + CIRCULATION_PUMP_HEATING: Final = 39 # ID_WEB_HUPout # C0040_MIXER1_OPENED: Final = "calculations.ID_WEB_MA1out" # True/False -> Mischer 1 auf # C0041_MIXER1_CLOSED: Final = "calculations.ID_WEB_MZ1out" # True/False -> Mischer 1 zu - C0043_PUMP_FLOW: Final = "calculations.ID_WEB_VBOout" - C0044_COMPRESSOR: Final = "calculations.ID_WEB_VD1out" - C0045_COMPRESSOR2: Final = "calculations.ID_WEB_VD2out" - C0046_DHW_CIRCULATION_PUMP: Final = "calculations.ID_WEB_ZIPout" - C0047_ADDITIONAL_CIRCULATION_PUMP: Final = "calculations.ID_WEB_ZUPout" - C0048_ADDITIONAL_HEAT_GENERATOR: Final = "calculations.ID_WEB_ZW1out" - C0049_DISTURBANCE_OUTPUT: Final = "calculations.ID_WEB_ZW2SSTout" + PUMP_FLOW: Final = 43 # ID_WEB_VBOout + COMPRESSOR: Final = 44 # ID_WEB_VD1out + COMPRESSOR2: Final = 45 # ID_WEB_VD2out + DHW_CIRCULATION_PUMP: Final = 46 # ID_WEB_ZIPout + ADDITIONAL_CIRCULATION_PUMP: Final = 47 # ID_WEB_ZUPout + ADDITIONAL_HEAT_GENERATOR: Final = 48 # ID_WEB_ZW1out + DISTURBANCE_OUTPUT: Final = 49 # ID_WEB_ZW2SSTout # C0051: Final = "calculations.ID_WEB_FP2out" # True/False -> FBH Umwälzpumpe 2 - C0052_SOLAR_PUMP: Final = "calculations.ID_WEB_SLPout" + SOLAR_PUMP: Final = 52 # ID_WEB_SLPout # C0054_MIXER2_CLOSED: Final = "calculations.ID_WEB_MZ2out" # True/False -> Mischer 2 zu # C0055_MIXER2_OPENED: Final = "calculations.ID_WEB_MA2out" # True/False -> Mischer 2 auf - C0056_COMPRESSOR1_OPERATION_HOURS: Final = "calculations.ID_WEB_Zaehler_BetrZeitVD1" - C0057_COMPRESSOR1_IMPULSES: Final = "calculations.ID_WEB_Zaehler_BetrZeitImpVD1" - C0058_COMPRESSOR2_OPERATION_HOURS: Final = "calculations.ID_WEB_Zaehler_BetrZeitVD2" - C0059_COMPRESSOR2_IMPULSES: Final = "calculations.ID_WEB_Zaehler_BetrZeitImpVD2" - C0060_ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS: Final = ( - "calculations.ID_WEB_Zaehler_BetrZeitZWE1" - ) - C0063_OPERATION_HOURS: Final = "calculations.ID_WEB_Zaehler_BetrZeitWP" - C0064_OPERATION_HOURS_HEATING: Final = "calculations.ID_WEB_Zaehler_BetrZeitHz" - C0065_DHW_OPERATION_HOURS: Final = "calculations.ID_WEB_Zaehler_BetrZeitBW" - C0066_OPERATION_HOURS_COOLING: Final = "calculations.ID_WEB_Zaehler_BetrZeitKue" - C0067_TIMER_HEATPUMP_ON: Final = "calculations.ID_WEB_Time_WPein_akt" - C0068_TIMER_ADD_HEAT_GENERATOR_ON: Final = "calculations.ID_WEB_Time_ZWE1_akt" - C0069_TIMER_SEC_HEAT_GENERATOR_ON: Final = "calculations.ID_WEB_Time_ZWE2_akt" - C0070_TIMER_NET_INPUT_DELAY: Final = "calculations.ID_WEB_Timer_EinschVerz" - C0071_TIMER_SCB_OFF: Final = "calculations.ID_WEB_Time_SSPAUS_akt" - C0072_TIMER_SCB_ON: Final = "calculations.ID_WEB_Time_SSPEIN_akt" - C0073_TIMER_COMPRESSOR_OFF: Final = "calculations.ID_WEB_Time_VDStd_akt" - C0074_TIMER_HC_ADD: Final = "calculations.ID_WEB_Time_HRM_akt" - C0075_TIMER_HC_LESS: Final = "calculations.ID_WEB_Time_HRW_akt" - C0076_TIMER_TDI: Final = "calculations.ID_WEB_Time_LGS_akt" - C0077_TIMER_BLOCK_DHW: Final = "calculations.ID_WEB_Time_SBW_akt" - C0078_MODEL_CODE: Final = "calculations.ID_WEB_Code_WP_akt" - C0080_STATUS: Final = "calculations.ID_WEB_WP_BZ_akt" - C0081_FIRMWARE_VERSION: Final = "calculations.ID_WEB_SoftStand" - C0095_ERROR_TIME: Final = "calculations.ID_WEB_ERROR_Time0" - C0100_ERROR_REASON: Final = "calculations.ID_WEB_ERROR_Nr0" + COMPRESSOR1_OPERATION_HOURS: Final = 56 # ID_WEB_Zaehler_BetrZeitVD1 + COMPRESSOR1_IMPULSES: Final = 57 # ID_WEB_Zaehler_BetrZeitImpVD1 + COMPRESSOR2_OPERATION_HOURS: Final = 58 # ID_WEB_Zaehler_BetrZeitVD2 + COMPRESSOR2_IMPULSES: Final = 59 # ID_WEB_Zaehler_BetrZeitImpVD2 + ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS: Final = 60 # ID_WEB_Zaehler_BetrZeitZWE1 + OPERATION_HOURS: Final = 63 # ID_WEB_Zaehler_BetrZeitWP + OPERATION_HOURS_HEATING: Final = 64 # ID_WEB_Zaehler_BetrZeitHz + DHW_OPERATION_HOURS: Final = 65 # ID_WEB_Zaehler_BetrZeitBW + OPERATION_HOURS_COOLING: Final = 66 # ID_WEB_Zaehler_BetrZeitKue + TIMER_HEATPUMP_ON: Final = 67 # ID_WEB_Time_WPein_akt + TIMER_ADD_HEAT_GENERATOR_ON: Final = 68 # ID_WEB_Time_ZWE1_akt + TIMER_SEC_HEAT_GENERATOR_ON: Final = 69 # ID_WEB_Time_ZWE2_akt + TIMER_NET_INPUT_DELAY: Final = 70 # ID_WEB_Timer_EinschVerz + TIMER_SCB_OFF: Final = 71 # ID_WEB_Time_SSPAUS_akt + TIMER_SCB_ON: Final = 72 # ID_WEB_Time_SSPEIN_akt + TIMER_COMPRESSOR_OFF: Final = 73 # ID_WEB_Time_VDStd_akt + TIMER_HC_ADD: Final = 74 # ID_WEB_Time_HRM_akt + TIMER_HC_LESS: Final = 75 # ID_WEB_Time_HRW_akt + TIMER_TDI: Final = 76 # ID_WEB_Time_LGS_akt + TIMER_BLOCK_DHW: Final = 77 # ID_WEB_Time_SBW_akt + MODEL_CODE: Final = 78 # ID_WEB_Code_WP_akt + STATUS: Final = 80 # ID_WEB_WP_BZ_akt + FIRMWARE_VERSION: Final = 81 # ID_WEB_SoftStand + ERROR_TIME: Final = 95 # ID_WEB_ERROR_Time0 + ERROR_REASON: Final = 100 # ID_WEB_ERROR_Nr0 # TODO: ! # C0105_ERROR_COUNTER: Final = "calculations.ID_WEB_AnzahlFehlerInSpeicher" - C0117_STATUS_LINE_1: Final = "calculations.ID_WEB_HauptMenuStatus_Zeile1" - C0118_STATUS_LINE_2: Final = "calculations.ID_WEB_HauptMenuStatus_Zeile2" - C0119_STATUS_LINE_3: Final = "calculations.ID_WEB_HauptMenuStatus_Zeile3" - C0120_STATUS_TIME: Final = "calculations.ID_WEB_HauptMenuStatus_Zeit" - C0141_TIMER_DEFROST: Final = "calculations.ID_WEB_Time_AbtIn" - C0146_APPROVAL_COOLING: Final = "calculations.ID_WEB_FreigabKuehl" - C0151_HEAT_AMOUNT_HEATING: Final = "calculations.ID_WEB_WMZ_Heizung" - C0152_DHW_HEAT_AMOUNT: Final = "calculations.ID_WEB_WMZ_Brauchwasser" - C0154_HEAT_AMOUNT_COUNTER: Final = "calculations.ID_WEB_WMZ_Seit" # 25668.9 - C0155_HEAT_AMOUNT_FLOW_RATE: Final = "calculations.ID_WEB_WMZ_Durchfluss" - C0156_ANALOG_OUT1: Final = "calculations.ID_WEB_AnalogOut1" - C0157_ANALOG_OUT2: Final = "calculations.ID_WEB_AnalogOut2" - C0158_TIMER_HOT_GAS: Final = "calculations.ID_WEB_Time_Heissgas" - C0173_HEAT_SOURCE_FLOW_RATE: Final = "calculations.ID_WEB_Durchfluss_WQ" - C0175_SUCTION_EVAPORATOR_TEMPERATURE: Final = ( - "calculations.ID_WEB_LIN_ANSAUG_VERDAMPFER" - ) - C0176_SUCTION_COMPRESSOR_TEMPERATURE: Final = ( - "calculations.ID_WEB_LIN_ANSAUG_VERDICHTER" - ) - C0177_COMPRESSOR_HEATING_TEMPERATURE: Final = "calculations.ID_WEB_LIN_VDH" - C0178_OVERHEATING_TEMPERATURE: Final = "calculations.ID_WEB_LIN_UH" - C0179_OVERHEATING_TARGET_TEMPERATURE: Final = "calculations.ID_WEB_LIN_UH_Soll" - C0180_HIGH_PRESSURE: Final = "calculations.ID_WEB_LIN_HD" - C0181_LOW_PRESSURE: Final = "calculations.ID_WEB_LIN_ND" - C0182_COMPRESSOR_HEATER: Final = "calculations.ID_WEB_LIN_VDH_out" + STATUS_LINE_1: Final = 117 # ID_WEB_HauptMenuStatus_Zeile1 + STATUS_LINE_2: Final = 118 # ID_WEB_HauptMenuStatus_Zeile2 + STATUS_LINE_3: Final = 119 # ID_WEB_HauptMenuStatus_Zeile3 + STATUS_TIME: Final = 120 # ID_WEB_HauptMenuStatus_Zeit + TIMER_DEFROST: Final = 141 # ID_WEB_Time_AbtIn + APPROVAL_COOLING: Final = 146 # ID_WEB_FreigabKuehl + HEAT_AMOUNT_HEATING: Final = 151 # ID_WEB_WMZ_Heizung + DHW_HEAT_AMOUNT: Final = 152 # ID_WEB_WMZ_Brauchwasser + HEAT_AMOUNT_COUNTER: Final = 154 # ID_WEB_WMZ_Seit" # 25668. + HEAT_AMOUNT_FLOW_RATE: Final = 155 # ID_WEB_WMZ_Durchfluss + ANALOG_OUT1: Final = 156 # ID_WEB_AnalogOut1 + ANALOG_OUT2: Final = 157 # ID_WEB_AnalogOut2 + TIMER_HOT_GAS: Final = 158 # ID_WEB_Time_Heissgas + HEAT_SOURCE_FLOW_RATE: Final = 173 # ID_WEB_Durchfluss_WQ + SUCTION_EVAPORATOR_TEMPERATURE: Final = 175 # ID_WEB_LIN_ANSAUG_VERDAMPFER + SUCTION_COMPRESSOR_TEMPERATURE: Final = 176 # ID_WEB_LIN_ANSAUG_VERDICHTER + COMPRESSOR_HEATING_TEMPERATURE: Final = 177 # ID_WEB_LIN_VDH + OVERHEATING_TEMPERATURE: Final = 178 # ID_WEB_LIN_UH + OVERHEATING_TARGET_TEMPERATURE: Final = 179 # ID_WEB_LIN_UH_Soll + HIGH_PRESSURE: Final = 180 # ID_WEB_LIN_HD + LOW_PRESSURE: Final = 181 # ID_WEB_LIN_ND + COMPRESSOR_HEATER: Final = 182 # ID_WEB_LIN_VDH_out # C0187_CURRENT_OUTPUT: Final = "calculations.ID_WEB_SEC_Qh_Soll" # C0188_CURRENT_OUTPUT: Final = "calculations.ID_WEB_SEC_Qh_Ist" - C0204_HEAT_SOURCE_INPUT_TEMPERATURE: Final = "calculations.ID_WEB_Temperatur_TWE" - C0227_ROOM_THERMOSTAT_TEMPERATURE: Final = "calculations.ID_WEB_RBE_RT_Ist" - C0228_ROOM_THERMOSTAT_TEMPERATURE_TARGET: Final = "calculations.ID_WEB_RBE_RT_Soll" - C0231_PUMP_FREQUENCY: Final = "calculations.ID_WEB_Freq_VD" - C0239_PUMP_FLOW_DELTA_TARGET: Final = "calculations.Unknown_Calculation_239" + HEAT_SOURCE_INPUT_TEMPERATURE_2: Final = 204 # ID_WEB_Temperatur_TWE_2 + ROOM_THERMOSTAT_TEMPERATURE: Final = 227 # ID_WEB_RBE_RT_Ist + ROOM_THERMOSTAT_TEMPERATURE_TARGET: Final = 228 # ID_WEB_RBE_RT_Soll + PUMP_FREQUENCY: Final = 231 # ID_WEB_Freq_VD + PUMP_FLOW_DELTA_TARGET: Final = 239 # Unknown_Calculation_239 # 239: Kelvin("VBO_Temp_Spread_Soll"), / 10, measurement, delta - ait_hup_vbo_calculated - C0240_PUMP_FLOW_DELTA: Final = "calculations.Unknown_Calculation_240" + PUMP_FLOW_DELTA: Final = 240 # Unknown_Calculation_240 # 240: Kelvin("VBO_Temp_Spread_Ist"), / 10, measurement, delta - ait_vbo_delta # 241: Percent2("HUP_PWM"), - C0242_CIRCULATION_PUMP_DELTA_TARGET: Final = "calculations.Unknown_Calculation_242" + CIRCULATION_PUMP_DELTA_TARGET: Final = 242 # Unknown_Calculation_242 # 242: Kelvin("HUP_Temp_Spread_Soll"), / 10, measurement, delta - ait_hup_delta_calculated - C0243_CIRCULATION_PUMP_DELTA: Final = "calculations.Unknown_Calculation_243" + CIRCULATION_PUMP_DELTA: Final = 243 # Unknown_Calculation_243 # 243: Kelvin("HUP_Temp_Spread_Ist"), / 10, measurement, delta - ait_hup_delta # 254 Flow Rate - C0257_CURRENT_HEAT_OUTPUT: Final = "calculations.Heat_Output" + CURRENT_HEAT_OUTPUT: Final = 257 # Heat_Output # 258 RBE Version -# endregion Lux calculations - - -# region visibilities -class LuxVisibility(StrEnum): - """Luxtronik visibility ids.""" - - UNSET: Final = "UNSET" - V0005_COOLING: Final = "visibilities.ID_Visi_Kuhlung" - V0008_MK2: Final = "visibilities.ID_Visi_MK2" - V0023_FLOW_IN_TEMPERATURE: Final = "visibilities.ID_Visi_Temp_Vorlauf" - V0024_FLOW_OUT_TEMPERATURE_EXTERNAL: Final = "visibilities.ID_Visi_Temp_Rucklauf" - V0027_HOT_GAS_TEMPERATURE: Final = "visibilities.ID_Visi_Temp_Heissgas" - V0029_DHW_TEMPERATURE: Final = "visibilities.ID_Visi_Temp_BW_Ist" - V0038_SOLAR_COLLECTOR: Final = "visibilities.ID_Visi_Temp_Solarkoll" - V0039_SOLAR_BUFFER: Final = "visibilities.ID_Visi_Temp_Solarsp" - V0041_DEFROST_END_FLOW_OKAY: Final = "visibilities.ID_Visi_IN_ASD" - V0043_EVU_IN: Final = "visibilities.ID_Visi_IN_EVU" - V0045_MOTOR_PROTECTION: Final = "visibilities.ID_Visi_IN_MOT" - V0049_DEFROST_VALVE: Final = "visibilities.ID_Visi_OUT_Abtauventil" - V0050_DHW_RECIRCULATION_PUMP: Final = "visibilities.ID_Visi_OUT_BUP" - V0052_CIRCULATION_PUMP_HEATING: Final = "visibilities.ID_Visi_OUT_HUP" - V0059_DHW_CIRCULATION_PUMP: Final = "visibilities.ID_Visi_OUT_ZIP" - V0059A_DHW_CHARGING_PUMP: Final = "V0059A_DHW_CHARGING_PUMP" - V0060_ADDITIONAL_CIRCULATION_PUMP: Final = "visibilities.ID_Visi_OUT_ZUP" - V0061_SECOND_HEAT_GENERATOR: Final = "visibilities.ID_Visi_OUT_ZWE1" - V0080_COMPRESSOR1_OPERATION_HOURS: Final = "visibilities.ID_Visi_Bst_BStdVD1" - V0081_COMPRESSOR1_IMPULSES: Final = "visibilities.ID_Visi_Bst_ImpVD1" - V0083_COMPRESSOR2_OPERATION_HOURS: Final = "visibilities.ID_Visi_Bst_BStdVD2" - V0084_COMPRESSOR2_IMPULSES: Final = "visibilities.ID_Visi_Bst_ImpVD2" - V0086_ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS: Final = ( - "visibilities.ID_Visi_Bst_BStdZWE1" - ) - V0121_EVU_LOCKED: Final = "visibilities.ID_Visi_SysEin_EVUSperre" - V0122_ROOM_THERMOSTAT: Final = "visibilities.ID_Visi_SysEin_Raumstation" - V0144_PUMP_OPTIMIZATION: Final = "visibilities.ID_Visi_SysEin_Pumpenoptim" - V0211_MK3: Final = "visibilities.ID_Visi_MK3" - V0239_EFFICIENCY_PUMP_NOMINAL: Final = ( - "visibilities.ID_Visi_SysEin_EffizienzpumpeNom" - ) - V0240_EFFICIENCY_PUMP_MINIMAL: Final = ( - "visibilities.ID_Visi_SysEin_EffizienzpumpeMin" - ) - V0248_ANALOG_OUT1: Final = "visibilities.ID_Visi_OUT_Analog_1" - V0249_ANALOG_OUT2: Final = "visibilities.ID_Visi_OUT_Analog_2" - V0250_SOLAR: Final = "visibilities.ID_Visi_Solar" - V0289_SUCTION_COMPRESSOR_TEMPERATURE: Final = ( - "visibilities.ID_Visi_LIN_ANSAUG_VERDICHTER" - ) - V0290_COMPRESSOR_HEATING: Final = "visibilities.ID_Visi_LIN_VDH" - V0291_OVERHEATING_TEMPERATURE: Final = "visibilities.ID_Visi_LIN_UH" - V0292_LIN_PRESSURE: Final = "visibilities.ID_Visi_LIN_Druck" - V0310_SUCTION_EVAPORATOR_TEMPERATURE: Final = ( - "visibilities.ID_Visi_LIN_ANSAUG_VERDAMPFER" - ) - V0324_ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER: Final = ( - "visibilities.ID_Visi_Waermemenge_ZWE" - ) - V0357_SILENT_MODE_TIME_MENU: Final = "visibilities.Unknown_Parameter_357" - - -# endregion visibilities - - -# region Keys -class SensorKey(StrEnum): - """Sensor keys.""" - - STATUS = "status" - STATUS_TIME = "status_time" - STATUS_LINE_1 = "status_line_1" - STATUS_LINE_2 = "status_line_2" - STATUS_LINE_3 = "status_line_3" - HEAT_SOURCE_INPUT_TEMPERATURE = "heat_source_input_temperature" - OUTDOOR_TEMPERATURE = "outdoor_temperature" - OUTDOOR_TEMPERATURE_AVERAGE = "outdoor_temperature_average" - COMPRESSOR1_IMPULSES = "compressor1_impulses" - COMPRESSOR1_OPERATION_HOURS = "compressor1_operation_hours" - COMPRESSOR2_IMPULSES = "compressor2_impulses" - COMPRESSOR2_OPERATION_HOURS = "compressor2_operation_hours" - OPERATION_HOURS = "operation_hours" - HEAT_AMOUNT_COUNTER = "heat_amount_counter" - HOT_GAS_TEMPERATURE = "hot_gas_temperature" - SUCTION_COMPRESSOR_TEMPERATURE = "suction_compressor_temperature" - SUCTION_EVAPORATOR_TEMPERATURE = "suction_evaporator_temperature" - COMPRESSOR_HEATING_TEMPERATURE = "compressor_heating_temperature" - OVERHEATING_TEMPERATURE = "overheating_temperature" - OVERHEATING_TARGET_TEMPERATURE = "overheating_target_temperature" - HIGH_PRESSURE = "high_pressure" - LOW_PRESSURE = "low_pressure" - ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS = ( - "additional_heat_generator_operation_hours" - ) - ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER = ( - "additional_heat_generator_amount_counter" - ) - ANALOG_OUT1 = "analog_out1" - ANALOG_OUT2 = "analog_out2" - CURRENT_HEAT_OUTPUT = "current_heat_output" - PUMP_FREQUENCY = "pump_frequency" - PUMP_FLOW_DELTA_TARGET = "pump_flow_delta_target" - PUMP_FLOW_DELTA = "pump_flow_delta" - CIRCULATION_PUMP_DELTA_TARGET = "circulation_pump_delta_target" - CIRCULATION_PUMP_DELTA = "circulation_pump_delta" - HEAT_SOURCE_OUTPUT_TEMPERATURE = "heat_source_output_temperature" - ERROR_REASON = "error_reason" - FLOW_IN_TEMPERATURE = "flow_in_temperature" - FLOW_OUT_TEMPERATURE = "flow_out_temperature" - FLOW_OUT_TEMPERATURE_TARGET = "flow_out_temperature_target" - FLOW_OUT_TEMPERATURE_EXTERNAL = "flow_out_temperature_external" - OPERATION_HOURS_HEATING = "operation_hours_heating" - OPERATION_HOURS_COOLING = "operation_hours_cooling" - HEAT_AMOUNT_HEATING = "heat_amount_heating" - HEAT_AMOUNT_FLOW_RATE = "heat_amount_flow_rate" - HEAT_SOURCE_FLOW_RATE = "heat_source_flow_rate" - DHW_HEAT_AMOUNT = "dhw_heat_amount" - HEAT_ENERGY_INPUT = "heat_energy_input" - DHW_ENERGY_INPUT = "dhw_energy_input" - DHW_TEMPERATURE = "dhw_temperature" - SOLAR_COLLECTOR_TEMPERATURE = "solar_collector_temperature" - SOLAR_BUFFER_TEMPERATURE = "solar_buffer_temperature" - OPERATION_HOURS_SOLAR = "operation_hours_solar" - DHW_OPERATION_HOURS = "dhw_operation_hours" - REMOTE_MAINTENANCE = "remote_maintenance" - EFFICIENCY_PUMP = "efficiency_pump" - EFFICIENCY_PUMP_NOMINAL = "efficiency_pump_nominal" - EFFICIENCY_PUMP_MINIMAL = "efficiency_pump_minimal" - PUMP_HEAT_CONTROL = "pump_heat_control" - HEATING = "heating" - PUMP_OPTIMIZATION = "pump_optimization" - HEATING_THRESHOLD = "heating_threshold" - DOMESTIC_WATER = "domestic_water" - COOLING = "cooling" - RELEASE_SECOND_HEAT_GENERATOR = "release_second_heat_generator" - RELEASE_TIME_SECOND_HEAT_GENERATOR = "release_time_second_heat_generator" - HEATING_TARGET_CORRECTION = "heating_target_correction" - PUMP_OPTIMIZATION_TIME = "pump_optimization_time" - HEATING_THRESHOLD_TEMPERATURE = "heating_threshold_temperature" - HEATING_MIN_FLOW_OUT_TEMPERATURE = "heating_min_flow_out_temperature" - HEATING_CIRCUIT_CURVE1_TEMPERATURE = "heating_circuit_curve1_temperature" - HEATING_CIRCUIT_CURVE2_TEMPERATURE = "heating_circuit_curve2_temperature" - HEATING_CIRCUIT_CURVE_NIGHT_TEMPERATURE = "heating_circuit_curve_night_temperature" - HEATING_CIRCUIT2_CURVE1_TEMPERATURE = "heating_circuit2_curve1_temperature" - HEATING_CIRCUIT2_CURVE2_TEMPERATURE = "heating_circuit2_curve2_temperature" - HEATING_CIRCUIT2_CURVE_NIGHT_TEMPERATURE = ( - "heating_circuit2_curve_night_temperature" - ) - HEATING_CIRCUIT3_CURVE1_TEMPERATURE = "heating_circuit3_curve1_temperature" - HEATING_CIRCUIT3_CURVE2_TEMPERATURE = "heating_circuit3_curve2_temperature" - HEATING_CIRCUIT3_CURVE_NIGHT_TEMPERATURE = ( - "heating_circuit3_curve_night_temperature" - ) - HEATING_NIGHT_LOWERING_TO_TEMPERATURE = "heating_night_lowering_to_temperature" - HEATING_HYSTERESIS = "heating_hysteresis" - HEATING_MAX_FLOW_OUT_INCREASE_TEMPERATURE = ( - "heating_max_flow_out_increase_temperature" - ) - HEATING_MAXIMUM_CIRCULATION_PUMP_SPEED = "heating_maximum_circulation_pump_speed" - HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR = "heating_room_temperature_impact_factor" - DHW_TARGET_TEMPERATURE = "dhw_target_temperature" - DHW_HYSTERESIS = "dhw_hysteresis" - DHW_THERMAL_DESINFECTION_TARGET = "dhw_thermal_desinfection_target" - SOLAR_PUMP_ON_DIFFERENCE_TEMPERATURE = "solar_pump_on_difference_temperature" - SOLAR_PUMP_OFF_DIFFERENCE_TEMPERATURE = "solar_pump_off_difference_temperature" - SOLAR_PUMP_OFF_MAX_DIFFERENCE_TEMPERATURE_BOILER = ( - "solar_pump_off_max_difference_temperature_boiler" - ) - SOLAR_PUMP_MAX_TEMPERATURE_COLLECTOR = "solar_pump_max_temperature_collector" - EVU_UNLOCKED = "evu_unlocked" - COMPRESSOR = "compressor" - COMPRESSOR2 = "compressor2" - PUMP_FLOW = "pump_flow" - CIRCULATION_PUMP_HEATING = "circulation_pump_heating" - ADDITIONAL_CIRCULATION_PUMP = "additional_circulation_pump" - DHW_RECIRCULATION_PUMP = "dhw_recirculation_pump" - DHW_CIRCULATION_PUMP = "dhw_circulation_pump" - DHW_CHARGING_PUMP = "dhw_charging_pump" - SOLAR_PUMP = "solar_pump" - COMPRESSOR_HEATER = "compressor_heater" - DEFROST_VALVE = "defrost_valve" - ADDITIONAL_HEAT_GENERATOR = "additional_heat_generator" - DISTURBANCE_OUTPUT = "disturbance_output" - DEFROST_END_FLOW_OKAY = "defrost_end_flow_okay" - MOTOR_PROTECTION = "motor_protection" - FIRMWARE = "firmware" - APPROVAL_COOLING = "approval_cooling" - ROOM_THERMOSTAT_TEMPERATURE = "room_thermostat_temperature" - ROOM_THERMOSTAT_TEMPERATURE_TARGET = "room_thermostat_temperature_target" - COOLING_START_DELAY_HOURS = "cooling_start_delay_hours" - COOLING_STOP_DELAY_HOURS = "cooling_stop_delay_hours" - COOLING_OUTDOOR_TEMP_THRESHOLD = "cooling_threshold_temperature" - COOLING_TARGET_TEMPERATURE_MK1 = "cooling_target_temperature_mk1" - COOLING_TARGET_TEMPERATURE_MK2 = "cooling_target_temperature_mk2" - COOLING_TARGET_TEMPERATURE_MK3 = "cooling_target_temperature_mk3" - SWITCHOFF_REASON = "switchoff_reason" - SILENT_MODE = "silent_mode" +class Visibility_SensorKey(Enum): + UNSET: Final = -1 + COOLING: Final = 5 # ID_Visi_Kuhlung + MK2: Final = 8 # ID_Visi_MK2 + FLOW_IN_TEMPERATURE: Final = 23 # ID_Visi_Temp_Vorlauf + FLOW_OUT_TEMPERATURE_EXTERNAL: Final = 24 # ID_Visi_Temp_Rucklauf + HOT_GAS_TEMPERATURE: Final = 27 # ID_Visi_Temp_Heissgas + DHW_TEMPERATURE: Final = 29 # ID_Visi_Temp_BW_Ist + SOLAR_COLLECTOR: Final = 38 # ID_Visi_Temp_Solarkoll + SOLAR_BUFFER: Final = 39 # ID_Visi_Temp_Solarsp + DEFROST_END_FLOW_OKAY: Final = 41 # ID_Visi_IN_ASD + EVU_IN: Final = 43 # ID_Visi_IN_EVU + MOTOR_PROTECTION: Final = 45 # ID_Visi_IN_MOT + DEFROST_VALVE: Final = 49 # ID_Visi_OUT_Abtauventil + DHW_RECIRCULATION_PUMP: Final = 50 # ID_Visi_OUT_BUP + CIRCULATION_PUMP_HEATING: Final = 52 # ID_Visi_OUT_HUP + DHW_CIRCULATION_PUMP: Final = 59 # ID_Visi_OUT_ZIP + DHW_CHARGING_PUMP: Final = -1 # V0059A_DHW_CHARGING_PUMP + ADDITIONAL_CIRCULATION_PUMP: Final = 60 # ID_Visi_OUT_ZUP + SECOND_HEAT_GENERATOR: Final = 61 # ID_Visi_OUT_ZWE1 + COMPRESSOR1_OPERATION_HOURS: Final = 80 # ID_Visi_Bst_BStdVD1 + COMPRESSOR1_IMPULSES: Final = 81 # ID_Visi_Bst_ImpVD1 + COMPRESSOR2_OPERATION_HOURS: Final = 83 # ID_Visi_Bst_BStdVD2 + COMPRESSOR2_IMPULSES: Final = 84 # ID_Visi_Bst_ImpVD2 + ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS: Final = 86 # ID_Visi_Bst_BStdZWE1 + EVU_LOCKED: Final = 121 # ID_Visi_SysEin_EVUSperre + ROOM_THERMOSTAT: Final = 122 # ID_Visi_SysEin_Raumstation + PUMP_OPTIMIZATION: Final = 144 # ID_Visi_SysEin_Pumpenoptim + MK3: Final = 211 # ID_Visi_MK3 + EFFICIENCY_PUMP_NOMINAL: Final = 239 # ID_Visi_SysEin_EffizienzpumpeNom + EFFICIENCY_PUMP_MINIMAL: Final = 240 # ID_Visi_SysEin_EffizienzpumpeMin + ANALOG_OUT1: Final = 248 # ID_Visi_OUT_Analog_1 + ANALOG_OUT2: Final = 249 # ID_Visi_OUT_Analog_2 + SOLAR: Final = 250 # ID_Visi_Solar + SUCTION_COMPRESSOR_TEMPERATURE: Final = 289 # ID_Visi_LIN_ANSAUG_VERDICHTER + COMPRESSOR_HEATING: Final = 290 # ID_Visi_LIN_VDH + OVERHEATING_TEMPERATURE: Final = 291 # ID_Visi_LIN_UH + LIN_PRESSURE: Final = 292 # ID_Visi_LIN_Druck + SUCTION_EVAPORATOR_TEMPERATURE: Final = 310 # ID_Visi_LIN_ANSAUG_VERDAMPFER + ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER: Final = 324 # ID_Visi_Waermemenge_ZWE + SILENT_MODE_TIME_MENU: Final = 357 # Unknown_Parameter_357 +LUX_PARAMETER_MK_SENSORS: Final = [ + Parameter_SensorKey.MIXING_CIRCUIT1_TYPE, + Parameter_SensorKey.MIXING_CIRCUIT2_TYPE, + Parameter_SensorKey.MIXING_CIRCUIT3_TYPE, +] # endregion Keys @@ -718,6 +644,7 @@ class SensorAttrKey(StrEnum): """Luxtronik sensor attribute keys.""" LUXTRONIK_KEY = "Luxtronik_Key" + DESCRIPTION = "description" STATUS_TEXT = "status_text" LAST_THERMAL_DESINFECTION = "last_thermal_desinfection" diff --git a/custom_components/luxtronik/coordinator.py b/custom_components/luxtronik/coordinator.py index 9eebb85..2a5a8e9 100644 --- a/custom_components/luxtronik/coordinator.py +++ b/custom_components/luxtronik/coordinator.py @@ -27,15 +27,16 @@ DEFAULT_MAX_DATA_LENGTH, DEFAULT_TIMEOUT, DOMAIN, + HEATPUMP_CODE_TYPE_MAP, LOGGER, LUX_PARAMETER_MK_SENSORS, UPDATE_INTERVAL_FAST, UPDATE_INTERVAL_NORMAL, DeviceKey, - LuxCalculation as LC, + Calculation_SensorKey as LC, LuxMkTypes, - LuxParameter as LP, - LuxVisibility as LV, + Parameter_SensorKey as LP, + Visibility_SensorKey as LV, ) from .lux_helper import Luxtronik, get_manufacturer_by_model from .model import LuxtronikCoordinatorData, LuxtronikEntityDescription @@ -101,18 +102,25 @@ async def _async_update_data(self) -> LuxtronikCoordinatorData: async def _async_read_data(self) -> LuxtronikCoordinatorData: return await self._async_read_or_write(False, None, None) - async def write(self, parameter, value) -> LuxtronikCoordinatorData: + def write( + self, parameter: int | str | dict[str, Any], value: Any = None + ) -> LuxtronikCoordinatorData: """Write a parameter to the Luxtronik heatpump.""" return asyncio.run_coroutine_threadsafe( self.async_write(parameter, value), self.hass.loop ).result() - async def async_write(self, parameter, value) -> LuxtronikCoordinatorData: + async def async_write( + self, parameter: int | str | dict[str, Any], value: Any = None + ) -> LuxtronikCoordinatorData: """Write a parameter to the Luxtronik heatpump.""" return await self._async_read_or_write(True, parameter, value) async def _async_read_or_write( - self, write, parameter, value + self, + write: bool, + parameter: int | str | dict[str, Any] | None, + value: any = None, ) -> LuxtronikCoordinatorData: if write: data = self._write(parameter, value) @@ -125,7 +133,7 @@ async def _async_read_or_write( self.async_set_updated_data(data) self.update_interval = ( UPDATE_INTERVAL_FAST - if bool(self.get_value(LC.C0044_COMPRESSOR)) + if bool(self.get_value(LC.COMPRESSOR)) else UPDATE_INTERVAL_NORMAL ) self.update_reason_write = False @@ -148,9 +156,15 @@ def _read(self) -> LuxtronikCoordinatorData: ) return self.data - def _write(self, parameter, value) -> LuxtronikCoordinatorData: + def _write( + self, parameter: int | str | dict[str, Any] | None, value: any = None + ) -> LuxtronikCoordinatorData: try: - self.client.parameters.set(parameter, value) + if isinstance(parameter, dict): + for k, v in parameter.items(): + self.client.parameters.set(k, v) + else: + self.client.parameters.set(parameter, value) with self.lock: self.client.write() except (ConnectionRefusedError, ConnectionResetError) as err: @@ -289,8 +303,8 @@ def _build_device_info( @property def serial_number(self) -> str: """Return the serial number.""" - serial_number_date = self.get_value(LP.P0874_SERIAL_NUMBER) - serial_number_hex = hex(int(self.get_value(LP.P0875_SERIAL_NUMBER_MODEL))) + serial_number_date = self.get_value(LP.SERIAL_NUMBER) + serial_number_hex = hex(int(self.get_value(LP.SERIAL_NUMBER_MODEL))) return f"{serial_number_date}-{serial_number_hex}".replace("x", "") @property @@ -301,7 +315,8 @@ def unique_id(self) -> str: @property def model(self) -> str: """Return the heatpump model.""" - return self.get_value(LC.C0078_MODEL_CODE) + # return self.get_value(LC.MODEL_CODE) + return HEATPUMP_CODE_TYPE_MAP.get(self.get_value(LC.MODEL_CODE)) @property def manufacturer(self) -> str | None: @@ -311,7 +326,11 @@ def manufacturer(self) -> str | None: @property def firmware_version(self) -> str: """Return the heatpump firmware version.""" - return str(self.get_value(LC.C0081_FIRMWARE_VERSION)) + # return str(self.get_value(LC.FIRMWARE_VERSION)) + value = [] + for i in range(LC.FIRMWARE_VERSION.value, LC.FIRMWARE_VERSION.value + 9): + value.append(self.get_value(i)) + return "".join([chr(c) for c in value]).strip("\x00") @property def firmware_version_minor(self) -> int: @@ -328,17 +347,17 @@ def entity_visible(self, description: LuxtronikEntityDescription) -> bool: # Detecting some options based on visibilities doesn't work reliably. # Use special functions if description.visibility in [ - LV.V0038_SOLAR_COLLECTOR, - LV.V0039_SOLAR_BUFFER, - LV.V0250_SOLAR, + LV.SOLAR_COLLECTOR, + LV.SOLAR_BUFFER, + LV.SOLAR, ]: return self._detect_solar_present() - if description.visibility == LV.V0059_DHW_CIRCULATION_PUMP: + if description.visibility == LV.DHW_CIRCULATION_PUMP: return self._detect_dhw_circulation_pump_present() - if description.visibility == LV.V0059A_DHW_CHARGING_PUMP: + if description.visibility == LV.DHW_CHARGING_PUMP: return not self._detect_dhw_circulation_pump_present() - if description.visibility == LV.V0005_COOLING: - return self.detect_cooling_present() + if description.visibility == LV.COOLING: + return self.detect_cooling_present() visibility_result = self.get_value(description.visibility) if visibility_result is None: LOGGER.warning("Could not load visibility %s", description.visibility) @@ -354,9 +373,9 @@ def entity_active(self, description: LuxtronikEntityDescription) -> bool: ): return False if description.visibility in [ - LP.P0042_MIXING_CIRCUIT1_TYPE, - LP.P0130_MIXING_CIRCUIT2_TYPE, - LP.P0780_MIXING_CIRCUIT3_TYPE, + LP.MIXING_CIRCUIT1_TYPE, + LP.MIXING_CIRCUIT2_TYPE, + LP.MIXING_CIRCUIT3_TYPE, ]: sensor_value = self.get_value(description.visibility) return sensor_value in [ @@ -364,12 +383,12 @@ def entity_active(self, description: LuxtronikEntityDescription) -> bool: LuxMkTypes.heating_cooling.value, ] if description.visibility in [ - LV.V0038_SOLAR_COLLECTOR, - LV.V0039_SOLAR_BUFFER, - LV.V0250_SOLAR, + LV.SOLAR_COLLECTOR, + LV.SOLAR_BUFFER, + LV.SOLAR, ]: - return self._detect_solar_present() - + return self._detect_solar_present() + if not self.device_key_active(description.device_key): return False if description.invisible_if_value is not None: @@ -393,21 +412,24 @@ def device_key_active(self, device_key: DeviceKey) -> bool: @property def has_heating(self) -> bool: """Is heating activated.""" - return bool(self.get_value(LV.V0023_FLOW_IN_TEMPERATURE)) + return bool(self.get_value(LV.FLOW_IN_TEMPERATURE)) @property def has_domestic_water(self) -> bool: """Is domestic water activated.""" - return bool(self.get_value(LV.V0029_DHW_TEMPERATURE)) + return bool(self.get_value(LV.DHW_TEMPERATURE)) - def get_value(self, group_sensor_id: str | LP | LC | LV): + def get_value(self, sensor_id: LP | LC | LV | int): """Get a sensor value from Luxtronik.""" - sensor = self.get_sensor_by_id(str(group_sensor_id)) + # sensor = self.get_sensor_by_id(str(group_sensor_id)) + sensor = self.get_sensor_by_id(sensor_id) if sensor is None: - return None - return correct_key_value(sensor.value, self.data, group_sensor_id) + return None + return correct_key_value(sensor.value, self.data, sensor_id) + # return correct_key_value(sensor.value, self.data, group_sensor_id) - def get_sensor_by_id(self, group_sensor_id: str): + # def get_sensor_by_id(self, group_sensor_id: str): + def get_sensor_by_id(self, sensor_id: LP | LC | LV | int): """Get a sensor object by id from Luxtronik.""" try: group = group_sensor_id.split(".")[0] @@ -419,12 +441,18 @@ def get_sensor_by_id(self, group_sensor_id: str): def get_sensor(self, group, sensor_id): """Get sensor by configured sensor ID.""" sensor = None - if group == CONF_PARAMETERS: - sensor = self.client.parameters.get(sensor_id) - if group == CONF_CALCULATIONS: - sensor = self.client.calculations.get(sensor_id) - if group == CONF_VISIBILITIES: - sensor = self.client.visibilities.get(sensor_id) + if isinstance(sensor_id, LP): # == CONF_PARAMETERS: + # sensor = self.client.parameters[sensor_id.value] + sensor = self.client.parameters.get(sensor_id.value) + elif isinstance(sensor_id, LV): #group == CONF_VISIBILITIES: + # sensor = self.client.visibilities[sensor_id.value] + sensor = self.client.calculations.get(sensor_id.value) + elif isinstance(sensor_id, LC): #group == CONF_CALCULATIONS: + # sensor = self.client.calculations[sensor_id.value] + sensor = self.client.visibilities.get(sensor_id.value) + elif isinstance(sensor_id, int): + # sensor = self.client.calculations[sensor_id] + sensor = self.client.calculations.get(sensor_id.value) return sensor def _detect_cooling_mk(self): @@ -443,19 +471,19 @@ def _detect_cooling_mk(self): def _detect_solar_present(self) -> bool: """Detect and returns True if solar is present.""" return ( - bool(self.get_value(LV.V0250_SOLAR)) - or self.get_value(LP.P0882_SOLAR_OPERATION_HOURS) > 0.01 # noqa: W503 + bool(self.get_value(LV.SOLAR)) + or self.get_value(LP.SOLAR_OPERATION_HOURS) > 0.01 # noqa: W503 or ( - bool(self.get_value(LV.V0038_SOLAR_COLLECTOR)) # noqa: W503 + bool(self.get_value(LV.SOLAR_COLLECTOR)) # noqa: W503 and float( - self.get_value(LC.C0026_SOLAR_COLLECTOR_TEMPERATURE) + self.get_value(LC.SOLAR_COLLECTOR_TEMPERATURE) ) # noqa: W503 != 5.0 # noqa: W503 ) or ( - bool(self.get_value(LV.V0039_SOLAR_BUFFER)) # noqa: W503 + bool(self.get_value(LV.SOLAR_BUFFER)) # noqa: W503 and float( - self.get_value(LC.C0027_SOLAR_BUFFER_TEMPERATURE) + self.get_value(LC.SOLAR_BUFFER_TEMPERATURE) ) # noqa: W503 != 150.0 # noqa: W503 ) @@ -464,7 +492,7 @@ def _detect_solar_present(self) -> bool: def _detect_dhw_circulation_pump_present(self) -> bool: """Detect and returns True if solar is present.""" try: - return int(self.get_value(LP.P0085_DHW_CHARGING_PUMP)) != 1 + return int(self.get_value(LP.DHW_CHARGING_PUMP)) != 1 except Exception: # pylint: disable=broad-except return False diff --git a/custom_components/luxtronik/manifest.json b/custom_components/luxtronik/manifest.json index e835d4d..bd610bf 100755 --- a/custom_components/luxtronik/manifest.json +++ b/custom_components/luxtronik/manifest.json @@ -3,13 +3,13 @@ "name": "Luxtronik", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/luxtronik", - "requirements": ["luxtronik==0.3.14", "requests>=2.28.2", "getmac==0.8.2"], + "requirements": ["luxtronik==0.3.14", "requests>=2.28.2", "getmac==0.8.2", "croniter"], "issue_tracker": "https://github.com/BenPru/luxtronik/issues", "dependencies": ["http"], "after_dependencies": [], "codeowners": ["@BenPru"], "iot_class": "local_polling", - "version": "2023.10.31", + "version": "2023.11.7", "homeassistant": "2023.1.0", "dhcp": [ { "macaddress": "000E8C*" }, diff --git a/custom_components/luxtronik/model.py b/custom_components/luxtronik/model.py index d3e0a80..ccb1000 100644 --- a/custom_components/luxtronik/model.py +++ b/custom_components/luxtronik/model.py @@ -29,14 +29,14 @@ from .const import ( UPDATE_INTERVAL_VERY_SLOW, + Calculation_SensorKey, DeviceKey, FirmwareVersionMinor, - LuxCalculation, LuxOperationMode, - LuxParameter, - LuxVisibility, + Parameter_SensorKey, SensorAttrFormat, SensorAttrKey, + Visibility_SensorKey, ) # endregion Imports @@ -57,26 +57,38 @@ class LuxtronikEntityAttributeDescription: # This is the key identifier for this entity key: SensorAttrKey - luxtronik_key: LuxParameter | LuxCalculation = LuxParameter.UNSET + luxtronik_key: Calculation_SensorKey | Parameter_SensorKey = Parameter_SensorKey.UNSET format: SensorAttrFormat | None = None restore_on_startup: bool = False + default_value: Any | None = None + + +ATTR_DESCRIPTION = LuxtronikEntityAttributeDescription( + key=SensorAttrKey.DESCRIPTION, default_value="_" +) @dataclass class LuxtronikEntityDescription(EntityDescription): """Class describing Luxtronik entities.""" + key: str = 'PLACEHOLDER' + has_entity_name = True + state_mapping: enumerate|None = None # Bug in python: Have to assign a value: platform = Platform.AIR_QUALITY + luxtronik_key: Calculation_SensorKey | Parameter_SensorKey = ( + Parameter_SensorKey.UNSET + ) + update_interval: timedelta | None = None icon_by_state: dict[StateType | date | datetime | Decimal, str] | None = None device_key: DeviceKey = DeviceKey.heatpump - luxtronik_key: LuxParameter | LuxCalculation = LuxParameter.UNSET translation_key_name: str | None = None - visibility: LuxVisibility = LuxVisibility.UNSET + visibility: Visibility_SensorKey = Visibility_SensorKey.UNSET invisible_if_value: Any | None = None min_firmware_version_minor: FirmwareVersionMinor | None = None @@ -84,6 +96,7 @@ class LuxtronikEntityDescription(EntityDescription): default_factory=list ) state_class: str | None = None + event_id_on_change: str | None = None @dataclass @@ -98,6 +111,17 @@ class LuxtronikSensorDescription( native_precision: int | None = None +@dataclass +class LuxtronikPeriodStatSensorDescription( + LuxtronikSensorDescription, + SensorEntityDescription, +): + """Class describing Luxtronik PeriodStat sensor entities.""" + + event_id_impulse_active: str | None = "IMPULSE_START" + event_id_impulse_inactive: str | None = "IMPULSE_END" + + @dataclass class LuxtronikIndexSensorDescription( LuxtronikSensorDescription, @@ -105,7 +129,7 @@ class LuxtronikIndexSensorDescription( ): """Class describing Luxtronik index sensor entities.""" - luxtronik_key_timestamp: LuxParameter | LuxCalculation = LuxParameter.UNSET + luxtronik_key_timestamp: Parameter_SensorKey | Calculation_SensorKey = Parameter_SensorKey.UNSET @dataclass @@ -134,6 +158,8 @@ class LuxtronikBinarySensorEntityDescription( on_states: list[str] | None = None off_state: str | bool = False inverted = False + event_id_on_true: str | None = None + event_id_on_false: str | None = None @dataclass @@ -164,12 +190,12 @@ class LuxtronikClimateDescription( hvac_action_mapping: dict[str, str] = field(default_factory=dict[str, str]) preset_modes: list[str] | None = None supported_features: ClimateEntityFeature = ClimateEntityFeature(0) - luxtronik_key_current_temperature: LuxCalculation | str = LuxCalculation.UNSET - luxtronik_key_current_action: LuxCalculation = LuxCalculation.UNSET + luxtronik_key_current_temperature: Calculation_SensorKey | str = Calculation_SensorKey.UNSET + luxtronik_key_current_action: Calculation_SensorKey = Calculation_SensorKey.UNSET luxtronik_action_active: str | None = None - luxtronik_key_target_temperature: LuxParameter | LuxCalculation = LuxParameter.UNSET - luxtronik_key_correction_factor: LuxParameter = LuxParameter.UNSET - luxtronik_key_correction_target: LuxParameter = LuxParameter.UNSET + luxtronik_key_target_temperature: Parameter_SensorKey | Calculation_SensorKey = Parameter_SensorKey.UNSET + luxtronik_key_correction_factor: Parameter_SensorKey = Parameter_SensorKey.UNSET + luxtronik_key_correction_target: Parameter_SensorKey = Parameter_SensorKey.UNSET temperature_unit: str = UnitOfTemperature.CELSIUS @@ -183,12 +209,12 @@ class LuxtronikWaterHeaterDescription( platform = Platform.WATER_HEATER operation_list: list[str] = field(default_factory=list) supported_features: WaterHeaterEntityFeature = WaterHeaterEntityFeature(0) - luxtronik_key_current_temperature: LuxCalculation = LuxCalculation.UNSET - luxtronik_key_current_action: LuxCalculation = LuxCalculation.UNSET + luxtronik_key_current_temperature: Calculation_SensorKey = Calculation_SensorKey.UNSET + luxtronik_key_current_action: Calculation_SensorKey = Calculation_SensorKey.UNSET luxtronik_action_heating: LuxOperationMode | None = None - luxtronik_key_target_temperature: LuxParameter = LuxParameter.UNSET - luxtronik_key_target_temperature_high: LuxParameter = LuxParameter.UNSET - luxtronik_key_target_temperature_low: LuxParameter = LuxParameter.UNSET + luxtronik_key_target_temperature: Parameter_SensorKey = Parameter_SensorKey.UNSET + luxtronik_key_target_temperature_high: Parameter_SensorKey = Parameter_SensorKey.UNSET + luxtronik_key_target_temperature_low: Parameter_SensorKey = Parameter_SensorKey.UNSET temperature_unit: str = UnitOfTemperature.CELSIUS diff --git a/custom_components/luxtronik/number.py b/custom_components/luxtronik/number.py index b77b0d6..af82479 100644 --- a/custom_components/luxtronik/number.py +++ b/custom_components/luxtronik/number.py @@ -17,6 +17,7 @@ CONF_COORDINATOR, CONF_HA_SENSOR_PREFIX, DOMAIN, + UNIT_FACTOR_MAP, DeviceKey, SensorAttrFormat, ) @@ -72,9 +73,13 @@ def __init__( self._attr_unique_id = self.entity_id self._attr_mode = description.mode self._sensor_data = get_sensor_data( - coordinator.data, description.luxtronik_key.value + coordinator.data, description.luxtronik_key ) + def enrich_description(self, d: LuxtronikNumberDescription) -> None: + super().enrich_description(d) + d.factor = d.factor or UNIT_FACTOR_MAP.get(d.native_unit_of_measurement) + async def _data_update(self, event): self._handle_coordinator_update() @@ -93,7 +98,7 @@ def _handle_coordinator_update( if data is None: return self._attr_native_value = get_sensor_data( - data, self.entity_description.luxtronik_key.value + data, self.entity_description.luxtronik_key ) if self._attr_native_value is not None: if self.entity_description.factor is not None: diff --git a/custom_components/luxtronik/number_entities_predefined.py b/custom_components/luxtronik/number_entities_predefined.py index 263f354..1d02c96 100644 --- a/custom_components/luxtronik/number_entities_predefined.py +++ b/custom_components/luxtronik/number_entities_predefined.py @@ -12,14 +12,14 @@ from .const import ( DeviceKey, - LuxCalculation as LC, - LuxParameter as LP, - LuxVisibility as LV, + Calculation_SensorKey as LC, + Parameter_SensorKey as LP, + Visibility_SensorKey as LV, SensorAttrFormat, SensorAttrKey as SA, - SensorKey, ) from .model import ( + ATTR_DESCRIPTION, LuxtronikEntityAttributeDescription as attr, LuxtronikNumberDescription, ) @@ -35,71 +35,81 @@ NUMBER_SENSORS: list[LuxtronikNumberDescription] = [ # region Main heatpump LuxtronikNumberDescription( - key=SensorKey.RELEASE_SECOND_HEAT_GENERATOR, - luxtronik_key=LP.P0090_RELEASE_SECOND_HEAT_GENERATOR, + luxtronik_key=LP.RELEASE_SECOND_HEAT_GENERATOR, icon="mdi:download-lock", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=-20.0, native_max_value=20.0, native_step=0.1, mode=NumberMode.BOX, entity_category=EntityCategory.CONFIG, - factor=0.1, - visibility=LV.V0061_SECOND_HEAT_GENERATOR, + factor=0.1, # TODO: Check + visibility=LV.SECOND_HEAT_GENERATOR, ), LuxtronikNumberDescription( - key=SensorKey.RELEASE_TIME_SECOND_HEAT_GENERATOR, - luxtronik_key=LP.P0992_RELEASE_TIME_SECOND_HEAT_GENERATOR, + luxtronik_key=LP.AIR_DEFROST_TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + native_min_value=6.0, + native_max_value=20.0, + native_step=1.0, + mode=NumberMode.SLIDER, + entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=False, + extra_attributes=[ATTR_DESCRIPTION], + ), + LuxtronikNumberDescription( + luxtronik_key=LP.AIR_DEFROST_STOP_TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + native_min_value=2.0, + native_max_value=10.0, + native_step=1.0, + mode=NumberMode.SLIDER, + entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=False, + extra_attributes=[ATTR_DESCRIPTION], + ), + LuxtronikNumberDescription( + luxtronik_key=LP.RELEASE_TIME_SECOND_HEAT_GENERATOR, translation_key_name="release_second_heat_generator", icon="mdi:timer-play", - device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.MINUTES, native_min_value=20, native_max_value=120, native_step=5, entity_category=EntityCategory.CONFIG, - factor=1, - visibility=LV.V0061_SECOND_HEAT_GENERATOR, + factor=1, # TODO: Check + visibility=LV.SECOND_HEAT_GENERATOR, ), LuxtronikNumberDescription( - key=SensorKey.EFFICIENCY_PUMP_NOMINAL, - luxtronik_key=LP.P0867_EFFICIENCY_PUMP_NOMINAL, - translation_key_name=SensorKey.EFFICIENCY_PUMP_NOMINAL, + luxtronik_key=LP.EFFICIENCY_PUMP_NOMINAL, icon="mdi:leaf-circle-outline", - device_class=SensorDeviceClass.VOLTAGE, native_unit_of_measurement=UnitOfElectricPotential.VOLT, native_min_value=3.0, native_max_value=10.0, native_step=0.5, entity_category=EntityCategory.CONFIG, - factor=0.01, + factor=0.01, # TODO: Check entity_registry_enabled_default=False, - visibility=LV.V0239_EFFICIENCY_PUMP_NOMINAL, + visibility=LV.EFFICIENCY_PUMP_NOMINAL, ), LuxtronikNumberDescription( - key=SensorKey.EFFICIENCY_PUMP_MINIMAL, - luxtronik_key=LP.P0868_EFFICIENCY_PUMP_MINIMAL, - translation_key_name=SensorKey.EFFICIENCY_PUMP_MINIMAL, + luxtronik_key=LP.EFFICIENCY_PUMP_MINIMAL, icon="mdi:leaf", - device_class=SensorDeviceClass.VOLTAGE, native_unit_of_measurement=UnitOfElectricPotential.VOLT, native_min_value=3.0, native_max_value=10.0, native_step=0.5, entity_category=EntityCategory.CONFIG, - factor=0.01, + factor=0.01, # TODO: Check entity_registry_enabled_default=False, - visibility=LV.V0240_EFFICIENCY_PUMP_MINIMAL, + visibility=LV.EFFICIENCY_PUMP_MINIMAL, ), # endregion Main heatpump # region Heating LuxtronikNumberDescription( - key=SensorKey.HEATING_TARGET_CORRECTION, - luxtronik_key=LP.P0001_HEATING_TARGET_CORRECTION, + luxtronik_key=LP.HEATING_TARGET_CORRECTION, device_key=DeviceKey.heating, icon="mdi:plus-minus-variant", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=-5.0, native_max_value=5.0, @@ -108,28 +118,24 @@ update_interval=None, ), LuxtronikNumberDescription( - key=SensorKey.PUMP_OPTIMIZATION_TIME, - luxtronik_key=LP.P0864_PUMP_OPTIMIZATION_TIME, + luxtronik_key=LP.PUMP_OPTIMIZATION_TIME, translation_key_name="pump_optimization", device_key=DeviceKey.heating, icon="mdi:timer-settings", - device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.MINUTES, native_min_value=5, native_max_value=180, native_step=5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - factor=1, - visibility=LV.V0144_PUMP_OPTIMIZATION, + factor=1, # TODO: Check + visibility=LV.PUMP_OPTIMIZATION, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_THRESHOLD_TEMPERATURE, - luxtronik_key=LP.P0700_HEATING_THRESHOLD_TEMPERATURE, + luxtronik_key=LP.HEATING_THRESHOLD_TEMPERATURE, translation_key_name="heating_threshold", device_key=DeviceKey.heating, icon="mdi:download-outline", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=5.0, native_max_value=30.0, @@ -138,25 +144,21 @@ mode=NumberMode.BOX, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_MIN_FLOW_OUT_TEMPERATURE, - luxtronik_key=LP.P0979_HEATING_MIN_FLOW_OUT_TEMPERATURE, + luxtronik_key=LP.HEATING_MIN_FLOW_OUT_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:waves-arrow-left", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=5.0, native_max_value=30.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - factor=0.1, + factor=0.1, # TODO: Check ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT_CURVE1_TEMPERATURE, - luxtronik_key=LP.P0011_HEATING_CIRCUIT_CURVE1_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT_CURVE1_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=20.0, native_max_value=70.0, @@ -165,11 +167,9 @@ mode=NumberMode.BOX, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT_CURVE2_TEMPERATURE, - luxtronik_key=LP.P0012_HEATING_CIRCUIT_CURVE2_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT_CURVE2_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=5.0, native_max_value=35.0, @@ -178,11 +178,9 @@ mode=NumberMode.BOX, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT_CURVE_NIGHT_TEMPERATURE, - luxtronik_key=LP.P0013_HEATING_CIRCUIT_CURVE_NIGHT_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT_CURVE_NIGHT_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=-15.0, native_max_value=10.0, @@ -191,140 +189,131 @@ mode=NumberMode.BOX, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT2_CURVE1_TEMPERATURE, - luxtronik_key=LP.P0014_HEATING_CIRCUIT2_CURVE1_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT2_CURVE1_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=20.0, native_max_value=70.0, native_step=0.5, - factor=0.1, + factor=0.1, # TODO: Check entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0008_MK2, + visibility=LV.MK2, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT2_CURVE2_TEMPERATURE, - luxtronik_key=LP.P0015_HEATING_CIRCUIT2_CURVE2_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT2_CURVE2_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=5.0, native_max_value=35.0, native_step=0.5, - factor=0.1, + factor=0.1, # TODO: Check entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0008_MK2, + visibility=LV.MK2, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT2_CURVE_NIGHT_TEMPERATURE, - luxtronik_key=LP.P0016_HEATING_CIRCUIT2_CURVE_NIGHT_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT2_CURVE_NIGHT_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=-15.0, native_max_value=10.0, native_step=0.5, - factor=0.1, + factor=0.1, # TODO: Check entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0008_MK2, + visibility=LV.MK2, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT3_CURVE1_TEMPERATURE, - luxtronik_key=LP.P0774_HEATING_CIRCUIT3_CURVE1_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT3_CURVE1_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=20.0, native_max_value=70.0, native_step=0.5, - factor=0.1, + factor=0.1, # TODO: Check entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0211_MK3, + visibility=LV.MK3, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT3_CURVE2_TEMPERATURE, - luxtronik_key=LP.P0775_HEATING_CIRCUIT3_CURVE2_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT3_CURVE2_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=5.0, native_max_value=35.0, native_step=0.5, - factor=0.1, + factor=0.1, # TODO: Check entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0211_MK3, + visibility=LV.MK3, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_CIRCUIT3_CURVE_NIGHT_TEMPERATURE, - luxtronik_key=LP.P0776_HEATING_CIRCUIT3_CURVE_NIGHT_TEMPERATURE, + luxtronik_key=LP.HEATING_CIRCUIT3_CURVE_NIGHT_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:chart-bell-curve", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=-15.0, native_max_value=10.0, native_step=0.5, - factor=0.1, + factor=0.1, # TODO: Check entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0211_MK3, + visibility=LV.MK3, ), LuxtronikNumberDescription( - key=SensorKey.HEATING_NIGHT_LOWERING_TO_TEMPERATURE, - luxtronik_key=LP.P0111_HEATING_NIGHT_LOWERING_TO_TEMPERATURE, + luxtronik_key=LP.HEATING_NIGHT_LOWERING_TO_TEMPERATURE, device_key=DeviceKey.heating, icon="mdi:thermometer-low", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=-20.0, native_max_value=10.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - factor=0.1, + factor=0.1, # TODO: Check ), LuxtronikNumberDescription( - key=SensorKey.HEATING_HYSTERESIS, - luxtronik_key=LP.P0088_HEATING_HYSTERESIS, + luxtronik_key=LP.HEATING_RETURN_TEMPERATURE_LIMIT, + device_key=DeviceKey.heating, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + native_min_value=35.0, + native_max_value=70.0, + native_step=1.0, + entity_category=EntityCategory.CONFIG, + mode=NumberMode.SLIDER, + extra_attributes=[ATTR_DESCRIPTION], + entity_registry_enabled_default=False, + ), + LuxtronikNumberDescription( + luxtronik_key=LP.HEATING_HYSTERESIS, device_key=DeviceKey.heating, - icon="mdi:thermometer", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, native_min_value=0.5, native_max_value=6.0, native_step=0.1, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - factor=0.1, + factor=0.1, # TODO: Check ), LuxtronikNumberDescription( - key=SensorKey.HEATING_MAX_FLOW_OUT_INCREASE_TEMPERATURE, - luxtronik_key=LP.P0089_HEATING_MAX_FLOW_OUT_INCREASE_TEMPERATURE, + luxtronik_key=LP.HEATING_MAX_FLOW_OUT_INCREASE_TEMPERATURE, device_key=DeviceKey.heating, - icon="mdi:thermometer", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, native_min_value=1.0, native_max_value=7.0, native_step=0.1, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - factor=0.1, + factor=0.1, # TODO: Check ), LuxtronikNumberDescription( - key=SensorKey.HEATING_MAXIMUM_CIRCULATION_PUMP_SPEED, - luxtronik_key=LP.P1032_HEATING_MAXIMUM_CIRCULATION_PUMP_SPEED, + luxtronik_key=LP.HEATING_MAXIMUM_CIRCULATION_PUMP_SPEED, device_key=DeviceKey.heating, icon="mdi:speedometer", device_class=SensorDeviceClass.SPEED, @@ -339,8 +328,7 @@ # 100% 1K Temperaturdifferenz im Raum führt zu 1K Anpassung der Rücklauf-Solltemperatur <-- empf. Fussbodenheizung # 200% 1K Temperaturdifferenz im Raum führt zu 2K Anpassung der Rücklauf-Solltemperatur <-- empf. Radiatoren / Gebläsekonvektoren LuxtronikNumberDescription( - key=SensorKey.HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, - luxtronik_key=LP.P0980_HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, + luxtronik_key=LP.HEATING_ROOM_TEMPERATURE_IMPACT_FACTOR, device_key=DeviceKey.heating, icon="mdi:thermometer-chevron-up", device_class=None, @@ -351,17 +339,15 @@ mode=NumberMode.BOX, entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, - visibility=LV.V0122_ROOM_THERMOSTAT, + visibility=LV.ROOM_THERMOSTAT, ), # endregion Heating # region Domestic water LuxtronikNumberDescription( - key=SensorKey.DHW_TARGET_TEMPERATURE, - luxtronik_key=LP.P0002_DHW_TARGET_TEMPERATURE, + luxtronik_key=LP.DHW_TARGET_TEMPERATURE, device_key=DeviceKey.domestic_water, mode=NumberMode.BOX, icon="mdi:thermometer-water", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=-40.0, native_max_value=60.0, @@ -369,11 +355,8 @@ update_interval=None, ), LuxtronikNumberDescription( - key=SensorKey.DHW_HYSTERESIS, - luxtronik_key=LP.P0074_DHW_HYSTERESIS, + luxtronik_key=LP.DHW_HYSTERESIS, device_key=DeviceKey.domestic_water, - icon="mdi:thermometer", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, native_min_value=1.0, native_max_value=30.0, @@ -382,22 +365,20 @@ native_step=0.1, ), LuxtronikNumberDescription( - key=SensorKey.DHW_THERMAL_DESINFECTION_TARGET, - luxtronik_key=LP.P0047_DHW_THERMAL_DESINFECTION_TARGET, + luxtronik_key=LP.DHW_THERMAL_DESINFECTION_TARGET, device_key=DeviceKey.domestic_water, icon="mdi:thermometer-high", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=50.0, native_max_value=70.0, native_step=1.0, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - factor=0.1, + factor=0.1, # TODO: Check extra_attributes=[ attr( SA.LAST_THERMAL_DESINFECTION, - LC.C0017_DHW_TEMPERATURE, + LC.DHW_TEMPERATURE, SensorAttrFormat.TIMESTAMP_LAST_OVER, True, ), @@ -405,146 +386,128 @@ ), # region Solar LuxtronikNumberDescription( - key=SensorKey.SOLAR_PUMP_ON_DIFFERENCE_TEMPERATURE, - luxtronik_key=LP.P0122_SOLAR_PUMP_ON_DIFFERENCE_TEMPERATURE, + luxtronik_key=LP.SOLAR_PUMP_ON_DIFFERENCE_TEMPERATURE, device_key=DeviceKey.domestic_water, icon="mdi:pump", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, native_min_value=2.0, native_max_value=15.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0250_SOLAR, + visibility=LV.SOLAR, ), LuxtronikNumberDescription( - key=SensorKey.SOLAR_PUMP_OFF_DIFFERENCE_TEMPERATURE, - luxtronik_key=LP.P0123_SOLAR_PUMP_OFF_DIFFERENCE_TEMPERATURE, + luxtronik_key=LP.SOLAR_PUMP_OFF_DIFFERENCE_TEMPERATURE, device_key=DeviceKey.domestic_water, icon="mdi:pump-off", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, native_min_value=0.5, native_max_value=10.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0250_SOLAR, + visibility=LV.SOLAR, ), LuxtronikNumberDescription( - key=SensorKey.SOLAR_PUMP_OFF_MAX_DIFFERENCE_TEMPERATURE_BOILER, - luxtronik_key=LP.P0289_SOLAR_PUMP_OFF_MAX_DIFFERENCE_TEMPERATURE_BOILER, + luxtronik_key=LP.SOLAR_PUMP_OFF_MAX_DIFFERENCE_TEMPERATURE_BOILER, device_key=DeviceKey.domestic_water, icon="mdi:water-boiler-alert", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=20, native_max_value=95, native_step=1, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0250_SOLAR, + visibility=LV.SOLAR, ), LuxtronikNumberDescription( - key=SensorKey.SOLAR_PUMP_MAX_TEMPERATURE_COLLECTOR, - luxtronik_key=LP.P0883_SOLAR_PUMP_MAX_TEMPERATURE_COLLECTOR, + luxtronik_key=LP.SOLAR_PUMP_MAX_TEMPERATURE_COLLECTOR, device_key=DeviceKey.domestic_water, icon="mdi:solar-panel-large", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=90, native_max_value=120, native_step=1, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0038_SOLAR_COLLECTOR, + visibility=LV.SOLAR_COLLECTOR, ), # endregion Solar # endregion Domestic water # region Cooling LuxtronikNumberDescription( - key=SensorKey.COOLING_OUTDOOR_TEMP_THRESHOLD, - luxtronik_key=LP.P0110_COOLING_OUTDOOR_TEMP_THRESHOLD, + luxtronik_key=LP.COOLING_OUTDOOR_TEMP_THRESHOLD, device_key=DeviceKey.cooling, - icon='mdi:sun-thermometer', - device_class=SensorDeviceClass.TEMPERATURE, + icon="mdi:sun-thermometer", native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=18.0, native_max_value=30.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0005_COOLING, - ), + visibility=LV.COOLING, + ), LuxtronikNumberDescription( - key=SensorKey.COOLING_START_DELAY_HOURS, - luxtronik_key=LP.P0850_COOLING_START_DELAY_HOURS, + luxtronik_key=LP.COOLING_START_DELAY_HOURS, device_key=DeviceKey.cooling, - icon='mdi:clock-start', + icon="mdi:clock-start", native_unit_of_measurement=UnitOfTime.HOURS, native_min_value=0.0, native_max_value=12.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0005_COOLING, + visibility=LV.COOLING, ), LuxtronikNumberDescription( - key=SensorKey.COOLING_STOP_DELAY_HOURS, - luxtronik_key=LP.P0851_COOLING_STOP_DELAY_HOURS, + luxtronik_key=LP.COOLING_STOP_DELAY_HOURS, device_key=DeviceKey.cooling, - icon='mdi:clock-end', + icon="mdi:clock-end", native_unit_of_measurement=UnitOfTime.HOURS, native_min_value=0.0, native_max_value=12.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LV.V0005_COOLING, + visibility=LV.COOLING, ), LuxtronikNumberDescription( - key=SensorKey.COOLING_TARGET_TEMPERATURE_MK1, - luxtronik_key=LP.P0132_COOLING_TARGET_TEMPERATURE_MK1, + luxtronik_key=LP.COOLING_TARGET_TEMPERATURE_MK1, device_key=DeviceKey.cooling, - icon='mdi:sun-thermometer', - device_class=SensorDeviceClass.TEMPERATURE, + icon="mdi:sun-thermometer", native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=18.0, native_max_value=30.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LP.P0042_MIXING_CIRCUIT1_TYPE, - ), + visibility=LP.MIXING_CIRCUIT1_TYPE, + ), LuxtronikNumberDescription( - key=SensorKey.COOLING_TARGET_TEMPERATURE_MK2, - luxtronik_key=LP.P0133_COOLING_TARGET_TEMPERATURE_MK2, + luxtronik_key=LP.COOLING_TARGET_TEMPERATURE_MK2, device_key=DeviceKey.cooling, - icon='mdi:sun-thermometer', - device_class=SensorDeviceClass.TEMPERATURE, + icon="mdi:sun-thermometer", native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=18.0, native_max_value=30.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LP.P0130_MIXING_CIRCUIT2_TYPE, - ), + visibility=LP.MIXING_CIRCUIT2_TYPE, + ), LuxtronikNumberDescription( - key=SensorKey.COOLING_TARGET_TEMPERATURE_MK3, - luxtronik_key=LP.P0966_COOLING_TARGET_TEMPERATURE_MK3, + luxtronik_key=LP.COOLING_TARGET_TEMPERATURE_MK3, device_key=DeviceKey.cooling, - icon='mdi:sun-thermometer', - device_class=SensorDeviceClass.TEMPERATURE, + icon="mdi:sun-thermometer", native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_min_value=18.0, native_max_value=30.0, native_step=0.5, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, - visibility=LP.P0780_MIXING_CIRCUIT3_TYPE, - ), + visibility=LP.MIXING_CIRCUIT3_TYPE, + ), # endregion Cooling ] diff --git a/custom_components/luxtronik/sensor.py b/custom_components/luxtronik/sensor.py index 2c055f1..8730e17 100644 --- a/custom_components/luxtronik/sensor.py +++ b/custom_components/luxtronik/sensor.py @@ -2,18 +2,38 @@ # flake8: noqa: W503 # region Imports from __future__ import annotations +from dataclasses import asdict, dataclass from datetime import date, datetime, time, timezone -from decimal import Decimal -from typing import Any +from decimal import Decimal, DecimalException, InvalidOperation +from typing import Any, Self -from homeassistant.components.sensor import ENTITY_ID_FORMAT, SensorEntity +from croniter import croniter + +from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.components.utility_meter.const import DAILY, SIGNAL_RESET_METER +from homeassistant.components.utility_meter.sensor import PERIOD2CRON + +from homeassistant.components.sensor import ( + ENTITY_ID_FORMAT, + RestoreSensor, + SensorEntity, + SensorExtraStoredData, +) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_UNAVAILABLE -from homeassistant.core import HomeAssistant, callback +from homeassistant.const import ( + ATTR_UNIT_OF_MEASUREMENT, + STATE_UNAVAILABLE, + STATE_UNKNOWN, +) +from homeassistant.core import HomeAssistant, State, callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.event import async_track_point_in_time +from homeassistant.helpers.restore_state import ExtraStoredData +from homeassistant.helpers.template import is_number from homeassistant.helpers.typing import StateType -from homeassistant.util.dt import utcnow, dt as dt_util +import homeassistant.util.dt as dt_util from .base import LuxtronikEntity from .common import get_sensor_data @@ -22,19 +42,45 @@ CONF_HA_SENSOR_PREFIX, DOMAIN, LOGGER, + UNIT_FACTOR_MAP, DeviceKey, - LuxCalculation as LC, + Calculation_SensorKey as LC, LuxOperationMode, LuxStatus1Option, LuxStatus3Option, SensorAttrKey as SA, ) from .coordinator import LuxtronikCoordinator, LuxtronikCoordinatorData -from .model import LuxtronikIndexSensorDescription, LuxtronikSensorDescription -from .sensor_entities_predefined import SENSORS, SENSORS_INDEX, SENSORS_STATUS +from .model import ( + LuxtronikIndexSensorDescription, + LuxtronikPeriodStatSensorDescription, + LuxtronikSensorDescription, +) +from .sensor_entities_predefined import ( + SENSORS, + SENSORS_INDEX, + SENSORS_PERIOD, + SENSORS_STATUS, +) # endregion Imports +# region Consts +ATTR_LAST_VALID_STATE = "last_valid_state" +ATTR_TODAY = "today" +ATTR_YESTERDAY = "yesterday" +ATTR_WEEK = "week" +ATTR_LAST_WEEK = "last_week" +ATTR_MONTH = "month" +ATTR_LAST_MONTH = "last_month" +ATTR_YEAR = "year" +ATTR_LAST_YEAR = "last_year" +ATTR_HEATING_PERIOD = "heating_period" +ATTR_LAST_HEATING_PERIOD = "last_heating_period" +ATTR_CURRENT_IMPULSE = "current_impulse" +ATTR_LAST_IMPULSE = "last_impulse" +# endregion Consts + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -77,8 +123,19 @@ async def async_setup_entry( True, ) + async_add_entities( + ( + LuxtronikPeriodStatSensor( + hass, entry, coordinator, description, description.device_key + ) + for description in SENSORS_PERIOD + if coordinator.entity_active(description) + ), + True, + ) + -class LuxtronikSensorEntity(LuxtronikEntity, SensorEntity): +class LuxtronikSensorEntity(LuxtronikEntity, SensorEntity): # RestoreSensor): """Luxtronik Sensor Entity.""" entity_description: LuxtronikSensorDescription @@ -120,15 +177,20 @@ def __init__( description=description, device_info_ident=device_info_ident, ) + self.hass = hass self._sensor_prefix = entry.data[CONF_HA_SENSOR_PREFIX] self.entity_id = ENTITY_ID_FORMAT.format( f"{self._sensor_prefix}_{description.key}" ) self._attr_unique_id = self.entity_id self._sensor_data = get_sensor_data( - coordinator.data, description.luxtronik_key.value + coordinator.data, description.luxtronik_key ) + def enrich_description(self, d: LuxtronikSensorDescription) -> None: + super().enrich_description(d) + d.factor = d.factor or UNIT_FACTOR_MAP.get(d.native_unit_of_measurement) + async def _data_update(self, event): self._handle_coordinator_update() @@ -139,14 +201,14 @@ def _handle_coordinator_update_internal( if ( not self.coordinator.update_reason_write and self.next_update is not None - and self.next_update > utcnow() + and self.next_update > dt_util.utcnow() ): return data = self.coordinator.data if data is None else data if data is None: return self._attr_native_value = get_sensor_data( - data, use_key or self.entity_description.luxtronik_key.value + data, use_key or self.entity_description.luxtronik_key ) if self._attr_native_value is None: @@ -246,9 +308,9 @@ def _handle_coordinator_update( else: # region Workaround: Inverter heater is active but not the heatpump! # Status shows heating but status 3 = no request! - sl1 = self._get_value(LC.C0117_STATUS_LINE_1) - sl3 = self._get_value(LC.C0119_STATUS_LINE_3) - add_circ_pump = self._get_value(LC.C0047_ADDITIONAL_CIRCULATION_PUMP) + sl1 = self._get_value(LC.STATUS_LINE_1) + sl3 = self._get_value(LC.STATUS_LINE_3) + add_circ_pump = self._get_value(LC.ADDITIONAL_CIRCULATION_PUMP) s1_workaround: list[str] = [ LuxStatus1Option.heatpump_idle, LuxStatus1Option.pump_forerun, @@ -278,8 +340,8 @@ def _handle_coordinator_update( LuxStatus3Option.cycle_lock, ] if sl3 in s3_workaround: - DHW_recirculation = self._get_value(LC.C0038_DHW_RECIRCULATION_PUMP) - AddHeat = self._get_value(LC.C0048_ADDITIONAL_HEAT_GENERATOR) + DHW_recirculation = self._get_value(LC.DHW_RECIRCULATION_PUMP) + AddHeat = self._get_value(LC.ADDITIONAL_HEAT_GENERATOR) if AddHeat and DHW_recirculation: # more fixes to detect thermal desinfection sequences self._attr_native_value = LuxOperationMode.domestic_water.value @@ -289,12 +351,14 @@ def _handle_coordinator_update( if self._attr_native_value == LuxOperationMode.no_request.value: # detect passive cooling if self.coordinator.detect_cooling_present(): - T_in = self._get_value(LC.C0010_FLOW_IN_TEMPERATURE) - T_out = self._get_value(LC.C0011_FLOW_OUT_TEMPERATURE) - T_heat_in = self._get_value(LC.C0204_HEAT_SOURCE_INPUT_TEMPERATURE) - T_heat_out = self._get_value(LC.C0020_HEAT_SOURCE_OUTPUT_TEMPERATURE) - Flow_WQ = self._get_value(LC.C0173_HEAT_SOURCE_FLOW_RATE) - Pump = self._get_value(LC.C0043_PUMP_FLOW) + T_in = self._get_value(LC.FLOW_IN_TEMPERATURE) + T_out = self._get_value(LC.FLOW_OUT_TEMPERATURE) + T_heat_in = self._get_value(LC.HEAT_SOURCE_INPUT_TEMPERATURE) + T_heat_out = self._get_value( + LC.HEAT_SOURCE_OUTPUT_TEMPERATURE + ) + Pump = self._get_value(LC.PUMP_FLOW) + Flow_WQ = self._get_value(LC.HEAT_SOURCE_FLOW_RATE) if (T_out > T_in) and (T_heat_out > T_heat_in) and (Flow_WQ > 0) and Pump: # LOGGER.info(f"Cooling mode detected!!!") self._attr_native_value = LuxOperationMode.cooling.value @@ -470,3 +534,276 @@ def format_time(self, value_timestamp: int | None) -> datetime | None: time_zone = dt_util.get_time_zone(self.hass.config.time_zone) value_timestamp = value_timestamp.replace(tzinfo=time_zone) return value_timestamp + + +@dataclass +class LuxtronikPeriodStatSensorExtraStoredData(SensorExtraStoredData): + """Object to hold extra stored data.""" + + _last_valid_state: int | float | Decimal | None = None + _today: int | float | Decimal | None = None + _yesterday: int | float | Decimal | None = None + _week: int | float | Decimal | None = None + _last_week: int | float | Decimal | None = None + _month: int | float | Decimal | None = None + _last_month: int | float | Decimal | None = None + _year: int | float | Decimal | None = None + _last_year: int | float | Decimal | None = None + _heating_period: Decimal = Decimal(0) + _last_heating_period: Decimal = Decimal(0) + _current_impulse: Decimal = Decimal(0) + _last_impulse: Decimal = Decimal(0) + + def as_dict(self) -> dict[str, Any]: + """Return a dict representation of the text data.""" + # return super().as_dict() | asdict(self) + return asdict(self) + + @classmethod + def from_dict(cls, restored: dict[str, Any]) -> Self | None: + # obj = super().from_dict(restored) + _last_valid_state = Decimal(restored["_last_valid_state"]) + _today = Decimal(restored["_today"]) + _yesterday = Decimal(restored["_yesterday"]) + _week = Decimal(restored["_week"]) + _last_week = Decimal(restored["_last_week"]) + _month = Decimal(restored["_month"]) + _last_month = Decimal(restored["_last_month"]) + _year = Decimal(restored["_year"]) + _last_year = Decimal(restored["_last_year"]) + _heating_period = Decimal(restored["_heating_period"]) + _last_heating_period = Decimal(restored["_last_heating_period"]) + _current_impulse: Decimal = Decimal(restored["_current_impulse"]) + _last_impulse: Decimal = Decimal(restored["_last_impulse"]) + cls( + _last_valid_state, + _today, + _yesterday, + _week, + _last_week, + _month, + _last_month, + _year, + _last_year, + _heating_period, + _last_heating_period, + _current_impulse, + _last_impulse, + ) + + +class LuxtronikPeriodStatSensor(LuxtronikSensorEntity, SensorEntity): # RestoreSensor): + # region Members + entity_description: LuxtronikPeriodStatSensorDescription + + _cron_pattern = PERIOD2CRON[DAILY].format(minute=0, hour=0) + + _impulse_active: bool | None = None + + _last_valid_state: Decimal = None + _today: Decimal = Decimal(0) + _yesterday: Decimal = Decimal(0) + _week: Decimal = Decimal(0) + _last_week: Decimal = Decimal(0) + _month: Decimal = Decimal(0) + _last_month: Decimal = Decimal(0) + _year: Decimal = Decimal(0) + _last_year: Decimal = Decimal(0) + _heating_period: Decimal = Decimal(0) + _last_heating_period: Decimal = Decimal(0) + _current_impulse: Decimal = Decimal(0) + _last_impulse: Decimal = Decimal(0) + # endregion Members + + # @native_value.setter + # def native_value(self, value: StateType | date | datetime | Decimal) -> None: + # """Set the value reported by the sensor.""" + # self._attr_native_value = value + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + def handle_event_impulse_active(event): + self._impulse_active = True + + def handle_event_impulse_inactive(event): + self._impulse_active = False + self._last_impulse = self._current_impulse + self._current_impulse = Decimal(0) + + # if self.entity_description.event_id_impulse_active is not None: + # self.hass.bus.listen( + # self.entity_description.event_id_impulse_active, + # handle_event_impulse_active, + # ) + # if self.entity_description.event_id_impulse_inactive is not None: + # self.hass.bus.listen( + # self.entity_description.event_id_impulse_inactive, + # handle_event_impulse_inactive, + # ) + + @staticmethod + def _validate_value( + state: StateType | date | datetime | Decimal | None, + ) -> Decimal | None: + """Parse the state as a Decimal if available. Throws DecimalException if the state is not a number.""" + try: + return ( + None + if state is None or state in [STATE_UNAVAILABLE, STATE_UNKNOWN] + else Decimal(state) + ) + except DecimalException: + return None + + @callback + def _handle_coordinator_update( + self, data: LuxtronikCoordinatorData | None = None + ) -> None: + """Handle updated data from the coordinator.""" + super()._handle_coordinator_update_internal(data) + new_value = self._validate_value(self._attr_native_value) + if new_value is not None: + if self._last_valid_state is None: + self._last_valid_state = new_value + else: + adjustment = new_value - self._last_valid_state + self._today += adjustment + self._week += adjustment + self._month += adjustment + self._year += adjustment + self._heating_period += adjustment + if self._impulse_active == True: + self._current_impulse += adjustment + + self._last_valid_state = new_value + + self.async_write_ha_state() + + async def _async_reset_meter(self, event): + """Determine cycle - Helper function for larger than daily cycles.""" + if self._cron_pattern is not None: + self.async_on_remove( + async_track_point_in_time( + self.hass, + self._async_reset_meter, + croniter(self._cron_pattern, dt_util.now()).get_next(datetime), + ) + ) + await self.async_reset_meter(self.entity_id) + + async def async_reset_meter(self, entity_id): + """Reset meter.""" + if self.entity_id != entity_id: + return + LOGGER.debug("Reset utility meter <%s>", self.entity_id) + last_reset = dt_util.utcnow() + + self._yesterday = self._today + self._today = Decimal(0) + + day_of_week = last_reset.weekday() # for 0 is Monday + if day_of_week == 0: + self._last_week = self._week + self._week = Decimal(0) + + if last_reset.day == 1: + self._last_month = self._month + self._month = Decimal(0) + + if last_reset.month == 9: + self._last_heating_period = self._heating_period + self._heating_period = Decimal(0) + + if last_reset.month == 1: + self._last_year = self._year + self._year = Decimal(0) + + self.async_write_ha_state() + + # async def async_added_to_hass(self) -> None: + # """Handle entity which will be added.""" + # await CoordinatorEntity.async_added_to_hass(self) + + # await super().async_added_to_hass() + + # if self._cron_pattern is not None: + # self.async_on_remove( + # async_track_point_in_time( + # self.hass, + # self._async_reset_meter, + # croniter(self._cron_pattern, dt_util.now()).get_next(datetime), + # ) + # ) + + # self.async_on_remove( + # async_dispatcher_connect( + # self.hass, SIGNAL_RESET_METER, self.async_reset_meter + # ) + # ) + + # if (last_sensor_data := await self.async_get_last_sensor_data()) is not None: + # # new introduced in 2022.04 + # self._attr_native_value = last_sensor_data.native_value + # self._attr_unit_of_measurement = last_sensor_data.native_unit_of_measurement + # # self._last_valid_state = last_sensor_data.last_valid_state + + # elif state := await self.async_get_last_state(): + # # legacy to be removed on 2022.10 (we are keeping this to avoid utility_meter counter losses) + # try: + # self._state = Decimal(state.state) + # except InvalidOperation: + # LOGGER.error( + # "Could not restore state <%s>. Resetting utility_meter.%s", + # state.state, + # self.name, + # ) + # else: + # self._attr_unit_of_measurement = state.attributes.get( + # ATTR_UNIT_OF_MEASUREMENT + # ) + # self._last_valid_state = ( + # Decimal(state.attributes[ATTR_LAST_VALID_STATE]) + # if state.attributes.get(ATTR_LAST_VALID_STATE) + # and is_number(state.attributes[ATTR_LAST_VALID_STATE]) + # else None + # ) + + @property + def extra_state_attributes(self): # -> dict[str, str]: + """Return the state attributes of the sensor.""" + state_attr = { + ATTR_LAST_VALID_STATE: str(self._last_valid_state), + ATTR_TODAY: str(self._today), + ATTR_YESTERDAY: str(self._yesterday), + ATTR_WEEK: str(self._week), + ATTR_LAST_WEEK: str(self._last_week), + ATTR_MONTH: str(self._month), + ATTR_LAST_MONTH: str(self._last_month), + ATTR_YEAR: str(self._year), + ATTR_LAST_YEAR: str(self._last_year), + ATTR_HEATING_PERIOD: str(self._heating_period), + ATTR_LAST_HEATING_PERIOD: str(self._last_heating_period), + ATTR_CURRENT_IMPULSE: str(self._current_impulse), + ATTR_LAST_IMPULSE: str(self._last_impulse), + } + return state_attr + + # @property + # def extra_restore_state_data(self) -> LuxtronikPeriodStatSensorExtraStoredData: + # """Return luxtronik climate specific state data to be restored.""" + # return LuxtronikPeriodStatSensorExtraStoredData( + # self._last_valid_state, + # self._today, + # self._yesterday, + # self._week, + # self._last_week, + # self._month, + # self._last_month, + # self._year, + # self._last_year, + # self._heating_period, + # self._last_heating_period, + # self._current_impulse, + # self._last_impulse, + # ) diff --git a/custom_components/luxtronik/sensor_entities_predefined.py b/custom_components/luxtronik/sensor_entities_predefined.py index 5df9121..0d654e3 100644 --- a/custom_components/luxtronik/sensor_entities_predefined.py +++ b/custom_components/luxtronik/sensor_entities_predefined.py @@ -20,33 +20,34 @@ UPDATE_INTERVAL_VERY_SLOW, DeviceKey, FirmwareVersionMinor, - LuxCalculation as LC, + Calculation_SensorKey as LC, LuxOperationMode, - LuxParameter as LP, + Parameter_SensorKey as LP, LuxStatus1Option, LuxStatus3Option, LuxSwitchoffReason, - LuxVisibility as LV, + Visibility_SensorKey as LV, SensorAttrFormat, SensorAttrKey as SA, - SensorKey, UnitOfVolumeFlowRateExt, ) from .model import ( + ATTR_DESCRIPTION, LuxtronikEntityAttributeDescription as attr, LuxtronikSensorDescription as descr, LuxtronikIndexSensorDescription as descr_index, + LuxtronikPeriodStatSensorDescription as descr_period, ) # endregion Imports SENSORS_STATUS: list[descr] = [ descr( - key=SensorKey.STATUS, - luxtronik_key=LC.C0080_STATUS, + luxtronik_key=LC.STATUS, icon_by_state=LUX_STATE_ICON_MAP, device_class=SensorDeviceClass.ENUM, extra_attributes=[ + ATTR_DESCRIPTION, attr(SA.EVU_FIRST_START_TIME, LC.UNSET, None, True), attr(SA.EVU_FIRST_END_TIME, LC.UNSET, None, True), attr(SA.EVU_SECOND_START_TIME, LC.UNSET, None, True), @@ -57,11 +58,10 @@ ), ] -SENSORS_INDEX: list[descr] = [ +SENSORS_INDEX: list[descr_index] = [ descr_index( - key=SensorKey.SWITCHOFF_REASON, - luxtronik_key=LP.P0716_0720_SWITCHOFF_REASON, - luxtronik_key_timestamp=LP.P0721_0725_SWITCHOFF_TIMESTAMP, + luxtronik_key=LP.SWITCHOFF_REASON, + luxtronik_key_timestamp=LP.SWITCHOFF_TIMESTAMP, entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:electric-switch", device_class=SensorDeviceClass.ENUM, @@ -69,36 +69,70 @@ ), ] -SENSORS: list[descr] = [ - # region Main heatpump - descr( - key=SensorKey.STATUS_TIME, - luxtronik_key=LC.C0120_STATUS_TIME, +SENSORS_PERIOD: list[descr_period] = [ + descr_period( + luxtronik_key=LC.COMPRESSOR1_IMPULSES, + icon="mdi:pulse", + state_class=SensorStateClass.TOTAL_INCREASING, + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement="\u2211", + entity_registry_enabled_default=False, + visibility=LV.COMPRESSOR1_IMPULSES, + ), + descr_period( + luxtronik_key=LC.STATUS_TIME, entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:timer-sand", native_unit_of_measurement=UnitOfTime.SECONDS, entity_registry_visible_default=False, native_precision=0, extra_attributes=[ - attr(SA.STATUS_TEXT, LC.C0120_STATUS_TIME, SensorAttrFormat.HOUR_MINUTE), - attr(SA.TIMER_HEATPUMP_ON, LC.C0067_TIMER_HEATPUMP_ON), - attr(SA.TIMER_ADD_HEAT_GENERATOR_ON, LC.C0068_TIMER_ADD_HEAT_GENERATOR_ON), - attr(SA.TIMER_SEC_HEAT_GENERATOR_ON, LC.C0069_TIMER_SEC_HEAT_GENERATOR_ON), - attr(SA.TIMER_NET_INPUT_DELAY, LC.C0070_TIMER_NET_INPUT_DELAY), - attr(SA.TIMER_SCB_OFF, LC.C0071_TIMER_SCB_OFF), - attr(SA.TIMER_SCB_ON, LC.C0072_TIMER_SCB_ON), - attr(SA.TIMER_COMPRESSOR_OFF, LC.C0073_TIMER_COMPRESSOR_OFF), - attr(SA.TIMER_HC_ADD, LC.C0074_TIMER_HC_ADD), - attr(SA.TIMER_HC_LESS, LC.C0075_TIMER_HC_LESS), - attr(SA.TIMER_TDI, LC.C0076_TIMER_TDI), - attr(SA.TIMER_BLOCK_DHW, LC.C0077_TIMER_BLOCK_DHW), - attr(SA.TIMER_DEFROST, LC.C0141_TIMER_DEFROST), - attr(SA.TIMER_HOT_GAS, LC.C0158_TIMER_HOT_GAS), + attr(SA.STATUS_TEXT, LC.STATUS_TIME, SensorAttrFormat.HOUR_MINUTE), + attr(SA.TIMER_HEATPUMP_ON, LC.TIMER_HEATPUMP_ON), + attr(SA.TIMER_ADD_HEAT_GENERATOR_ON, LC.TIMER_ADD_HEAT_GENERATOR_ON), + attr(SA.TIMER_SEC_HEAT_GENERATOR_ON, LC.TIMER_SEC_HEAT_GENERATOR_ON), + attr(SA.TIMER_NET_INPUT_DELAY, LC.TIMER_NET_INPUT_DELAY), + attr(SA.TIMER_SCB_OFF, LC.TIMER_SCB_OFF), + attr(SA.TIMER_SCB_ON, LC.TIMER_SCB_ON), + attr(SA.TIMER_COMPRESSOR_OFF, LC.TIMER_COMPRESSOR_OFF), + attr(SA.TIMER_HC_ADD, LC.TIMER_HC_ADD), + attr(SA.TIMER_HC_LESS, LC.TIMER_HC_LESS), + attr(SA.TIMER_TDI, LC.TIMER_TDI), + attr(SA.TIMER_BLOCK_DHW, LC.TIMER_BLOCK_DHW), + attr(SA.TIMER_DEFROST, LC.TIMER_DEFROST), + attr(SA.TIMER_HOT_GAS, LC.TIMER_HOT_GAS), ], ), - descr( - key=SensorKey.STATUS_LINE_1, - luxtronik_key=LC.C0117_STATUS_LINE_1, +] + +SENSORS: list[descr] = [ + # region Main heatpump + # descr( + # luxtronik_key=LC.STATUS_TIME, + # entity_category=EntityCategory.DIAGNOSTIC, + # icon="mdi:timer-sand", + # native_unit_of_measurement=UnitOfTime.SECONDS, + # entity_registry_visible_default=False, + # native_precision=0, + # extra_attributes=[ + # attr(SA.STATUS_TEXT, LC.STATUS_TIME, SensorAttrFormat.HOUR_MINUTE), + # attr(SA.TIMER_HEATPUMP_ON, LC.TIMER_HEATPUMP_ON), + # attr(SA.TIMER_ADD_HEAT_GENERATOR_ON, LC.TIMER_ADD_HEAT_GENERATOR_ON), + # attr(SA.TIMER_SEC_HEAT_GENERATOR_ON, LC.TIMER_SEC_HEAT_GENERATOR_ON), + # attr(SA.TIMER_NET_INPUT_DELAY, LC.TIMER_NET_INPUT_DELAY), + # attr(SA.TIMER_SCB_OFF, LC.TIMER_SCB_OFF), + # attr(SA.TIMER_SCB_ON, LC.TIMER_SCB_ON), + # attr(SA.TIMER_COMPRESSOR_OFF, LC.TIMER_COMPRESSOR_OFF), + # attr(SA.TIMER_HC_ADD, LC.TIMER_HC_ADD), + # attr(SA.TIMER_HC_LESS, LC.TIMER_HC_LESS), + # attr(SA.TIMER_TDI, LC.TIMER_TDI), + # attr(SA.TIMER_BLOCK_DHW, LC.TIMER_BLOCK_DHW), + # attr(SA.TIMER_DEFROST, LC.TIMER_DEFROST), + # attr(SA.TIMER_HOT_GAS, LC.TIMER_HOT_GAS), + # ], + # ), + descr( + luxtronik_key=LC.STATUS_LINE_1, entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:numeric-1-circle", entity_registry_visible_default=False, @@ -107,8 +141,7 @@ # translation_key="status1", ), descr( - key=SensorKey.STATUS_LINE_2, - luxtronik_key=LC.C0118_STATUS_LINE_2, + luxtronik_key=LC.STATUS_LINE_2, entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:numeric-2-circle", entity_registry_visible_default=False, @@ -117,8 +150,7 @@ # translation_key="status2", ), descr( - key=SensorKey.STATUS_LINE_3, - luxtronik_key=LC.C0119_STATUS_LINE_3, + luxtronik_key=LC.STATUS_LINE_3, entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:numeric-3-circle", entity_registry_visible_default=False, @@ -127,81 +159,55 @@ # translation_key="status3", ), descr( - key=SensorKey.HEAT_SOURCE_INPUT_TEMPERATURE, - luxtronik_key=LC.C0204_HEAT_SOURCE_INPUT_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.HEAT_SOURCE_INPUT_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_registry_enabled_default=False, update_interval=UPDATE_INTERVAL_NORMAL, ), descr( - key=SensorKey.OUTDOOR_TEMPERATURE, - luxtronik_key=LC.C0015_OUTDOOR_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.OUTDOOR_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, update_interval=UPDATE_INTERVAL_SLOW, ), descr( - key=SensorKey.OUTDOOR_TEMPERATURE_AVERAGE, - luxtronik_key=LC.C0016_OUTDOOR_TEMPERATURE_AVERAGE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.OUTDOOR_TEMPERATURE_AVERAGE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_registry_enabled_default=False, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.COMPRESSOR1_IMPULSES, - luxtronik_key=LC.C0057_COMPRESSOR1_IMPULSES, - icon="mdi:pulse", - state_class=SensorStateClass.TOTAL_INCREASING, - entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement="\u2211", - entity_registry_enabled_default=False, - visibility=LV.V0081_COMPRESSOR1_IMPULSES, - ), - descr( - key=SensorKey.COMPRESSOR1_OPERATION_HOURS, - luxtronik_key=LC.C0056_COMPRESSOR1_OPERATION_HOURS, + luxtronik_key=LC.COMPRESSOR1_OPERATION_HOURS, icon="mdi:timer-sand", state_class=SensorStateClass.TOTAL_INCREASING, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfTime.HOURS, - visibility=LV.V0080_COMPRESSOR1_OPERATION_HOURS, + visibility=LV.COMPRESSOR1_OPERATION_HOURS, factor=SECOUND_TO_HOUR_FACTOR, native_precision=2, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.COMPRESSOR2_IMPULSES, - luxtronik_key=LC.C0059_COMPRESSOR2_IMPULSES, + luxtronik_key=LC.COMPRESSOR2_IMPULSES, icon="mdi:pulse", state_class=SensorStateClass.TOTAL_INCREASING, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement="\u2211", entity_registry_enabled_default=False, - visibility=LV.V0084_COMPRESSOR2_IMPULSES, + visibility=LV.COMPRESSOR2_IMPULSES, ), descr( - key=SensorKey.COMPRESSOR2_OPERATION_HOURS, - luxtronik_key=LC.C0058_COMPRESSOR2_OPERATION_HOURS, + luxtronik_key=LC.COMPRESSOR2_OPERATION_HOURS, icon="mdi:timer-sand", state_class=SensorStateClass.TOTAL_INCREASING, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfTime.HOURS, - visibility=LV.V0083_COMPRESSOR2_OPERATION_HOURS, + visibility=LV.COMPRESSOR2_OPERATION_HOURS, factor=SECOUND_TO_HOUR_FACTOR, native_precision=2, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.OPERATION_HOURS, - luxtronik_key=LC.C0063_OPERATION_HOURS, + luxtronik_key=LC.OPERATION_HOURS, icon="mdi:timer-sand", state_class=SensorStateClass.TOTAL_INCREASING, entity_category=EntityCategory.DIAGNOSTIC, @@ -211,8 +217,7 @@ update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.HEAT_AMOUNT_COUNTER, - luxtronik_key=LC.C0154_HEAT_AMOUNT_COUNTER, + luxtronik_key=LC.HEAT_AMOUNT_COUNTER, icon="mdi:lightning-bolt-circle", state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, @@ -222,8 +227,7 @@ update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.HEAT_AMOUNT_FLOW_RATE, - luxtronik_key=LC.C0155_HEAT_AMOUNT_FLOW_RATE, + luxtronik_key=LC.HEAT_AMOUNT_FLOW_RATE, device_key=DeviceKey.heating, icon="mdi:water-sync", state_class=SensorStateClass.MEASUREMENT, @@ -234,8 +238,7 @@ update_interval=UPDATE_INTERVAL_NORMAL, ), descr( - key=SensorKey.HEAT_SOURCE_FLOW_RATE, - luxtronik_key=LC.C0173_HEAT_SOURCE_FLOW_RATE, + luxtronik_key=LC.HEAT_SOURCE_FLOW_RATE, device_key=DeviceKey.heating, icon="mdi:water-sync", state_class=SensorStateClass.MEASUREMENT, @@ -246,249 +249,181 @@ update_interval=UPDATE_INTERVAL_NORMAL, ), descr( - key=SensorKey.HOT_GAS_TEMPERATURE, - luxtronik_key=LC.C0014_HOT_GAS_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.HOT_GAS_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0027_HOT_GAS_TEMPERATURE, + visibility=LV.HOT_GAS_TEMPERATURE, ), descr( - key=SensorKey.SUCTION_COMPRESSOR_TEMPERATURE, - luxtronik_key=LC.C0176_SUCTION_COMPRESSOR_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.SUCTION_COMPRESSOR_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0289_SUCTION_COMPRESSOR_TEMPERATURE, + visibility=LV.SUCTION_COMPRESSOR_TEMPERATURE, ), descr( - key=SensorKey.SUCTION_EVAPORATOR_TEMPERATURE, - luxtronik_key=LC.C0175_SUCTION_EVAPORATOR_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.SUCTION_EVAPORATOR_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0310_SUCTION_EVAPORATOR_TEMPERATURE, + visibility=LV.SUCTION_EVAPORATOR_TEMPERATURE, ), descr( - key=SensorKey.COMPRESSOR_HEATING_TEMPERATURE, - luxtronik_key=LC.C0177_COMPRESSOR_HEATING_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.COMPRESSOR_HEATING_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0290_COMPRESSOR_HEATING, + visibility=LV.COMPRESSOR_HEATING, ), descr( - key=SensorKey.OVERHEATING_TEMPERATURE, - luxtronik_key=LC.C0178_OVERHEATING_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.OVERHEATING_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, - visibility=LV.V0291_OVERHEATING_TEMPERATURE, + visibility=LV.OVERHEATING_TEMPERATURE, ), descr( - key=SensorKey.OVERHEATING_TARGET_TEMPERATURE, - luxtronik_key=LC.C0179_OVERHEATING_TARGET_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.OVERHEATING_TARGET_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.KELVIN, - visibility=LV.V0291_OVERHEATING_TEMPERATURE, + visibility=LV.OVERHEATING_TEMPERATURE, ), descr( - key=SensorKey.HIGH_PRESSURE, - luxtronik_key=LC.C0180_HIGH_PRESSURE, + luxtronik_key=LC.HIGH_PRESSURE, icon="mdi:gauge-full", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=UnitOfPressure.BAR, - visibility=LV.V0292_LIN_PRESSURE, + visibility=LV.LIN_PRESSURE, ), descr( - key=SensorKey.LOW_PRESSURE, - luxtronik_key=LC.C0181_LOW_PRESSURE, + luxtronik_key=LC.LOW_PRESSURE, icon="mdi:gauge-low", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=UnitOfPressure.BAR, - visibility=LV.V0292_LIN_PRESSURE, + visibility=LV.LIN_PRESSURE, ), descr( - key=SensorKey.ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS, - luxtronik_key=LC.C0060_ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS, + luxtronik_key=LC.ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS, icon="mdi:timer-sand", state_class=SensorStateClass.TOTAL_INCREASING, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfTime.HOURS, - visibility=LV.V0086_ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS, + visibility=LV.ADDITIONAL_HEAT_GENERATOR_OPERATION_HOURS, factor=SECOUND_TO_HOUR_FACTOR, native_precision=2, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER, - luxtronik_key=LP.P1059_ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER, - icon="mdi:lightning-bolt-circle", - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, + luxtronik_key=LP.ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, invisible_if_value=0.0, - visibility=LV.V0324_ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER, - factor=0.1, + visibility=LV.ADDITIONAL_HEAT_GENERATOR_AMOUNT_COUNTER, + factor=0.1, # TODO: Check native_precision=1, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.ANALOG_OUT1, - luxtronik_key=LC.C0156_ANALOG_OUT1, + luxtronik_key=LC.ANALOG_OUT1, icon="mdi:alpha-v-circle-outline", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.VOLTAGE, native_unit_of_measurement=UnitOfElectricPotential.VOLT, - visibility=LV.V0248_ANALOG_OUT1, + visibility=LV.ANALOG_OUT1, entity_registry_enabled_default=False, - factor=0.1, + factor=0.1, # TODO!: Check ), descr( - key=SensorKey.ANALOG_OUT2, - luxtronik_key=LC.C0157_ANALOG_OUT2, + luxtronik_key=LC.ANALOG_OUT2, icon="mdi:alpha-v-circle-outline", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.VOLTAGE, native_unit_of_measurement=UnitOfElectricPotential.VOLT, - visibility=LV.V0249_ANALOG_OUT2, + visibility=LV.ANALOG_OUT2, entity_registry_enabled_default=False, - factor=0.1, + factor=0.1, # TODO: Check ), descr( - key=SensorKey.CURRENT_HEAT_OUTPUT, - luxtronik_key=LC.C0257_CURRENT_HEAT_OUTPUT, + luxtronik_key=LC.CURRENT_HEAT_OUTPUT, icon="mdi:lightning-bolt-circle", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.POWER, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfPower.WATT, entity_registry_enabled_default=False, native_precision=0, ), descr( - key=SensorKey.PUMP_FREQUENCY, - luxtronik_key=LC.C0231_PUMP_FREQUENCY, + luxtronik_key=LC.PUMP_FREQUENCY, icon="mdi:sine-wave", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.FREQUENCY, native_unit_of_measurement=UnitOfFrequency.HERTZ, entity_registry_enabled_default=False, ), descr( - key=SensorKey.PUMP_FLOW_DELTA_TARGET, - luxtronik_key=LC.C0239_PUMP_FLOW_DELTA_TARGET, + luxtronik_key=LC.PUMP_FLOW_DELTA_TARGET, icon="mdi:delta", - state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfTemperature.KELVIN, - factor=0.1, + factor=0.1, # TODO: Check entity_registry_enabled_default=False, ), descr( - key=SensorKey.PUMP_FLOW_DELTA, - luxtronik_key=LC.C0240_PUMP_FLOW_DELTA, + luxtronik_key=LC.PUMP_FLOW_DELTA, icon="mdi:delta", - state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfTemperature.KELVIN, - factor=0.1, + factor=0.1, # TODO: Check entity_registry_enabled_default=False, ), descr( - key=SensorKey.CIRCULATION_PUMP_DELTA_TARGET, - luxtronik_key=LC.C0242_CIRCULATION_PUMP_DELTA_TARGET, + luxtronik_key=LC.CIRCULATION_PUMP_DELTA_TARGET, icon="mdi:delta", - state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfTemperature.KELVIN, - factor=0.1, + factor=0.1, # TODO: Check entity_registry_enabled_default=False, ), descr( - key=SensorKey.CIRCULATION_PUMP_DELTA, - luxtronik_key=LC.C0243_CIRCULATION_PUMP_DELTA, + luxtronik_key=LC.CIRCULATION_PUMP_DELTA, icon="mdi:delta", - state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfTemperature.KELVIN, - factor=0.1, + factor=0.1, # TODO: Check entity_registry_enabled_default=False, ), descr( - key=SensorKey.HEAT_SOURCE_OUTPUT_TEMPERATURE, - luxtronik_key=LC.C0020_HEAT_SOURCE_OUTPUT_TEMPERATURE, - icon="mdi:thermometer", - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.TEMPERATURE, + luxtronik_key=LC.HEAT_SOURCE_OUTPUT_TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_registry_enabled_default=False, invisible_if_value=-50.0, - visibility=LV.V0291_OVERHEATING_TEMPERATURE, + visibility=LV.OVERHEATING_TEMPERATURE, ), descr( - key=SensorKey.ERROR_REASON, - luxtronik_key=LC.C0100_ERROR_REASON, + luxtronik_key=LC.ERROR_REASON, icon="mdi:alert", extra_attributes=[ - attr(SA.TIMESTAMP, LC.C0095_ERROR_TIME), - attr(SA.CODE, LC.C0100_ERROR_REASON), - attr(SA.CAUSE, LC.C0100_ERROR_REASON), - attr(SA.REMEDY, LC.C0100_ERROR_REASON), + attr(SA.TIMESTAMP, LC.ERROR_TIME), + attr(SA.CODE, LC.ERROR_REASON), + attr(SA.CAUSE, LC.ERROR_REASON), + attr(SA.REMEDY, LC.ERROR_REASON), ], ), # endregion Main heatpump # region Heating descr( - key=SensorKey.FLOW_IN_TEMPERATURE, - luxtronik_key=LC.C0010_FLOW_IN_TEMPERATURE, + luxtronik_key=LC.FLOW_IN_TEMPERATURE, device_key=DeviceKey.heating, entity_category=None, icon="mdi:waves-arrow-right", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, extra_attributes=[ attr( SA.MAX_ALLOWED, - LP.P0149_FLOW_IN_TEMPERATURE_MAX_ALLOWED, + LP.FLOW_IN_TEMPERATURE_MAX_ALLOWED, SensorAttrFormat.CELSIUS_TENTH, ), ], ), descr( - key=SensorKey.FLOW_OUT_TEMPERATURE, - luxtronik_key=LC.C0011_FLOW_OUT_TEMPERATURE, + luxtronik_key=LC.FLOW_OUT_TEMPERATURE, device_key=DeviceKey.heating, entity_category=None, icon="mdi:waves-arrow-left", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), descr( - key=SensorKey.FLOW_OUT_TEMPERATURE_TARGET, - luxtronik_key=LC.C0012_FLOW_OUT_TEMPERATURE_TARGET, + luxtronik_key=LC.FLOW_OUT_TEMPERATURE_TARGET, device_key=DeviceKey.heating, entity_category=None, - icon="mdi:thermometer", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, extra_attributes=[ attr( SA.SWITCH_GAP, - LC.C0011_FLOW_OUT_TEMPERATURE, + LC.FLOW_OUT_TEMPERATURE, SensorAttrFormat.SWITCH_GAP, ), ], ), descr( - key=SensorKey.OPERATION_HOURS_HEATING, - luxtronik_key=LC.C0064_OPERATION_HOURS_HEATING, + luxtronik_key=LC.OPERATION_HOURS_HEATING, device_key=DeviceKey.heating, icon="mdi:timer-sand", state_class=SensorStateClass.TOTAL_INCREASING, @@ -499,75 +434,57 @@ update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.HEAT_AMOUNT_HEATING, - luxtronik_key=LC.C0151_HEAT_AMOUNT_HEATING, + luxtronik_key=LC.HEAT_AMOUNT_HEATING, device_key=DeviceKey.heating, - icon="mdi:lightning-bolt-circle", - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_precision=1, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.HEAT_ENERGY_INPUT, - luxtronik_key=LP.P1136_HEAT_ENERGY_INPUT, + luxtronik_key=LP.HEAT_ENERGY_INPUT, device_key=DeviceKey.heating, icon="mdi:circle-slice-3", - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_precision=2, - factor=0.01, + factor=0.01, # TODO: Check! min_firmware_version_minor=FirmwareVersionMinor.minor_88, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.FLOW_OUT_TEMPERATURE_EXTERNAL, - luxtronik_key=LC.C0013_FLOW_OUT_TEMPERATURE_EXTERNAL, + luxtronik_key=LC.FLOW_OUT_TEMPERATURE_EXTERNAL, device_key=DeviceKey.heating, entity_category=None, icon="mdi:waves-arrow-right", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0024_FLOW_OUT_TEMPERATURE_EXTERNAL, + visibility=LV.FLOW_OUT_TEMPERATURE_EXTERNAL, ), descr( - key=SensorKey.ROOM_THERMOSTAT_TEMPERATURE, - luxtronik_key=LC.C0227_ROOM_THERMOSTAT_TEMPERATURE, + luxtronik_key=LC.ROOM_THERMOSTAT_TEMPERATURE, device_key=DeviceKey.heating, entity_category=None, - icon="mdi:thermometer", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0122_ROOM_THERMOSTAT, + visibility=LV.ROOM_THERMOSTAT, ), descr( - key=SensorKey.ROOM_THERMOSTAT_TEMPERATURE_TARGET, - luxtronik_key=LC.C0228_ROOM_THERMOSTAT_TEMPERATURE_TARGET, + luxtronik_key=LC.ROOM_THERMOSTAT_TEMPERATURE_TARGET, device_key=DeviceKey.heating, entity_category=None, - icon="mdi:thermometer", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0122_ROOM_THERMOSTAT, + visibility=LV.ROOM_THERMOSTAT, ), # endregion Heating # region Domestic water descr( - key=SensorKey.DHW_TEMPERATURE, - luxtronik_key=LC.C0017_DHW_TEMPERATURE, + luxtronik_key=LC.DHW_TEMPERATURE, device_key=DeviceKey.domestic_water, entity_category=None, icon="mdi:coolant-temperature", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), descr( - key=SensorKey.DHW_OPERATION_HOURS, - luxtronik_key=LC.C0065_DHW_OPERATION_HOURS, + luxtronik_key=LC.DHW_OPERATION_HOURS, device_key=DeviceKey.domestic_water, entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:timer-sand", @@ -578,54 +495,42 @@ update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.DHW_HEAT_AMOUNT, - luxtronik_key=LC.C0152_DHW_HEAT_AMOUNT, + luxtronik_key=LC.DHW_HEAT_AMOUNT, device_key=DeviceKey.domestic_water, - icon="mdi:lightning-bolt-circle", - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_precision=1, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.DHW_ENERGY_INPUT, - luxtronik_key=LP.P1137_DHW_ENERGY_INPUT, + luxtronik_key=LP.DHW_ENERGY_INPUT, device_key=DeviceKey.domestic_water, icon="mdi:circle-slice-3", - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_precision=2, - factor=0.01, + factor=0.01, # TODO!: Check min_firmware_version_minor=FirmwareVersionMinor.minor_88, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), descr( - key=SensorKey.SOLAR_COLLECTOR_TEMPERATURE, - luxtronik_key=LC.C0026_SOLAR_COLLECTOR_TEMPERATURE, + luxtronik_key=LC.SOLAR_COLLECTOR_TEMPERATURE, device_key=DeviceKey.domestic_water, entity_category=None, icon="mdi:solar-panel-large", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0038_SOLAR_COLLECTOR, + visibility=LV.SOLAR_COLLECTOR, ), descr( - key=SensorKey.SOLAR_BUFFER_TEMPERATURE, - luxtronik_key=LC.C0027_SOLAR_BUFFER_TEMPERATURE, + luxtronik_key=LC.SOLAR_BUFFER_TEMPERATURE, device_key=DeviceKey.domestic_water, entity_category=None, icon="mdi:propane-tank-outline", - device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - visibility=LV.V0039_SOLAR_BUFFER, + visibility=LV.SOLAR_BUFFER, ), descr( - key=SensorKey.OPERATION_HOURS_SOLAR, - luxtronik_key=LP.P0882_SOLAR_OPERATION_HOURS, + luxtronik_key=LP.SOLAR_OPERATION_HOURS, device_key=DeviceKey.domestic_water, icon="mdi:timer-sand", state_class=SensorStateClass.TOTAL_INCREASING, @@ -633,14 +538,13 @@ native_unit_of_measurement=UnitOfTime.HOURS, factor=SECOUND_TO_HOUR_FACTOR, native_precision=2, - visibility=LV.V0038_SOLAR_COLLECTOR, + visibility=LV.SOLAR_COLLECTOR, update_interval=UPDATE_INTERVAL_VERY_SLOW, ), # endregion Domestic water # region Cooling descr( - key=SensorKey.OPERATION_HOURS_COOLING, - luxtronik_key=LC.C0066_OPERATION_HOURS_COOLING, + luxtronik_key=LC.OPERATION_HOURS_COOLING, device_key=DeviceKey.cooling, icon="mdi:timer-sand", state_class=SensorStateClass.TOTAL_INCREASING, diff --git a/custom_components/luxtronik/switch.py b/custom_components/luxtronik/switch.py index 5a6fe5a..5ef80e7 100644 --- a/custom_components/luxtronik/switch.py +++ b/custom_components/luxtronik/switch.py @@ -64,7 +64,7 @@ def __init__( self.entity_id = ENTITY_ID_FORMAT.format(f"{prefix}_{description.key}") self._attr_unique_id = self.entity_id self._sensor_data = get_sensor_data( - coordinator.data, description.luxtronik_key.value + coordinator.data, description.luxtronik_key ) hass.bus.async_listen(f"{DOMAIN}_data_update", self._data_update) @@ -87,7 +87,7 @@ def _handle_coordinator_update( if data is None: return descr = self.entity_description - self._attr_state = get_sensor_data(data, descr.luxtronik_key.value) + self._attr_state = get_sensor_data(data, descr.luxtronik_key) if descr.on_state is True or descr.on_state is False: if self._attr_state is not None: self._attr_state = bool(self._attr_state) @@ -112,7 +112,7 @@ async def _set_state(self, state): data = await self.coordinator.async_write( self.entity_description.luxtronik_key.value.split(".")[1], state ) - value = get_sensor_data(data, self.entity_description.luxtronik_key.value) + value = get_sensor_data(data, self.entity_description.luxtronik_key) if ( self.entity_description.on_state is True or self.entity_description.on_state is False diff --git a/custom_components/luxtronik/switch_entities_predefined.py b/custom_components/luxtronik/switch_entities_predefined.py index 218ae23..bd4ae75 100644 --- a/custom_components/luxtronik/switch_entities_predefined.py +++ b/custom_components/luxtronik/switch_entities_predefined.py @@ -5,9 +5,8 @@ UPDATE_INTERVAL_NORMAL, DeviceKey, LuxMode, - LuxParameter as LP, - LuxVisibility as LV, - SensorKey, + Parameter_SensorKey as LP, + Visibility_SensorKey as LV, ) from .model import LuxtronikSwitchDescription @@ -16,36 +15,32 @@ # ... # region Main heatpump LuxtronikSwitchDescription( - luxtronik_key=LP.P0860_REMOTE_MAINTENANCE, - key=SensorKey.REMOTE_MAINTENANCE, + luxtronik_key=LP.REMOTE_MAINTENANCE, icon="mdi:remote-desktop", entity_category=EntityCategory.CONFIG, ), LuxtronikSwitchDescription( - luxtronik_key=LP.P0869_EFFICIENCY_PUMP, - key=SensorKey.EFFICIENCY_PUMP, + luxtronik_key=LP.EFFICIENCY_PUMP, icon="mdi:leaf-circle", entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, # device_class=SensorDeviceClass.HEAT ), LuxtronikSwitchDescription( - luxtronik_key=LP.P1033_PUMP_HEAT_CONTROL, - key=SensorKey.PUMP_HEAT_CONTROL, + luxtronik_key=LP.PUMP_HEAT_CONTROL, icon="mdi:pump", entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, # device_class=SensorDeviceClass.HEAT ), LuxtronikSwitchDescription( - luxtronik_key=LP.P1087_SILENT_MODE, - key=SensorKey.SILENT_MODE, + luxtronik_key=LP.SILENT_MODE, icon="mdi:volume-minus", entity_category=EntityCategory.CONFIG, - visibility=LV.V0357_SILENT_MODE_TIME_MENU, + visibility=LV.SILENT_MODE_TIME_MENU, ), # LuxtronikSwitchDescription( - # luxtronik_key=LP.P0870_AMOUNT_COUNTER_ACTIVE, + # luxtronik_key=LP.AMOUNT_COUNTER_ACTIVE, # key="amount_counter_active", # icon="mdi:counter", # entity_category=EntityCategory.CONFIG, @@ -53,9 +48,9 @@ # endregion Main heatpump # region Heating LuxtronikSwitchDescription( + key="heating", + luxtronik_key=LP.MODE_HEATING, device_key=DeviceKey.heating, - luxtronik_key=LP.P0003_MODE_HEATING, - key=SensorKey.HEATING, icon_by_state={True: "mdi:radiator", False: "mdi:radiator-off"}, device_class=None, on_state=LuxMode.automatic.value, @@ -63,26 +58,24 @@ update_interval=UPDATE_INTERVAL_NORMAL, ), LuxtronikSwitchDescription( + luxtronik_key=LP.PUMP_OPTIMIZATION, device_key=DeviceKey.heating, - luxtronik_key=LP.P0049_PUMP_OPTIMIZATION, - key=SensorKey.PUMP_OPTIMIZATION, icon="mdi:tune", entity_category=EntityCategory.CONFIG, - visibility=LV.V0144_PUMP_OPTIMIZATION, + visibility=LV.PUMP_OPTIMIZATION, ), LuxtronikSwitchDescription( + luxtronik_key=LP.HEATING_THRESHOLD, device_key=DeviceKey.heating, - luxtronik_key=LP.P0699_HEATING_THRESHOLD, - key=SensorKey.HEATING_THRESHOLD, icon="mdi:download-outline", entity_category=EntityCategory.CONFIG, ), # endregion Heating # region Domestic water LuxtronikSwitchDescription( + key="domestic_water", + luxtronik_key=LP.MODE_DHW, device_key=DeviceKey.domestic_water, - luxtronik_key=LP.P0004_MODE_DHW, - key=SensorKey.DOMESTIC_WATER, icon_by_state={True: "mdi:water-boiler-auto", False: "mdi:water-boiler-off"}, device_class=None, on_state=LuxMode.automatic.value, @@ -98,9 +91,9 @@ # endregion Domestic water # region Cooling LuxtronikSwitchDescription( + key="cooling", + luxtronik_key=LP.MODE_COOLING, device_key=DeviceKey.cooling, - luxtronik_key=LP.P0108_MODE_COOLING, - key=SensorKey.COOLING, icon="mdi:snowflake", entity_category=EntityCategory.CONFIG, device_class=None, diff --git a/custom_components/luxtronik/translations/de.json b/custom_components/luxtronik/translations/de.json index b7ead60..b499a7d 100644 --- a/custom_components/luxtronik/translations/de.json +++ b/custom_components/luxtronik/translations/de.json @@ -216,6 +216,12 @@ "no request": "Leerlauf (keine Anforderung)" }, "state_attributes": { + "description": { + "name": "Beschreibung", + "state": { + "_": "Status der W\u00e4rmepumpe." + } + }, "evu_text": { "state": { "evu_until": "Noch {evu_time} Minuten Netzsperre.", diff --git a/custom_components/luxtronik/translations/en.json b/custom_components/luxtronik/translations/en.json index 64b78b6..9f9194f 100644 --- a/custom_components/luxtronik/translations/en.json +++ b/custom_components/luxtronik/translations/en.json @@ -174,6 +174,36 @@ "heating_max_flow_out_increase_temperature": { "name": "Max. target flow out increase" }, + "air_defrost_temperature": { + "name": "Air defrost temperature", + "state_attributes": { + "description": { + "state": { + "_": "Setting for the release temperature for the air defrost. Below the set temperature, the air defrosting is locked." + } + } + } + }, + "air_defrost_stop_temperature": { + "name": "Air defrost stop temperature", + "state_attributes": { + "description": { + "state": { + "_": "A value is only displayed for L/W devices and if the air defrost is switched on. Setting for the temperature at which the air defrosting is terminated at the outlet of the evaporator." + } + } + } + }, + "heating_return_temperature_limit": { + "name": "Return temperature limit", + "state_attributes": { + "description": { + "state": { + "_": "Setting the maximum return setpoint temperatures in heating mode." + } + } + } + }, "heating_hysteresis": { "name": "Hysteresis heating" }, @@ -305,13 +335,15 @@ "793": "Major VSS fault", "-1": "Unknown error" }, - "cause": { - "name": "Cause" - }, - "remedy": { - "name": "Remedy" + "state_attributes": { + "cause": { + "name": "Cause" + }, + "remedy": { + "name": "Remedy" + } } - }, + }, "switchoff_reason": { "name": "Switchoff reason", "state": { diff --git a/custom_components/luxtronik/update.py b/custom_components/luxtronik/update.py index b672824..555956a 100644 --- a/custom_components/luxtronik/update.py +++ b/custom_components/luxtronik/update.py @@ -32,8 +32,7 @@ LANG_DE, LOGGER, DeviceKey, - LuxCalculation, - SensorKey, + Calculation_SensorKey, ) from .coordinator import LuxtronikCoordinator from .lux_helper import get_firmware_download_id, get_manufacturer_firmware_url_by_model @@ -57,8 +56,8 @@ async def async_setup_entry( await coordinator.async_config_entry_first_refresh() description = LuxtronikUpdateEntityDescription( - luxtronik_key=LuxCalculation.C0081_FIRMWARE_VERSION, - key=SensorKey.FIRMWARE, + key="firmware", + luxtronik_key=Calculation_SensorKey.FIRMWARE_VERSION, entity_category=EntityCategory.CONFIG, ) update_entity = LuxtronikUpdateEntity( diff --git a/custom_components/luxtronik/water_heater.py b/custom_components/luxtronik/water_heater.py index 3b9d863..12bdc80 100644 --- a/custom_components/luxtronik/water_heater.py +++ b/custom_components/luxtronik/water_heater.py @@ -27,12 +27,11 @@ CONF_HA_SENSOR_PREFIX, DOMAIN, DeviceKey, - LuxCalculation, + Calculation_SensorKey, LuxMode, LuxOperationMode, - LuxParameter, - LuxVisibility, - SensorKey, + Parameter_SensorKey, + Visibility_SensorKey, ) from .coordinator import LuxtronikCoordinator, LuxtronikCoordinatorData from .model import LuxtronikWaterHeaterDescription @@ -50,21 +49,21 @@ WATER_HEATERS: list[LuxtronikWaterHeaterDescription] = [ LuxtronikWaterHeaterDescription( - key=SensorKey.DOMESTIC_WATER, + key="domestic_water", + luxtronik_key=Parameter_SensorKey.MODE_DHW, operation_list=[STATE_OFF, STATE_HEAT_PUMP, STATE_ELECTRIC, STATE_PERFORMANCE], supported_features=WaterHeaterEntityFeature.OPERATION_MODE | WaterHeaterEntityFeature.TARGET_TEMPERATURE | WaterHeaterEntityFeature.AWAY_MODE, - luxtronik_key=LuxParameter.P0004_MODE_DHW, - luxtronik_key_current_temperature=LuxCalculation.C0017_DHW_TEMPERATURE, - luxtronik_key_target_temperature=LuxParameter.P0002_DHW_TARGET_TEMPERATURE, - luxtronik_key_current_action=LuxCalculation.C0080_STATUS, + luxtronik_key_current_temperature=Calculation_SensorKey.DHW_TEMPERATURE, + luxtronik_key_target_temperature=Parameter_SensorKey.DHW_TARGET_TEMPERATURE, + luxtronik_key_current_action=Calculation_SensorKey.STATUS, luxtronik_action_heating=LuxOperationMode.domestic_water, # luxtronik_key_target_temperature_high=LuxParameter, # luxtronik_key_target_temperature_low=LuxParameter, icon="mdi:water-boiler", temperature_unit=UnitOfTemperature.CELSIUS, - visibility=LuxVisibility.V0029_DHW_TEMPERATURE, + visibility=Visibility_SensorKey.DHW_TEMPERATURE, ) ] # endregion Const @@ -124,7 +123,7 @@ def __init__( self._attr_supported_features = description.supported_features self._sensor_data = get_sensor_data( - coordinator.data, description.luxtronik_key.value + coordinator.data, description.luxtronik_key ) @property @@ -151,7 +150,7 @@ def _handle_coordinator_update( if data is None: return descr = self.entity_description - mode = get_sensor_data(data, descr.luxtronik_key.value) + mode = get_sensor_data(data, descr.luxtronik_key) self._attr_current_operation = None if mode is None else OPERATION_MAPPING[mode] self._current_action = get_sensor_data( data, descr.luxtronik_key_current_action.value