diff --git a/custom_components/maestro_mcz/__init__.py b/custom_components/maestro_mcz/__init__.py index 3e0ab63..dfc31c6 100644 --- a/custom_components/maestro_mcz/__init__.py +++ b/custom_components/maestro_mcz/__init__.py @@ -5,7 +5,7 @@ import os import async_timeout from .config_flow import CONF_POLLING_INTERVAL -from .maestro.responses.model import Configuration, ModelConfiguration, SensorConfiguration +from .maestro.responses.model import Configuration, ModelConfiguration, SensorConfiguration, SensorConfigurationMultipleModes from .models import MczConfigItem from homeassistant.config_entries import ConfigEntry @@ -122,61 +122,53 @@ def get_device_info(self) -> DeviceInfo: + f", DB:{self._maestroapi.Status.nome_banca_dati_sel}", ) - def get_model_configuration_by_model_configuration_id(self, model_configuration_id:str) -> ModelConfiguration | None: - return next((x for x in self._maestroapi.Model.model_configurations if x.configuration_id is not None and x.configuration_id.lower() == model_configuration_id.lower()), None) - def get_model_configuration_by_model_configuration_name(self, model_configuration_name:str) -> ModelConfiguration | None: - return next((x for x in self._maestroapi.Model.model_configurations if x.configuration_name is not None and x.configuration_name.lower() == model_configuration_name.lower()), None) + return next((x for x in self._maestroapi.Model.model_configurations if x.configuration_name is not None and model_configuration_name is not None and x.configuration_name.lower() == model_configuration_name.lower()), None) - def get_sensor_configuration_by_model_configuration_id_and_sensor_id(self, model_configuration_id:str, sensor_id:str) -> SensorConfiguration | None: - model_configuration = self.get_model_configuration_by_model_configuration_id(model_configuration_id) - if(model_configuration is None): - return None - else: - sensor_configuration = next((x for x in model_configuration.configurations if x.sensor_id is not None and x.sensor_id.lower() == sensor_id.lower()), None) - if(sensor_configuration is not None): - return SensorConfiguration(sensor_configuration, model_configuration.configuration_id) def get_sensor_configuration_by_model_configuration_name_and_sensor_name(self, model_configuration_name:str, sensor_name:str) -> SensorConfiguration | None: model_configuration = self.get_model_configuration_by_model_configuration_name(model_configuration_name) if(model_configuration is None): return None else: - sensor_configuration = next((x for x in model_configuration.configurations if x.sensor_name is not None and x.sensor_name.lower() == sensor_name.lower()), None) + sensor_configuration = next((x for x in model_configuration.configurations if x.sensor_name is not None and sensor_name is not None and x.sensor_name.lower() == sensor_name.lower()), None) if(sensor_configuration is not None): return SensorConfiguration(sensor_configuration, model_configuration.configuration_id) - def get_first_matching_sensor_configuration_by_model_configuration_id_and_sensor_id(self, mcz_config_items_list_to_match: list[MczConfigItem]) -> tuple[MczConfigItem,SensorConfiguration] | None: - for x in mcz_config_items_list_to_match: - matching_configuration = self.get_sensor_configuration_by_model_configuration_id_and_sensor_id(x.sensor_set_config_id, x.sensor_set_id) - if(matching_configuration is not None): - return (x, matching_configuration) - return None def get_first_matching_sensor_configuration_by_model_configuration_name_and_sensor_name(self, mcz_config_items_list_to_match: list[MczConfigItem]) -> tuple[MczConfigItem,SensorConfiguration] | None: for x in mcz_config_items_list_to_match: - matching_configuration = self.get_sensor_configuration_by_model_configuration_name_and_sensor_name(x.sensor_set_config_name, x.sensor_set_name) - if(matching_configuration is not None): - return (x, matching_configuration) + if x.sensor_set_config_name is not None: + matching_configuration = self.get_sensor_configuration_by_model_configuration_name_and_sensor_name(x.sensor_set_config_name, x.sensor_set_name) + if(matching_configuration is not None): + return (x, matching_configuration) return None - - def get_all_matching_sensor_configurations_by_model_configuration_id_and_sensor_id(self, mcz_config_items_list_to_match: list[MczConfigItem]) -> list(tuple[MczConfigItem,SensorConfiguration]) | None: + + def get_all_matching_sensor_configurations_by_model_configuration_name_and_sensor_name(self, mcz_config_items_list_to_match: list[MczConfigItem]) -> list(tuple[MczConfigItem,SensorConfiguration]) | None: temp_list = [] for x in mcz_config_items_list_to_match: - matching_configuration = self.get_sensor_configuration_by_model_configuration_id_and_sensor_id(x.sensor_set_config_id, x.sensor_set_id) - if(matching_configuration is not None): - temp_list.append((x, matching_configuration)) + if x.sensor_set_config_name is not None: + matching_configuration = self.get_sensor_configuration_by_model_configuration_name_and_sensor_name(x.sensor_set_config_name, x.sensor_set_name) + if(matching_configuration is not None): + temp_list.append((x, matching_configuration)) if temp_list: return temp_list else: return None - def get_all_matching_sensor_configurations_by_model_configuration_name_and_sensor_name(self, mcz_config_items_list_to_match: list[MczConfigItem]) -> list(tuple[MczConfigItem,SensorConfiguration]) | None: + + def get_all_matching_sensor_for_all_configurations_by_model_mode_and_sensor_name(self, mcz_config_items_list_to_match: list[MczConfigItem]) -> list(tuple[MczConfigItem,SensorConfigurationMultipleModes]) | None: temp_list = [] for x in mcz_config_items_list_to_match: - matching_configuration = self.get_sensor_configuration_by_model_configuration_name_and_sensor_name(x.sensor_set_config_name, x.sensor_set_name) - if(matching_configuration is not None): - temp_list.append((x, matching_configuration)) + if x.mode_to_configuration_name_mapping is not None: + temp_mode_configurations: dict[str,SensorConfiguration] = {} + for mode in x.mode_to_configuration_name_mapping: + matching_configuration = self.get_sensor_configuration_by_model_configuration_name_and_sensor_name(x.mode_to_configuration_name_mapping[mode], x.sensor_set_name) + if(matching_configuration is not None): + temp_mode_configurations[mode] = matching_configuration + + if(temp_mode_configurations is not None and len(temp_mode_configurations) > 0): + temp_list.append((x,SensorConfigurationMultipleModes(temp_mode_configurations))) if temp_list: return temp_list else: diff --git a/custom_components/maestro_mcz/fan.py b/custom_components/maestro_mcz/fan.py index 123bfb4..54b9155 100644 --- a/custom_components/maestro_mcz/fan.py +++ b/custom_components/maestro_mcz/fan.py @@ -1,7 +1,7 @@ """Platform for Fan integration.""" import logging -from ..maestro_mcz.maestro.responses.model import SensorConfiguration +from ..maestro_mcz.maestro.responses.model import SensorConfiguration, SensorConfigurationMultipleModes from ..maestro_mcz.maestro.types.enums import TypeEnum from . import MczCoordinator, models @@ -23,7 +23,9 @@ async def async_setup_entry(hass, entry, async_add_entities): entities = [] for stove in stove_list: stove:MczCoordinator = stove - supported_fans = stove.get_all_matching_sensor_configurations_by_model_configuration_name_and_sensor_name(models.supported_fans) + + supported_fans = stove.get_all_matching_sensor_for_all_configurations_by_model_mode_and_sensor_name(models.supported_fans) + if(supported_fans is not None): for supported_fan in supported_fans: if(supported_fan[0] is not None and supported_fan[1] is not None): @@ -37,10 +39,11 @@ class MczFanEntity(CoordinatorEntity, FanEntity): _attr_preset_mode = None _attr_is_on = None # - _fan_configuration: SensorConfiguration | None = None + _fan_configuration: SensorConfigurationMultipleModes | None = None + _current_fan_configuration: SensorConfiguration | None = None _presets: list | None = None - def __init__(self, coordinator, supported_fan: models.FanMczConfigItem, matching_fan_configuration: SensorConfiguration): + def __init__(self, coordinator, supported_fan: models.FanMczConfigItem, matching_fan_configuration: SensorConfigurationMultipleModes): super().__init__(coordinator) self.coordinator:MczCoordinator = coordinator self._attr_name = supported_fan.user_friendly_name @@ -50,9 +53,9 @@ def __init__(self, coordinator, supported_fan: models.FanMczConfigItem, matching self._enabled_default = supported_fan.enabled_by_default self._category = supported_fan.category self._fan_configuration = matching_fan_configuration - if(matching_fan_configuration.configuration.type == TypeEnum.INT.value): - self._presets = self._attr_preset_modes = list(map(str,range(int(matching_fan_configuration.configuration.min), int(matching_fan_configuration.configuration.max) + 1 , 1))) - self._attr_supported_features = (FanEntityFeature.PRESET_MODE) + + self.update_features_based_on_current_stove_state() + self.handle_coordinator_update_internal() #getting the initial update directly without delay @property @@ -78,28 +81,50 @@ def entity_category(self): async def async_set_preset_mode(self, preset_mode: str) -> None: """Set the preset mode of the fan.""" - if(self._fan_configuration is not None): - await self.coordinator._maestroapi.ActivateProgram(self._fan_configuration.configuration.sensor_id, self._fan_configuration.configuration_id, int(preset_mode)) + if(self._current_fan_configuration is not None): + await self.coordinator._maestroapi.ActivateProgram(self._current_fan_configuration.configuration.sensor_id, self._current_fan_configuration.configuration_id, int(preset_mode)) await self.coordinator.async_refresh() async def async_turn_on(self) -> None: """Turn on the fan.""" - if(self._fan_configuration is not None and self._presets is not None and len(self._presets) > 0): - await self.coordinator._maestroapi.ActivateProgram(self._fan_configuration.configuration.sensor_id, self._fan_configuration.configuration_id, int(self._presets[-1])) + if(self._current_fan_configuration is not None and self._presets is not None and len(self._presets) > 0): + await self.coordinator._maestroapi.ActivateProgram(self._current_fan_configuration.configuration.sensor_id, self._current_fan_configuration.configuration_id, int(self._presets[-1])) await self.coordinator.async_refresh() async def async_turn_off(self) -> None: """Turn off the fan.""" - if(self._fan_configuration is not None and self._presets is not None and len(self._presets) > 0): - await self.coordinator._maestroapi.ActivateProgram(self._fan_configuration.configuration.sensor_id, self._fan_configuration.configuration_id, int(self._presets[0])) + if(self._current_fan_configuration is not None and self._presets is not None and len(self._presets) > 0): + await self.coordinator._maestroapi.ActivateProgram(self._current_fan_configuration.configuration.sensor_id, self._current_fan_configuration.configuration_id, int(self._presets[0])) await self.coordinator.async_refresh() + def get_configuration_for_current_stove_mode(self) -> SensorConfiguration | None: + """Get the correct sensor configuration for the current mode that the stove is in""" + if(self.coordinator._maestroapi.State is not None and self.coordinator._maestroapi.State.mode is not None + and self._fan_configuration is not None and self.coordinator._maestroapi.State.mode in self._fan_configuration.mode_configurations): + return self._fan_configuration.mode_configurations[self.coordinator._maestroapi.State.mode] + return None + + def update_features_based_on_current_stove_state(self) -> None: + """Refresh all fan features that are applicable for the current mode that the stove is in""" + self._current_fan_configuration = self.get_configuration_for_current_stove_mode() + + self._attr_supported_features = FanEntityFeature(0) # resetting the features + if(self._current_fan_configuration is not None and + self._current_fan_configuration.configuration is not None and + self._current_fan_configuration.configuration.enabled == True): + if(self._current_fan_configuration.configuration.type == TypeEnum.INT.value): + self._presets = self._attr_preset_modes = list(map(str,range(int(self._current_fan_configuration.configuration.min), int(self._current_fan_configuration.configuration.max) + 1 , 1))) + self._attr_supported_features = (FanEntityFeature.PRESET_MODE) + @callback def _handle_coordinator_update(self) -> None: + """handle coordinator updates""" + self.update_features_based_on_current_stove_state() self.handle_coordinator_update_internal() self.async_write_ha_state() def handle_coordinator_update_internal(self) -> None: + """handle coordinator updates for this fan""" #presets if(hasattr(self.coordinator._maestroapi.State, self._prop)): self._attr_preset_mode = str(getattr(self.coordinator._maestroapi.State, self._prop)) @@ -109,7 +134,7 @@ def handle_coordinator_update_internal(self) -> None: self._attr_preset_mode = None # on/off - if(self._fan_configuration is not None and self._presets is not None and len(self._presets) > 0): + if(self._current_fan_configuration is not None and self._presets is not None and len(self._presets) > 0): self._attr_is_on = self.preset_mode != self._presets[0] else: self._attr_is_on = False \ No newline at end of file diff --git a/custom_components/maestro_mcz/maestro/responses/model.py b/custom_components/maestro_mcz/maestro/responses/model.py index c902a4a..7340ecc 100644 --- a/custom_components/maestro_mcz/maestro/responses/model.py +++ b/custom_components/maestro_mcz/maestro/responses/model.py @@ -83,3 +83,14 @@ class SensorConfiguration: def __init__(self, configuration: Configuration, configuration_id: str) -> None: self.configuration = configuration self.configuration_id = configuration_id + + +@dataclass +class SensorConfigurationMultipleModes(): + mode_configurations: dict[str,SensorConfiguration] | None = None # key => Mode | value => SensorConfiguration for that mode + + def __init__(self, mode_configurations: dict[str,SensorConfiguration]) -> None: + self.mode_configurations = mode_configurations + + + diff --git a/custom_components/maestro_mcz/models.py b/custom_components/maestro_mcz/models.py index 85633f8..9bc54fc 100644 --- a/custom_components/maestro_mcz/models.py +++ b/custom_components/maestro_mcz/models.py @@ -17,10 +17,7 @@ class MczConfigItem: sensor_set_config_name: str | None = None #optional - sensor_set_id: str | None = None #id used for setting data trough the API (resolved in configs first) - sensor_set_config_id: str | None = None - - + mode_to_configuration_name_mapping: dict[str,str] | None = None #key => Mode | value => Configuration Name user_friendly_name: str | None = None icon: str | None = None enabled_by_default: bool = True @@ -66,13 +63,13 @@ def __init__(self, user_friendly_name:str, sensor_get_name:str, sensor_set_name: @dataclass class FanMczConfigItem(MczConfigItem): - - def __init__(self, user_friendly_name:str, sensor_get_name:str, sensor_set_name:str, sensor_set_config_name:str, enabled_by_default: bool): + + def __init__(self, user_friendly_name:str, sensor_get_name:str, sensor_set_name:str, mode_to_configuration_name_mapping: dict[str,str], enabled_by_default: bool): super().__init__(user_friendly_name) self.sensor_get_name = sensor_get_name self.icon = "mdi:fan" self.sensor_set_name = sensor_set_name - self.sensor_set_config_name = sensor_set_config_name + self.mode_to_configuration_name_mapping = mode_to_configuration_name_mapping # means we want to find the sensor name in the different mode configs self.enabled_by_default = enabled_by_default @dataclass @@ -184,7 +181,7 @@ def __init__(self, user_friendly_name:str, sensor_get_name:str, icon:str, unit:s supported_thermostats = [ ThermostatMczConfigItem("Ambient Temperature", "set_amb1", "set_amb1", "Set_amb_temp", True), - ThermostatMczConfigItem("Ambient Temperature", "set_amb1", "m1_set_amb1", "Set_amb_temp", True), #for first generation M1+ + ThermostatMczConfigItem("Ambient Temperature", "set_amb1", "m1_set_amb1", "Set_amb_temp", True), #for first generation M1+ ThermostatMczConfigItem("Ambient Temperature 2", "set_amb2", "set_amb2", "Set_amb_temp", True), ThermostatMczConfigItem("Ambient Temperature 2", "set_amb2", "m1_set_amb2", "Set_amb_temp", True), #for first generation M1+ ThermostatMczConfigItem("Ambient Temperature 3", "set_amb3", "set_amb3", "Set_amb_temp", True), @@ -197,12 +194,12 @@ def __init__(self, user_friendly_name:str, sensor_get_name:str, icon:str, unit:s ] supported_fans = [ - FanMczConfigItem("Fan 1", "set_vent_v1", "set_vent_v1", "set_v1", True), - FanMczConfigItem("Fan 1", "set_vent_v1", "m1_set_vent_v1", "set_v1", True), #for first generation M1+ - FanMczConfigItem("Fan 2", "set_vent_v2", "set_vent_v2", "set_v2", True), - FanMczConfigItem("Fan 2", "set_vent_v2", "m1_set_vent_v2", "set_v2", True), #for first generation M1+ - FanMczConfigItem("Fan 3", "set_vent_v3", "set_vent_v3", "set_v3", True), - FanMczConfigItem("Fan 3", "set_vent_v3", "m1_set_vent_v3", "set_v3", True), #for first generation M1+ + FanMczConfigItem("Fan 1", "set_vent_v1", "set_vent_v1", {"manual":"Manuale", "auto":"Auto", "overnight":"Overnight", "comfort":"Comfort", "turbo":"Turbo"}, True), + FanMczConfigItem("Fan 1", "set_vent_v1", "m1_set_vent_v1", {"manual":"Manuale","dynamic":"Dynamic", "overnight":"Overnight", "comfort":"Comfort", "power":"Power"}, True), #for first generation M1+ + FanMczConfigItem("Fan 2", "set_vent_v2", "set_vent_v2", {"manual":"Manuale", "auto":"Auto", "overnight":"Overnight", "comfort":"Comfort", "turbo":"Turbo"}, True), + FanMczConfigItem("Fan 2", "set_vent_v2", "m1_set_vent_v2", {"manual":"Manuale","dynamic":"Dynamic", "overnight":"Overnight", "comfort":"Comfort", "power":"Power"}, True), #for first generation M1+ + FanMczConfigItem("Fan 3", "set_vent_v3", "set_vent_v3", {"manual":"Manuale", "auto":"Auto", "overnight":"Overnight", "comfort":"Comfort", "turbo":"Turbo"}, True), + FanMczConfigItem("Fan 3", "set_vent_v3", "m1_set_vent_v3", {"manual":"Manuale","dynamic":"Dynamic", "overnight":"Overnight", "comfort":"Comfort", "power":"Power"}, True), #for first generation M1+ ] supported_switches = [