398 lines
14 KiB
Python
398 lines
14 KiB
Python
"""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()
|