From 61ed9425f019537e292d2fd9fe9ad2547d023026 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Thu, 10 Nov 2022 15:59:01 +0100 Subject: [PATCH 01/11] Extra entities with data from getDevRealKpi for Residential Inverter --- .../realtime_device_data_sensor.py | 326 ++++++++++++++++++ custom_components/fusion_solar/sensor.py | 188 +++++++++- 2 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py diff --git a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py new file mode 100644 index 0000000..61891bf --- /dev/null +++ b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py @@ -0,0 +1,326 @@ +import datetime + +from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorEntity +from homeassistant.components.binary_sensor import DEVICE_CLASS_CONNECTIVITY, BinarySensorEntity +from homeassistant.const import DEVICE_CLASS_VOLTAGE, ELECTRIC_POTENTIAL_VOLT, DEVICE_CLASS_CURRENT, \ + ELECTRIC_CURRENT_AMPERE, DEVICE_CLASS_ENERGY, ENERGY_KILO_WATT_HOUR, DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, \ + DEVICE_CLASS_POWER_FACTOR, PERCENTAGE, DEVICE_CLASS_FREQUENCY, FREQUENCY_HERTZ, DEVICE_CLASS_POWER, \ + POWER_KILO_WATT, DEVICE_CLASS_TIMESTAMP + +import logging + +_LOGGER = logging.getLogger(__name__) + + +class FusionSolarRealtimeDeviceDataSensor(CoordinatorEntity, SensorEntity): + """Base class for all FusionSolarRealtimeDeviceDataSensor sensors.""" + + def __init__( + self, + coordinator, + unique_id, + name, + attribute, + data_name, + device_info=None + ): + """Initialize the entity""" + super().__init__(coordinator) + self._unique_id = unique_id + self._name = name + self._attribute = attribute + self._data_name = data_name + self._device_info = device_info + + @property + def unique_id(self) -> str: + return self._unique_id + + @property + def name(self) -> str: + return self._name + + @property + def state(self) -> float: + if self._data_name not in self.coordinator.data: + return None + + if self._attribute not in self.coordinator.data[self._data_name]: + return None + + return float(self.coordinator.data[self._data_name][self._attribute]) + + @property + def device_info(self) -> dict: + return self._device_info + + +class FusionSolarRealtimeDeviceDataReadableInverterStateSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def state(self) -> str: + if self._data_name not in self.coordinator.data: + return None + + if self._attribute not in self.coordinator.data[self._data_name]: + return None + + state = self.coordinator.data[self._data_name][self._attribute] + + if state == 0: + return "Standby: initializing" + if state == 1: + return "Standby: insulation resistance detection" + if state == 2: + return "Standby: sunlight detection" + if state == 3: + return "Standby: power grid detection" + if state == 256: + return "Start" + if state == 512: + return "Grid connection" + if state == 513: + return "Grid connection: limited power" + if state == 514: + return "Grid connection: self-derating" + if state == 768: + return "Shutdown: unexpected shutdown" + if state == 769: + return "Shutdown: commanded shutdown" + if state == 770: + return "Shutdown: OVGR" + if state == 771: + return "Shutdown: communication disconnection" + if state == 772: + return "Shutdown: limited power" + if state == 773: + return "Shutdown: manual startup is required" + if state == 774: + return "Shutdown: DC switch disconnected" + if state == 1025: + return "Grid scheduling: cosĪˆ-P curve" + if state == 1026: + return "Grid scheduling: Q-U curve" + if state == 1280: + return "Spot-check ready" + if state == 1281: + return "Spot-checking" + if state == 1536: + return "Inspecting" + if state == 1792: + return "AFCI self-check" + if state == 2048: + return "I-V scanning" + if state == 2304: + return "DC input detection" + if state == 40960: + return "Standby: no sunlight" + if state == 45056: + return "Communication disconnection (written by the SmartLogger)" + if state == 49152: + return "Loading (written by the SmartLogger)" + + return state + + +class FusionSolarRealtimeDeviceDataVoltageSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_VOLTAGE + + @property + def unit_of_measurement(self) -> str: + return ELECTRIC_POTENTIAL_VOLT + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataCurrentSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_CURRENT + + @property + def unit_of_measurement(self) -> str: + return ELECTRIC_CURRENT_AMPERE + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataEnergySensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_ENERGY + + @property + def unit_of_measurement(self) -> str: + return ENERGY_KILO_WATT_HOUR + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataTemperatureSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_TEMPERATURE + + @property + def unit_of_measurement(self) -> str: + return TEMP_CELSIUS + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataPowerFactorSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_POWER_FACTOR + + @property + def unit_of_measurement(self) -> str: + return PERCENTAGE + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataFrequencySensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_FREQUENCY + + @property + def unit_of_measurement(self) -> str: + return FREQUENCY_HERTZ + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataPowerSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_POWER + + @property + def unit_of_measurement(self) -> str: + return POWER_KILO_WATT + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataReactivePowerSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return 'reactive_power' + + @property + def unit_of_measurement(self) -> str: + return 'kVar' + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataTimestampSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_TIMESTAMP + + @property + def state(self) -> float: + if self._data_name not in self.coordinator.data: + return None + + if self._attribute not in self.coordinator.data[self._data_name]: + return None + + return datetime.datetime.fromtimestamp(float(self.coordinator.data[self._data_name][self._attribute]) / 1000) + + +class FusionSolarRealtimeDeviceDataReadableStateSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def state(self) -> float: + if self._data_name not in self.coordinator.data: + return None + + if self._attribute not in self.coordinator.data[self._data_name]: + return None + + state = self.coordinator.data[self._data_name][self._attribute] + + if state == 0: + return "disconnected" + if state == 1: + return "connected" + + return None + + +class FusionSolarRealtimeDeviceDataBinarySensor(CoordinatorEntity, BinarySensorEntity): + """Base class for all FusionSolarRealtimeDeviceDataSensor sensors.""" + + def __init__( + self, + coordinator, + unique_id, + name, + attribute, + data_name, + device_info=None + ): + """Initialize the entity""" + super().__init__(coordinator) + self._unique_id = unique_id + self._name = name + self._attribute = attribute + self._data_name = data_name + self._device_info = device_info + + @property + def unique_id(self) -> str: + return self._unique_id + + @property + def name(self) -> str: + return self._name + + @property + def device_info(self) -> dict: + return self._device_info + + +class FusionSolarRealtimeDeviceDataStateBinarySensor(FusionSolarRealtimeDeviceDataBinarySensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_CONNECTIVITY + + @property + def is_on(self) -> bool: + if self._data_name not in self.coordinator.data: + return None + + if self._attribute not in self.coordinator.data[self._data_name]: + return None + + state = self.coordinator.data[self._data_name][self._attribute] + + if state == 0: + return False + if state == 1: + return True + + return None diff --git a/custom_components/fusion_solar/sensor.py b/custom_components/fusion_solar/sensor.py index 726e2c7..812c074 100644 --- a/custom_components/fusion_solar/sensor.py +++ b/custom_components/fusion_solar/sensor.py @@ -24,6 +24,13 @@ FusionSolarEnergySensorTotalCurrentMonth, FusionSolarEnergySensorTotalCurrentYear, \ FusionSolarEnergySensorTotalLifetime from .fusion_solar.power_entity import FusionSolarPowerEntityRealtime +from .fusion_solar.realtime_device_data_sensor import FusionSolarRealtimeDeviceDataSensor, \ + FusionSolarRealtimeDeviceDataReadableInverterStateSensor, FusionSolarRealtimeDeviceDataVoltageSensor, \ + FusionSolarRealtimeDeviceDataCurrentSensor, FusionSolarRealtimeDeviceDataEnergySensor, \ + FusionSolarRealtimeDeviceDataTemperatureSensor, FusionSolarRealtimeDeviceDataPowerFactorSensor, \ + FusionSolarRealtimeDeviceDataFrequencySensor, FusionSolarRealtimeDeviceDataPowerSensor, \ + FusionSolarRealtimeDeviceDataReactivePowerSensor, FusionSolarRealtimeDeviceDataTimestampSensor, \ + FusionSolarRealtimeDeviceDataReadableStateSensor, FusionSolarRealtimeDeviceDataStateBinarySensor from .fusion_solar.station_attribute_entity import FusionSolarAttributeEntity, FusionSolarAddressEntity, \ FusionSolarCapacityEntity, FusionSolarContactPersonEntity, FusionSolarContactPersonPhoneEntity, \ FusionSolarLatitudeEntity, FusionSolarLongitudeEntity @@ -367,7 +374,7 @@ async def async_update_device_real_kpi_data(): ATTR_DEVICE_REAL_KPI_ACTIVE_POWER, f'{DOMAIN}-{device.device_id}', device.device_info() - ) + ), ]) if device.type_id in [PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER]: @@ -380,6 +387,185 @@ async def async_update_device_real_kpi_data(): ), ]) + if device.type_id == PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER: + entities = [] + + entities.append(FusionSolarRealtimeDeviceDataSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-inverter_state', + f'{device.name} ({device.esn_code}) - Inverter State', + 'inverter_state', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + entities.append(FusionSolarRealtimeDeviceDataReadableInverterStateSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-readable-inverter_state', + f'{device.name} ({device.esn_code}) - Readable Inverter State', + 'inverter_state', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + voltages = [ + {'attribute': 'ab_u', 'name': 'Grid AB Voltage'}, + {'attribute': 'bc_u', 'name': 'Grid BC Voltage'}, + {'attribute': 'ca_u', 'name': 'Grid CA Voltage'}, + {'attribute': 'a_u', 'name': 'Phase A Voltage'}, + {'attribute': 'b_u', 'name': 'Phase B Voltage'}, + {'attribute': 'c_u', 'name': 'Phase C Voltage'}, + {'attribute': 'pv1_u', 'name': 'PV1 Input Voltage'}, + {'attribute': 'pv2_u', 'name': 'PV2 Input Voltage'}, + {'attribute': 'pv3_u', 'name': 'PV3 Input Voltage'}, + {'attribute': 'pv4_u', 'name': 'PV4 Input Voltage'}, + {'attribute': 'pv5_u', 'name': 'PV5 Input Voltage'}, + {'attribute': 'pv6_u', 'name': 'PV6 Input Voltage'}, + {'attribute': 'pv7_u', 'name': 'PV7 Input Voltage'}, + {'attribute': 'pv8_u', 'name': 'PV8 Input Voltage'}, + ] + for item in voltages: + entities.append(FusionSolarRealtimeDeviceDataVoltageSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', + f'{device.name} ({device.esn_code}) - {item["name"]}', + item['attribute'], + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + currents = [ + {'attribute': 'a_i', 'name': 'Phase A Current'}, + {'attribute': 'b_i', 'name': 'Phase B Current'}, + {'attribute': 'c_i', 'name': 'Phase C Current'}, + {'attribute': 'pv1_i', 'name': 'PV1 Input Current'}, + {'attribute': 'pv2_i', 'name': 'PV2 Input Current'}, + {'attribute': 'pv3_i', 'name': 'PV3 Input Current'}, + {'attribute': 'pv4_i', 'name': 'PV4 Input Current'}, + {'attribute': 'pv5_i', 'name': 'PV5 Input Current'}, + {'attribute': 'pv6_i', 'name': 'PV6 Input Current'}, + {'attribute': 'pv7_i', 'name': 'PV7 Input Current'}, + {'attribute': 'pv8_i', 'name': 'PV8 Input Current'}, + ] + for item in currents: + entities.append(FusionSolarRealtimeDeviceDataCurrentSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', + f'{device.name} ({device.esn_code}) - {item["name"]}', + item['attribute'], + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + entities.append(FusionSolarRealtimeDeviceDataCurrentSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-efficiency', + f'{device.name} ({device.esn_code}) - Inverter efficiency % (manufacturer)', + 'efficiency', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + entities.append(FusionSolarRealtimeDeviceDataTemperatureSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-temperature', + f'{device.name} ({device.esn_code}) - Inverter internal temperature', + 'temperature', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + entities.append(FusionSolarRealtimeDeviceDataPowerFactorSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-power_factor', + f'{device.name} ({device.esn_code}) - Power factor', + 'power_factor', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + entities.append(FusionSolarRealtimeDeviceDataFrequencySensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-elec_freq', + f'{device.name} ({device.esn_code}) - Grid frequency', + 'elec_freq', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + powers = [ + {'attribute': 'active_power', 'name': 'Active Power'}, + {'attribute': 'mppt_power', 'name': 'MPPT total input power'}, + ] + for item in powers: + entities.append(FusionSolarRealtimeDeviceDataPowerSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', + f'{device.name} ({device.esn_code}) - {item["name"]}', + item['attribute'], + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + entities.append(FusionSolarRealtimeDeviceDataReactivePowerSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-reactive_power', + f'{device.name} ({device.esn_code}) - Reactive output power', + 'reactive_power', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + energies = [ + {'attribute': 'day_cap', 'name': 'Yield Today'}, + {'attribute': 'total_cap', 'name': 'Total Yield'}, + {'attribute': 'mppt_1_cap', 'name': 'MPPT 1 DC total yield'}, + {'attribute': 'mppt_2_cap', 'name': 'MPPT 2 DC total yield'}, + {'attribute': 'mppt_3_cap', 'name': 'MPPT 3 DC total yield'}, + {'attribute': 'mppt_4_cap', 'name': 'MPPT 4 DC total yield'}, + ] + for item in energies: + entities.append(FusionSolarRealtimeDeviceDataEnergySensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', + f'{device.name} ({device.esn_code}) - {item["name"]}', + item['attribute'], + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + timestamps = [ + {'attribute': 'open_time', 'name': 'Inverter startup time'}, + {'attribute': 'close_time', 'name': 'Inverter shutdown time'}, + ] + for item in timestamps: + entities.append(FusionSolarRealtimeDeviceDataTimestampSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', + f'{device.name} ({device.esn_code}) - {item["name"]}', + item['attribute'], + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + entities.append(FusionSolarRealtimeDeviceDataReadableStateSensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-readable-run_state', + f'{device.name} ({device.esn_code}) - Readable status', + 'run_state', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + entities.append(FusionSolarRealtimeDeviceDataStateBinarySensor( + coordinator, + f'{DOMAIN}-{device.esn_code}-run_state', + f'{device.name} ({device.esn_code}) - Status', + 'run_state', + f'{DOMAIN}-{device.device_id}', + device.device_info() + )) + + async_add_entities(entities) + async def async_setup_entry(hass, config_entry, async_add_entities): config = hass.data[DOMAIN][config_entry.entry_id] From 7acd39444748fcd42ee8903072b542276cb665c2 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Thu, 10 Nov 2022 17:21:07 +0100 Subject: [PATCH 02/11] Refactor to have less code --- .../fusion_solar/openapi/device.py | 4 +- .../realtime_device_data_sensor.py | 74 +++--- custom_components/fusion_solar/sensor.py | 224 ++++-------------- 3 files changed, 88 insertions(+), 214 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/openapi/device.py b/custom_components/fusion_solar/fusion_solar/openapi/device.py index d881754..051f7cb 100644 --- a/custom_components/fusion_solar/fusion_solar/openapi/device.py +++ b/custom_components/fusion_solar/fusion_solar/openapi/device.py @@ -4,7 +4,7 @@ class FusionSolarDevice: def __init__( self, - id: str, + device_id: str, name: str, station_code: str, esn_code: str, @@ -14,7 +14,7 @@ def __init__( longitude: float, latitude: float ): - self.device_id = id + self.device_id = device_id self.name = name self.station_code = station_code self.esn_code = esn_code diff --git a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py index 61891bf..84146fd 100644 --- a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py @@ -8,9 +8,8 @@ DEVICE_CLASS_POWER_FACTOR, PERCENTAGE, DEVICE_CLASS_FREQUENCY, FREQUENCY_HERTZ, DEVICE_CLASS_POWER, \ POWER_KILO_WATT, DEVICE_CLASS_TIMESTAMP -import logging - -_LOGGER = logging.getLogger(__name__) +from .openapi.device import FusionSolarDevice +from ..const import DOMAIN class FusionSolarRealtimeDeviceDataSensor(CoordinatorEntity, SensorEntity): @@ -19,27 +18,25 @@ class FusionSolarRealtimeDeviceDataSensor(CoordinatorEntity, SensorEntity): def __init__( self, coordinator, - unique_id, - name, - attribute, - data_name, - device_info=None + device: FusionSolarDevice, + name: str, + attribute: str ): """Initialize the entity""" super().__init__(coordinator) - self._unique_id = unique_id + self._device = device self._name = name self._attribute = attribute - self._data_name = data_name - self._device_info = device_info + self._data_name = f'{DOMAIN}-{device.device_id}' + self._device_info = device.device_info() @property def unique_id(self) -> str: - return self._unique_id + return f'{DOMAIN}-{self._device.esn_code}-{self._attribute}' @property def name(self) -> str: - return self._name + return f'{self._device.name} - {self._name}' @property def state(self) -> float: @@ -57,16 +54,17 @@ def device_info(self) -> dict: class FusionSolarRealtimeDeviceDataReadableInverterStateSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + @property def state(self) -> str: - if self._data_name not in self.coordinator.data: - return None + state = super().state - if self._attribute not in self.coordinator.data[self._data_name]: + if state is None: return None - state = self.coordinator.data[self._data_name][self._attribute] - if state == 0: return "Standby: initializing" if state == 1: @@ -241,27 +239,27 @@ def device_class(self) -> str: return DEVICE_CLASS_TIMESTAMP @property - def state(self) -> float: - if self._data_name not in self.coordinator.data: - return None + def state(self) -> datetime: + state = super().state - if self._attribute not in self.coordinator.data[self._data_name]: + if state is None: return None - return datetime.datetime.fromtimestamp(float(self.coordinator.data[self._data_name][self._attribute]) / 1000) + return datetime.datetime.fromtimestamp(state / 1000) class FusionSolarRealtimeDeviceDataReadableStateSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + @property def state(self) -> float: - if self._data_name not in self.coordinator.data: - return None + state = super().state - if self._attribute not in self.coordinator.data[self._data_name]: + if state is None: return None - state = self.coordinator.data[self._data_name][self._attribute] - if state == 0: return "disconnected" if state == 1: @@ -271,32 +269,30 @@ def state(self) -> float: class FusionSolarRealtimeDeviceDataBinarySensor(CoordinatorEntity, BinarySensorEntity): - """Base class for all FusionSolarRealtimeDeviceDataSensor sensors.""" + """Base class for all FusionSolarRealtimeDeviceDataBinarySensor sensors.""" def __init__( self, coordinator, - unique_id, - name, - attribute, - data_name, - device_info=None + device: FusionSolarDevice, + name: str, + attribute: str ): """Initialize the entity""" super().__init__(coordinator) - self._unique_id = unique_id + self._device = device self._name = name self._attribute = attribute - self._data_name = data_name - self._device_info = device_info + self._data_name = f'{DOMAIN}-{device.device_id}' + self._device_info = device.device_info() @property def unique_id(self) -> str: - return self._unique_id + return f'{DOMAIN}-{self._device.esn_code}-{self._attribute}' @property def name(self) -> str: - return self._name + return f'{self._device.name} - {self._name}' @property def device_info(self) -> dict: diff --git a/custom_components/fusion_solar/sensor.py b/custom_components/fusion_solar/sensor.py index 812c074..c96527b 100644 --- a/custom_components/fusion_solar/sensor.py +++ b/custom_components/fusion_solar/sensor.py @@ -388,181 +388,59 @@ async def async_update_device_real_kpi_data(): ]) if device.type_id == PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER: - entities = [] - - entities.append(FusionSolarRealtimeDeviceDataSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-inverter_state', - f'{device.name} ({device.esn_code}) - Inverter State', - 'inverter_state', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - entities.append(FusionSolarRealtimeDeviceDataReadableInverterStateSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-readable-inverter_state', - f'{device.name} ({device.esn_code}) - Readable Inverter State', - 'inverter_state', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - voltages = [ - {'attribute': 'ab_u', 'name': 'Grid AB Voltage'}, - {'attribute': 'bc_u', 'name': 'Grid BC Voltage'}, - {'attribute': 'ca_u', 'name': 'Grid CA Voltage'}, - {'attribute': 'a_u', 'name': 'Phase A Voltage'}, - {'attribute': 'b_u', 'name': 'Phase B Voltage'}, - {'attribute': 'c_u', 'name': 'Phase C Voltage'}, - {'attribute': 'pv1_u', 'name': 'PV1 Input Voltage'}, - {'attribute': 'pv2_u', 'name': 'PV2 Input Voltage'}, - {'attribute': 'pv3_u', 'name': 'PV3 Input Voltage'}, - {'attribute': 'pv4_u', 'name': 'PV4 Input Voltage'}, - {'attribute': 'pv5_u', 'name': 'PV5 Input Voltage'}, - {'attribute': 'pv6_u', 'name': 'PV6 Input Voltage'}, - {'attribute': 'pv7_u', 'name': 'PV7 Input Voltage'}, - {'attribute': 'pv8_u', 'name': 'PV8 Input Voltage'}, + entities_to_create = [ + {'class': 'FusionSolarRealtimeDeviceDataSensor','attribute': 'inverter_state', 'name': 'Inverter status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableInverterStateSensor', 'attribute': 'inverter_state', 'name': 'Readable inverter status'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'ab_u', 'name': 'Grid AB voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'bc_u', 'name': 'Grid BC voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'ca_u', 'name': 'Grid CA voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'a_u', 'name': 'Phase A voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'b_u', 'name': 'Phase B voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'c_u', 'name': 'Phase C voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'a_i', 'name': 'Phase A current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'b_i', 'name': 'Phase B current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'c_i', 'name': 'Phase C current'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'efficiency', 'name': 'Inverter efficiency % (manufacturer)'}, + {'class': 'FusionSolarRealtimeDeviceDataTemperatureSensor', 'attribute': 'temperature', 'name': 'Inverter internal temperature'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerFactorSensor', 'attribute': 'power_factor', 'name': 'Power factor'}, + {'class': 'FusionSolarRealtimeDeviceDataFrequencySensor', 'attribute': 'elec_freq', 'name': 'Grid frequency'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power', 'name': 'Active power'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power', 'name': 'Reactive output power'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'day_cap', 'name': 'Yield Today'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'mppt_power', 'name': 'MPPT total input power'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv1_u', 'name': 'PV1 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv2_u', 'name': 'PV2 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv3_u', 'name': 'PV3 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv4_u', 'name': 'PV4 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv5_u', 'name': 'PV5 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv6_u', 'name': 'PV6 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv7_u', 'name': 'PV7 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv8_u', 'name': 'PV8 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv1_i', 'name': 'PV1 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv2_i', 'name': 'PV2 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv3_i', 'name': 'PV3 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv4_i', 'name': 'PV4 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv5_i', 'name': 'PV5 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv6_i', 'name': 'PV6 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv7_i', 'name': 'PV7 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv8_i', 'name': 'PV8 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'total_cap', 'name': 'Total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataTimestampSensor', 'attribute': 'open_time', 'name': 'Inverter startup time'}, + {'class': 'FusionSolarRealtimeDeviceDataTimestampSensor', 'attribute': 'close_time', 'name': 'Inverter shutdown time'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_1_cap', 'name': 'MPPT 1 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_2_cap', 'name': 'MPPT 2 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_3_cap', 'name': 'MPPT 3 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_4_cap', 'name': 'MPPT 4 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataStateBinarySensor', 'attribute': 'run_state', 'name': 'Status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableStateSensor', 'attribute': 'run_state', 'name': 'Readable status'}, ] - for item in voltages: - entities.append(FusionSolarRealtimeDeviceDataVoltageSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', - f'{device.name} ({device.esn_code}) - {item["name"]}', - item['attribute'], - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - currents = [ - {'attribute': 'a_i', 'name': 'Phase A Current'}, - {'attribute': 'b_i', 'name': 'Phase B Current'}, - {'attribute': 'c_i', 'name': 'Phase C Current'}, - {'attribute': 'pv1_i', 'name': 'PV1 Input Current'}, - {'attribute': 'pv2_i', 'name': 'PV2 Input Current'}, - {'attribute': 'pv3_i', 'name': 'PV3 Input Current'}, - {'attribute': 'pv4_i', 'name': 'PV4 Input Current'}, - {'attribute': 'pv5_i', 'name': 'PV5 Input Current'}, - {'attribute': 'pv6_i', 'name': 'PV6 Input Current'}, - {'attribute': 'pv7_i', 'name': 'PV7 Input Current'}, - {'attribute': 'pv8_i', 'name': 'PV8 Input Current'}, - ] - for item in currents: - entities.append(FusionSolarRealtimeDeviceDataCurrentSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', - f'{device.name} ({device.esn_code}) - {item["name"]}', - item['attribute'], - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - entities.append(FusionSolarRealtimeDeviceDataCurrentSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-efficiency', - f'{device.name} ({device.esn_code}) - Inverter efficiency % (manufacturer)', - 'efficiency', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - entities.append(FusionSolarRealtimeDeviceDataTemperatureSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-temperature', - f'{device.name} ({device.esn_code}) - Inverter internal temperature', - 'temperature', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - entities.append(FusionSolarRealtimeDeviceDataPowerFactorSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-power_factor', - f'{device.name} ({device.esn_code}) - Power factor', - 'power_factor', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - entities.append(FusionSolarRealtimeDeviceDataFrequencySensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-elec_freq', - f'{device.name} ({device.esn_code}) - Grid frequency', - 'elec_freq', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - powers = [ - {'attribute': 'active_power', 'name': 'Active Power'}, - {'attribute': 'mppt_power', 'name': 'MPPT total input power'}, - ] - for item in powers: - entities.append(FusionSolarRealtimeDeviceDataPowerSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', - f'{device.name} ({device.esn_code}) - {item["name"]}', - item['attribute'], - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - entities.append(FusionSolarRealtimeDeviceDataReactivePowerSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-reactive_power', - f'{device.name} ({device.esn_code}) - Reactive output power', - 'reactive_power', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - energies = [ - {'attribute': 'day_cap', 'name': 'Yield Today'}, - {'attribute': 'total_cap', 'name': 'Total Yield'}, - {'attribute': 'mppt_1_cap', 'name': 'MPPT 1 DC total yield'}, - {'attribute': 'mppt_2_cap', 'name': 'MPPT 2 DC total yield'}, - {'attribute': 'mppt_3_cap', 'name': 'MPPT 3 DC total yield'}, - {'attribute': 'mppt_4_cap', 'name': 'MPPT 4 DC total yield'}, - ] - for item in energies: - entities.append(FusionSolarRealtimeDeviceDataEnergySensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', - f'{device.name} ({device.esn_code}) - {item["name"]}', - item['attribute'], - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - - timestamps = [ - {'attribute': 'open_time', 'name': 'Inverter startup time'}, - {'attribute': 'close_time', 'name': 'Inverter shutdown time'}, - ] - for item in timestamps: - entities.append(FusionSolarRealtimeDeviceDataTimestampSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-{item["attribute"]}', - f'{device.name} ({device.esn_code}) - {item["name"]}', - item['attribute'], - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) + entities = [] - entities.append(FusionSolarRealtimeDeviceDataReadableStateSensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-readable-run_state', - f'{device.name} ({device.esn_code}) - Readable status', - 'run_state', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) - entities.append(FusionSolarRealtimeDeviceDataStateBinarySensor( - coordinator, - f'{DOMAIN}-{device.esn_code}-run_state', - f'{device.name} ({device.esn_code}) - Status', - 'run_state', - f'{DOMAIN}-{device.device_id}', - device.device_info() - )) + for entity_to_create in entities_to_create: + class_name = globals()[entity_to_create['class']] + entities.append( + class_name(coordinator, device, entity_to_create['name'], entity_to_create['attribute']) + ) async_add_entities(entities) From 13bfdc0b9878767fb564593295208fd9ece8ca42 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Thu, 10 Nov 2022 18:33:25 +0100 Subject: [PATCH 03/11] Add support for String inverter, EMI, Grid meter, Battery and Power Sensor Which also includes a lot of extra entities per device --- .../fusion_solar/fusion_solar/const.py | 2 + .../realtime_device_data_sensor.py | 124 ++++- custom_components/fusion_solar/sensor.py | 439 ++++++++++++++++-- 3 files changed, 512 insertions(+), 53 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/const.py b/custom_components/fusion_solar/fusion_solar/const.py index 3a5eb37..c81ba1d 100644 --- a/custom_components/fusion_solar/fusion_solar/const.py +++ b/custom_components/fusion_solar/fusion_solar/const.py @@ -46,6 +46,8 @@ ATTR_DEVICE_REAL_KPI_ACTIVE_POWER = 'active_power' PARAM_DEVICE_TYPE_ID_STRING_INVERTER = 1 +PARAM_DEVICE_TYPE_ID_EMI = 10 PARAM_DEVICE_TYPE_ID_GRID_METER = 17 PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER = 38 +PARAM_DEVICE_TYPE_ID_BATTERY = 39 PARAM_DEVICE_TYPE_ID_POWER_SENSOR = 47 diff --git a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py index 84146fd..fcfd17c 100644 --- a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py @@ -6,7 +6,7 @@ from homeassistant.const import DEVICE_CLASS_VOLTAGE, ELECTRIC_POTENTIAL_VOLT, DEVICE_CLASS_CURRENT, \ ELECTRIC_CURRENT_AMPERE, DEVICE_CLASS_ENERGY, ENERGY_KILO_WATT_HOUR, DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, \ DEVICE_CLASS_POWER_FACTOR, PERCENTAGE, DEVICE_CLASS_FREQUENCY, FREQUENCY_HERTZ, DEVICE_CLASS_POWER, \ - POWER_KILO_WATT, DEVICE_CLASS_TIMESTAMP + POWER_KILO_WATT, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_BATTERY from .openapi.device import FusionSolarDevice from ..const import DOMAIN @@ -46,6 +46,12 @@ def state(self) -> float: if self._attribute not in self.coordinator.data[self._data_name]: return None + if self.coordinator.data[self._data_name][self._attribute] is None: + return None + + if self.coordinator.data[self._data_name][self._attribute] is None: + return None + return float(self.coordinator.data[self._data_name][self._attribute]) @property @@ -233,6 +239,48 @@ def state_class(self) -> str: return STATE_CLASS_MEASUREMENT +class FusionSolarRealtimeDeviceDataApparentPowerSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return 'apparent_power' + + @property + def unit_of_measurement(self) -> str: + return 'kVA' + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataWindSpeedSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return 'wind_speed' + + @property + def unit_of_measurement(self) -> str: + return 'm/s' + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + +class FusionSolarRealtimeDeviceDataBatterySensor(FusionSolarRealtimeDeviceDataSensor): + @property + def device_class(self) -> str: + return DEVICE_CLASS_BATTERY + + @property + def unit_of_measurement(self) -> str: + return PERCENTAGE + + @property + def state_class(self) -> str: + return STATE_CLASS_MEASUREMENT + + class FusionSolarRealtimeDeviceDataTimestampSensor(FusionSolarRealtimeDeviceDataSensor): @property def device_class(self) -> str: @@ -248,7 +296,7 @@ def state(self) -> datetime: return datetime.datetime.fromtimestamp(state / 1000) -class FusionSolarRealtimeDeviceDataReadableStateSensor(FusionSolarRealtimeDeviceDataSensor): +class FusionSolarRealtimeDeviceDataReadableRunStateSensor(FusionSolarRealtimeDeviceDataSensor): @property def unique_id(self) -> str: return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' @@ -268,6 +316,78 @@ def state(self) -> float: return None +class FusionSolarRealtimeDeviceDataReadableChargeDischargeModeSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + + @property + def state(self) -> float: + state = super().state + + if state is None: + return None + + if state == 0: + return "none" + if state == 1: + return "forced charge/discharge" + if state == 2: + return "time-of-use price" + if state == 3: + return "fixed charge/discharge" + if state == 4: + return "automatic charge/discharge" + + return None + + +class FusionSolarRealtimeDeviceDataReadableBatteryStatusSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + + @property + def state(self) -> float: + state = super().state + + if state is None: + return None + + if state == 0: + return "offline" + if state == 1: + return "standby" + if state == 2: + return "running" + if state == 3: + return "faulty" + if state == 4: + return "hibernation" + + return None + + +class FusionSolarRealtimeDeviceDataReadableMeterStatusSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + + @property + def state(self) -> float: + state = super().state + + if state is None: + return None + + if state == 0: + return "offline" + if state == 1: + return "normal" + + return None + + class FusionSolarRealtimeDeviceDataBinarySensor(CoordinatorEntity, BinarySensorEntity): """Base class for all FusionSolarRealtimeDeviceDataBinarySensor sensors.""" diff --git a/custom_components/fusion_solar/sensor.py b/custom_components/fusion_solar/sensor.py index c96527b..e0be8a5 100644 --- a/custom_components/fusion_solar/sensor.py +++ b/custom_components/fusion_solar/sensor.py @@ -9,14 +9,15 @@ from homeassistant.const import CONF_NAME, CONF_URL, CONF_HOST, CONF_USERNAME, CONF_PASSWORD from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from .fusion_solar.const import ATTR_DATA_REALKPI, ATTR_REALTIME_POWER, ATTR_TOTAL_CURRENT_DAY_ENERGY, \ +from .fusion_solar.const import ATTR_REALTIME_POWER, ATTR_TOTAL_CURRENT_DAY_ENERGY, \ ATTR_TOTAL_CURRENT_MONTH_ENERGY, ATTR_TOTAL_CURRENT_YEAR_ENERGY, ATTR_TOTAL_LIFETIME_ENERGY, \ ATTR_STATION_CODE, ATTR_STATION_REAL_KPI_DATA_ITEM_MAP, ATTR_STATION_REAL_KPI_TOTAL_CURRENT_DAY_ENERGY, \ ATTR_STATION_REAL_KPI_TOTAL_CURRENT_MONTH_ENERGY, ATTR_STATION_REAL_KPI_TOTAL_LIFETIME_ENERGY, \ ATTR_DATA_COLLECT_TIME, ATTR_KPI_YEAR_INVERTER_POWER, ATTR_DEVICE_REAL_KPI_ACTIVE_POWER, \ PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_GRID_METER, PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER, \ - PARAM_DEVICE_TYPE_ID_POWER_SENSOR, ATTR_DEVICE_REAL_KPI_DEV_ID, ATTR_DEVICE_REAL_KPI_DATA_ITEM_MAP, \ - ATTR_DEVICE_TYPE_ID + PARAM_DEVICE_TYPE_ID_POWER_SENSOR, PARAM_DEVICE_TYPE_ID_EMI, PARAM_DEVICE_TYPE_ID_BATTERY, \ + ATTR_DEVICE_REAL_KPI_DEV_ID, \ + ATTR_DEVICE_REAL_KPI_DATA_ITEM_MAP from .fusion_solar.kiosk.kiosk import FusionSolarKiosk from .fusion_solar.kiosk.kiosk_api import FusionSolarKioskApi from .fusion_solar.openapi.openapi_api import FusionSolarOpenApi @@ -24,13 +25,7 @@ FusionSolarEnergySensorTotalCurrentMonth, FusionSolarEnergySensorTotalCurrentYear, \ FusionSolarEnergySensorTotalLifetime from .fusion_solar.power_entity import FusionSolarPowerEntityRealtime -from .fusion_solar.realtime_device_data_sensor import FusionSolarRealtimeDeviceDataSensor, \ - FusionSolarRealtimeDeviceDataReadableInverterStateSensor, FusionSolarRealtimeDeviceDataVoltageSensor, \ - FusionSolarRealtimeDeviceDataCurrentSensor, FusionSolarRealtimeDeviceDataEnergySensor, \ - FusionSolarRealtimeDeviceDataTemperatureSensor, FusionSolarRealtimeDeviceDataPowerFactorSensor, \ - FusionSolarRealtimeDeviceDataFrequencySensor, FusionSolarRealtimeDeviceDataPowerSensor, \ - FusionSolarRealtimeDeviceDataReactivePowerSensor, FusionSolarRealtimeDeviceDataTimestampSensor, \ - FusionSolarRealtimeDeviceDataReadableStateSensor, FusionSolarRealtimeDeviceDataStateBinarySensor +from .fusion_solar.realtime_device_data_sensor import * from .fusion_solar.station_attribute_entity import FusionSolarAttributeEntity, FusionSolarAddressEntity, \ FusionSolarCapacityEntity, FusionSolarContactPersonEntity, FusionSolarContactPersonPhoneEntity, \ FusionSolarLatitudeEntity, FusionSolarLongitudeEntity @@ -276,8 +271,9 @@ async def async_update_station_year_kpi_data(): devices = await hass.async_add_executor_job(api.get_dev_list, station_codes) devices_grouped_per_type_id = {} for device in devices: - if device.type_id not in [PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_GRID_METER, - PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER, PARAM_DEVICE_TYPE_ID_POWER_SENSOR]: + if device.type_id not in [PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_EMI, + PARAM_DEVICE_TYPE_ID_GRID_METER, PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER, + PARAM_DEVICE_TYPE_ID_BATTERY, PARAM_DEVICE_TYPE_ID_POWER_SENSOR]: continue if device.type_id not in devices_grouped_per_type_id: @@ -387,10 +383,267 @@ async def async_update_device_real_kpi_data(): ), ]) + entities_to_create = [] + + if device.type_id == PARAM_DEVICE_TYPE_ID_STRING_INVERTER: + entities_to_create = [ + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'inverter_state', + 'name': 'Inverter status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableInverterStateSensor', 'attribute': 'inverter_state', + 'name': 'Readable inverter status'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'ab_u', 'name': 'Grid AB voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'bc_u', 'name': 'Grid BC voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'ca_u', 'name': 'Grid CA voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'a_u', 'name': 'Phase A voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'b_u', 'name': 'Phase B voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'c_u', 'name': 'Phase C voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'a_i', 'name': 'Phase A current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'b_i', 'name': 'Phase B current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'c_i', 'name': 'Phase C current'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'efficiency', + 'name': 'Inverter efficiency % (manufacturer)'}, + {'class': 'FusionSolarRealtimeDeviceDataTemperatureSensor', 'attribute': 'temperature', + 'name': 'Inverter internal temperature'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerFactorSensor', 'attribute': 'power_factor', + 'name': 'Power factor'}, + {'class': 'FusionSolarRealtimeDeviceDataFrequencySensor', 'attribute': 'elec_freq', + 'name': 'Grid frequency'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power', + 'name': 'Active power'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power', + 'name': 'Reactive output power'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'day_cap', 'name': 'Yield Today'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'mppt_power', + 'name': 'MPPT total input power'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv1_u', + 'name': 'PV1 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv2_u', + 'name': 'PV2 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv3_u', + 'name': 'PV3 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv4_u', + 'name': 'PV4 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv5_u', + 'name': 'PV5 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv6_u', + 'name': 'PV6 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv7_u', + 'name': 'PV7 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv8_u', + 'name': 'PV8 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv9_u', + 'name': 'PV9 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv10_u', + 'name': 'PV10 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv11_u', + 'name': 'PV11 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv12_u', + 'name': 'PV12 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv13_u', + 'name': 'PV13 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv14_u', + 'name': 'PV14 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv15_u', + 'name': 'PV15 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv16_u', + 'name': 'PV16 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv17_u', + 'name': 'PV17 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv18_u', + 'name': 'PV18 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv19_u', + 'name': 'PV19 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv20_u', + 'name': 'PV20 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv21_u', + 'name': 'PV21 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv22_u', + 'name': 'PV22 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv23_u', + 'name': 'PV23 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv24_u', + 'name': 'PV24 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv1_i', + 'name': 'PV1 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv2_i', + 'name': 'PV2 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv3_i', + 'name': 'PV3 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv4_i', + 'name': 'PV4 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv5_i', + 'name': 'PV5 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv6_i', + 'name': 'PV6 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv7_i', + 'name': 'PV7 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv8_i', + 'name': 'PV8 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv9_i', + 'name': 'PV9 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv10_i', + 'name': 'PV10 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv11_i', + 'name': 'PV11 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv12_i', + 'name': 'PV12 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv13_i', + 'name': 'PV13 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv14_i', + 'name': 'PV14 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv15_i', + 'name': 'PV15 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv16_i', + 'name': 'PV16 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv17_i', + 'name': 'PV17 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv18_i', + 'name': 'PV18 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv19_i', + 'name': 'PV19 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv20_i', + 'name': 'PV20 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv21_i', + 'name': 'PV21 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv22_i', + 'name': 'PV22 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv23_i', + 'name': 'PV23 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv24_i', + 'name': 'PV24 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'total_cap', 'name': 'Total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataTimestampSensor', 'attribute': 'open_time', + 'name': 'Inverter startup time'}, + {'class': 'FusionSolarRealtimeDeviceDataTimestampSensor', 'attribute': 'close_time', + 'name': 'Inverter shutdown time'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_1_cap', + 'name': 'MPPT 1 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_2_cap', + 'name': 'MPPT 2 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_3_cap', + 'name': 'MPPT 3 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_4_cap', + 'name': 'MPPT 4 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_5_cap', + 'name': 'MPPT 5 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_6_cap', + 'name': 'MPPT 6 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_7_cap', + 'name': 'MPPT 7 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_8_cap', + 'name': 'MPPT 8 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_9_cap', + 'name': 'MPPT 9 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_10_cap', + 'name': 'MPPT 10 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataStateBinarySensor', 'attribute': 'run_state', 'name': 'Status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableRunStateSensor', 'attribute': 'run_state', + 'name': 'Readable status'}, + ] + + if device.type_id == PARAM_DEVICE_TYPE_ID_EMI: + entities_to_create = [ + {'class': 'FusionSolarRealtimeDeviceDataTemperatureSensor', 'attribute': 'temperature', + 'name': 'Temperature'}, + {'class': 'FusionSolarRealtimeDeviceDataTemperatureSensor', 'attribute': 'pv_temperature', + 'name': 'PV temperature'}, + {'class': 'FusionSolarRealtimeDeviceDataWindSpeedSensor', 'attribute': 'wind_speed', + 'name': 'Wind speed'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'wind_direction', + 'name': 'Wind direction'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'radiant_total', + 'name': 'Daily irradiation'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'radiant_line', 'name': 'Irradiance'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'horiz_radiant_line', + 'name': 'Horizontal irradiance'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'horiz_radiant_total', + 'name': 'Horizontal irradiation'}, + {'class': 'FusionSolarRealtimeDeviceDataStateBinarySensor', 'attribute': 'run_state', 'name': 'Status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableRunStateSensor', 'attribute': 'run_state', + 'name': 'Readable status'}, + ] + + if device.type_id == PARAM_DEVICE_TYPE_ID_GRID_METER: + entities_to_create = [ + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'ab_u', 'name': 'Grid AB voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'bc_u', 'name': 'Grid BC voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'ca_u', 'name': 'Grid CA voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'a_u', 'name': 'Phase A voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'b_u', 'name': 'Phase B voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'c_u', 'name': 'Phase C voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'a_i', 'name': 'Phase A current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'b_i', 'name': 'Phase B current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'c_i', 'name': 'Phase C current'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power', + 'name': 'Active power'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerFactorSensor', 'attribute': 'power_factor', + 'name': 'Power factor'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'active_cap', + 'name': 'Active energy (forward active energy)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power', + 'name': 'Reactive power'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'reverse_active_cap', + 'name': 'Reverse active energy'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'forward_reactive_cap', + 'name': 'Forward active energy'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'reverse_reactive_cap', + 'name': 'Reverse reactive energy'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power_a', + 'name': 'Active power PA'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power_b', + 'name': 'Active power PB'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power_c', + 'name': 'Active power PC'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power_a', + 'name': 'Reactive power QA'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power_b', + 'name': 'Reactive power QB'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power_c', + 'name': 'Reactive power QC'}, + {'class': 'FusionSolarRealtimeDeviceDataApparentPowerSensor', 'attribute': 'total_apparent_power', + 'name': 'Total apparent power'}, + {'class': 'FusionSolarRealtimeDeviceDataFrequencySensor', 'attribute': 'grid_frequency', + 'name': 'Grid frequency'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'reverse_active_peak', + 'name': 'Reverse active energy (peak)'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'reverse_active_power', + 'name': 'Reverse active energy (shoulder)'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'reverse_active_valley', + 'name': 'Reverse active energy (off-peak)'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'reverse_active_top', + 'name': 'Reverse active energy (sharp)'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'positive_active_peak', + 'name': 'Forward active energy (peak)'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'positive_active_power', + 'name': 'Forward active energy (shoulder)'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'positive_active_valley', + 'name': 'Forward active energy (off-peak)'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'positive_active_top', + 'name': 'Forward active energy (sharp)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reverse_reactive_peak', + 'name': 'Reverse reactive energy (peak)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reverse_reactive_power', + 'name': 'Reverse reactive energy (shoulder)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reverse_reactive_valley', + 'name': 'Reverse reactive energy (off-peak)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reverse_reactive_top', + 'name': 'Reverse reactive energy (sharp)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'positive_reactive_peak', + 'name': 'Forward reactive energy (peak)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'positive_reactive_power', + 'name': 'Forward reactive energy (shoulder)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'positive_reactive_valley', + 'name': 'Forward reactive energy (off-peak)'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'positive_reactive_top', + 'name': 'Forward reactive energy (sharp)'}, + ] + if device.type_id == PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER: entities_to_create = [ - {'class': 'FusionSolarRealtimeDeviceDataSensor','attribute': 'inverter_state', 'name': 'Inverter status'}, - {'class': 'FusionSolarRealtimeDeviceDataReadableInverterStateSensor', 'attribute': 'inverter_state', 'name': 'Readable inverter status'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'inverter_state', + 'name': 'Inverter status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableInverterStateSensor', 'attribute': 'inverter_state', + 'name': 'Readable inverter status'}, {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'ab_u', 'name': 'Grid AB voltage'}, {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'bc_u', 'name': 'Grid BC voltage'}, {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'ca_u', 'name': 'Grid CA voltage'}, @@ -400,49 +653,133 @@ async def async_update_device_real_kpi_data(): {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'a_i', 'name': 'Phase A current'}, {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'b_i', 'name': 'Phase B current'}, {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'c_i', 'name': 'Phase C current'}, - {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'efficiency', 'name': 'Inverter efficiency % (manufacturer)'}, - {'class': 'FusionSolarRealtimeDeviceDataTemperatureSensor', 'attribute': 'temperature', 'name': 'Inverter internal temperature'}, - {'class': 'FusionSolarRealtimeDeviceDataPowerFactorSensor', 'attribute': 'power_factor', 'name': 'Power factor'}, - {'class': 'FusionSolarRealtimeDeviceDataFrequencySensor', 'attribute': 'elec_freq', 'name': 'Grid frequency'}, - {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power', 'name': 'Active power'}, - {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power', 'name': 'Reactive output power'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'efficiency', + 'name': 'Inverter efficiency % (manufacturer)'}, + {'class': 'FusionSolarRealtimeDeviceDataTemperatureSensor', 'attribute': 'temperature', + 'name': 'Inverter internal temperature'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerFactorSensor', 'attribute': 'power_factor', + 'name': 'Power factor'}, + {'class': 'FusionSolarRealtimeDeviceDataFrequencySensor', 'attribute': 'elec_freq', + 'name': 'Grid frequency'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power', + 'name': 'Active power'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power', + 'name': 'Reactive output power'}, {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'day_cap', 'name': 'Yield Today'}, - {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'mppt_power', 'name': 'MPPT total input power'}, - {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv1_u', 'name': 'PV1 input voltage'}, - {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv2_u', 'name': 'PV2 input voltage'}, - {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv3_u', 'name': 'PV3 input voltage'}, - {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv4_u', 'name': 'PV4 input voltage'}, - {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv5_u', 'name': 'PV5 input voltage'}, - {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv6_u', 'name': 'PV6 input voltage'}, - {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv7_u', 'name': 'PV7 input voltage'}, - {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv8_u', 'name': 'PV8 input voltage'}, - {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv1_i', 'name': 'PV1 input current'}, - {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv2_i', 'name': 'PV2 input current'}, - {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv3_i', 'name': 'PV3 input current'}, - {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv4_i', 'name': 'PV4 input current'}, - {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv5_i', 'name': 'PV5 input current'}, - {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv6_i', 'name': 'PV6 input current'}, - {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv7_i', 'name': 'PV7 input current'}, - {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv8_i', 'name': 'PV8 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'mppt_power', + 'name': 'MPPT total input power'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv1_u', + 'name': 'PV1 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv2_u', + 'name': 'PV2 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv3_u', + 'name': 'PV3 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv4_u', + 'name': 'PV4 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv5_u', + 'name': 'PV5 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv6_u', + 'name': 'PV6 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv7_u', + 'name': 'PV7 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'pv8_u', + 'name': 'PV8 input voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv1_i', + 'name': 'PV1 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv2_i', + 'name': 'PV2 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv3_i', + 'name': 'PV3 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv4_i', + 'name': 'PV4 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv5_i', + 'name': 'PV5 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv6_i', + 'name': 'PV6 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv7_i', + 'name': 'PV7 input current'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'pv8_i', + 'name': 'PV8 input current'}, {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'total_cap', 'name': 'Total yield'}, - {'class': 'FusionSolarRealtimeDeviceDataTimestampSensor', 'attribute': 'open_time', 'name': 'Inverter startup time'}, - {'class': 'FusionSolarRealtimeDeviceDataTimestampSensor', 'attribute': 'close_time', 'name': 'Inverter shutdown time'}, - {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_1_cap', 'name': 'MPPT 1 DC total yield'}, - {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_2_cap', 'name': 'MPPT 2 DC total yield'}, - {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_3_cap', 'name': 'MPPT 3 DC total yield'}, - {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_4_cap', 'name': 'MPPT 4 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataTimestampSensor', 'attribute': 'open_time', + 'name': 'Inverter startup time'}, + {'class': 'FusionSolarRealtimeDeviceDataTimestampSensor', 'attribute': 'close_time', + 'name': 'Inverter shutdown time'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_1_cap', + 'name': 'MPPT 1 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_2_cap', + 'name': 'MPPT 2 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_3_cap', + 'name': 'MPPT 3 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'mppt_4_cap', + 'name': 'MPPT 4 DC total yield'}, + {'class': 'FusionSolarRealtimeDeviceDataStateBinarySensor', 'attribute': 'run_state', 'name': 'Status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableRunStateSensor', 'attribute': 'run_state', + 'name': 'Readable status'}, + ] + + if device.type_id == PARAM_DEVICE_TYPE_ID_BATTERY: + entities_to_create = [ + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'battery_status', + 'name': 'Battery running status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableBatteryStatusSensor', 'attribute': 'battery_status', + 'name': 'Readable battery running status'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'max_charge_power', + 'name': 'Maximum charge power'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'max_discharge_power', + 'name': 'Maximum discharge power'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'ch_discharge_power', + 'name': 'Charge/Discharge power'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'busbar_u', + 'name': 'Battery voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataBatterySensor', 'attribute': 'battery_soc', + 'name': 'Battery state of charge (SOC)'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'battery_soh', + 'name': 'Battery state of health (SOH)'}, + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'ch_discharge_model', + 'name': 'Charge/Discharge mode'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableChargeDischargeModeSensor', + 'attribute': 'ch_discharge_model', 'name': 'Readable charge/Discharge mode'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'charge_cap', + 'name': 'Charging capacity'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'discharge_cap', + 'name': 'Discharging capacity'}, {'class': 'FusionSolarRealtimeDeviceDataStateBinarySensor', 'attribute': 'run_state', 'name': 'Status'}, - {'class': 'FusionSolarRealtimeDeviceDataReadableStateSensor', 'attribute': 'run_state', 'name': 'Readable status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableRunStateSensor', 'attribute': 'run_state', + 'name': 'Readable status'}, ] - entities = [] - for entity_to_create in entities_to_create: - class_name = globals()[entity_to_create['class']] - entities.append( - class_name(coordinator, device, entity_to_create['name'], entity_to_create['attribute']) - ) + if device.type_id == PARAM_DEVICE_TYPE_ID_POWER_SENSOR: + entities_to_create = [ + {'class': 'FusionSolarRealtimeDeviceDataSensor', 'attribute': 'meter_status', 'name': 'Meter status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableMeterStatusSensor', 'attribute': 'meter_status', + 'name': 'Meter status'}, + {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'meter_u', 'name': 'Grid voltage'}, + {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'meter_i', 'name': 'Grid current'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power', + 'name': 'Active power'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power', + 'name': 'Reactive power'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerFactorSensor', 'attribute': 'power_factor', + 'name': 'Power factor'}, + {'class': 'FusionSolarRealtimeDeviceDataFrequencySensor', 'attribute': 'grid_frequency', + 'name': 'Grid frequency'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'active_cap', + 'name': 'Active energy (forward active energy)'}, + {'class': 'FusionSolarRealtimeDeviceDataEnergySensor', 'attribute': 'reverse_active_cap', + 'name': 'Reverse active energy'}, + {'class': 'FusionSolarRealtimeDeviceDataStateBinarySensor', 'attribute': 'run_state', 'name': 'Status'}, + {'class': 'FusionSolarRealtimeDeviceDataReadableRunStateSensor', 'attribute': 'run_state', + 'name': 'Readable status'}, + ] - async_add_entities(entities) + entities = [] + for entity_to_create in entities_to_create: + class_name = globals()[entity_to_create['class']] + entities.append( + class_name(coordinator, device, entity_to_create['name'], entity_to_create['attribute']) + ) + async_add_entities(entities) async def async_setup_entry(hass, config_entry, async_add_entities): From 7912664065e78c68b4d1cd1fba8c0945667d3f80 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Mon, 14 Nov 2022 14:02:44 +0100 Subject: [PATCH 04/11] Use ID instead of ESN code --- .../realtime_device_data_sensor.py | 14 ++++++------- custom_components/fusion_solar/sensor.py | 20 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py index fcfd17c..9c58f5c 100644 --- a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py @@ -32,7 +32,7 @@ def __init__( @property def unique_id(self) -> str: - return f'{DOMAIN}-{self._device.esn_code}-{self._attribute}' + return f'{DOMAIN}-{self._device.device_id}-{self._attribute}' @property def name(self) -> str: @@ -62,7 +62,7 @@ def device_info(self) -> dict: class FusionSolarRealtimeDeviceDataReadableInverterStateSensor(FusionSolarRealtimeDeviceDataSensor): @property def unique_id(self) -> str: - return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + return f'{DOMAIN}-{self._device.device_id}-readable-{self._attribute}' @property def state(self) -> str: @@ -299,7 +299,7 @@ def state(self) -> datetime: class FusionSolarRealtimeDeviceDataReadableRunStateSensor(FusionSolarRealtimeDeviceDataSensor): @property def unique_id(self) -> str: - return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + return f'{DOMAIN}-{self._device.device_id}-readable-{self._attribute}' @property def state(self) -> float: @@ -319,7 +319,7 @@ def state(self) -> float: class FusionSolarRealtimeDeviceDataReadableChargeDischargeModeSensor(FusionSolarRealtimeDeviceDataSensor): @property def unique_id(self) -> str: - return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + return f'{DOMAIN}-{self._device.device_id}-readable-{self._attribute}' @property def state(self) -> float: @@ -345,7 +345,7 @@ def state(self) -> float: class FusionSolarRealtimeDeviceDataReadableBatteryStatusSensor(FusionSolarRealtimeDeviceDataSensor): @property def unique_id(self) -> str: - return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + return f'{DOMAIN}-{self._device.device_id}-readable-{self._attribute}' @property def state(self) -> float: @@ -371,7 +371,7 @@ def state(self) -> float: class FusionSolarRealtimeDeviceDataReadableMeterStatusSensor(FusionSolarRealtimeDeviceDataSensor): @property def unique_id(self) -> str: - return f'{DOMAIN}-{self._device.esn_code}-readable-{self._attribute}' + return f'{DOMAIN}-{self._device.device_id}-readable-{self._attribute}' @property def state(self) -> float: @@ -408,7 +408,7 @@ def __init__( @property def unique_id(self) -> str: - return f'{DOMAIN}-{self._device.esn_code}-{self._attribute}' + return f'{DOMAIN}-{self._device.device_id}-{self._attribute}' @property def name(self) -> str: diff --git a/custom_components/fusion_solar/sensor.py b/custom_components/fusion_solar/sensor.py index e0be8a5..cbdf7ac 100644 --- a/custom_components/fusion_solar/sensor.py +++ b/custom_components/fusion_solar/sensor.py @@ -311,49 +311,49 @@ async def async_update_device_real_kpi_data(): for device in devices: async_add_entities([ FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-device-id', + f'{DOMAIN}-{device.device_id}-device-id', f'{device.name} ({device.esn_code}) - Device ID', device.device_id, device.device_info() ), FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-device-name', + f'{DOMAIN}-{device.device_id}-device-name', f'{device.name} ({device.esn_code}) - Device Name', device.name, device.device_info() ), FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-station-code', + f'{DOMAIN}-{device.device_id}-station-code', f'{device.name} ({device.esn_code}) - Station Code', device.station_code, device.device_info() ), FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-esn-code', + f'{DOMAIN}-{device.device_id}-esn-code', f'{device.name} ({device.esn_code}) - Serial Number', device.esn_code, device.device_info() ), FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-device-type-id', + f'{DOMAIN}-{device.device_id}-device-type-id', f'{device.name} ({device.esn_code}) - Device Type ID', device.type_id, device.device_info() ), FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-device-type', + f'{DOMAIN}-{device.device_id}-device-type', f'{device.name} ({device.esn_code}) - Device Type', device.device_type, device.device_info() ), FusionSolarLatitudeEntity( - f'{DOMAIN}-{device.esn_code}-latitude', + f'{DOMAIN}-{device.device_id}-latitude', f'{device.name} ({device.esn_code}) - Latitude', device.latitude, device.device_info() ), FusionSolarLongitudeEntity( - f'{DOMAIN}-{device.esn_code}-longitude', + f'{DOMAIN}-{device.device_id}-longitude', f'{device.name} ({device.esn_code}) - Longitude', device.longitude, device.device_info() @@ -365,7 +365,7 @@ async def async_update_device_real_kpi_data(): async_add_entities([ FusionSolarPowerEntityRealtime( coordinator, - f'{DOMAIN}-{device.esn_code}-{ID_REALTIME_POWER}', + f'{DOMAIN}-{device.device_id}-{ID_REALTIME_POWER}', f'{device.name} ({device.esn_code}) - {NAME_REALTIME_POWER}', ATTR_DEVICE_REAL_KPI_ACTIVE_POWER, f'{DOMAIN}-{device.device_id}', @@ -376,7 +376,7 @@ async def async_update_device_real_kpi_data(): if device.type_id in [PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER]: async_add_entities([ FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-inverter_type', + f'{DOMAIN}-{device.device_id}-inverter_type', f'{device.name} ({device.esn_code}) - Inverter Type', device.inverter_type, device.device_info() From cc25ebe7473a1fdbedcf1dfc034042bb8ea586b1 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Mon, 14 Nov 2022 14:19:02 +0100 Subject: [PATCH 05/11] Handle N/A value --- .../fusion_solar/fusion_solar/realtime_device_data_sensor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py index 9c58f5c..b1ff58a 100644 --- a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py @@ -52,6 +52,9 @@ def state(self) -> float: if self.coordinator.data[self._data_name][self._attribute] is None: return None + if self.coordinator.data[self._data_name][self._attribute] == 'N/A': + return None + return float(self.coordinator.data[self._data_name][self._attribute]) @property From f8c6bff54e73597eb9c91e04d4ae29fbbdcf52ac Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Mon, 14 Nov 2022 16:53:50 +0100 Subject: [PATCH 06/11] Updated readme --- README.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9ff8d3d..5e8ce77 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,7 @@ When this is done, just install the repository. The configuration happens in the configuration flow when you add the integration. -## Configuration -### Kiosk +## Kiosk FusionSolar has a kiosk mode. The kiosk is a dashboard that is accessible for everyone that has the url. The integration uses a JSON REST api that is also consumed by the kiosk. @@ -24,8 +23,25 @@ The integration updates the data every 10 minutes. If you need more accurate information you should use the OpenAPI mode. -### OpenAPI +## OpenAPI You will need an OpenAPI account from Huawei for this to work. [More information](https://forum.huawei.com/enterprise/en/communicate-with-fusionsolar-through-an-openapi-account/thread/591478-100027) -The integration updates the total yields (current day, current month, current year, lifetime) every 10 minutes. +The integration will expose the different devices (Residential inverter, String inverter, Battery, Dongle, ...) in +your plant/station. + +### Realtime data +The devices that support realtime information (getDevRealKpi api call): +* String inverter +* EMI +* Grid meter +* Residential inverter +* Battery +* Power Sensor + +The exposed entities can be different per device. These are documented in the "Interface reference" that you can +request from Huawei. But the names are pretty self explanatory. + The realtime data is updated every minute. + +### Total yields +The integration updates the total yields (current day, current month, current year, lifetime) every 10 minutes. From 35aa0838df8eedfa3f450c216dd264076f681bd9 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Mon, 14 Nov 2022 17:38:07 +0100 Subject: [PATCH 07/11] Refactored code --- .../fusion_solar/device_attribute_entity.py | 55 ++++++ .../fusion_solar/station_attribute_entity.py | 36 ++-- custom_components/fusion_solar/sensor.py | 157 +++++++----------- 3 files changed, 127 insertions(+), 121 deletions(-) create mode 100644 custom_components/fusion_solar/fusion_solar/device_attribute_entity.py diff --git a/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py b/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py new file mode 100644 index 0000000..aa2f889 --- /dev/null +++ b/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py @@ -0,0 +1,55 @@ +from homeassistant.helpers.entity import Entity, EntityCategory + +from .openapi.device import FusionSolarDevice +from ..const import DOMAIN + + +class FusionSolarDeviceAttributeEntity(Entity): + def __init__( + self, + device: FusionSolarDevice, + name, + attribute, + value + ): + """Initialize the entity""" + self._device = device + self._name = name + self._attribute = attribute + self._device_info = device.device_info() + self._value = value + + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.device_id}-{self._attribute}' + + @property + def name(self): + if not self._device.esn_code: + return f'{self._device.name} ({self._device.esn_code}) - {self._name}' + + return f'{self._device.name} - {self._name}' + + @property + def state(self): + return self._value + + @property + def device_info(self) -> dict: + return self._device_info + + @property + def entity_category(self) -> str: + return EntityCategory.DIAGNOSTIC + + @property + def should_poll(self) -> bool: + return False + + +class FusionSolarDeviceLatitudeEntity(FusionSolarDeviceAttributeEntity): + _attr_icon = 'mdi:latitude' + + +class FusionSolarDeviceLongitudeEntity(FusionSolarDeviceAttributeEntity): + _attr_icon = 'mdi:longitude' diff --git a/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py b/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py index 0047bb7..165de64 100644 --- a/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py +++ b/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py @@ -1,29 +1,31 @@ from homeassistant.helpers.entity import Entity, EntityCategory +from .openapi.station import FusionSolarStation +from ..const import DOMAIN -class FusionSolarAttributeEntity(Entity): - """Base class for all FusionSolarAttributeEntity entities.""" +class FusionSolarStationAttributeEntity(Entity): def __init__( self, - unique_id, + station: FusionSolarStation, name, - value, - device_info=None + attribute, + value ): """Initialize the entity""" - self._unique_id = unique_id + self._station = station self._name = name + self._attribute = attribute + self._device_info = station.device_info() self._value = value - self._device_info = device_info @property def unique_id(self) -> str: - return self._unique_id + return f'{DOMAIN}-{self._station.code}-{self._attribute}' @property def name(self): - return self._name + return f'{self._station.name} ({self._station.code}) - {self._name}' @property def state(self): @@ -42,25 +44,17 @@ def should_poll(self) -> bool: return False -class FusionSolarCapacityEntity(FusionSolarAttributeEntity): +class FusionSolarStationCapacityEntity(FusionSolarStationAttributeEntity): _attr_icon = 'mdi:lightning-bolt' -class FusionSolarContactPersonEntity(FusionSolarAttributeEntity): +class FusionSolarStationContactPersonEntity(FusionSolarStationAttributeEntity): _attr_icon = 'mdi:account' -class FusionSolarContactPersonPhoneEntity(FusionSolarAttributeEntity): +class FusionSolarStationContactPersonPhoneEntity(FusionSolarStationAttributeEntity): _attr_icon = 'mdi:card-account-phone' -class FusionSolarAddressEntity(FusionSolarAttributeEntity): +class FusionSolarStationAddressEntity(FusionSolarStationAttributeEntity): _attr_icon = 'mdi:map-marker' - - -class FusionSolarLatitudeEntity(FusionSolarAttributeEntity): - _attr_icon = 'mdi:latitude' - - -class FusionSolarLongitudeEntity(FusionSolarAttributeEntity): - _attr_icon = 'mdi:longitude' diff --git a/custom_components/fusion_solar/sensor.py b/custom_components/fusion_solar/sensor.py index cbdf7ac..065076e 100644 --- a/custom_components/fusion_solar/sensor.py +++ b/custom_components/fusion_solar/sensor.py @@ -25,10 +25,9 @@ FusionSolarEnergySensorTotalCurrentMonth, FusionSolarEnergySensorTotalCurrentYear, \ FusionSolarEnergySensorTotalLifetime from .fusion_solar.power_entity import FusionSolarPowerEntityRealtime +from .fusion_solar.device_attribute_entity import * from .fusion_solar.realtime_device_data_sensor import * -from .fusion_solar.station_attribute_entity import FusionSolarAttributeEntity, FusionSolarAddressEntity, \ - FusionSolarCapacityEntity, FusionSolarContactPersonEntity, FusionSolarContactPersonPhoneEntity, \ - FusionSolarLatitudeEntity, FusionSolarLongitudeEntity +from .fusion_solar.station_attribute_entity import * from .const import CONF_KIOSKS, CONF_OPENAPI_CREDENTIALS, DOMAIN, ID_REALTIME_POWER, NAME_REALTIME_POWER, \ ID_TOTAL_CURRENT_DAY_ENERGY, NAME_TOTAL_CURRENT_DAY_ENERGY, \ @@ -160,44 +159,30 @@ async def async_update_station_real_kpi_data(): await coordinator.async_refresh() for station in stations: - async_add_entities([ - FusionSolarAttributeEntity( - f'{DOMAIN}-{station.code}-station-code', - f'{station.name} ({station.code}) - Station Code', - station.code, - station.device_info() - ), - FusionSolarAttributeEntity( - f'{DOMAIN}-{station.code}-station-name', - f'{station.name} ({station.code}) - Station Name', - station.name, - station.device_info() - ), - FusionSolarAddressEntity( - f'{DOMAIN}-{station.code}-station-address', - f'{station.name} ({station.code}) - Station Address', - station.address, - station.device_info() - ), - FusionSolarCapacityEntity( - f'{DOMAIN}-{station.code}-capacity', - f'{station.name} ({station.code}) - Capacity', - station.capacity, - station.device_info() - ), - FusionSolarContactPersonEntity( - f'{DOMAIN}-{station.code}-contact-person', - f'{station.name} ({station.code}) - Contact Person', - station.contact_person, - station.device_info() - ), - FusionSolarContactPersonPhoneEntity( - f'{DOMAIN}-{station.code}-contact-phone', - f'{station.name} ({station.code}) - Contact Phone', - station.contact_phone, - station.device_info() - ), + entities_to_create = [ + {'class': 'FusionSolarStationAttributeEntity', 'name': 'Station Code', 'suffix': 'station_code', + 'value': station.code}, + {'class': 'FusionSolarStationAttributeEntity', 'name': 'Station Name', 'suffix': 'station_name', + 'value': station.name}, + {'class': 'FusionSolarStationAddressEntity', 'name': 'Station Address', 'suffix': 'station_address', + 'value': station.address}, + {'class': 'FusionSolarStationCapacityEntity', 'name': 'Capacity', 'suffix': 'capacity', + 'value': station.capacity}, + {'class': 'FusionSolarStationContactPersonEntity', 'name': 'Contact Person', 'suffix': 'contact_person', + 'value': station.contact_person}, + {'class': 'FusionSolarStationContactPersonPhoneEntity', 'name': 'Contact Phone', 'suffix': 'contact_phone', + 'value': station.contact_phone}, + ] + entities = [] + for entity_to_create in entities_to_create: + class_name = globals()[entity_to_create['class']] + entities.append( + class_name(station, entity_to_create['name'], entity_to_create['suffix'], entity_to_create['value'], ) + ) + async_add_entities(entities) + + async_add_entities([ FusionSolarEnergySensorTotalCurrentDay( coordinator, f'{DOMAIN}-{station.code}-{ID_TOTAL_CURRENT_DAY_ENERGY}', @@ -309,56 +294,38 @@ async def async_update_device_real_kpi_data(): await coordinator.async_refresh() for device in devices: - async_add_entities([ - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.device_id}-device-id', - f'{device.name} ({device.esn_code}) - Device ID', - device.device_id, - device.device_info() - ), - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.device_id}-device-name', - f'{device.name} ({device.esn_code}) - Device Name', - device.name, - device.device_info() - ), - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.device_id}-station-code', - f'{device.name} ({device.esn_code}) - Station Code', - device.station_code, - device.device_info() - ), - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.device_id}-esn-code', - f'{device.name} ({device.esn_code}) - Serial Number', - device.esn_code, - device.device_info() - ), - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.device_id}-device-type-id', - f'{device.name} ({device.esn_code}) - Device Type ID', - device.type_id, - device.device_info() - ), - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.device_id}-device-type', - f'{device.name} ({device.esn_code}) - Device Type', - device.device_type, - device.device_info() - ), - FusionSolarLatitudeEntity( - f'{DOMAIN}-{device.device_id}-latitude', - f'{device.name} ({device.esn_code}) - Latitude', - device.latitude, - device.device_info() - ), - FusionSolarLongitudeEntity( - f'{DOMAIN}-{device.device_id}-longitude', - f'{device.name} ({device.esn_code}) - Longitude', - device.longitude, - device.device_info() - ), - ]) + entities_to_create = [ + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device ID', 'suffix': 'device_id', + 'value': device.device_id}, + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device Name', 'suffix': 'device_name', + 'value': device.name}, + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Station Code', 'suffix': 'station_code', + 'value': device.station_code}, + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Serial Number', 'suffix': 'esn_code', + 'value': device.esn_code}, + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device Type ID', 'suffix': 'device_type_id', + 'value': device.type_id}, + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device Type', 'suffix': 'device_type', + 'value': device.device_type}, + {'class': 'FusionSolarDeviceLatitudeEntity', 'name': 'Latitude', 'suffix': 'latitude', + 'value': device.latitude}, + {'class': 'FusionSolarDeviceLongitudeEntity', 'name': 'Longitude', 'suffix': 'longitude', + 'value': device.longitude}, + ] + + if device.type_id in [PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER]: + entity_to_create.update({ + 'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Inverter Type', 'suffix': 'inverter_type', + 'value': device.inverter_type + }) + + entities = [] + for entity_to_create in entities_to_create: + class_name = globals()[entity_to_create['class']] + entities.append( + class_name(device, entity_to_create['name'], entity_to_create['suffix'], entity_to_create['value'], ) + ) + async_add_entities(entities) if device.type_id in [PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_GRID_METER, PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER, PARAM_DEVICE_TYPE_ID_POWER_SENSOR]: @@ -373,16 +340,6 @@ async def async_update_device_real_kpi_data(): ), ]) - if device.type_id in [PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER]: - async_add_entities([ - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.device_id}-inverter_type', - f'{device.name} ({device.esn_code}) - Inverter Type', - device.inverter_type, - device.device_info() - ), - ]) - entities_to_create = [] if device.type_id == PARAM_DEVICE_TYPE_ID_STRING_INVERTER: From 9b079343e3a19323160365d3bd7985a1c47e7761 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Mon, 14 Nov 2022 18:03:59 +0100 Subject: [PATCH 08/11] Better naming --- .../fusion_solar/device_attribute_entity.py | 5 +---- .../fusion_solar/openapi/device.py | 10 +++++++++ .../fusion_solar/openapi/station.py | 7 ++++++ .../fusion_solar/station_attribute_entity.py | 3 +-- custom_components/fusion_solar/sensor.py | 22 +++++++++---------- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py b/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py index aa2f889..0557692 100644 --- a/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py +++ b/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py @@ -25,10 +25,7 @@ def unique_id(self) -> str: @property def name(self): - if not self._device.esn_code: - return f'{self._device.name} ({self._device.esn_code}) - {self._name}' - - return f'{self._device.name} - {self._name}' + return f'{self._device.readable_name} - {self._name}' @property def state(self): diff --git a/custom_components/fusion_solar/fusion_solar/openapi/device.py b/custom_components/fusion_solar/fusion_solar/openapi/device.py index 051f7cb..988fc46 100644 --- a/custom_components/fusion_solar/fusion_solar/openapi/device.py +++ b/custom_components/fusion_solar/fusion_solar/openapi/device.py @@ -83,3 +83,13 @@ def device_info(self): 'sw_version': self.software_version, 'via_device': (DOMAIN, self.station_code) } + + @property + def readable_name(self): + if self.name == self.esn_code: + return self.name + + if self.esn_code is not None and self.esn_code != '': + return f'{self.name} ({self.esn_code})' + + return self.name diff --git a/custom_components/fusion_solar/fusion_solar/openapi/station.py b/custom_components/fusion_solar/fusion_solar/openapi/station.py index 82072b3..2e35341 100644 --- a/custom_components/fusion_solar/fusion_solar/openapi/station.py +++ b/custom_components/fusion_solar/fusion_solar/openapi/station.py @@ -33,3 +33,10 @@ def device_info(self): 'manufacturer': 'Huawei FusionSolar', 'model': 'Station' } + + @property + def readable_name(self): + if self.name is not None and self.name != '': + return self.name + + return self.code \ No newline at end of file diff --git a/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py b/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py index 165de64..29207c3 100644 --- a/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py +++ b/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py @@ -3,7 +3,6 @@ from .openapi.station import FusionSolarStation from ..const import DOMAIN - class FusionSolarStationAttributeEntity(Entity): def __init__( self, @@ -25,7 +24,7 @@ def unique_id(self) -> str: @property def name(self): - return f'{self._station.name} ({self._station.code}) - {self._name}' + return f'{self._station.readable_name} - {self._name}' @property def state(self): diff --git a/custom_components/fusion_solar/sensor.py b/custom_components/fusion_solar/sensor.py index 065076e..703af09 100644 --- a/custom_components/fusion_solar/sensor.py +++ b/custom_components/fusion_solar/sensor.py @@ -186,7 +186,7 @@ async def async_update_station_real_kpi_data(): FusionSolarEnergySensorTotalCurrentDay( coordinator, f'{DOMAIN}-{station.code}-{ID_TOTAL_CURRENT_DAY_ENERGY}', - f'{station.name} ({station.code}) - {NAME_TOTAL_CURRENT_DAY_ENERGY}', + f'{station.readable_name} - {NAME_TOTAL_CURRENT_DAY_ENERGY}', ATTR_STATION_REAL_KPI_TOTAL_CURRENT_DAY_ENERGY, f'{DOMAIN}-{station.code}', station.device_info() @@ -194,7 +194,7 @@ async def async_update_station_real_kpi_data(): FusionSolarEnergySensorTotalCurrentMonth( coordinator, f'{DOMAIN}-{station.code}-{ID_TOTAL_CURRENT_MONTH_ENERGY}', - f'{station.name} ({station.code}) - {NAME_TOTAL_CURRENT_MONTH_ENERGY}', + f'{station.readable_name} - {NAME_TOTAL_CURRENT_MONTH_ENERGY}', ATTR_STATION_REAL_KPI_TOTAL_CURRENT_MONTH_ENERGY, f'{DOMAIN}-{station.code}', station.device_info() @@ -202,7 +202,7 @@ async def async_update_station_real_kpi_data(): FusionSolarEnergySensorTotalLifetime( coordinator, f'{DOMAIN}-{station.code}-{ID_TOTAL_LIFETIME_ENERGY}', - f'{station.name} ({station.code}) - {NAME_TOTAL_LIFETIME_ENERGY}', + f'{station.readable_name} - {NAME_TOTAL_LIFETIME_ENERGY}', ATTR_STATION_REAL_KPI_TOTAL_LIFETIME_ENERGY, f'{DOMAIN}-{station.code}', station.device_info() @@ -246,7 +246,7 @@ async def async_update_station_year_kpi_data(): FusionSolarEnergySensorTotalCurrentYear( coordinator, f'{DOMAIN}-{station.code}-{ID_TOTAL_CURRENT_YEAR_ENERGY}', - f'{station.name} ({station.code}) - {NAME_TOTAL_CURRENT_YEAR_ENERGY}', + f'{station.readable_name} - {NAME_TOTAL_CURRENT_YEAR_ENERGY}', ATTR_KPI_YEAR_INVERTER_POWER, f'{DOMAIN}-{station.code}', station.device_info() @@ -297,15 +297,15 @@ async def async_update_device_real_kpi_data(): entities_to_create = [ {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device ID', 'suffix': 'device_id', 'value': device.device_id}, - {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device Name', 'suffix': 'device_name', + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device name', 'suffix': 'device_name', 'value': device.name}, - {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Station Code', 'suffix': 'station_code', + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Station code', 'suffix': 'station_code', 'value': device.station_code}, - {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Serial Number', 'suffix': 'esn_code', + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Serial number', 'suffix': 'esn_code', 'value': device.esn_code}, - {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device Type ID', 'suffix': 'device_type_id', + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device type ID', 'suffix': 'device_type_id', 'value': device.type_id}, - {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device Type', 'suffix': 'device_type', + {'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Device type', 'suffix': 'device_type', 'value': device.device_type}, {'class': 'FusionSolarDeviceLatitudeEntity', 'name': 'Latitude', 'suffix': 'latitude', 'value': device.latitude}, @@ -315,7 +315,7 @@ async def async_update_device_real_kpi_data(): if device.type_id in [PARAM_DEVICE_TYPE_ID_STRING_INVERTER, PARAM_DEVICE_TYPE_ID_RESIDENTIAL_INVERTER]: entity_to_create.update({ - 'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Inverter Type', 'suffix': 'inverter_type', + 'class': 'FusionSolarDeviceAttributeEntity', 'name': 'Inverter model', 'suffix': 'inverter_type', 'value': device.inverter_type }) @@ -333,7 +333,7 @@ async def async_update_device_real_kpi_data(): FusionSolarPowerEntityRealtime( coordinator, f'{DOMAIN}-{device.device_id}-{ID_REALTIME_POWER}', - f'{device.name} ({device.esn_code}) - {NAME_REALTIME_POWER}', + f'{device.readable_name} - {NAME_REALTIME_POWER}', ATTR_DEVICE_REAL_KPI_ACTIVE_POWER, f'{DOMAIN}-{device.device_id}', device.device_info() From 07343dddd5d580fe86d6d3792ead7028a6148c56 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Tue, 15 Nov 2022 11:28:40 +0100 Subject: [PATCH 09/11] Fix percentage --- .../fusion_solar/realtime_device_data_sensor.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py index b1ff58a..7661db5 100644 --- a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py @@ -199,6 +199,15 @@ def unit_of_measurement(self) -> str: def state_class(self) -> str: return STATE_CLASS_MEASUREMENT + @property + def state(self) -> str: + state = super().state + + if state is None: + return None + + return state * 100 + class FusionSolarRealtimeDeviceDataFrequencySensor(FusionSolarRealtimeDeviceDataSensor): @property From 87c9b627f9a2c5a98d36292ccf955714cd755120 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Tue, 15 Nov 2022 11:33:44 +0100 Subject: [PATCH 10/11] Use correct units for some specific parameters --- .../fusion_solar/realtime_device_data_sensor.py | 14 +++++++++++++- custom_components/fusion_solar/sensor.py | 10 +++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py index 7661db5..186b2ca 100644 --- a/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py @@ -6,7 +6,7 @@ from homeassistant.const import DEVICE_CLASS_VOLTAGE, ELECTRIC_POTENTIAL_VOLT, DEVICE_CLASS_CURRENT, \ ELECTRIC_CURRENT_AMPERE, DEVICE_CLASS_ENERGY, ENERGY_KILO_WATT_HOUR, DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, \ DEVICE_CLASS_POWER_FACTOR, PERCENTAGE, DEVICE_CLASS_FREQUENCY, FREQUENCY_HERTZ, DEVICE_CLASS_POWER, \ - POWER_KILO_WATT, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_BATTERY + POWER_WATT, POWER_KILO_WATT, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_BATTERY from .openapi.device import FusionSolarDevice from ..const import DOMAIN @@ -237,6 +237,12 @@ def state_class(self) -> str: return STATE_CLASS_MEASUREMENT +class FusionSolarRealtimeDeviceDataPowerInWattSensor(FusionSolarRealtimeDeviceDataPowerSensor): + @property + def unit_of_measurement(self) -> str: + return POWER_WATT + + class FusionSolarRealtimeDeviceDataReactivePowerSensor(FusionSolarRealtimeDeviceDataSensor): @property def device_class(self) -> str: @@ -251,6 +257,12 @@ def state_class(self) -> str: return STATE_CLASS_MEASUREMENT +class FusionSolarRealtimeDeviceDataReactivePowerInVarSensor(FusionSolarRealtimeDeviceDataReactivePowerSensor): + @property + def unit_of_measurement(self) -> str: + return 'Var' + + class FusionSolarRealtimeDeviceDataApparentPowerSensor(FusionSolarRealtimeDeviceDataSensor): @property def device_class(self) -> str: diff --git a/custom_components/fusion_solar/sensor.py b/custom_components/fusion_solar/sensor.py index 703af09..8fd7e43 100644 --- a/custom_components/fusion_solar/sensor.py +++ b/custom_components/fusion_solar/sensor.py @@ -681,11 +681,11 @@ async def async_update_device_real_kpi_data(): 'name': 'Battery running status'}, {'class': 'FusionSolarRealtimeDeviceDataReadableBatteryStatusSensor', 'attribute': 'battery_status', 'name': 'Readable battery running status'}, - {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'max_charge_power', + {'class': 'FusionSolarRealtimeDeviceDataPowerInWattSensor', 'attribute': 'max_charge_power', 'name': 'Maximum charge power'}, - {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'max_discharge_power', + {'class': 'FusionSolarRealtimeDeviceDataPowerInWattSensor', 'attribute': 'max_discharge_power', 'name': 'Maximum discharge power'}, - {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'ch_discharge_power', + {'class': 'FusionSolarRealtimeDeviceDataPowerInWattSensor', 'attribute': 'ch_discharge_power', 'name': 'Charge/Discharge power'}, {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'busbar_u', 'name': 'Battery voltage'}, @@ -713,9 +713,9 @@ async def async_update_device_real_kpi_data(): 'name': 'Meter status'}, {'class': 'FusionSolarRealtimeDeviceDataVoltageSensor', 'attribute': 'meter_u', 'name': 'Grid voltage'}, {'class': 'FusionSolarRealtimeDeviceDataCurrentSensor', 'attribute': 'meter_i', 'name': 'Grid current'}, - {'class': 'FusionSolarRealtimeDeviceDataPowerSensor', 'attribute': 'active_power', + {'class': 'FusionSolarRealtimeDeviceDataPowerInWattSensor', 'attribute': 'active_power', 'name': 'Active power'}, - {'class': 'FusionSolarRealtimeDeviceDataReactivePowerSensor', 'attribute': 'reactive_power', + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerInVarSensor', 'attribute': 'reactive_power', 'name': 'Reactive power'}, {'class': 'FusionSolarRealtimeDeviceDataPowerFactorSensor', 'attribute': 'power_factor', 'name': 'Power factor'}, From bbf6d36139a4fe7fee40cddfbcc54446545fa026 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Tue, 15 Nov 2022 14:52:47 +0100 Subject: [PATCH 11/11] Prepare for release --- custom_components/fusion_solar/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/fusion_solar/manifest.json b/custom_components/fusion_solar/manifest.json index 6083201..36a26b1 100644 --- a/custom_components/fusion_solar/manifest.json +++ b/custom_components/fusion_solar/manifest.json @@ -1,7 +1,7 @@ { "domain": "fusion_solar", "name": "FusionSolar", - "version": "1.1.0", + "version": "2.0.0", "documentation": "https://github.com/tijsverkoyen/Home-Assistant-FusionSolar", "issue_tracker": "https://github.com/tijsverkoyen/Home-Assistant-FusionSolar/issues", "dependencies": [],