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. 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/device_attribute_entity.py b/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py new file mode 100644 index 0000000..0557692 --- /dev/null +++ b/custom_components/fusion_solar/fusion_solar/device_attribute_entity.py @@ -0,0 +1,52 @@ +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): + return f'{self._device.readable_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/openapi/device.py b/custom_components/fusion_solar/fusion_solar/openapi/device.py index d881754..988fc46 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 @@ -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/realtime_device_data_sensor.py b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py new file mode 100644 index 0000000..186b2ca --- /dev/null +++ b/custom_components/fusion_solar/fusion_solar/realtime_device_data_sensor.py @@ -0,0 +1,466 @@ +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_WATT, POWER_KILO_WATT, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_BATTERY + +from .openapi.device import FusionSolarDevice +from ..const import DOMAIN + + +class FusionSolarRealtimeDeviceDataSensor(CoordinatorEntity, SensorEntity): + """Base class for all FusionSolarRealtimeDeviceDataSensor sensors.""" + + def __init__( + self, + coordinator, + device: FusionSolarDevice, + name: str, + attribute: str + ): + """Initialize the entity""" + super().__init__(coordinator) + self._device = device + self._name = name + self._attribute = attribute + self._data_name = f'{DOMAIN}-{device.device_id}' + self._device_info = device.device_info() + + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.device_id}-{self._attribute}' + + @property + def name(self) -> str: + return f'{self._device.name} - {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 + + 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 + + if self.coordinator.data[self._data_name][self._attribute] == 'N/A': + 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 unique_id(self) -> str: + return f'{DOMAIN}-{self._device.device_id}-readable-{self._attribute}' + + @property + def state(self) -> str: + state = super().state + + if state is None: + return None + + 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 + + @property + def state(self) -> str: + state = super().state + + if state is None: + return None + + return state * 100 + + +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 FusionSolarRealtimeDeviceDataPowerInWattSensor(FusionSolarRealtimeDeviceDataPowerSensor): + @property + def unit_of_measurement(self) -> str: + return POWER_WATT + + +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 FusionSolarRealtimeDeviceDataReactivePowerInVarSensor(FusionSolarRealtimeDeviceDataReactivePowerSensor): + @property + def unit_of_measurement(self) -> str: + return 'Var' + + +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: + return DEVICE_CLASS_TIMESTAMP + + @property + def state(self) -> datetime: + state = super().state + + if state is None: + return None + + return datetime.datetime.fromtimestamp(state / 1000) + + +class FusionSolarRealtimeDeviceDataReadableRunStateSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.device_id}-readable-{self._attribute}' + + @property + def state(self) -> float: + state = super().state + + if state is None: + return None + + if state == 0: + return "disconnected" + if state == 1: + return "connected" + + return None + + +class FusionSolarRealtimeDeviceDataReadableChargeDischargeModeSensor(FusionSolarRealtimeDeviceDataSensor): + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.device_id}-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.device_id}-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.device_id}-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.""" + + def __init__( + self, + coordinator, + device: FusionSolarDevice, + name: str, + attribute: str + ): + """Initialize the entity""" + super().__init__(coordinator) + self._device = device + self._name = name + self._attribute = attribute + self._data_name = f'{DOMAIN}-{device.device_id}' + self._device_info = device.device_info() + + @property + def unique_id(self) -> str: + return f'{DOMAIN}-{self._device.device_id}-{self._attribute}' + + @property + def name(self) -> str: + return f'{self._device.name} - {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/fusion_solar/station_attribute_entity.py b/custom_components/fusion_solar/fusion_solar/station_attribute_entity.py index 0047bb7..29207c3 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,30 @@ 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.readable_name} - {self._name}' @property def state(self): @@ -42,25 +43,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/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": [], diff --git a/custom_components/fusion_solar/sensor.py b/custom_components/fusion_solar/sensor.py index afebcc9..8629a83 100644 --- a/custom_components/fusion_solar/sensor.py +++ b/custom_components/fusion_solar/sensor.py @@ -10,14 +10,15 @@ from homeassistant.exceptions import IntegrationError 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 @@ -25,9 +26,9 @@ FusionSolarEnergySensorTotalCurrentMonth, FusionSolarEnergySensorTotalCurrentYear, \ FusionSolarEnergySensorTotalLifetime from .fusion_solar.power_entity import FusionSolarPowerEntityRealtime -from .fusion_solar.station_attribute_entity import FusionSolarAttributeEntity, FusionSolarAddressEntity, \ - FusionSolarCapacityEntity, FusionSolarContactPersonEntity, FusionSolarContactPersonPhoneEntity, \ - FusionSolarLatitudeEntity, FusionSolarLongitudeEntity +from .fusion_solar.device_attribute_entity import * +from .fusion_solar.realtime_device_data_sensor import * +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, \ @@ -159,48 +160,34 @@ 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}', - 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() @@ -208,7 +195,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() @@ -216,7 +203,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() @@ -260,7 +247,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() @@ -270,8 +257,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: @@ -307,80 +295,450 @@ async def async_update_device_real_kpi_data(): await coordinator.async_refresh() for device in devices: - async_add_entities([ - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-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'{device.name} ({device.esn_code}) - Device Name', - device.name, - device.device_info() - ), - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-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'{device.name} ({device.esn_code}) - Serial Number', - device.esn_code, - device.device_info() - ), - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-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'{device.name} ({device.esn_code}) - Device Type', - device.device_type, - device.device_info() - ), - FusionSolarLatitudeEntity( - f'{DOMAIN}-{device.esn_code}-latitude', - f'{device.name} ({device.esn_code}) - Latitude', - device.latitude, - device.device_info() - ), - FusionSolarLongitudeEntity( - f'{DOMAIN}-{device.esn_code}-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 model', '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]: async_add_entities([ FusionSolarPowerEntityRealtime( coordinator, - f'{DOMAIN}-{device.esn_code}-{ID_REALTIME_POWER}', - f'{device.name} ({device.esn_code}) - {NAME_REALTIME_POWER}', + f'{DOMAIN}-{device.device_id}-{ID_REALTIME_POWER}', + f'{device.readable_name} - {NAME_REALTIME_POWER}', 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]: - async_add_entities([ - FusionSolarAttributeEntity( - f'{DOMAIN}-{device.esn_code}-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: + 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': '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': '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': 'FusionSolarRealtimeDeviceDataPowerInWattSensor', 'attribute': 'max_charge_power', + 'name': 'Maximum charge power'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerInWattSensor', 'attribute': 'max_discharge_power', + 'name': 'Maximum discharge power'}, + {'class': 'FusionSolarRealtimeDeviceDataPowerInWattSensor', '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': 'FusionSolarRealtimeDeviceDataReadableRunStateSensor', 'attribute': 'run_state', + 'name': 'Readable status'}, + ] + + 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': 'FusionSolarRealtimeDeviceDataPowerInWattSensor', 'attribute': 'active_power', + 'name': 'Active power'}, + {'class': 'FusionSolarRealtimeDeviceDataReactivePowerInVarSensor', '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'}, + ] + + 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): config = hass.data[DOMAIN][config_entry.entry_id]