diff --git a/custom_components/linkytic/__init__.py b/custom_components/linkytic/__init__.py index 94aca16..04080e7 100644 --- a/custom_components/linkytic/__init__.py +++ b/custom_components/linkytic/__init__.py @@ -12,7 +12,8 @@ OPTIONS_REALTIME, SETUP_SERIAL, SETUP_THREEPHASE, - TICMODE_HISTORIC, + SETUP_TICMODE, + SETUP_PRODUCER, ) from .serial_reader import LinkyTICReader @@ -27,7 +28,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: serial_reader = LinkyTICReader( title=entry.title, port=entry.data.get(SETUP_SERIAL), - std_mode=entry.data.get(TICMODE_HISTORIC), + std_mode=entry.data.get(SETUP_TICMODE), + producer_mode=entry.data.get(SETUP_PRODUCER), three_phase=entry.data.get(SETUP_THREEPHASE), real_time=entry.options.get(OPTIONS_REALTIME), ) diff --git a/custom_components/linkytic/config_flow.py b/custom_components/linkytic/config_flow.py index d7dfe46..8e57d6c 100644 --- a/custom_components/linkytic/config_flow.py +++ b/custom_components/linkytic/config_flow.py @@ -22,6 +22,8 @@ SETUP_THREEPHASE, SETUP_THREEPHASE_DEFAULT, SETUP_TICMODE, + SETUP_PRODUCER, + SETUP_PRODUCER_DEFAULT, TICMODE_HISTORIC, TICMODE_HISTORIC_LABEL, TICMODE_STANDARD, @@ -46,6 +48,7 @@ ] ), ), + vol.Required(SETUP_PRODUCER, default=SETUP_PRODUCER_DEFAULT): bool, vol.Required(SETUP_THREEPHASE, default=SETUP_THREEPHASE_DEFAULT): bool, } ) diff --git a/custom_components/linkytic/const.py b/custom_components/linkytic/const.py index bf7d189..e462619 100644 --- a/custom_components/linkytic/const.py +++ b/custom_components/linkytic/const.py @@ -15,12 +15,13 @@ SETUP_SERIAL = "serial_device" SETUP_SERIAL_DEFAULT = "/dev/ttyUSB0" SETUP_TICMODE = "tic_mode" +SETUP_PRODUCER = "producer_mode" +SETUP_PRODUCER_DEFAULT = False SETUP_THREEPHASE = "three_phase" SETUP_THREEPHASE_DEFAULT = False OPTIONS_REALTIME = "real_time" - # Protocol configuration # # https://www.enedis.fr/media/2035/download @@ -127,6 +128,6 @@ "64": "Compteur monophasé 60 A généralisation Linky G3 - arrivée puissance basse", "70": "Compteur monophasé Linky 60 A mise au point G3", "71": "Compteur triphasé Linky 60 A mise au point G3", - "72": "Compteur monophasé 90 A généralisation Linky G3 - arrivée puissance basse", + "75": "Compteur monophasé 90 A généralisation Linky G3 - arrivée puissance basse", "76": "Compteur triphasé 60 A généralisation Linky G3 - arrivée puissance basse", } diff --git a/custom_components/linkytic/sensor.py b/custom_components/linkytic/sensor.py index 9c13446..c28325f 100644 --- a/custom_components/linkytic/sensor.py +++ b/custom_components/linkytic/sensor.py @@ -16,6 +16,7 @@ UnitOfElectricCurrent, UnitOfEnergy, UnitOfPower, + UnitOfElectricPotential, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo @@ -33,10 +34,33 @@ DOMAIN, SETUP_THREEPHASE, SETUP_TICMODE, + SETUP_PRODUCER, TICMODE_STANDARD, ) + from .serial_reader import LinkyTICReader +from enum import Enum +class StatusRegister(Enum): + CONTACT_SEC=1, + ORGANE_DE_COUPURE=2, + ETAT_DU_CACHE_BORNE_DISTRIBUTEUR=3, + SURTENSION_SUR_UNE_DES_PHASES=4, + DEPASSEMENT_PUISSANCE_REFERENCE=5, + PRODUCTEUR_CONSOMMATEUR=6, + SENS_ENERGIE_ACTIVE=7, + TARIF_CONTRAT_FOURNITURE=8, + TARIF_CONTRAT_DISTRIBUTEUR=9, + MODE_DEGRADE_HORLOGE=10, + MODE_TIC=11, + ETAT_SORTIE_COMMUNICATION_EURIDIS=12, + STATUS_CPL=13, + SYNCHRO_CPL=14, + COULEUR_JOUR_CONTRAT_TEMPO=15, + COULEUR_LENDEMAIN_CONTRAT_TEMPO=16, + PREAVIS_POINTES_MOBILES=17, + POINTE_MOBILE=18 + _LOGGER = logging.getLogger(__name__) @@ -69,25 +93,882 @@ async def async_setup_entry( "%s: a full frame has been read, initializing sensors", config_entry.title, ) - break - if i == 8: - _LOGGER.warning( - "%s: wait time is over but a full frame has yet to be read: initializing sensors anyway", - config_entry.title, + break + if i == 8: + _LOGGER.warning( + "%s: wait time is over but a full frame has yet to be read: initializing sensors anyway", + config_entry.title, + ) + # Init sensors + sensors = [] + if config_entry.data.get(SETUP_TICMODE) == TICMODE_STANDARD: + # standard mode + sensors = [ + ADCOSensor( + config_entry.title, "ADSC", config_entry.entry_id, serial_reader + ), # needs to be the first for ADS parsing + RegularStrSensor( + tag="VTIC", + name="Version de la TIC", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:tag", + category=EntityCategory.CONFIG, + ), + DateEtHeureSensor( + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="NGTF", + name="Nom du calendrier tarifaire fournisseur", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:cash-check", + category=EntityCategory.CONFIG, + ), + RegularStrSensor( + tag="LTARF", + name="Libellé tarif fournisseur en cours", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:cash-check", + category=EntityCategory.CONFIG, + ), + EnergyIndexSensor( + tag="EAST", + name="Energie active soutirée totale", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF01", + name="Energie active soutirée fournisseur, index 01", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF02", + name="Energie active soutirée fournisseur, index 02", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF03", + name="Energie active soutirée fournisseur, index 03", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF04", + name="Energie active soutirée fournisseur, index 04", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF05", + name="Energie active soutirée fournisseur, index 05", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF06", + name="Energie active soutirée fournisseur, index 06", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF07", + name="Energie active soutirée fournisseur, index 07", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF08", + name="Energie active soutirée fournisseur, index 08", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASF09", + name="Energie active soutirée fournisseur, index 09", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASD01", + name="Energie active soutirée distributeur, index 01", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASD02", + name="Energie active soutirée distributeur, index 02", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASD03", + name="Energie active soutirée distributeur, index 03", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + EnergyIndexSensor( + tag="EASD04", + name="Energie active soutirée distributeur, index 04", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + ), + RegularIntSensor( + tag="IRMS1", + name="Courant efficace, phase 1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularIntSensor( + tag="URMS1", + name="Tension efficace, phase 1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularIntSensor( + # Should be kVA + tag="PREF", + name="Puissance app. de référence", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularIntSensor( + # Should be kVA + tag="PCOUP", + name="Puissance app. de coupure", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularIntSensor( + tag="SINSTS", + name="Puissance app. instantanée soutirée", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularIntSensor( + tag="SMAXSN", + name="Puissance app. max. soutirée n", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularIntSensor( + tag="SMAXSN-1", + name="Puissance app. max. soutirée n-1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularIntSensor( + tag="CCASN", + name="Point n de la courbe de charge active soutirée", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + ), + RegularIntSensor( + tag="CCASN-1", + name="Point n-1 de la courbe de charge active soutirée", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + ), + RegularIntSensor( + tag="UMOY1", + name="Tension moy. ph. 1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularStrSensor( + tag="DPM1", + name="Début pointe mobile 1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:clock-start", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="FPM1", + name="Fin pointe mobile 1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:clock-end", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="DPM2", + name="Début pointe mobile 2", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:clock-start", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="FPM2", + name="Fin pointe mobile 2", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:clock-end", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="DPM3", + name="Début pointe mobile 3", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:clock-start", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="FPM3", + name="Fin pointe mobile 3", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:clock-end", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="MSG1", + name="Message court", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:message-text-outline", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="MSG2", + name="Message Ultra court", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:message-text-outline", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="PRM", + name="PRM", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:tag", + category=EntityCategory.CONFIG, + ), + RegularStrSensor( + tag="RELAIS", + name="Relais", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:electric-switch", + category=EntityCategory.CONFIG, + ), + RegularStrSensor( + tag="NTARF", + name="Numéro de l’index tarifaire en cours", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:cash-check", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="NJOURF", + name="Numéro du jour en cours calendrier fournisseur", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:calendar-month-outline", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="NJOURF+1", + name="Numéro du prochain jour calendrier fournisseur", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:calendar-month-outline", + category=EntityCategory.DIAGNOSTIC, + ), + ProfilDuProchainJourCalendrierFournisseurSensor( + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="PPOINTE", + name="Profil du prochain jour de pointe", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:calendar-month-outline", + category=EntityCategory.DIAGNOSTIC, + ), + RegularStrSensor( + tag="STGE", + name="Registre de statuts", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:list-status", + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut contact sec", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:electric-switch", + data=StatusRegister.CONTACT_SEC, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut organe de coupure", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:connection", + data=StatusRegister.ORGANE_DE_COUPURE, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut état du cache-bornes distributeur", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:toy-brick-outline", + data=StatusRegister.ETAT_DU_CACHE_BORNE_DISTRIBUTEUR, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut surtension sur une des phases", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:flash-alert", + data=StatusRegister.SURTENSION_SUR_UNE_DES_PHASES, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut dépassement de la puissance de référence", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:flash-alert", + data=StatusRegister.DEPASSEMENT_PUISSANCE_REFERENCE, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut producteur/consommateur", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:transmission-tower", + data=StatusRegister.PRODUCTEUR_CONSOMMATEUR, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut sens de l’énergie active", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:transmission-tower", + data=StatusRegister.SENS_ENERGIE_ACTIVE, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut tarif contrat fourniture", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:cash-check", + data=StatusRegister.TARIF_CONTRAT_FOURNITURE, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut tarif contrat distributeur", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:cash-check", + data=StatusRegister.TARIF_CONTRAT_DISTRIBUTEUR, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut mode dégradée de l'horloge", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:clock-alert-outline", + data=StatusRegister.MODE_DEGRADE_HORLOGE, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut sortie télé-information", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:tag", + data=StatusRegister.MODE_TIC, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut sortie communication Euridis", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:tag", + data=StatusRegister.ETAT_SORTIE_COMMUNICATION_EURIDIS, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut CPL", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:tag", + data=StatusRegister.STATUS_CPL, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut synchronisation CPL", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:sync", + data=StatusRegister.SYNCHRO_CPL, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut couleur du jour tempo", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:palette", + data=StatusRegister.COULEUR_JOUR_CONTRAT_TEMPO, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut couleur du lendemain tempo", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:palette", + data=StatusRegister.COULEUR_LENDEMAIN_CONTRAT_TEMPO, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut préavis pointes mobiles", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:clock-alert-outline", + data=StatusRegister.PREAVIS_POINTES_MOBILES, + category=EntityCategory.DIAGNOSTIC, + ), + StatusRegisterData( + name="Statut pointe mobile", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:progress-clock", + data=StatusRegister.POINTE_MOBILE, + category=EntityCategory.DIAGNOSTIC, + ), + ] + + # Add producer specific sensors + if bool(config_entry.data.get(SETUP_PRODUCER)): + sensors.append( + EnergyIndexSensor( + tag="EAIT", + name="Energie active injectée totale", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + EnergyIndexSensor( + tag="ERQ1", + name="Energie réactive Q1 totale", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + EnergyIndexSensor( + tag="ERQ2", + name="Energie réactive Q2 totale", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + EnergyIndexSensor( + tag="ERQ3", + name="Energie réactive Q3 totale", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + EnergyIndexSensor( + tag="ERQ4", + name="Energie réactive Q4 totale", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + RegularIntSensor( + tag="SINSTI", + name="Puissance app. instantanée injectée", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + RegularIntSensor( + tag="SMAXIN", + name="Puissance app. max. injectée n", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + RegularIntSensor( + tag="SMAXIN-1", + name="Puissance app. max. injectée n-1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + RegularIntSensor( + tag="CCAIN", + name="Point n de la courbe de charge active injectée", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + icon="mdi:transmission-tower-import", + ) + ) + sensors.append( + RegularIntSensor( + tag="CCAIN-1", + name="Point n-1 de la courbe de charge active injectée", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + icon="mdi:transmission-tower-import", + ) + ) + + # Add three-phase specific sensors + if bool(config_entry.data.get(SETUP_THREEPHASE)): + sensors.append( + RegularIntSensor( + tag="IRMS2", + name="Courant efficace, phase 2", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="IRMS3", + name="Courant efficace, phase 3", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="URMS2", + name="Tension efficace, phase 2", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="URMS3", + name="Tension efficace, phase 3", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="SINSTS1", + name="Puissance app. instantanée soutirée phase 1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="SINSTS2", + name="Puissance app. instantanée soutirée phase 2", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="SINSTS3", + name="Puissance app. instantanée soutirée phase 3", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="SMAXSN1", + name="Puissance app max. soutirée n phase 1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="SMAXSN2", + name="Puissance app max. soutirée n phase 2", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="SMAXSN3", + name="Puissance app max. soutirée n phase 3", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) ) - # Init sensors - sensors = [] - if config_entry.data.get(SETUP_TICMODE) == TICMODE_STANDARD: - # standard mode - _LOGGER.error( - "%s: standard mode is not supported (yet ?): no entities will be spawned", - config_entry.title, - ) + sensors.append( + RegularIntSensor( + tag="SMAXSN1-1", + name="Puissance app max. soutirée n-1 phase 1", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="SMAXSN2-1", + name="Puissance app max. soutirée n-1 phase 2", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + sensors.append( + RegularIntSensor( + tag="SMAXSN3-1", + name="Puissance app max. soutirée n-1 phase 3", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.APPARENT_POWER, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ) + ) + RegularIntSensor( + tag="UMOY2", + name="Tension moy. ph. 2", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + RegularIntSensor( + tag="UMOY3", + name="Tension moy. ph. 3", + config_title=config_entry.title, + config_uniq_id=config_entry.entry_id, + serial_reader=serial_reader, + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + register_callback=True, + ), + else: # historic mode sensors = [ ADCOSensor( - config_entry.title, config_entry.entry_id, serial_reader + config_entry.title, "ADCO", config_entry.entry_id, serial_reader ), # needs to be the first for ADS parsing RegularStrSensor( tag="OPTARIF", @@ -438,16 +1319,16 @@ class ADCOSensor(SensorEntity): _attr_icon = "mdi:tag" def __init__( - self, config_title: str, config_uniq_id: str, serial_reader: LinkyTICReader + self, config_title: str, tag: str, config_uniq_id: str, serial_reader: LinkyTICReader ) -> None: - """Initialize an ADCO Sensor.""" - _LOGGER.debug("%s: initializing ADCO sensor", config_title) + """Initialize an ADCO/ADSC Sensor.""" + _LOGGER.debug("%s: initializing %s sensor", config_title, tag) # Linky TIC sensor properties self._config_title = config_title self._config_uniq_id = config_uniq_id self._last_value: str | None = None self._serial_controller = serial_reader - self._tag = "ADCO" + self._tag = tag # Generic entity properties self._attr_unique_id = f"{DOMAIN}_{config_uniq_id}_adco" # We need to parse the ADS value first thing to have correct values for the device identification @@ -703,6 +1584,7 @@ def __init__( self._last_value: int | None = None self._serial_controller = serial_reader self._tag = tag.upper() + if register_callback: self._serial_controller.register_push_notif( self._tag, self.update_notification @@ -813,6 +1695,7 @@ def __init__( config_title: str, config_uniq_id: str, serial_reader: LinkyTICReader, + icon: str | None = "mdi:counter", ) -> None: """Initialize an Energy Index sensor.""" super().__init__( @@ -821,7 +1704,7 @@ def __init__( config_title=config_title, config_uniq_id=config_uniq_id, serial_reader=serial_reader, - icon="mdi:counter", + icon=icon, device_class=SensorDeviceClass.ENERGY, native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, @@ -913,3 +1796,359 @@ def update(self): self._attr_available = True # Save value self._last_value = value + +class DateEtHeureSensor(RegularStrSensor): + """Date et heure courante sensor.""" + + _attr_has_entity_name = True + _attr_name = "Date et heure courante" + _attr_should_poll = True + _attr_icon = "mdi:clock-outline" + + def __init__( + self, config_title: str, config_uniq_id: str, serial_reader: LinkyTICReader, category: EntityCategory | None = None, + ) -> None: + """Initialize a Date et heure sensor.""" + _LOGGER.debug("%s: initializing Date et heure courante sensor", config_title) + # Linky TIC sensor properties + self._config_title = config_title + self._config_uniq_id = config_uniq_id + self._last_value: str | None = None + self._serial_controller = serial_reader + self._tag = "DATE" + + if category: + self._attr_entity_category = category + + # Generic Entity properties + self._attr_unique_id = f"{DOMAIN}_{config_uniq_id}_{self._tag.lower()}" + + @callback + def update(self): + """Update the value of the sensor from the thread object memory cache.""" + # Get last seen value from controller + value, horodate = self._serial_controller.get_values(self._tag) + _LOGGER.debug( + "%s: retrieved %s value from serial controller: %s", + self._config_title, + self._tag, + repr(value), + ) + # Handle entity availability + if value is None: + if self._attr_available: + if not self._serial_controller.is_connected(): + _LOGGER.debug( + "%s: marking the %s sensor as unavailable: serial connection lost", + self._config_title, + self._tag, + ) + self._attr_available = False + elif self._serial_controller.has_read_full_frame(): + _LOGGER.info( + "%s: marking the %s sensor as unavailable: a full frame has been read but %s has not been found", + self._config_title, + self._tag, + self._tag, + ) + self._attr_available = False + # else: we are connected but a full frame has not been read yet, let's wait a little longer before marking it unavailable + else: + if not self._attr_available: + _LOGGER.info( + "%s: marking the %s sensor as available now !", + self._config_title, + self._tag, + ) + self._attr_available = True + # Save value + saison=""; + if horodate[0:1]=='E': + saison=" (Eté)" + elif horodate[0:1]=='H': + saison=" (Hiver)" + + self._last_value = horodate[5:7] + "/" + horodate[3:5] + "/" + horodate[1:3] + " " + horodate[7:9] + ":" + horodate[9:11] + saison + +class ProfilDuProchainJourCalendrierFournisseurSensor(RegularStrSensor): + """Profil du prochain jour du calendrier fournisseur sensor.""" + + _attr_has_entity_name = True + _attr_name = "Profil du prochain jour calendrier fournisseur" + _attr_should_poll = True + _attr_icon = "mdi:calendar-month-outline" + + def __init__( + self, config_title: str, config_uniq_id: str, serial_reader: LinkyTICReader, category: EntityCategory | None = None, + ) -> None: + """Initialize a Profil du prochain jour du calendrier fournisseur sensor.""" + _LOGGER.debug("%s: initializing Date et heure courante sensor", config_title) + # Linky TIC sensor properties + self._config_title = config_title + self._config_uniq_id = config_uniq_id + self._last_value: str | None = None + self._serial_controller = serial_reader + self._tag = "PJOURF+1" + + if category: + self._attr_entity_category = category + + # Generic Entity properties + self._attr_unique_id = f"{DOMAIN}_{config_uniq_id}_{self._tag.lower()}" + + @callback + def update(self): + """Update the value of the sensor from the thread object memory cache.""" + # Get last seen value from controller + value, horodate = self._serial_controller.get_values(self._tag) + _LOGGER.debug( + "%s: retrieved %s value from serial controller: %s", + self._config_title, + self._tag, + repr(value), + ) + # Handle entity availability + if value is None: + if self._attr_available: + if not self._serial_controller.is_connected(): + _LOGGER.debug( + "%s: marking the %s sensor as unavailable: serial connection lost", + self._config_title, + self._tag, + ) + self._attr_available = False + elif self._serial_controller.has_read_full_frame(): + _LOGGER.info( + "%s: marking the %s sensor as unavailable: a full frame has been read but %s has not been found", + self._config_title, + self._tag, + self._tag, + ) + self._attr_available = False + # else: we are connected but a full frame has not been read yet, let's wait a little longer before marking it unavailable + else: + if not self._attr_available: + _LOGGER.info( + "%s: marking the %s sensor as available now !", + self._config_title, + self._tag, + ) + self._attr_available = True + # Save value + self._last_value=value.replace(" NONUTILE", "") + +class StatusRegisterData(RegularStrSensor): + """Data from status register.""" + _attr_has_entity_name = True + _attr_should_poll = True + + def __init__( + self, + name: str, + config_title: str, + config_uniq_id: str, + serial_reader: LinkyTICReader, + data: StatusRegisterData, + enabled_by_default: bool = True, + icon: str | None = None, + category: EntityCategory | None = None, + ) -> None: + """Initialize a status register data sensor.""" + _LOGGER.debug("%s: initializing a status register data sensor", config_title) + # Linky TIC sensor properties + self._config_title = config_title + self._config_uniq_id = config_uniq_id + self._last_value: str | None = None + self._serial_controller = serial_reader + self._tag = "STGE" + self._data = data + + # Generic Entity properties + self._attr_name = name + self._attr_unique_id = f"{DOMAIN}_{config_uniq_id}_{self._tag.lower()}_{data.name.lower()}" + _LOGGER.debug( + "%s: uniq_id: %s", + self._config_title, + self._attr_unique_id, + ) + + if icon: + self._attr_icon = icon + if category: + self._attr_entity_category = category + self._attr_entity_registry_enabled_default = enabled_by_default + + if category: + self._attr_entity_category = category + + @callback + def update(self): + """Update the value of the sensor from the thread object memory cache.""" + # Get last seen value from controller + value, _ = self._serial_controller.get_values(self._tag) + _LOGGER.debug( + "%s: retrieved %s value from serial controller: %s", + self._config_title, + self._tag, + repr(value), + ) + # Handle entity availability + if value is None: + if self._attr_available: + if not self._serial_controller.is_connected(): + _LOGGER.debug( + "%s: marking the %s sensor as unavailable: serial connection lost", + self._config_title, + self._tag, + ) + self._attr_available = False + elif self._serial_controller.has_read_full_frame(): + _LOGGER.info( + "%s: marking the %s sensor as unavailable: a full frame has been read but %s has not been found", + self._config_title, + self._tag, + self._tag, + ) + self._attr_available = False + # else: we are connected but a full frame has not been read yet, let's wait a little longer before marking it unavailable + else: + if not self._attr_available: + _LOGGER.info( + "%s: marking the %s sensor as available now !", + self._config_title, + self._tag, + ) + self._attr_available = True + + try: + val = int(value, 16) + + # Save value + if self._data==StatusRegister.CONTACT_SEC: + self._last_value = "Ouvert" if (val&0x01) else "Fermé" + + elif self._data==StatusRegister.ORGANE_DE_COUPURE: + val_organe_de_coupure=(val>>1)&0x07 + if val_organe_de_coupure==0: + self._last_value="Fermé" + elif val_organe_de_coupure==1: + self._last_value="Ouvert sur surpuissance" + elif val_organe_de_coupure==2: + self._last_value="Ouvert sur surtension" + elif val_organe_de_coupure==3: + self._last_value="Ouvert sur délestage" + elif val_organe_de_coupure==4: + self._last_value="Ouvert sur ordre CPL ou Euridis" + elif val_organe_de_coupure==5: + self._last_value="Ouvert sur une surchauffe (>Imax)" + elif val_organe_de_coupure==6: + self._last_value="Ouvert sur une surchauffe (>4)&0x01) else "Fermé" + + elif self._data==StatusRegister.SURTENSION_SUR_UNE_DES_PHASES: + self._last_value = "Surtension" if ((val>>6)&0x01) else "Pas de surtension" + + elif self._data==StatusRegister.DEPASSEMENT_PUISSANCE_REFERENCE: + self._last_value = "Dépassement en cours" if ((val>>7)&0x01) else "Pas de dépassement" + + elif self._data==StatusRegister.PRODUCTEUR_CONSOMMATEUR: + self._last_value = "Producteur" if ((val>>8)&0x01) else "Consommateur" + + elif self._data==StatusRegister.SENS_ENERGIE_ACTIVE: + self._last_value = "Energie active négative" if ((val>>9)&0x01) else "Energie active positive" + + elif self._data==StatusRegister.TARIF_CONTRAT_FOURNITURE: + index=(val>>10)&0x0F + self._last_value = "Energie ventillée sur index " + str(index+1) + + elif self._data==StatusRegister.TARIF_CONTRAT_DISTRIBUTEUR: + index=(val>>14)&0x03 + self._last_value = "Energie ventillée sur index " + str(index+1) + + elif self._data==StatusRegister.MODE_DEGRADE_HORLOGE: + self._last_value = "Horloge en mode dégradée" if ((val>>16)&0x01) else "Horloge correcte" + + elif self._data==StatusRegister.MODE_TIC: + self._last_value = "Mode standard" if ((val>>17)&0x01) else "Mode historique" + + elif self._data==StatusRegister.ETAT_SORTIE_COMMUNICATION_EURIDIS: + etat=(val>>19)&0x03 + if etat==0: + self._last_value = "Désactivée" + elif etat==1: + self._last_value = "Activée sans sécurité" + elif etat==3: + self._last_value = "Activée avec sécurité" + else: + self._last_value = "Inconnue" + + elif self._data==StatusRegister.STATUS_CPL: + etat=(val>>21)&0x03 + if etat==0: + self._last_value = "New/Unlock" + elif etat==1: + self._last_value = "New/Lock" + elif etat==2: + self._last_value = "Registered" + else: + self._last_value = "Inconnue" + + elif self._data==StatusRegister.SYNCHRO_CPL: + self._last_value = "Compteur synchronisé" if ((val>>23)&0x01) else "Compteur non synchronisé" + + elif self._data==StatusRegister.COULEUR_JOUR_CONTRAT_TEMPO: + etat=(val>>24)&0x03 + if etat==0: + self._last_value = "Pas d'annonce" + elif etat==1: + self._last_value = "Bleu" + elif etat==2: + self._last_value = "Blanc" + else: + self._last_value = "Rouge" + + elif self._data==StatusRegister.COULEUR_LENDEMAIN_CONTRAT_TEMPO: + etat=(val>>26)&0x03 + if etat==0: + self._last_value = "Pas d'annonce" + elif etat==1: + self._last_value = "Bleu" + elif etat==2: + self._last_value = "Blanc" + else: + self._last_value = "Rouge" + + elif self._data==StatusRegister.PREAVIS_POINTES_MOBILES: + etat=(val>>28)&0x03 + if etat==0: + self._last_value = "Pas de préavis en cours" + elif etat==1: + self._last_value = "Préavis PM1 en cours" + elif etat==2: + self._last_value = "Préavis PM2 en cours" + else: + self._last_value = "Préavis PM3 en cours" + + elif self._data==StatusRegister.POINTE_MOBILE: + etat=(val>>28)&0x03 + if etat==0: + self._last_value = "Pas de pointe mobile" + elif etat==1: + self._last_value = "PM1 en cours" + elif etat==2: + self._last_value = "PM2 en cours" + else: + self._last_value = "PM3 en cours" + + else: + self._last_value = self._data.name + + except ValueError: + _LOGGER.error( + "%s: Invalid status register : %s", + self._config_title, + value, + ) + diff --git a/custom_components/linkytic/serial_reader.py b/custom_components/linkytic/serial_reader.py index 98318bd..4dc5a81 100644 --- a/custom_components/linkytic/serial_reader.py +++ b/custom_components/linkytic/serial_reader.py @@ -36,10 +36,9 @@ class LinkyTICReader(threading.Thread): """Implements the reading of a serial Linky TIC.""" def __init__( - self, title: str, port, std_mode, three_phase, real_time: bool | None = False + self, title: str, port, std_mode, producer_mode, three_phase, real_time: bool | None = False ) -> None: - """Init the LinkyTIC thread serial reader.""" - # Thread + """Init the LinkyTIC thread serial reader.""" # Thread self._stopsignal = False self._title = title # Options @@ -52,6 +51,9 @@ def __init__( MODE_STANDARD_BAUD_RATE if std_mode else MODE_HISTORIC_BAUD_RATE ) self._std_mode = std_mode + self._producer_mode = ( + producer_mode if std_mode else False + ) self._three_phase = three_phase # Run self._reader: serial.Serial | None = None diff --git a/custom_components/linkytic/strings.json b/custom_components/linkytic/strings.json index 5f5ee8e..1bbbca8 100644 --- a/custom_components/linkytic/strings.json +++ b/custom_components/linkytic/strings.json @@ -6,6 +6,7 @@ "data": { "serial_device": "Path to the serial device", "tic_mode": "TIC mode", + "producer_mode": "Producer mode", "three_phase": "Three-Phase" } } @@ -31,4 +32,4 @@ } } } -} \ No newline at end of file +} diff --git a/custom_components/linkytic/translations/en.json b/custom_components/linkytic/translations/en.json index 65bf9d3..1cf52b6 100644 --- a/custom_components/linkytic/translations/en.json +++ b/custom_components/linkytic/translations/en.json @@ -14,7 +14,8 @@ "data": { "serial_device": "Path/URL to the serial device", "three_phase": "Three-Phase", - "tic_mode": "TIC mode" + "tic_mode": "TIC mode", + "producer_mode": "Producer mode" }, "description": "If you need help to fill this form, please check the [readme](https://github.com/hekmon/linkytic/tree/v2)." } @@ -31,4 +32,4 @@ } } } -} \ No newline at end of file +} diff --git a/custom_components/linkytic/translations/fr.json b/custom_components/linkytic/translations/fr.json index 8aa063d..24dc4d1 100644 --- a/custom_components/linkytic/translations/fr.json +++ b/custom_components/linkytic/translations/fr.json @@ -14,7 +14,8 @@ "data": { "serial_device": "Chemin/Adresse vers le périphérique série", "three_phase": "Triphasé", - "tic_mode": "Mode TIC" + "tic_mode": "Mode TIC", + "producer_mode": "Mode producteur" }, "description": "Si vous avez besoin d'aide pour remplir ces champs de configuration, allez voir le fichier [lisezmoi](https://github.com/hekmon/linkytic/tree/v2)." } @@ -31,4 +32,4 @@ } } } -} \ No newline at end of file +}