Add battery data to Autarco integration (#125924)

* Rename site to account_site

* Add battery service with entities

* Test UpdateFailed exception in coordinator

* Add battery data to diagnostics report

* Add TOTAL state_class where needed

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
Klaas Schoute 2024-09-20 12:53:15 +02:00 committed by GitHub
parent 76967e848d
commit 184580257d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 669 additions and 19 deletions

View File

@ -4,11 +4,19 @@ from __future__ import annotations
from typing import NamedTuple from typing import NamedTuple
from autarco import AccountSite, Autarco, Inverter, Solar from autarco import (
AccountSite,
Autarco,
AutarcoConnectionError,
Battery,
Inverter,
Site,
Solar,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN, LOGGER, SCAN_INTERVAL from .const import DOMAIN, LOGGER, SCAN_INTERVAL
@ -18,6 +26,8 @@ class AutarcoData(NamedTuple):
solar: Solar solar: Solar
inverters: dict[str, Inverter] inverters: dict[str, Inverter]
site: Site
battery: Battery | None
class AutarcoDataUpdateCoordinator(DataUpdateCoordinator[AutarcoData]): class AutarcoDataUpdateCoordinator(DataUpdateCoordinator[AutarcoData]):
@ -29,7 +39,7 @@ class AutarcoDataUpdateCoordinator(DataUpdateCoordinator[AutarcoData]):
self, self,
hass: HomeAssistant, hass: HomeAssistant,
client: Autarco, client: Autarco,
site: AccountSite, account_site: AccountSite,
) -> None: ) -> None:
"""Initialize global Autarco data updater.""" """Initialize global Autarco data updater."""
super().__init__( super().__init__(
@ -39,11 +49,22 @@ class AutarcoDataUpdateCoordinator(DataUpdateCoordinator[AutarcoData]):
update_interval=SCAN_INTERVAL, update_interval=SCAN_INTERVAL,
) )
self.client = client self.client = client
self.site = site self.account_site = account_site
async def _async_update_data(self) -> AutarcoData: async def _async_update_data(self) -> AutarcoData:
"""Fetch data from Autarco API.""" """Fetch data from Autarco API."""
battery = None
try:
site = await self.client.get_site(self.account_site.public_key)
solar = await self.client.get_solar(self.account_site.public_key)
inverters = await self.client.get_inverters(self.account_site.public_key)
if site.has_battery:
battery = await self.client.get_battery(self.account_site.public_key)
except AutarcoConnectionError as error:
raise UpdateFailed(error) from error
return AutarcoData( return AutarcoData(
solar=await self.client.get_solar(self.site.public_key), solar=solar,
inverters=await self.client.get_inverters(self.site.public_key), inverters=inverters,
site=site,
battery=battery,
) )

View File

