Bump solarlog_cli to 0.2.2 (#124948)
* Add inverter-devices * Minor code adjustments * Update manifest.json Seperate dependency upgrade to seperate PR * Update requirements_all.txt Seperate dependency upgrade to seperate PR * Update requirements_test_all.txt Seperate dependency upgrade to seperate PR * Update homeassistant/components/solarlog/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Split up base class, document SolarLogSensorEntityDescription * Split up sensor types * Update snapshot * Bump solarlog_cli to 0.2.1 * Add strict typing * Bump fyta_cli to 0.6.3 (#124574) * Ensure write access to hassrelease data folder (#124573) Co-authored-by: Robert Resch <robert@resch.dev> * Update a roborock blocking call to be fully async (#124266) Remove a blocking call in roborock * Add inverter-devices * Split up sensor types * Update snapshot * Bump solarlog_cli to 0.2.1 * Backport/rebase * Tidy up * Simplyfication coordinator.py * Minor adjustments * Ruff * Bump solarlog_cli to 0.2.2 * Update homeassistant/components/solarlog/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/solarlog/config_flow.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/solarlog/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update persentage-values in fixture --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Paulus Schoutsen <balloob@gmail.com> Co-authored-by: Robert Resch <robert@resch.dev> Co-authored-by: Allen Porter <allen@thebends.org>pull/124597/head
parent
68162e1a27
commit
1661304f10
|
@ -411,6 +411,7 @@ homeassistant.components.slack.*
|
|||
homeassistant.components.sleepiq.*
|
||||
homeassistant.components.smhi.*
|
||||
homeassistant.components.snooz.*
|
||||
homeassistant.components.solarlog.*
|
||||
homeassistant.components.sonarr.*
|
||||
homeassistant.components.speedtestdotnet.*
|
||||
homeassistant.components.sql.*
|
||||
|
|
|
@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
|
||||
@callback
|
||||
def solarlog_entries(hass: HomeAssistant):
|
||||
def solarlog_entries(hass: HomeAssistant) -> set[str]:
|
||||
"""Return the hosts already configured."""
|
||||
return {
|
||||
entry.data[CONF_HOST] for entry in hass.config_entries.async_entries(DOMAIN)
|
||||
|
@ -36,7 +36,7 @@ class SolarLogConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
"""Initialize the config flow."""
|
||||
self._errors: dict = {}
|
||||
|
||||
def _host_in_configuration_exists(self, host) -> bool:
|
||||
def _host_in_configuration_exists(self, host: str) -> bool:
|
||||
"""Return True if host exists in configuration."""
|
||||
if host in solarlog_entries(self.hass):
|
||||
return True
|
||||
|
@ -50,7 +50,7 @@ class SolarLogConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
url = ParseResult("http", netloc, path, *url[3:])
|
||||
return url.geturl()
|
||||
|
||||
async def _test_connection(self, host):
|
||||
async def _test_connection(self, host: str) -> bool:
|
||||
"""Check if we can connect to the Solar-Log device."""
|
||||
solarlog = SolarLogConnector(host)
|
||||
try:
|
||||
|
@ -66,11 +66,12 @@ class SolarLogConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
return True
|
||||
|
||||
async def async_step_user(self, user_input=None) -> ConfigFlowResult:
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Step when user initializes a integration."""
|
||||
self._errors = {}
|
||||
if user_input is not None:
|
||||
# set some defaults in case we need to return to the form
|
||||
user_input[CONF_NAME] = slugify(user_input[CONF_NAME])
|
||||
user_input[CONF_HOST] = self._parse_url(user_input[CONF_HOST])
|
||||
|
||||
|
@ -81,20 +82,14 @@ class SolarLogConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
title=user_input[CONF_NAME], data=user_input
|
||||
)
|
||||
else:
|
||||
user_input = {}
|
||||
user_input[CONF_NAME] = DEFAULT_NAME
|
||||
user_input[CONF_HOST] = DEFAULT_HOST
|
||||
user_input = {CONF_NAME: DEFAULT_NAME, CONF_HOST: DEFAULT_HOST}
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_NAME, default=user_input.get(CONF_NAME, DEFAULT_NAME)
|
||||
): str,
|
||||
vol.Required(
|
||||
CONF_HOST, default=user_input.get(CONF_HOST, DEFAULT_HOST)
|
||||
): str,
|
||||
vol.Required(CONF_NAME, default=user_input[CONF_NAME]): str,
|
||||
vol.Required(CONF_HOST, default=user_input[CONF_HOST]): str,
|
||||
vol.Required("extended_data", default=False): bool,
|
||||
}
|
||||
),
|
||||
|
|
|
@ -12,11 +12,12 @@ from solarlog_cli.solarlog_exceptions import (
|
|||
SolarLogConnectionError,
|
||||
SolarLogUpdateError,
|
||||
)
|
||||
from solarlog_cli.solarlog_models import SolarlogData
|
||||
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import update_coordinator
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -24,7 +25,7 @@ if TYPE_CHECKING:
|
|||
from . import SolarlogConfigEntry
|
||||
|
||||
|
||||
class SolarLogCoordinator(update_coordinator.DataUpdateCoordinator):
|
||||
class SolarLogCoordinator(DataUpdateCoordinator[SolarlogData]):
|
||||
"""Get and update the latest data."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: SolarlogConfigEntry) -> None:
|
||||
|
@ -43,29 +44,29 @@ class SolarLogCoordinator(update_coordinator.DataUpdateCoordinator):
|
|||
self.name = entry.title
|
||||
self.host = url.geturl()
|
||||
|
||||
extended_data = entry.data["extended_data"]
|
||||
|
||||
self.solarlog = SolarLogConnector(
|
||||
self.host, extended_data, hass.config.time_zone
|
||||
self.host, entry.data["extended_data"], hass.config.time_zone
|
||||
)
|
||||
|
||||
async def _async_setup(self) -> None:
|
||||
"""Do initialization logic."""
|
||||
if self.solarlog.extended_data:
|
||||
device_list = await self.solarlog.client.get_device_list()
|
||||
device_list = await self.solarlog.update_device_list()
|
||||
self.solarlog.set_enabled_devices({key: True for key in device_list})
|
||||
|
||||
async def _async_update_data(self):
|
||||
async def _async_update_data(self) -> SolarlogData:
|
||||
"""Update the data from the SolarLog device."""
|
||||
_LOGGER.debug("Start data update")
|
||||
|
||||
try:
|
||||
data = await self.solarlog.update_data()
|
||||
await self.solarlog.update_device_list()
|
||||
if self.solarlog.extended_data:
|
||||
await self.solarlog.update_device_list()
|
||||
data.inverter_data = await self.solarlog.update_inverter_data()
|
||||
except SolarLogConnectionError as err:
|
||||
raise ConfigEntryNotReady(err) from err
|
||||
except SolarLogUpdateError as err:
|
||||
raise update_coordinator.UpdateFailed(err) from err
|
||||
raise UpdateFailed(err) from err
|
||||
|
||||
_LOGGER.debug("Data successfully updated")
|
||||
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
"documentation": "https://www.home-assistant.io/integrations/solarlog",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["solarlog_cli"],
|
||||
"requirements": ["solarlog_cli==0.1.6"]
|
||||
"requirements": ["solarlog_cli==0.2.2"]
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ from __future__ import annotations
|
|||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from solarlog_cli.solarlog_models import InverterData, SolarlogData
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
|
@ -21,200 +22,219 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from . import SolarlogConfigEntry
|
||||
from .entity import SolarLogCoordinatorEntity, SolarLogInverterEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SolarLogSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes Solarlog sensor entity."""
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class SolarLogCoordinatorSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes Solarlog coordinator sensor entity."""
|
||||
|
||||
value_fn: Callable[[float | int], float] | Callable[[datetime], datetime] = (
|
||||
lambda value: value
|
||||
)
|
||||
value_fn: Callable[[SolarlogData], StateType | datetime | None]
|
||||
|
||||
|
||||
SOLARLOG_SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = (
|
||||
SolarLogSensorEntityDescription(
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class SolarLogInverterSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes Solarlog inverter sensor entity."""
|
||||
|
||||
value_fn: Callable[[InverterData], float | None]
|
||||
|
||||
|
||||
SOLARLOG_SENSOR_TYPES: tuple[SolarLogCoordinatorSensorEntityDescription, ...] = (
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="last_updated",
|
||||
translation_key="last_update",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=lambda data: data.last_updated,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="power_ac",
|
||||
translation_key="power_ac",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.power_ac,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="power_dc",
|
||||
translation_key="power_dc",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.power_dc,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="voltage_ac",
|
||||
translation_key="voltage_ac",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.voltage_ac,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="voltage_dc",
|
||||
translation_key="voltage_dc",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.voltage_dc,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="yield_day",
|
||||
translation_key="yield_day",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.yield_day / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="yield_yesterday",
|
||||
translation_key="yield_yesterday",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.yield_yesterday / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="yield_month",
|
||||
translation_key="yield_month",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.yield_month / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="yield_year",
|
||||
translation_key="yield_year",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.yield_year / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="yield_total",
|
||||
translation_key="yield_total",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.yield_total / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="consumption_ac",
|
||||
translation_key="consumption_ac",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.consumption_ac,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="consumption_day",
|
||||
translation_key="consumption_day",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.consumption_day / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="consumption_yesterday",
|
||||
translation_key="consumption_yesterday",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.consumption_yesterday / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="consumption_month",
|
||||
translation_key="consumption_month",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.consumption_month / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="consumption_year",
|
||||
translation_key="consumption_year",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.consumption_year / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="consumption_total",
|
||||
translation_key="consumption_total",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda data: round(data.consumption_total / 1000, 3),
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="self_consumption_year",
|
||||
translation_key="self_consumption_year",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
value_fn=lambda data: data.self_consumption_year,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="total_power",
|
||||
translation_key="total_power",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
value_fn=lambda data: data.total_power,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="alternator_loss",
|
||||
translation_key="alternator_loss",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.alternator_loss,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="capacity",
|
||||
translation_key="capacity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda value: round(value * 100, 1),
|
||||
value_fn=lambda data: data.capacity,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="efficiency",
|
||||
translation_key="efficiency",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda value: round(value * 100, 1),
|
||||
value_fn=lambda data: data.efficiency,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="power_available",
|
||||
translation_key="power_available",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda data: data.power_available,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogCoordinatorSensorEntityDescription(
|
||||
key="usage",
|
||||
translation_key="usage",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda value: round(value * 100, 1),
|
||||
value_fn=lambda data: data.usage,
|
||||
),
|
||||
)
|
||||
|
||||
INVERTER_SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = (
|
||||
SolarLogSensorEntityDescription(
|
||||
INVERTER_SENSOR_TYPES: tuple[SolarLogInverterSensorEntityDescription, ...] = (
|
||||
SolarLogInverterSensorEntityDescription(
|
||||
key="current_power",
|
||||
translation_key="current_power",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda inverter: inverter.current_power,
|
||||
),
|
||||
SolarLogSensorEntityDescription(
|
||||
SolarLogInverterSensorEntityDescription(
|
||||
key="consumption_year",
|
||||
translation_key="consumption_year",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
value_fn=lambda value: round(value / 1000, 3),
|
||||
value_fn=lambda inverter: None
|
||||
if inverter.consumption_year is None
|
||||
else round(inverter.consumption_year / 1000, 3),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -227,21 +247,18 @@ async def async_setup_entry(
|
|||
"""Add solarlog entry."""
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
# https://github.com/python/mypy/issues/14294
|
||||
|
||||
entities: list[SensorEntity] = [
|
||||
SolarLogCoordinatorSensor(coordinator, sensor)
|
||||
for sensor in SOLARLOG_SENSOR_TYPES
|
||||
]
|
||||
|
||||
device_data: dict[str, Any] = coordinator.data["devices"]
|
||||
device_data = coordinator.data.inverter_data
|
||||
|
||||
if not device_data:
|
||||
if device_data:
|
||||
entities.extend(
|
||||
SolarLogInverterSensor(coordinator, sensor, int(device_id))
|
||||
SolarLogInverterSensor(coordinator, sensor, device_id)
|
||||
for device_id in device_data
|
||||
for sensor in INVERTER_SENSOR_TYPES
|
||||
if sensor.key in device_data[device_id]
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
@ -250,26 +267,24 @@ async def async_setup_entry(
|
|||
class SolarLogCoordinatorSensor(SolarLogCoordinatorEntity, SensorEntity):
|
||||
"""Represents a SolarLog sensor."""
|
||||
|
||||
entity_description: SolarLogSensorEntityDescription
|
||||
entity_description: SolarLogCoordinatorSensorEntityDescription
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | datetime:
|
||||
def native_value(self) -> StateType | datetime:
|
||||
"""Return the state for this sensor."""
|
||||
|
||||
val = self.coordinator.data[self.entity_description.key]
|
||||
return self.entity_description.value_fn(val)
|
||||
return self.entity_description.value_fn(self.coordinator.data)
|
||||
|
||||
|
||||
class SolarLogInverterSensor(SolarLogInverterEntity, SensorEntity):
|
||||
"""Represents a SolarLog inverter sensor."""
|
||||
|
||||
entity_description: SolarLogSensorEntityDescription
|
||||
entity_description: SolarLogInverterSensorEntityDescription
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | datetime:
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state for this sensor."""
|
||||
|
||||
val = self.coordinator.data["devices"][self.device_id][
|
||||
self.entity_description.key
|
||||
]
|
||||
return self.entity_description.value_fn(val)
|
||||
return self.entity_description.value_fn(
|
||||
self.coordinator.data.inverter_data[self.device_id]
|
||||
)
|
||||
|
|
10
mypy.ini
10
mypy.ini
|
@ -3866,6 +3866,16 @@ disallow_untyped_defs = true
|
|||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.solarlog.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_decorators = true
|
||||
disallow_untyped_defs = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.sonarr.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
|
|
|
@ -2650,7 +2650,7 @@ soco==0.30.4
|
|||
solaredge-local==0.2.3
|
||||
|
||||
# homeassistant.components.solarlog
|
||||
solarlog_cli==0.1.6
|
||||
solarlog_cli==0.2.2
|
||||
|
||||
# homeassistant.components.solax
|
||||
solax==3.1.1
|
||||
|
|
|
@ -2093,7 +2093,7 @@ snapcast==2.3.6
|
|||
soco==0.30.4
|
||||
|
||||
# homeassistant.components.solarlog
|
||||
solarlog_cli==0.1.6
|
||||
solarlog_cli==0.2.2
|
||||
|
||||
# homeassistant.components.solax
|
||||
solax==3.1.1
|
||||
|
|
|
@ -17,3 +17,5 @@ async def setup_platform(
|
|||
with patch("homeassistant.components.solarlog.PLATFORMS", platforms):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return config_entry
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
"""Test helpers."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from datetime import UTC, datetime
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from solarlog_cli.solarlog_models import InverterData, SolarlogData
|
||||
|
||||
from homeassistant.components.solarlog.const import DOMAIN as SOLARLOG_DOMAIN
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME
|
||||
|
@ -13,6 +13,19 @@ from .const import HOST, NAME
|
|||
|
||||
from tests.common import MockConfigEntry, load_json_object_fixture
|
||||
|
||||
DEVICE_LIST = {
|
||||
0: InverterData(name="Inverter 1", enabled=True),
|
||||
1: InverterData(name="Inverter 2", enabled=True),
|
||||
}
|
||||
INVERTER_DATA = {
|
||||
0: InverterData(
|
||||
name="Inverter 1", enabled=True, consumption_year=354687, current_power=5
|
||||
),
|
||||
1: InverterData(
|
||||
name="Inverter 2", enabled=True, consumption_year=354, current_power=6
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
|
@ -34,28 +47,18 @@ def mock_config_entry() -> MockConfigEntry:
|
|||
def mock_solarlog_connector():
|
||||
"""Build a fixture for the SolarLog API that connects successfully and returns one device."""
|
||||
|
||||
data = SolarlogData.from_dict(
|
||||
load_json_object_fixture("solarlog_data.json", SOLARLOG_DOMAIN)
|
||||
)
|
||||
data.inverter_data = INVERTER_DATA
|
||||
|
||||
mock_solarlog_api = AsyncMock()
|
||||
mock_solarlog_api.test_connection = AsyncMock(return_value=True)
|
||||
|
||||
data = {
|
||||
"devices": {
|
||||
0: {"consumption_total": 354687, "current_power": 5},
|
||||
}
|
||||
}
|
||||
data |= load_json_object_fixture("solarlog_data.json", SOLARLOG_DOMAIN)
|
||||
data["last_updated"] = datetime.fromisoformat(data["last_updated"]).astimezone(UTC)
|
||||
|
||||
mock_solarlog_api.test_connection.return_value = True
|
||||
mock_solarlog_api.update_data.return_value = data
|
||||
mock_solarlog_api.device_list.return_value = {
|
||||
0: {"name": "Inverter 1"},
|
||||
1: {"name": "Inverter 2"},
|
||||
}
|
||||
mock_solarlog_api.update_device_list.return_value = INVERTER_DATA
|
||||
mock_solarlog_api.update_inverter_data.return_value = INVERTER_DATA
|
||||
mock_solarlog_api.device_name = {0: "Inverter 1", 1: "Inverter 2"}.get
|
||||
mock_solarlog_api.client.get_device_list.return_value = {
|
||||
0: {"name": "Inverter 1"},
|
||||
1: {"name": "Inverter 2"},
|
||||
}
|
||||
mock_solarlog_api.client.close = AsyncMock(return_value=None)
|
||||
mock_solarlog_api.device_enabled = {0: True, 1: False}.get
|
||||
|
||||
with (
|
||||
patch(
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
"total_power": 120,
|
||||
"self_consumption_year": 545,
|
||||
"alternator_loss": 2,
|
||||
"efficiency": 0.9804,
|
||||
"usage": 0.5487,
|
||||
"efficiency": 98.1,
|
||||
"usage": 54.8,
|
||||
"power_available": 45.13,
|
||||
"capacity": 0.85,
|
||||
"last_updated": "2024-08-01T15:20:45"
|
||||
"capacity": 85.5,
|
||||
"last_updated": "2024-08-01T15:20:45Z"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,103 @@
|
|||
# serializer version: 1
|
||||
# name: test_all_entities[sensor.inverter_1_consumption_total-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.inverter_1_consumption_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': 'Consumption total',
|
||||
'platform': 'solarlog',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'consumption_total',
|
||||
'unique_id': 'ce5f5431554d101905d31797e1232da8-inverter_1-consumption_total',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.inverter_1_consumption_total-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Inverter 1 Consumption total',
|
||||
'state_class': <SensorStateClass.TOTAL: 'total'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.inverter_1_consumption_total',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '354.687',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.inverter_1_consumption_year-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.inverter_1_consumption_year',
|
||||
'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': 'Consumption year',
|
||||
'platform': 'solarlog',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'consumption_year',
|
||||
'unique_id': 'ce5f5431554d101905d31797e1232da8-inverter_1-consumption_year',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.inverter_1_consumption_year-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Inverter 1 Consumption year',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.inverter_1_consumption_year',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '354.687',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.inverter_1_power-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
@ -50,6 +149,105 @@
|
|||
'state': '5',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.inverter_2_consumption_year-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.inverter_2_consumption_year',
|
||||
'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': 'Consumption year',
|
||||
'platform': 'solarlog',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'consumption_year',
|
||||
'unique_id': 'ce5f5431554d101905d31797e1232da8-inverter_2-consumption_year',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.inverter_2_consumption_year-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Inverter 2 Consumption year',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.inverter_2_consumption_year',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.354',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.inverter_2_power-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.inverter_2_power',
|
||||
'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': 'Power',
|
||||
'platform': 'solarlog',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'current_power',
|
||||
'unique_id': 'ce5f5431554d101905d31797e1232da8-inverter_2-current_power',
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.inverter_2_power-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'power',
|
||||
'friendly_name': 'Inverter 2 Power',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.inverter_2_power',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '6',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_alternator_loss-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
@ -98,7 +296,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '2',
|
||||
'state': '2.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_capacity-entry]
|
||||
|
@ -149,7 +347,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '85.0',
|
||||
'state': '85.5',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_consumption_ac-entry]
|
||||
|
@ -494,7 +692,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '98.0',
|
||||
'state': '98.1',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_installed_peak_power-entry]
|
||||
|
@ -542,7 +740,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '120',
|
||||
'state': '120.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_last_update-entry]
|
||||
|
@ -640,7 +838,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '100',
|
||||
'state': '100.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_power_available-entry]
|
||||
|
@ -742,7 +940,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '102',
|
||||
'state': '102.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_self_consumption_year-entry]
|
||||
|
@ -793,7 +991,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '545',
|
||||
'state': '545.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_usage-entry]
|
||||
|
@ -844,7 +1042,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '54.9',
|
||||
'state': '54.8',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_voltage_ac-entry]
|
||||
|
@ -895,7 +1093,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '100',
|
||||
'state': '100.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_voltage_dc-entry]
|
||||
|
@ -946,7 +1144,7 @@
|
|||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '100',
|
||||
'state': '100.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.solarlog_yield_day-entry]
|
||||
|
|
|
@ -123,7 +123,7 @@ async def test_form_exceptions(
|
|||
assert result["data"]["extended_data"] is False
|
||||
|
||||
|
||||
async def test_abort_if_already_setup(hass: HomeAssistant, test_connect) -> None:
|
||||
async def test_abort_if_already_setup(hass: HomeAssistant, test_connect: None) -> None:
|
||||
"""Test we abort if the device is already setup."""
|
||||
flow = init_config_flow(hass)
|
||||
MockConfigEntry(
|
||||
|
|
Loading…
Reference in New Issue