diff --git a/homeassistant/components/solaredge_modbus/__init__.py b/homeassistant/components/solaredge_modbus/__init__.py index e32533594ca8..d26ae2bf0ea5 100644 --- a/homeassistant/components/solaredge_modbus/__init__.py +++ b/homeassistant/components/solaredge_modbus/__init__.py @@ -6,6 +6,7 @@ connection per device between everything talking to it, and hands that unit to the ``solaredged`` library. """ +from collections.abc import Set as AbstractSet from typing import TYPE_CHECKING from solaredged import SolarEdge, SolarEdgeConnectionError, SolarEdgeError @@ -24,6 +25,7 @@ from .const import ( CONF_UNIT_ID, DOMAIN, LOGGER, + SUBSYSTEM_BATTERIES, SUBSYSTEM_COMMON, SUBSYSTEM_INVERTER, SUBSYSTEM_METERS, @@ -33,7 +35,7 @@ from .coordinator import ( SolarEdgeModbusDataUpdateCoordinator, SolarEdgeModbusRuntimeData, ) -from .entity import inverter_device_info, meter_identity +from .entity import attachment_identity, inverter_device_info from .helpers import create_modbus_params PLATFORMS = [Platform.SENSOR] @@ -92,6 +94,7 @@ async def async_setup_entry( # entities would stay missing until a reload. measuring = {SUBSYSTEM_INVERTER} measuring.update(f"meters[{index}]" for index in range(len(solaredge.meters))) + measuring.update(f"batteries[{index}]" for index in range(len(solaredge.batteries))) if measuring & readings.data.failed.keys(): raise ConfigEntryNotReady( translation_domain=DOMAIN, @@ -108,17 +111,18 @@ async def async_setup_entry( readings=readings, device_info=device_info, inverter_device_id=inverter.id ) - # A block that stayed silent while probing is taken for absent, so a meter - # that timed out cannot be told from one that was unwired. Its device stays - # where it is until the device says for itself that it is gone. - if SUBSYSTEM_METERS in solaredge.unresponsive_blocks: + if silent := solaredge.unresponsive_blocks & { + SUBSYSTEM_BATTERIES, + SUBSYSTEM_METERS, + }: LOGGER.warning( - "%s did not answer for its meters while probing, so their entities" - " are missing until it does; reloading probes again", + "%s did not answer for its %s while probing, so their entities are" + " missing until it does; reloading probes again", entry.title, + " and ".join(sorted(silent)), ) - else: - _async_remove_stale_devices(hass, entry, solaredge, serial_number) + + _async_remove_stale_devices(hass, entry, solaredge, serial_number, silent=silent) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -130,18 +134,41 @@ def _async_remove_stale_devices( entry: SolarEdgeModbusConfigEntry, solaredge: SolarEdge, serial_number: str, + *, + silent: AbstractSet[str], ) -> None: - """Remove devices for meters no longer attached to the inverter.""" + """Remove devices for meters and batteries no longer attached. + + A block that stayed silent while probing is taken for absent, and silence + is not the inverter saying its hardware is gone. Devices of that kind stay + where they are; the kind that did answer is still cleaned up. + """ current = {(DOMAIN, serial_number)} current.update( - (DOMAIN, f"{serial_number}_meter_{meter_identity(meter, index)}") + (DOMAIN, f"{serial_number}_meter_{attachment_identity(meter, index)}") for index, meter in enumerate(solaredge.meters, 1) ) + current.update( + (DOMAIN, f"{serial_number}_battery_{attachment_identity(battery, index)}") + for index, battery in enumerate(solaredge.batteries, 1) + ) + + unproven = tuple( + f"{serial_number}_{kind}_" + for block, kind in ( + (SUBSYSTEM_BATTERIES, "battery"), + (SUBSYSTEM_METERS, "meter"), + ) + if block in silent + ) device_registry = dr.async_get(hass) for device in dr.async_entries_for_config_entry(device_registry, entry.entry_id): - if not current.intersection(device.identifiers): - device_registry.async_remove_device(device.id) + if current.intersection(device.identifiers): + continue + if any(identifier.startswith(unproven) for _, identifier in device.identifiers): + continue + device_registry.async_remove_device(device.id) async def async_unload_entry( diff --git a/homeassistant/components/solaredge_modbus/const.py b/homeassistant/components/solaredge_modbus/const.py index 8339aa22eb38..c7eaa5bba73f 100644 --- a/homeassistant/components/solaredge_modbus/const.py +++ b/homeassistant/components/solaredge_modbus/const.py @@ -23,7 +23,8 @@ DEFAULT_UNIT_ID: Final = 1 SUBSYSTEM_COMMON: Final = "common" SUBSYSTEM_INVERTER: Final = "inverter" -# How the library names the meter block it probes for. +# How the library names the blocks it probes for. +SUBSYSTEM_BATTERIES: Final = "batteries" SUBSYSTEM_METERS: Final = "meters" # Local Modbus is cheap to read and PV production moves fast. diff --git a/homeassistant/components/solaredge_modbus/entity.py b/homeassistant/components/solaredge_modbus/entity.py index 132306d6b33f..4f527397bc39 100644 --- a/homeassistant/components/solaredge_modbus/entity.py +++ b/homeassistant/components/solaredge_modbus/entity.py @@ -1,14 +1,14 @@ """Base entities for the SolarEdge Modbus integration. -Each meter attached to the inverter is its own sub-device, linked to the -inverter as its parent; everything else belongs to the inverter. All +Each meter and battery attached to the inverter is its own sub-device, linked +to the inverter as its parent; everything else belongs to the inverter. All identities derive from the inverter's serial number, which the config flow stores as the config entry unique ID. """ from typing import TYPE_CHECKING, override -from solaredged import Meter, SolarEdge +from solaredged import Battery, Meter, SolarEdge from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity import EntityDescription @@ -41,15 +41,16 @@ def inverter_name(model: str | None) -> str: return f"SolarEdge {commercial}" -def meter_identity(meter: Meter, index: int) -> str: - """Return what tells a meter apart from the next one in its place. +def attachment_identity(component: Battery | Meter, index: int) -> str: + """Return what tells an attached device apart from the next in its place. - A meter that reports a serial number is known by it, so replacing one is a + One that reports a serial number is known by it, so replacing it is a different device rather than the same slot with other numbers in it. Not - every meter reports one, and then the slot it is wired to is all there is. - That fallback says so, since a bare number could be a serial itself. + every meter or battery reports one, and then the slot it is wired to is all + there is. That fallback says so, since a bare number could be a serial + itself. """ - return meter.serial_number or f"slot_{index}" + return component.serial_number or f"slot_{index}" def inverter_device_info(solaredge: SolarEdge, serial_number: str) -> DeviceInfo: @@ -129,7 +130,7 @@ class SolarEdgeModbusMeterEntity(SolarEdgeModbusEntity): ) -> None: """Initialize a SolarEdge Modbus meter entity.""" meter = entry.runtime_data.solaredge.meters[index - 1] - identity = meter_identity(meter, index) + identity = attachment_identity(meter, index) super().__init__( entry=entry, subsystem=f"meters[{index - 1}]", @@ -148,3 +149,37 @@ class SolarEdgeModbusMeterEntity(SolarEdgeModbusEntity): serial_number=meter.serial_number or None, via_device_id=entry.runtime_data.inverter_device_id, ) + + +class SolarEdgeModbusBatteryEntity(SolarEdgeModbusEntity): + """Defines a SolarEdge Modbus entity on a battery sub-device.""" + + def __init__( + self, + *, + entry: SolarEdgeModbusConfigEntry, + description: EntityDescription, + index: int, + ) -> None: + """Initialize a SolarEdge Modbus battery entity.""" + battery = entry.runtime_data.solaredge.batteries[index - 1] + identity = attachment_identity(battery, index) + super().__init__( + entry=entry, + subsystem=f"batteries[{index - 1}]", + description=description, + key_prefix=f"battery_{identity}_", + ) + self._index = index + + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, f"{self._serial_number}_battery_{identity}")}, + manufacturer=battery.manufacturer or "SolarEdge", + # A battery names itself the same way a meter does, with a part + # number rather than something it is sold under. + model_id=battery.model or None, + name=f"Battery {index}", + sw_version=battery.version or None, + serial_number=battery.serial_number or None, + via_device_id=entry.runtime_data.inverter_device_id, + ) diff --git a/homeassistant/components/solaredge_modbus/icons.json b/homeassistant/components/solaredge_modbus/icons.json index 49cff864afa2..f5c90440354c 100644 --- a/homeassistant/components/solaredge_modbus/icons.json +++ b/homeassistant/components/solaredge_modbus/icons.json @@ -1,8 +1,14 @@ { "entity": { "sensor": { + "battery_status": { + "default": "mdi:home-battery" + }, "inverter_status": { "default": "mdi:solar-power" + }, + "state_of_health": { + "default": "mdi:battery-heart-variant" } } } diff --git a/homeassistant/components/solaredge_modbus/sensor.py b/homeassistant/components/solaredge_modbus/sensor.py index 0f9ca9e4b974..727ecc8d6e7f 100644 --- a/homeassistant/components/solaredge_modbus/sensor.py +++ b/homeassistant/components/solaredge_modbus/sensor.py @@ -4,7 +4,14 @@ from collections.abc import Callable from dataclasses import dataclass from typing import override -from solaredged import Inverter, InverterStatus, Meter, SunSpecDID +from solaredged import ( + Battery, + BatteryStatus, + Inverter, + InverterStatus, + Meter, + SunSpecDID, +) from homeassistant.components.sensor import ( RestoreSensor, @@ -31,7 +38,11 @@ from homeassistant.helpers.typing import StateType from .const import LOGGER from .coordinator import SolarEdgeModbusConfigEntry -from .entity import SolarEdgeModbusInverterEntity, SolarEdgeModbusMeterEntity +from .entity import ( + SolarEdgeModbusBatteryEntity, + SolarEdgeModbusInverterEntity, + SolarEdgeModbusMeterEntity, +) PARALLEL_UPDATES = 0 @@ -292,7 +303,7 @@ INVERTER_SENSORS: tuple[SolarEdgeModbusSensorEntityDescription[Inverter], ...] = device_class=SensorDeviceClass.ENUM, options=[status.name.lower() for status in InverterStatus], value_fn=lambda inverter: ( - inverter.status.name.lower() if inverter.status else None + inverter.status.name.lower() if inverter.status is not None else None ), ), ) @@ -591,6 +602,178 @@ METER_SENSORS: tuple[SolarEdgeModbusSensorEntityDescription[Meter], ...] = ( ) +BATTERY_SENSORS: tuple[SolarEdgeModbusSensorEntityDescription[Battery], ...] = ( + SolarEdgeModbusSensorEntityDescription( + key="dc_power", + translation_key="dc_power", + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda battery: battery.dc_power, + ), + SolarEdgeModbusSensorEntityDescription( + key="energy_exported", + translation_key="energy_exported", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, + value_fn=lambda battery: battery.energy_exported, + ), + SolarEdgeModbusSensorEntityDescription( + key="energy_imported", + translation_key="energy_imported", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, + value_fn=lambda battery: battery.energy_imported, + ), + SolarEdgeModbusSensorEntityDescription( + key="state_of_energy", + translation_key="state_of_energy", + device_class=SensorDeviceClass.BATTERY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=0, + value_fn=lambda battery: battery.state_of_energy, + ), + SolarEdgeModbusSensorEntityDescription( + key="state_of_health", + translation_key="state_of_health", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + suggested_display_precision=0, + value_fn=lambda battery: battery.state_of_health, + ), + SolarEdgeModbusSensorEntityDescription( + key="temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + suggested_display_precision=1, + value_fn=lambda battery: battery.temperature_average, + ), + SolarEdgeModbusSensorEntityDescription( + key="energy_available", + translation_key="energy_available", + device_class=SensorDeviceClass.ENERGY_STORAGE, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=2, + value_fn=lambda battery: battery.energy_available, + ), + SolarEdgeModbusSensorEntityDescription( + key="energy_max", + translation_key="energy_max", + device_class=SensorDeviceClass.ENERGY_STORAGE, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + suggested_display_precision=2, + value_fn=lambda battery: battery.energy_max, + ), + SolarEdgeModbusSensorEntityDescription( + key="rated_energy", + translation_key="rated_energy", + device_class=SensorDeviceClass.ENERGY_STORAGE, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + suggested_display_precision=2, + value_fn=lambda battery: battery.rated_energy, + ), + SolarEdgeModbusSensorEntityDescription( + key="dc_voltage", + translation_key="dc_voltage", + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + entity_category=EntityCategory.DIAGNOSTIC, + suggested_display_precision=1, + value_fn=lambda battery: battery.dc_voltage, + ), + SolarEdgeModbusSensorEntityDescription( + key="dc_current", + translation_key="dc_current", + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + suggested_display_precision=2, + value_fn=lambda battery: battery.dc_current, + ), + SolarEdgeModbusSensorEntityDescription( + key="max_charge_power", + translation_key="max_charge_power", + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda battery: battery.max_charge_power, + ), + SolarEdgeModbusSensorEntityDescription( + key="max_discharge_power", + translation_key="max_discharge_power", + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda battery: battery.max_discharge_power, + ), + SolarEdgeModbusSensorEntityDescription( + key="max_charge_peak_power", + translation_key="max_charge_peak_power", + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda battery: battery.max_charge_peak_power, + ), + SolarEdgeModbusSensorEntityDescription( + key="max_discharge_peak_power", + translation_key="max_discharge_peak_power", + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda battery: battery.max_discharge_peak_power, + ), + SolarEdgeModbusSensorEntityDescription( + key="temperature_max", + translation_key="temperature_max", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + suggested_display_precision=1, + value_fn=lambda battery: battery.temperature_max, + ), + SolarEdgeModbusSensorEntityDescription( + key="status", + translation_key="battery_status", + device_class=SensorDeviceClass.ENUM, + options=[status.name.lower() for status in BatteryStatus], + value_fn=lambda battery: ( + battery.status.name.lower() if battery.status is not None else None + ), + ), +) + + def _inverter_sensor( entry: SolarEdgeModbusConfigEntry, description: SolarEdgeModbusSensorEntityDescription[Inverter], @@ -618,6 +801,21 @@ def _meter_sensor( ) +def _battery_sensor( + entry: SolarEdgeModbusConfigEntry, + description: SolarEdgeModbusSensorEntityDescription[Battery], + index: int, +) -> SensorEntity: + """Build a battery sensor, monotonic where its state class asks for it.""" + if description.state_class is SensorStateClass.TOTAL_INCREASING: + return SolarEdgeModbusBatteryEnergySensorEntity( + entry=entry, description=description, index=index + ) + return SolarEdgeModbusBatterySensorEntity( + entry=entry, description=description, index=index + ) + + async def async_setup_entry( hass: HomeAssistant, entry: SolarEdgeModbusConfigEntry, @@ -637,6 +835,12 @@ async def async_setup_entry( for description in METER_SENSORS if description.exists_fn(meter) ) + entities.extend( + _battery_sensor(entry, description, index) + for index, battery in enumerate(solaredge.batteries, 1) + for description in BATTERY_SENSORS + if description.exists_fn(battery) + ) async_add_entities(entities) @@ -728,3 +932,23 @@ class SolarEdgeModbusMeterEnergySensorEntity( SolarEdgeModbusEnergySensorEntity, SolarEdgeModbusMeterSensorEntity ): """Defines a monotonic SolarEdge Modbus meter energy sensor entity.""" + + +class SolarEdgeModbusBatterySensorEntity(SolarEdgeModbusBatteryEntity, SensorEntity): + """Defines a SolarEdge Modbus battery sensor entity.""" + + entity_description: SolarEdgeModbusSensorEntityDescription[Battery] + + @property + @override + def native_value(self) -> StateType: + """Return the sensor value.""" + return self.entity_description.value_fn( + self.coordinator.solaredge.batteries[self._index - 1] + ) + + +class SolarEdgeModbusBatteryEnergySensorEntity( + SolarEdgeModbusEnergySensorEntity, SolarEdgeModbusBatterySensorEntity +): + """Defines a monotonic SolarEdge Modbus battery energy sensor entity.""" diff --git a/homeassistant/components/solaredge_modbus/strings.json b/homeassistant/components/solaredge_modbus/strings.json index 246f7afc4f77..dcb652f9cf3e 100644 --- a/homeassistant/components/solaredge_modbus/strings.json +++ b/homeassistant/components/solaredge_modbus/strings.json @@ -101,6 +101,20 @@ }, "entity": { "sensor": { + "battery_status": { + "name": "Status", + "state": { + "charge": "Charging", + "discharge": "Discharging", + "fault": "Fault", + "idle": "[%key:common::state::idle%]", + "init": "Initializing", + "off": "[%key:common::state::off%]", + "power_saving": "Power saving", + "preserve_charge": "Preserving charge", + "standby": "[%key:common::state::standby%]" + } + }, "current_phase_a": { "name": "Current phase A" }, @@ -119,6 +133,9 @@ "dc_voltage": { "name": "DC voltage" }, + "energy_available": { + "name": "Available energy" + }, "energy_exported": { "name": "Energy exported" }, @@ -143,6 +160,9 @@ "energy_imported_phase_c": { "name": "Energy imported phase C" }, + "energy_max": { + "name": "Usable capacity" + }, "inverter_status": { "name": "Status", "state": { @@ -156,6 +176,18 @@ "throttled": "Throttled" } }, + "max_charge_peak_power": { + "name": "Maximum charge peak power" + }, + "max_charge_power": { + "name": "Maximum charge power" + }, + "max_discharge_peak_power": { + "name": "Maximum discharge peak power" + }, + "max_discharge_power": { + "name": "Maximum discharge power" + }, "power_phase_a": { "name": "Power phase A" }, @@ -165,6 +197,18 @@ "power_phase_c": { "name": "Power phase C" }, + "rated_energy": { + "name": "Rated energy" + }, + "state_of_energy": { + "name": "State of energy" + }, + "state_of_health": { + "name": "State of health" + }, + "temperature_max": { + "name": "Maximum temperature" + }, "voltage_phase_ab": { "name": "Voltage phase A-B" }, diff --git a/tests/components/solaredge_modbus/conftest.py b/tests/components/solaredge_modbus/conftest.py index 64071d6d0abe..f0d53108c470 100644 --- a/tests/components/solaredge_modbus/conftest.py +++ b/tests/components/solaredge_modbus/conftest.py @@ -30,6 +30,7 @@ PORT = 1502 UNIT_ID = 1 SERIAL_NUMBER = "7E123ABC" METER_SERIAL_NUMBER = "7E4A11C2" +BATTERY_SERIAL_NUMBERS = ("7E7C33E4", "7E8D44F5") # Where a meter's block starts, how far the next one sits, and where in it the # serial number lives, as SunSpec lays them out. @@ -37,6 +38,12 @@ METER_BASE = 40121 METER_STRIDE = 174 METER_SERIAL_BASE = 40171 +# The same for the batteries, whose blocks sit at fixed offsets rather than a +# stride, with the rated-energy register the probe counts them by. +BATTERY_SERIAL_BASE = 57648 +BATTERY_RATED_ENERGY = 57666 +BATTERY_OFFSETS = (0, 256, 768) + def tcp_data(unit_id: int = UNIT_ID) -> dict[str, Any]: """Config entry data for an inverter reached over Modbus TCP.""" diff --git a/tests/components/solaredge_modbus/fixtures/se10000h.json b/tests/components/solaredge_modbus/fixtures/se10000h.json index a6e6d9a1afe0..fb3f72d4151d 100644 --- a/tests/components/solaredge_modbus/fixtures/se10000h.json +++ b/tests/components/solaredge_modbus/fixtures/se10000h.json @@ -177,6 +177,70 @@ "57359": 17970, "57360": 8192, "57361": 17970, + "57600": 21359, + "57601": 27745, + "57602": 29253, + "57603": 25703, + "57604": 25856, + "57605": 0, + "57606": 0, + "57607": 0, + "57608": 0, + "57609": 0, + "57610": 0, + "57611": 0, + "57612": 0, + "57613": 0, + "57614": 0, + "57615": 0, + "57616": 21317, + "57617": 11586, + "57618": 16724, + "57619": 11572, + "57620": 14422, + "57621": 11569, + "57622": 12363, + "57623": 22344, + "57624": 0, + "57625": 0, + "57626": 0, + "57627": 0, + "57628": 0, + "57629": 0, + "57630": 0, + "57631": 0, + "57632": 12590, + "57633": 12846, + "57634": 13056, + "57635": 0, + "57636": 0, + "57637": 0, + "57638": 0, + "57639": 0, + "57640": 0, + "57641": 0, + "57642": 0, + "57643": 0, + "57644": 0, + "57645": 0, + "57646": 0, + "57647": 0, + "57648": 14149, + "57649": 14147, + "57650": 13107, + "57651": 17716, + "57652": 0, + "57653": 0, + "57654": 0, + "57655": 0, + "57656": 0, + "57657": 0, + "57658": 0, + "57659": 0, + "57660": 0, + "57661": 0, + "57662": 0, + "57663": 0, "57666": 36864, "57667": 17943, "57668": 16384, @@ -207,6 +271,70 @@ "57733": 17095, "57734": 6, "57735": 0, + "57856": 21359, + "57857": 27745, + "57858": 29253, + "57859": 25703, + "57860": 25856, + "57861": 0, + "57862": 0, + "57863": 0, + "57864": 0, + "57865": 0, + "57866": 0, + "57867": 0, + "57868": 0, + "57869": 0, + "57870": 0, + "57871": 0, + "57872": 21317, + "57873": 11586, + "57874": 16724, + "57875": 11572, + "57876": 14422, + "57877": 11569, + "57878": 12363, + "57879": 22344, + "57880": 0, + "57881": 0, + "57882": 0, + "57883": 0, + "57884": 0, + "57885": 0, + "57886": 0, + "57887": 0, + "57888": 12590, + "57889": 12846, + "57890": 13056, + "57891": 0, + "57892": 0, + "57893": 0, + "57894": 0, + "57895": 0, + "57896": 0, + "57897": 0, + "57898": 0, + "57899": 0, + "57900": 0, + "57901": 0, + "57902": 0, + "57903": 0, + "57904": 14149, + "57905": 14404, + "57906": 13364, + "57907": 17973, + "57908": 0, + "57909": 0, + "57910": 0, + "57911": 0, + "57912": 0, + "57913": 0, + "57914": 0, + "57915": 0, + "57916": 0, + "57917": 0, + "57918": 0, + "57919": 0, "57922": 36864, "57923": 17943, "57964": 58463, diff --git a/tests/components/solaredge_modbus/snapshots/test_diagnostics.ambr b/tests/components/solaredge_modbus/snapshots/test_diagnostics.ambr index e086af93533c..c96d5cbea1b7 100644 --- a/tests/components/solaredge_modbus/snapshots/test_diagnostics.ambr +++ b/tests/components/solaredge_modbus/snapshots/test_diagnostics.ambr @@ -10,20 +10,20 @@ 'energy_exported': 0, 'energy_imported': 0, 'energy_max': 9700.0, - 'manufacturer': '', + 'manufacturer': 'SolarEdge', 'max_charge_peak_power': 6000.0, 'max_charge_power': 5000.0, 'max_discharge_peak_power': 6100.0, 'max_discharge_power': 5100.0, - 'model': '', + 'model': 'SE-BAT-48V-10KWH', 'rated_energy': 9700.0, - 'serial_number': '', + 'serial_number': '**REDACTED**', 'state_of_energy': 99.94781494140625, 'state_of_health': 100.0, 'status': 6, 'temperature_average': 23.608806610107422, 'temperature_max': 24.5, - 'version': '', + 'version': '1.2.3', }), dict({ 'dc_current': -0.0, @@ -33,20 +33,20 @@ 'energy_exported': 0, 'energy_imported': 0, 'energy_max': 9700.0, - 'manufacturer': '', + 'manufacturer': 'SolarEdge', 'max_charge_peak_power': 0.0, 'max_charge_power': 0.0, 'max_discharge_peak_power': 0.0, 'max_discharge_power': 0.0, - 'model': '', + 'model': 'SE-BAT-48V-10KWH', 'rated_energy': 9700.0, - 'serial_number': '', + 'serial_number': '**REDACTED**', 'state_of_energy': 99.2170181274414, 'state_of_health': 100.0, 'status': 6, 'temperature_average': 23.861509323120117, 'temperature_max': 0.0, - 'version': '', + 'version': '1.2.3', }), ]), 'common': dict({ diff --git a/tests/components/solaredge_modbus/snapshots/test_sensor.ambr b/tests/components/solaredge_modbus/snapshots/test_sensor.ambr index a240a8825adc..25fe61c87e31 100644 --- a/tests/components/solaredge_modbus/snapshots/test_sensor.ambr +++ b/tests/components/solaredge_modbus/snapshots/test_sensor.ambr @@ -1,4 +1,2036 @@ # serializer version: 1 +# name: test_sensors[sensor.battery_1_available_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_1_available_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Available energy', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Available energy', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_available', + 'unique_id': '7E123ABC_battery_7E7C33E4_energy_available', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_available_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy_storage', + : 'Battery 1 Available energy', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_available_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '9.581759765625', + }) +# --- +# name: test_sensors[sensor.battery_1_dc_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_dc_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'DC current', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DC current', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_current', + 'unique_id': '7E123ABC_battery_7E7C33E4_dc_current', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_dc_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'current', + : 'Battery 1 DC current', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_dc_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-0.0', + }) +# --- +# name: test_sensors[sensor.battery_1_dc_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_1_dc_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'DC power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DC power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_power', + 'unique_id': '7E123ABC_battery_7E7C33E4_dc_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_dc_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 1 DC power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_dc_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_1_dc_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_dc_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'DC voltage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DC voltage', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_voltage', + 'unique_id': '7E123ABC_battery_7E7C33E4_dc_voltage', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_dc_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'voltage', + : 'Battery 1 DC voltage', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_dc_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '403.846710205078', + }) +# --- +# name: test_sensors[sensor.battery_1_energy_exported-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_1_energy_exported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Energy exported', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Energy exported', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_exported', + 'unique_id': '7E123ABC_battery_7E7C33E4_energy_exported', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_energy_exported-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy', + : 'Battery 1 Energy exported', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_energy_exported', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_1_energy_imported-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_1_energy_imported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Energy imported', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Energy imported', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_imported', + 'unique_id': '7E123ABC_battery_7E7C33E4_energy_imported', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_energy_imported-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy', + : 'Battery 1 Energy imported', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_energy_imported', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_charge_peak_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_maximum_charge_peak_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum charge peak power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum charge peak power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_charge_peak_power', + 'unique_id': '7E123ABC_battery_7E7C33E4_max_charge_peak_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_charge_peak_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 1 Maximum charge peak power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_maximum_charge_peak_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '6000.0', + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_charge_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_maximum_charge_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum charge power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum charge power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_charge_power', + 'unique_id': '7E123ABC_battery_7E7C33E4_max_charge_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_charge_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 1 Maximum charge power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_maximum_charge_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '5000.0', + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_discharge_peak_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_maximum_discharge_peak_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum discharge peak power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum discharge peak power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_discharge_peak_power', + 'unique_id': '7E123ABC_battery_7E7C33E4_max_discharge_peak_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_discharge_peak_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 1 Maximum discharge peak power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_maximum_discharge_peak_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '6100.0', + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_discharge_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_maximum_discharge_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum discharge power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum discharge power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_discharge_power', + 'unique_id': '7E123ABC_battery_7E7C33E4_max_discharge_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_discharge_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 1 Maximum discharge power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_maximum_discharge_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '5100.0', + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_maximum_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum temperature', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum temperature', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'temperature_max', + 'unique_id': '7E123ABC_battery_7E7C33E4_temperature_max', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_maximum_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'temperature', + : 'Battery 1 Maximum temperature', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_maximum_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '24.5', + }) +# --- +# name: test_sensors[sensor.battery_1_rated_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_rated_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Rated energy', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Rated energy', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rated_energy', + 'unique_id': '7E123ABC_battery_7E7C33E4_rated_energy', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_rated_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy_storage', + : 'Battery 1 Rated energy', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_rated_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '9.7', + }) +# --- +# name: test_sensors[sensor.battery_1_state_of_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_1_state_of_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'State of energy', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'State of energy', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'state_of_energy', + 'unique_id': '7E123ABC_battery_7E7C33E4_state_of_energy', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensors[sensor.battery_1_state_of_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'battery', + : 'Battery 1 State of energy', + : , + : '%', + }), + 'context': , + 'entity_id': 'sensor.battery_1_state_of_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '99.9478149414062', + }) +# --- +# name: test_sensors[sensor.battery_1_state_of_health-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_state_of_health', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'State of health', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'State of health', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'state_of_health', + 'unique_id': '7E123ABC_battery_7E7C33E4_state_of_health', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensors[sensor.battery_1_state_of_health-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Battery 1 State of health', + : , + : '%', + }), + 'context': , + 'entity_id': 'sensor.battery_1_state_of_health', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '100.0', + }) +# --- +# name: test_sensors[sensor.battery_1_status-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : list([ + 'off', + 'standby', + 'init', + 'charge', + 'discharge', + 'fault', + 'preserve_charge', + 'idle', + 'power_saving', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_1_status', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Status', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Status', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'battery_status', + 'unique_id': '7E123ABC_battery_7E7C33E4_status', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[sensor.battery_1_status-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'enum', + : 'Battery 1 Status', + : list([ + 'off', + 'standby', + 'init', + 'charge', + 'discharge', + 'fault', + 'preserve_charge', + 'idle', + 'power_saving', + ]), + }), + 'context': , + 'entity_id': 'sensor.battery_1_status', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'preserve_charge', + }) +# --- +# name: test_sensors[sensor.battery_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Temperature', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '7E123ABC_battery_7E7C33E4_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'temperature', + : 'Battery 1 Temperature', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '23.6088066101074', + }) +# --- +# name: test_sensors[sensor.battery_1_usable_capacity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_1_usable_capacity', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Usable capacity', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Usable capacity', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_max', + 'unique_id': '7E123ABC_battery_7E7C33E4_energy_max', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_1_usable_capacity-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy_storage', + : 'Battery 1 Usable capacity', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_1_usable_capacity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '9.7', + }) +# --- +# name: test_sensors[sensor.battery_2_available_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_2_available_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Available energy', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Available energy', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_available', + 'unique_id': '7E123ABC_battery_7E8D44F5_energy_available', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_available_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy_storage', + : 'Battery 2 Available energy', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_available_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '9.7065595703125', + }) +# --- +# name: test_sensors[sensor.battery_2_dc_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_dc_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'DC current', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DC current', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_current', + 'unique_id': '7E123ABC_battery_7E8D44F5_dc_current', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_dc_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'current', + : 'Battery 2 DC current', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_dc_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_dc_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_2_dc_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'DC power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DC power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_power', + 'unique_id': '7E123ABC_battery_7E8D44F5_dc_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_dc_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 2 DC power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_dc_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_dc_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_dc_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'DC voltage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DC voltage', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_voltage', + 'unique_id': '7E123ABC_battery_7E8D44F5_dc_voltage', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_dc_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'voltage', + : 'Battery 2 DC voltage', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_dc_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '404.802947998047', + }) +# --- +# name: test_sensors[sensor.battery_2_energy_exported-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_2_energy_exported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Energy exported', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Energy exported', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_exported', + 'unique_id': '7E123ABC_battery_7E8D44F5_energy_exported', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_energy_exported-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy', + : 'Battery 2 Energy exported', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_energy_exported', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_energy_imported-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_2_energy_imported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Energy imported', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Energy imported', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_imported', + 'unique_id': '7E123ABC_battery_7E8D44F5_energy_imported', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_energy_imported-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy', + : 'Battery 2 Energy imported', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_energy_imported', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_charge_peak_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_maximum_charge_peak_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum charge peak power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum charge peak power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_charge_peak_power', + 'unique_id': '7E123ABC_battery_7E8D44F5_max_charge_peak_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_charge_peak_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 2 Maximum charge peak power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_maximum_charge_peak_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_charge_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_maximum_charge_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum charge power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum charge power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_charge_power', + 'unique_id': '7E123ABC_battery_7E8D44F5_max_charge_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_charge_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 2 Maximum charge power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_maximum_charge_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_discharge_peak_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_maximum_discharge_peak_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum discharge peak power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum discharge peak power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_discharge_peak_power', + 'unique_id': '7E123ABC_battery_7E8D44F5_max_discharge_peak_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_discharge_peak_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 2 Maximum discharge peak power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_maximum_discharge_peak_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_discharge_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_maximum_discharge_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum discharge power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum discharge power', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_discharge_power', + 'unique_id': '7E123ABC_battery_7E8D44F5_max_discharge_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_discharge_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'power', + : 'Battery 2 Maximum discharge power', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_maximum_discharge_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_maximum_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum temperature', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum temperature', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'temperature_max', + 'unique_id': '7E123ABC_battery_7E8D44F5_temperature_max', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_maximum_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'temperature', + : 'Battery 2 Maximum temperature', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_maximum_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[sensor.battery_2_rated_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_rated_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Rated energy', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Rated energy', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rated_energy', + 'unique_id': '7E123ABC_battery_7E8D44F5_rated_energy', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_rated_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy_storage', + : 'Battery 2 Rated energy', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_rated_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '9.7', + }) +# --- +# name: test_sensors[sensor.battery_2_state_of_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_2_state_of_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'State of energy', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'State of energy', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'state_of_energy', + 'unique_id': '7E123ABC_battery_7E8D44F5_state_of_energy', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensors[sensor.battery_2_state_of_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'battery', + : 'Battery 2 State of energy', + : , + : '%', + }), + 'context': , + 'entity_id': 'sensor.battery_2_state_of_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '99.2170181274414', + }) +# --- +# name: test_sensors[sensor.battery_2_state_of_health-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_state_of_health', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'State of health', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'State of health', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'state_of_health', + 'unique_id': '7E123ABC_battery_7E8D44F5_state_of_health', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensors[sensor.battery_2_state_of_health-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Battery 2 State of health', + : , + : '%', + }), + 'context': , + 'entity_id': 'sensor.battery_2_state_of_health', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '100.0', + }) +# --- +# name: test_sensors[sensor.battery_2_status-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : list([ + 'off', + 'standby', + 'init', + 'charge', + 'discharge', + 'fault', + 'preserve_charge', + 'idle', + 'power_saving', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.battery_2_status', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Status', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Status', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'battery_status', + 'unique_id': '7E123ABC_battery_7E8D44F5_status', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[sensor.battery_2_status-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'enum', + : 'Battery 2 Status', + : list([ + 'off', + 'standby', + 'init', + 'charge', + 'discharge', + 'fault', + 'preserve_charge', + 'idle', + 'power_saving', + ]), + }), + 'context': , + 'entity_id': 'sensor.battery_2_status', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'preserve_charge', + }) +# --- +# name: test_sensors[sensor.battery_2_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Temperature', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '7E123ABC_battery_7E8D44F5_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'temperature', + : 'Battery 2 Temperature', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '23.8615093231201', + }) +# --- +# name: test_sensors[sensor.battery_2_usable_capacity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.battery_2_usable_capacity', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Usable capacity', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Usable capacity', + 'platform': 'solaredge_modbus', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_max', + 'unique_id': '7E123ABC_battery_7E8D44F5_energy_max', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.battery_2_usable_capacity-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy_storage', + : 'Battery 2 Usable capacity', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.battery_2_usable_capacity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '9.7', + }) +# --- # name: test_sensors[sensor.meter_1_apparent_power-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ diff --git a/tests/components/solaredge_modbus/test_init.py b/tests/components/solaredge_modbus/test_init.py index 3b052d34a849..0ee935e5c212 100644 --- a/tests/components/solaredge_modbus/test_init.py +++ b/tests/components/solaredge_modbus/test_init.py @@ -18,7 +18,15 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry as dr -from .conftest import METER_SERIAL_NUMBER, SERIAL_NUMBER, async_seed_unit, tcp_data +from .conftest import ( + BATTERY_RATED_ENERGY, + BATTERY_SERIAL_BASE, + BATTERY_SERIAL_NUMBERS, + METER_SERIAL_NUMBER, + SERIAL_NUMBER, + async_seed_unit, + tcp_data, +) from tests.common import MockConfigEntry, async_fire_time_changed @@ -27,6 +35,9 @@ POWER_ENTITY = "sensor.solaredge_se10000h_power" # An address inside the inverter's read, to make that read fail. INVERTER_REGISTER = 40069 +# The register the probe counts meters by. +METER_MODEL_REGISTER = 40188 + async def _setup(hass: HomeAssistant, entry: MockConfigEntry) -> None: entry.add_to_hass(hass) @@ -250,6 +261,157 @@ async def test_replaced_meter_is_a_new_device( ) +async def test_batteries_are_sub_devices_of_the_inverter( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + mock_config_entry: MockConfigEntry, +) -> None: + """Each battery is hardware of its own, hanging off the inverter.""" + await _setup(hass, mock_config_entry) + + inverter = device_registry.async_get_device_by_identifier( + (DOMAIN, SERIAL_NUMBER), mock_config_entry.entry_id + ) + assert inverter is not None + + for index, serial_number in enumerate(BATTERY_SERIAL_NUMBERS, 1): + battery = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{SERIAL_NUMBER}_battery_{serial_number}"), + mock_config_entry.entry_id, + ) + assert battery is not None + assert battery.via_device_id == inverter.id + assert battery.name == f"Battery {index}" + assert battery.model_id == "SE-BAT-48V-10KWH" + assert battery.serial_number == serial_number + + +async def test_battery_that_left_the_installation_is_removed( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + mock_config_entry: MockConfigEntry, + mock_modbus_unit: MockModbusUnit, +) -> None: + """A battery taken out does not linger as a device. + + The inverter refusing its block is the device saying it is gone, where + silence would only mean it did not answer this time. + """ + await _setup(hass, mock_config_entry) + + identifiers = [ + (DOMAIN, f"{SERIAL_NUMBER}_battery_{serial_number}") + for serial_number in BATTERY_SERIAL_NUMBERS + ] + assert all( + device_registry.async_get_device_by_identifier( + identifier, mock_config_entry.entry_id + ) + is not None + for identifier in identifiers + ) + + mock_modbus_unit.fail_read(BATTERY_RATED_ENERGY, IllegalDataAddressError()) + + await hass.config_entries.async_reload(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert all( + device_registry.async_get_device_by_identifier( + identifier, mock_config_entry.entry_id + ) + is None + for identifier in identifiers + ) + + +async def test_battery_that_did_not_answer_the_probe_is_kept( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + mock_config_entry: MockConfigEntry, + mock_modbus_unit: MockModbusUnit, +) -> None: + """Silence while probing is not proof that a battery is gone.""" + await _setup(hass, mock_config_entry) + + identifier = (DOMAIN, f"{SERIAL_NUMBER}_battery_{BATTERY_SERIAL_NUMBERS[0]}") + assert ( + device_registry.async_get_device_by_identifier( + identifier, mock_config_entry.entry_id + ) + is not None + ) + + mock_modbus_unit.fail_read(BATTERY_RATED_ENERGY, ModbusTimeoutError("timed out")) + + await hass.config_entries.async_reload(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert ( + device_registry.async_get_device_by_identifier( + identifier, mock_config_entry.entry_id + ) + is not None + ) + + +async def test_silence_about_one_kind_does_not_shield_the_other( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + mock_config_entry: MockConfigEntry, + mock_modbus_unit: MockModbusUnit, +) -> None: + """A meter that is really gone goes, even when the batteries kept quiet. + + Silence about one kind of attached hardware says nothing about the other, + and holding on to everything would leave a removed meter behind for as long + as a battery is slow to answer. + """ + await _setup(hass, mock_config_entry) + + meter = (DOMAIN, f"{SERIAL_NUMBER}_meter_{METER_SERIAL_NUMBER}") + battery = (DOMAIN, f"{SERIAL_NUMBER}_battery_{BATTERY_SERIAL_NUMBERS[0]}") + + mock_modbus_unit.fail_read(BATTERY_RATED_ENERGY, ModbusTimeoutError("timed out")) + mock_modbus_unit.fail_read(METER_MODEL_REGISTER, IllegalDataAddressError()) + + await hass.config_entries.async_reload(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert ( + device_registry.async_get_device_by_identifier( + meter, mock_config_entry.entry_id + ) + is None + ) + assert ( + device_registry.async_get_device_by_identifier( + battery, mock_config_entry.entry_id + ) + is not None + ) + + +async def test_battery_without_a_serial_number_is_known_by_its_slot( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + mock_config_entry: MockConfigEntry, + mock_modbus_unit: MockModbusUnit, +) -> None: + """Not every battery names itself, and then its place on the inverter does.""" + mock_modbus_unit.holding.update( + dict.fromkeys(range(BATTERY_SERIAL_BASE, BATTERY_SERIAL_BASE + 16), 0) + ) + + await _setup(hass, mock_config_entry) + + battery = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{SERIAL_NUMBER}_battery_slot_1"), mock_config_entry.entry_id + ) + assert battery is not None + assert battery.serial_number is None + + async def test_single_late_answer_is_retried( hass: HomeAssistant, freezer: FrozenDateTimeFactory,