@ -18,9 +18,9 @@ async def async_get_config_entry_diagnostics(
return { return {
"sites_data": [ "sites_data": [
{ {
"id": coordinator.site.site_id, "id": coordinator.account_site.site_id,
"name": coordinator.site.system_name, "name": coordinator.account_site.system_name,
"health": coordinator.site.health, "health": coordinator.account_site.health,
"solar": { "solar": {
"power_production": coordinator.data.solar.power_production, "power_production": coordinator.data.solar.power_production,
"energy_production_today": coordinator.data.solar.energy_production_today, "energy_production_today": coordinator.data.solar.energy_production_today,
@ -37,6 +37,23 @@ async def async_get_config_entry_diagnostics(
} }
for inverter in coordinator.data.inverters.values() for inverter in coordinator.data.inverters.values()
], ],
**(
{
"battery": {
"flow_now": coordinator.data.battery.flow_now,
"net_charged_now": coordinator.data.battery.net_charged_now,
"state_of_charge": coordinator.data.battery.state_of_charge,
"discharged_today": coordinator.data.battery.discharged_today,
"discharged_month": coordinator.data.battery.discharged_month,
"discharged_total": coordinator.data.battery.discharged_total,
"charged_today": coordinator.data.battery.charged_today,
"charged_month": coordinator.data.battery.charged_month,
"charged_total": coordinator.data.battery.charged_total,
}
}
if coordinator.data.battery is not None
else {}
),
} }
for coordinator in autarco_data for coordinator in autarco_data
], ],

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from autarco import Inverter, Solar from autarco import Battery, Inverter, Solar
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
SensorDeviceClass, SensorDeviceClass,
@ -13,7 +13,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.const import UnitOfEnergy, UnitOfPower from homeassistant.const import PERCENTAGE, UnitOfEnergy, UnitOfPower
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -25,6 +25,81 @@ from .const import DOMAIN
from .coordinator import AutarcoDataUpdateCoordinator from .coordinator import AutarcoDataUpdateCoordinator
@dataclass(frozen=True, kw_only=True)
class AutarcoBatterySensorEntityDescription(SensorEntityDescription):
"""Describes an Autarco sensor entity."""
value_fn: Callable[[Battery], StateType]
SENSORS_BATTERY: tuple[AutarcoBatterySensorEntityDescription, ...] = (
AutarcoBatterySensorEntityDescription(
key="flow_now",
translation_key="flow_now",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda battery: battery.flow_now,
),
AutarcoBatterySensorEntityDescription(
key="state_of_charge",
translation_key="state_of_charge",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda battery: battery.state_of_charge,
),
AutarcoBatterySensorEntityDescription(
key="discharged_today",
translation_key="discharged_today",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda battery: battery.discharged_today,
),
AutarcoBatterySensorEntityDescription(
key="discharged_month",
translation_key="discharged_month",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda battery: battery.discharged_month,
),
AutarcoBatterySensorEntityDescription(
key="discharged_total",
translation_key="discharged_total",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda battery: battery.discharged_total,
),
AutarcoBatterySensorEntityDescription(
key="charged_today",
translation_key="charged_today",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda battery: battery.charged_today,
),
AutarcoBatterySensorEntityDescription(
key="charged_month",
translation_key="charged_month",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda battery: battery.charged_month,
),
AutarcoBatterySensorEntityDescription(
key="charged_total",
translation_key="charged_total",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda battery: battery.charged_total,
),
)
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
class AutarcoSolarSensorEntityDescription(SensorEntityDescription): class AutarcoSolarSensorEntityDescription(SensorEntityDescription):
"""Describes an Autarco sensor entity.""" """Describes an Autarco sensor entity."""
@ -46,6 +121,7 @@ SENSORS_SOLAR: tuple[AutarcoSolarSensorEntityDescription, ...] = (
translation_key="energy_production_today", translation_key="energy_production_today",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda solar: solar.energy_production_today, value_fn=lambda solar: solar.energy_production_today,
), ),
AutarcoSolarSensorEntityDescription( AutarcoSolarSensorEntityDescription(
@ -53,6 +129,7 @@ SENSORS_SOLAR: tuple[AutarcoSolarSensorEntityDescription, ...] = (
translation_key="energy_production_month", translation_key="energy_production_month",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda solar: solar.energy_production_month, value_fn=lambda solar: solar.energy_production_month,
), ),
AutarcoSolarSensorEntityDescription( AutarcoSolarSensorEntityDescription(
@ -117,9 +194,52 @@ async def async_setup_entry(
for description in SENSORS_INVERTER for description in SENSORS_INVERTER
for inverter in coordinator.data.inverters for inverter in coordinator.data.inverters
) )
if coordinator.data.battery:
entities.extend(
AutarcoBatterySensorEntity(
coordinator=coordinator,
description=description,
)
for description in SENSORS_BATTERY
)
async_add_entities(entities) async_add_entities(entities)
class AutarcoBatterySensorEntity(
CoordinatorEntity[AutarcoDataUpdateCoordinator], SensorEntity
):
"""Defines an Autarco battery sensor."""
entity_description: AutarcoBatterySensorEntityDescription
_attr_has_entity_name = True
def __init__(
self,
*,
coordinator: AutarcoDataUpdateCoordinator,
description: AutarcoBatterySensorEntityDescription,
) -> None:
"""Initialize Autarco sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = (
f"{coordinator.account_site.site_id}_battery_{description.key}"
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{coordinator.account_site.site_id}_battery")},
entry_type=DeviceEntryType.SERVICE,
manufacturer="Autarco",
name="Battery",
)
@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
assert self.coordinator.data.battery is not None
return self.entity_description.value_fn(self.coordinator.data.battery)
class AutarcoSolarSensorEntity( class AutarcoSolarSensorEntity(
CoordinatorEntity[AutarcoDataUpdateCoordinator], SensorEntity CoordinatorEntity[AutarcoDataUpdateCoordinator], SensorEntity
): ):
@ -138,9 +258,11 @@ class AutarcoSolarSensorEntity(
super().__init__(coordinator) super().__init__(coordinator)
self.entity_description = description self.entity_description = description
self._attr_unique_id = f"{coordinator.site.site_id}_solar_{description.key}" self._attr_unique_id = (
f"{coordinator.account_site.site_id}_solar_{description.key}"
)
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{coordinator.site.site_id}_solar")}, identifiers={(DOMAIN, f"{coordinator.account_site.site_id}_solar")},
entry_type=DeviceEntryType.SERVICE, entry_type=DeviceEntryType.SERVICE,
manufacturer="Autarco", manufacturer="Autarco",
name="Solar", name="Solar",

View File

@ -23,6 +23,30 @@
}, },
"entity": { "entity": {
"sensor": { "sensor": {
"flow_now": {
"name": "Flow now"
},
"state_of_charge": {
"name": "State of charge"
},
"discharged_today": {
"name": "Discharged today"
},
"discharged_month": {
"name": "Discharged month"
},
"discharged_total": {
"name": "Discharged total"
},
"charged_today": {
"name": "Charged today"
},
"charged_month": {
"name": "Charged month"
},
"charged_total": {
"name": "Charged total"
},
"power_production": { "power_production": {
"name": "Power production" "name": "Power production"
}, },

View File

@ -3,7 +3,7 @@
from collections.abc import Generator from collections.abc import Generator
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
from autarco import AccountSite, Inverter, Solar from autarco import AccountSite, Battery, Inverter, Solar
import pytest import pytest
from homeassistant.components.autarco.const import DOMAIN from homeassistant.components.autarco.const import DOMAIN
@ -66,6 +66,17 @@ def mock_autarco_client() -> Generator[AsyncMock]:
health="OK", health="OK",
), ),
} }
client.get_battery.return_value = Battery(
flow_now=777,
net_charged_now=777,
state_of_charge=56,
discharged_today=2,
discharged_month=25,
discharged_total=696,
charged_today=1,
charged_month=26,
charged_total=748,
)
yield client yield client

View File

@ -3,6 +3,17 @@
dict({ dict({
'sites_data': list([ 'sites_data': list([
dict({ dict({
'battery': dict({
'charged_month': 26,
'charged_today': 1,
'charged_total': 748,
'discharged_month': 25,
'discharged_today': 2,
'discharged_total': 696,
'flow_now': 777,
'net_charged_now': 777,
'state_of_charge': 56,
}),
'health': 'OK', 'health': 'OK',
'id': 1, 'id': 1,
'inverters': list([ 'inverters': list([

View File

@ -1,4 +1,412 @@
# serializer version: 1 # serializer version: 1
# name: test_all_sensors[sensor.battery_charged_month-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.battery_charged_month',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Charged month',
'platform': 'autarco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'charged_month',
'unique_id': '1_battery_charged_month',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_sensors[sensor.battery_charged_month-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Battery Charged month',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.battery_charged_month',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '26',
})
# ---
# name: test_all_sensors[sensor.battery_charged_today-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.battery_charged_today',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Charged today',
'platform': 'autarco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'charged_today',
'unique_id': '1_battery_charged_today',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_sensors[sensor.battery_charged_today-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Battery Charged today',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.battery_charged_today',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1',
})
# ---
# name: test_all_sensors[sensor.battery_charged_total-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.battery_charged_total',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Charged total',
'platform': 'autarco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'charged_total',
'unique_id': '1_battery_charged_total',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_sensors[sensor.battery_charged_total-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Battery Charged total',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.battery_charged_total',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '748',
})
# ---
# name: test_all_sensors[sensor.battery_discharged_month-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.battery_discharged_month',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Discharged month',
'platform': 'autarco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'discharged_month',
'unique_id': '1_battery_discharged_month',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_sensors[sensor.battery_discharged_month-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Battery Discharged month',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.battery_discharged_month',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '25',
})
# ---
# name: test_all_sensors[sensor.battery_discharged_today-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.battery_discharged_today',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Discharged today',
'platform': 'autarco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'discharged_today',
'unique_id': '1_battery_discharged_today',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_sensors[sensor.battery_discharged_today-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Battery Discharged today',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.battery_discharged_today',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2',
})
# ---
# name: test_all_sensors[sensor.battery_discharged_total-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.battery_discharged_total',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Discharged total',
'platform': 'autarco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'discharged_total',
'unique_id': '1_battery_discharged_total',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_sensors[sensor.battery_discharged_total-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Battery Discharged total',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.battery_discharged_total',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '696',
})
# ---
# name: test_all_sensors[sensor.battery_flow_now-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.battery_flow_now',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Flow now',
'platform': 'autarco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'flow_now',
'unique_id': '1_battery_flow_now',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_all_sensors[sensor.battery_flow_now-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Battery Flow now',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.battery_flow_now',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '777',
})
# ---
# name: test_all_sensors[sensor.battery_state_of_charge-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.battery_state_of_charge',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
'original_icon': None,
'original_name': 'State of charge',
'platform': 'autarco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'state_of_charge',
'unique_id': '1_battery_state_of_charge',
'unit_of_measurement': '%',
})
# ---
# name: test_all_sensors[sensor.battery_state_of_charge-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Battery State of charge',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.battery_state_of_charge',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '56',
})
# ---
# name: test_all_sensors[sensor.inverter_test_serial_1_energy_ac_output_total-entry] # name: test_all_sensors[sensor.inverter_test_serial_1_energy_ac_output_total-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -208,7 +616,9 @@
'aliases': set({ 'aliases': set({
}), }),
'area_id': None, 'area_id': None,
'capabilities': None, 'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>, 'config_entry_id': <ANY>,
'device_class': None, 'device_class': None,
'device_id': <ANY>, 'device_id': <ANY>,
@ -241,6 +651,7 @@
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'energy', 'device_class': 'energy',
'friendly_name': 'Solar Energy production month', 'friendly_name': 'Solar Energy production month',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>, 'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}), }),
'context': <ANY>, 'context': <ANY>,
@ -256,7 +667,9 @@
'aliases': set({ 'aliases': set({
}), }),
'area_id': None, 'area_id': None,
'capabilities': None, 'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>, 'config_entry_id': <ANY>,
'device_class': None, 'device_class': None,
'device_id': <ANY>, 'device_id': <ANY>,
@ -289,6 +702,7 @@
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'energy', 'device_class': 'energy',
'friendly_name': 'Solar Energy production today', 'friendly_name': 'Solar Energy production today',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>, 'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}), }),
'context': <ANY>, 'context': <ANY>,

View File

@ -1,16 +1,20 @@
"""Test the sensor provided by the Autarco integration.""" """Test the sensor provided by the Autarco integration."""
from unittest.mock import MagicMock, patch from datetime import timedelta
from unittest.mock import AsyncMock, MagicMock, patch
from autarco import AutarcoConnectionError
from freezegun.api import FrozenDateTimeFactory
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
from homeassistant.const import Platform from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from . import setup_integration from . import setup_integration
from tests.common import MockConfigEntry, snapshot_platform from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
async def test_all_sensors( async def test_all_sensors(
@ -25,3 +29,29 @@ async def test_all_sensors(
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_update_failed(
hass: HomeAssistant,
mock_autarco_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test entities become unavailable after failed update."""
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.LOADED
assert (
hass.states.get("sensor.inverter_test_serial_1_energy_ac_output_total").state
is not None
)
mock_autarco_client.get_solar.side_effect = AutarcoConnectionError
freezer.tick(timedelta(minutes=5))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert (
hass.states.get("sensor.inverter_test_serial_1_energy_ac_output_total").state
== STATE_UNAVAILABLE
)