Aurora abb energy metering (#58454)
Co-authored-by: J. Nick Koston <nick@koston.org>pull/58994/head
parent
ae99b678dd
commit
0a27b0f353
|
@ -32,7 +32,7 @@ class AuroraDevice(Entity):
|
|||
def unique_id(self) -> str:
|
||||
"""Return the unique id for this device."""
|
||||
serial = self._data[ATTR_SERIAL_NUMBER]
|
||||
return f"{serial}_{self.type}"
|
||||
return f"{serial}_{self.entity_description.key}"
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
"""Support for Aurora ABB PowerOne Solar Photvoltaic (PV) inverter."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aurorapy.client import AuroraError, AuroraSerialClient
|
||||
import voluptuous as vol
|
||||
|
@ -8,19 +11,22 @@ import voluptuous as vol
|
|||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_DEVICE,
|
||||
CONF_NAME,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ENERGY_KILO_WATT_HOUR,
|
||||
POWER_WATT,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.exceptions import InvalidStateError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from .aurora_device import AuroraDevice
|
||||
|
@ -28,6 +34,29 @@ from .const import DEFAULT_ADDRESS, DOMAIN
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SENSOR_TYPES = [
|
||||
SensorEntityDescription(
|
||||
key="instantaneouspower",
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
native_unit_of_measurement=POWER_WATT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
name="Power Output",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="temp",
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
name="Temperature",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="totalenergy",
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
name="Total Energy",
|
||||
),
|
||||
]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
|
@ -55,15 +84,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities) -> None:
|
|||
"""Set up aurora_abb_powerone sensor based on a config entry."""
|
||||
entities = []
|
||||
|
||||
sensortypes = [
|
||||
{"parameter": "instantaneouspower", "name": "Power Output"},
|
||||
{"parameter": "temperature", "name": "Temperature"},
|
||||
]
|
||||
client = hass.data[DOMAIN][config_entry.unique_id]
|
||||
data = config_entry.data
|
||||
|
||||
for sens in sensortypes:
|
||||
entities.append(AuroraSensor(client, data, sens["name"], sens["parameter"]))
|
||||
for sens in SENSOR_TYPES:
|
||||
entities.append(AuroraSensor(client, data, sens))
|
||||
|
||||
_LOGGER.debug("async_setup_entry adding %d entities", len(entities))
|
||||
async_add_entities(entities, True)
|
||||
|
@ -72,22 +97,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities) -> None:
|
|||
class AuroraSensor(AuroraDevice, SensorEntity):
|
||||
"""Representation of a Sensor on a Aurora ABB PowerOne Solar inverter."""
|
||||
|
||||
_attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
|
||||
def __init__(self, client: AuroraSerialClient, data, name, typename):
|
||||
def __init__(
|
||||
self,
|
||||
client: AuroraSerialClient,
|
||||
data: Mapping[str, Any],
|
||||
entity_description: SensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(client, data)
|
||||
if typename == "instantaneouspower":
|
||||
self.type = typename
|
||||
self._attr_native_unit_of_measurement = POWER_WATT
|
||||
self._attr_device_class = DEVICE_CLASS_POWER
|
||||
elif typename == "temperature":
|
||||
self.type = typename
|
||||
self._attr_native_unit_of_measurement = TEMP_CELSIUS
|
||||
self._attr_device_class = DEVICE_CLASS_TEMPERATURE
|
||||
else:
|
||||
raise InvalidStateError(f"Unrecognised typename '{typename}'")
|
||||
self._attr_name = f"{name}"
|
||||
self.entity_description = entity_description
|
||||
self.availableprev = True
|
||||
|
||||
def update(self):
|
||||
|
@ -98,13 +116,16 @@ class AuroraSensor(AuroraDevice, SensorEntity):
|
|||
try:
|
||||
self.availableprev = self._attr_available
|
||||
self.client.connect()
|
||||
if self.type == "instantaneouspower":
|
||||
if self.entity_description.key == "instantaneouspower":
|
||||
# read ADC channel 3 (grid power output)
|
||||
power_watts = self.client.measure(3, True)
|
||||
self._attr_native_value = round(power_watts, 1)
|
||||
elif self.type == "temperature":
|
||||
elif self.entity_description.key == "temp":
|
||||
temperature_c = self.client.measure(21)
|
||||
self._attr_native_value = round(temperature_c, 1)
|
||||
elif self.entity_description.key == "totalenergy":
|
||||
energy_wh = self.client.cumulated_energy(5)
|
||||
self._attr_native_value = round(energy_wh / 1000, 2)
|
||||
self._attr_available = True
|
||||
|
||||
except AuroraError as error:
|
||||
|
|
|
@ -3,7 +3,6 @@ from datetime import timedelta
|
|||
from unittest.mock import patch
|
||||
|
||||
from aurorapy.client import AuroraError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.aurora_abb_powerone.const import (
|
||||
ATTR_DEVICE_NAME,
|
||||
|
@ -13,10 +12,8 @@ from homeassistant.components.aurora_abb_powerone.const import (
|
|||
DEFAULT_INTEGRATION_TITLE,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.components.aurora_abb_powerone.sensor import AuroraSensor
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import CONF_ADDRESS, CONF_PORT
|
||||
from homeassistant.exceptions import InvalidStateError
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
|
@ -39,6 +36,7 @@ def _simulated_returns(index, global_measure=None):
|
|||
returns = {
|
||||
3: 45.678, # power
|
||||
21: 9.876, # temperature
|
||||
5: 12345, # energy
|
||||
}
|
||||
return returns[index]
|
||||
|
||||
|
@ -66,7 +64,12 @@ async def test_setup_platform_valid_config(hass):
|
|||
with patch("aurorapy.client.AuroraSerialClient.connect", return_value=None), patch(
|
||||
"aurorapy.client.AuroraSerialClient.measure",
|
||||
side_effect=_simulated_returns,
|
||||
), assert_setup_component(1, "sensor"):
|
||||
), patch(
|
||||
"aurorapy.client.AuroraSerialClient.cumulated_energy",
|
||||
side_effect=_simulated_returns,
|
||||
), assert_setup_component(
|
||||
1, "sensor"
|
||||
):
|
||||
assert await async_setup_component(hass, "sensor", TEST_CONFIG)
|
||||
await hass.async_block_till_done()
|
||||
power = hass.states.get("sensor.power_output")
|
||||
|
@ -91,6 +94,9 @@ async def test_sensors(hass):
|
|||
with patch("aurorapy.client.AuroraSerialClient.connect", return_value=None), patch(
|
||||
"aurorapy.client.AuroraSerialClient.measure",
|
||||
side_effect=_simulated_returns,
|
||||
), patch(
|
||||
"aurorapy.client.AuroraSerialClient.cumulated_energy",
|
||||
side_effect=_simulated_returns,
|
||||
):
|
||||
mock_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
|
@ -104,24 +110,9 @@ async def test_sensors(hass):
|
|||
assert temperature
|
||||
assert temperature.state == "9.9"
|
||||
|
||||
|
||||
async def test_sensor_invalid_type(hass):
|
||||
"""Test invalid sensor type during setup."""
|
||||
entities = []
|
||||
mock_entry = _mock_config_entry()
|
||||
|
||||
with patch("aurorapy.client.AuroraSerialClient.connect", return_value=None), patch(
|
||||
"aurorapy.client.AuroraSerialClient.measure",
|
||||
side_effect=_simulated_returns,
|
||||
):
|
||||
mock_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
client = hass.data[DOMAIN][mock_entry.unique_id]
|
||||
data = mock_entry.data
|
||||
with pytest.raises(InvalidStateError):
|
||||
entities.append(AuroraSensor(client, data, "WrongSensor", "wrongparameter"))
|
||||
energy = hass.states.get("sensor.total_energy")
|
||||
assert energy
|
||||
assert energy.state == "12.35"
|
||||
|
||||
|
||||
async def test_sensor_dark(hass):
|
||||
|
|
Loading…
Reference in New Issue