"""Capteurs natifs en lecture seule pour les données Arkteos.""" from __future__ import annotations from dataclasses import dataclass from typing import Final, Literal from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.const import ( EntityCategory, PERCENTAGE, REVOLUTIONS_PER_MINUTE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, UnitOfElectricPotential, UnitOfFrequency, UnitOfPower, UnitOfPressure, UnitOfTemperature, UnitOfTime, ) from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import _get_client from .client import ArkteosClient from .const import DOMAIN from .entity import ArkteosEntity FRIGO_FRAME_TYPE: Final = "frigo" REGULATION_FRAME_TYPE: Final = "regulation" FRIGO_STATUS_OPTIONS: Final = ( "Arret", "Refroidissement", "Chauffage", "Degivrage", "Inconnu", ) PAC_STATUS_OPTIONS: Final = ( "Arret", "Attente", "Chaud", "Froid", "Hors Gel", "Ext Chaud", "Ext Froid", "Chaud Froid", "ECS", "Piscine", "Inconnu", ) @dataclass(frozen=True, kw_only=True) class ArkteosSensorDescription(SensorEntityDescription): """Description d'un capteur et du type de trame qui l'alimente.""" frame_type: Literal["frigo", "regulation"] FRIGO_SENSORS: Final[tuple[ArkteosSensorDescription, ...]] = ( ArkteosSensorDescription( key="exterieur_temp", translation_key="exterieur_temp", frame_type=FRIGO_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=1, ), ArkteosSensorDescription( key="nb_degivrages", translation_key="nb_degivrages", frame_type=FRIGO_FRAME_TYPE, state_class=SensorStateClass.TOTAL_INCREASING, ), ArkteosSensorDescription( key="temps_compresseur", translation_key="temps_compresseur", frame_type=FRIGO_FRAME_TYPE, device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.HOURS, state_class=SensorStateClass.TOTAL_INCREASING, ), ArkteosSensorDescription( key="nb_cycles_compresseur", translation_key="nb_cycles_compresseur", frame_type=FRIGO_FRAME_TYPE, state_class=SensorStateClass.TOTAL_INCREASING, ), ArkteosSensorDescription( key="freq_comp_actuelle", translation_key="freq_comp_actuelle", frame_type=FRIGO_FRAME_TYPE, device_class=SensorDeviceClass.FREQUENCY, native_unit_of_measurement=UnitOfFrequency.HERTZ, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="freq_comp_cible", translation_key="freq_comp_cible", frame_type=FRIGO_FRAME_TYPE, device_class=SensorDeviceClass.FREQUENCY, native_unit_of_measurement=UnitOfFrequency.HERTZ, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="fan_speed_evaporator_1", translation_key="fan_speed_evaporator_1", frame_type=FRIGO_FRAME_TYPE, native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="dc_voltage", translation_key="dc_voltage", frame_type=FRIGO_FRAME_TYPE, device_class=SensorDeviceClass.VOLTAGE, native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="statut_frigo_s", translation_key="statut_frigo_s", frame_type=FRIGO_FRAME_TYPE, device_class=SensorDeviceClass.ENUM, options=FRIGO_STATUS_OPTIONS, ), ArkteosSensorDescription( key="statut_frigo", translation_key="statut_frigo", frame_type=FRIGO_FRAME_TYPE, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, ), ArkteosSensorDescription( key="active_error_fri", translation_key="active_error_fri", frame_type=FRIGO_FRAME_TYPE, entity_category=EntityCategory.DIAGNOSTIC, ), ) REGULATION_SENSORS: Final[tuple[ArkteosSensorDescription, ...]] = ( ArkteosSensorDescription( key="puissance_inst_produite", translation_key="puissance_inst_produite", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.POWER, native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="puissance_inst_consommee", translation_key="puissance_inst_consommee", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.POWER, native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="temps_mise_sous_tension", translation_key="temps_mise_sous_tension", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.HOURS, state_class=SensorStateClass.TOTAL_INCREASING, ), ArkteosSensorDescription( key="modele_pac_s", translation_key="modele_pac_s", frame_type=REGULATION_FRAME_TYPE, entity_category=EntityCategory.DIAGNOSTIC, ), ArkteosSensorDescription( key="modele_pac", translation_key="modele_pac", frame_type=REGULATION_FRAME_TYPE, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, ), ArkteosSensorDescription( key="primaire_temp_eau_aller_consigne", translation_key="primaire_temp_eau_aller_consigne", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="primaire_temp_eau_aller", translation_key="primaire_temp_eau_aller", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="primaire_temp_eau_retour", translation_key="primaire_temp_eau_retour", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="primaire_debit_eau", translation_key="primaire_debit_eau", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.VOLUME_FLOW_RATE, native_unit_of_measurement="L/h", state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="primaire_pression", translation_key="primaire_pression", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=UnitOfPressure.BAR, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="primaire_circulateur_consigne", translation_key="primaire_circulateur_consigne", frame_type=REGULATION_FRAME_TYPE, native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="zone1_temp_interieur", translation_key="zone1_temp_interieur", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), ArkteosSensorDescription( key="zone1_consigne", translation_key="zone1_consigne", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), ArkteosSensorDescription( key="ecs_temp_eau_milieu", translation_key="ecs_temp_eau_milieu", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="ecs_temp_eau_bas", translation_key="ecs_temp_eau_bas", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="ecs_consigne", translation_key="ecs_consigne", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ArkteosSensorDescription( key="nb_cycles_compresseur_reg", translation_key="nb_cycles_compresseur_reg", frame_type=REGULATION_FRAME_TYPE, state_class=SensorStateClass.TOTAL_INCREASING, ), ArkteosSensorDescription( key="statut_pac_s", translation_key="statut_pac_s", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.ENUM, options=PAC_STATUS_OPTIONS, ), ArkteosSensorDescription( key="statut_pac", translation_key="statut_pac", frame_type=REGULATION_FRAME_TYPE, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, ), ArkteosSensorDescription( key="active_error_reg", translation_key="active_error_reg", frame_type=REGULATION_FRAME_TYPE, entity_category=EntityCategory.DIAGNOSTIC, ), ArkteosSensorDescription( key="signal_rf_sonde_1", translation_key="signal_rf_sonde_1", frame_type=REGULATION_FRAME_TYPE, device_class=SensorDeviceClass.SIGNAL_STRENGTH, native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), ) SENSOR_DESCRIPTIONS: Final = FRIGO_SENSORS + REGULATION_SENSORS async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback) -> None: """Ajoute les capteurs alimentés par le client partagé de l'entrée.""" client = _get_client(hass, entry) if client is None: return async_add_entities(ArkteosSensor(client, description) for description in SENSOR_DESCRIPTIONS) class ArkteosSensor(ArkteosEntity, SensorEntity): """Expose une valeur validée issue d'un seul type de trame Arkteos.""" _attr_should_poll = False def __init__(self, client: ArkteosClient, description: ArkteosSensorDescription) -> None: super().__init__(client) self.entity_description = description self._attr_translation_key = description.translation_key self._attr_unique_id = f"arkteos_zuran4_{description.key}" latest_data = self._latest_data() self._has_received_frame = latest_data is not None self._last_value = latest_data.get(description.key) if latest_data is not None else None @property def available(self) -> bool: """N'est disponible qu'après la première trame de son propre type.""" return self._client.available and self._has_received_frame @property def native_value(self) -> int | float | str | None: """Retourne la dernière valeur acceptée, sans accès réseau.""" return self._last_value @property def device_info(self) -> DeviceInfo: """Regroupe tous les capteurs sous l'unique PAC Arkteos.""" regulation_data = self._client.latest_regulation_data model = "Zuran 4" if regulation_data is not None: detected_model = regulation_data.get("modele_pac_s") if isinstance(detected_model, str): model = detected_model return DeviceInfo( identifiers={(DOMAIN, "arkteos_zuran4")}, manufacturer="Arkteos", model=model, name="PAC Arkteos Zuran 4", ) async def async_added_to_hass(self) -> None: """Abonne le capteur aux seules données transmises par le client.""" await super().async_added_to_hass() self._client.add_data_callback(self._handle_data) async def async_will_remove_from_hass(self) -> None: """Désabonne le callback de données avant la destruction.""" self._client.remove_data_callback(self._handle_data) await super().async_will_remove_from_hass() def _latest_data(self) -> dict[str, int | float | str] | None: if self.entity_description.frame_type == FRIGO_FRAME_TYPE: return self._client.latest_frigo_data return self._client.latest_regulation_data def _handle_data( self, frame_type: str, data: dict[str, int | float | str], _rejected: dict[str, int | float], ) -> None: if frame_type != self.entity_description.frame_type: return self._has_received_frame = True if self.entity_description.key in data: self._last_value = data[self.entity_description.key] self.async_write_ha_state()