Aurora abb energy metering (#58454)

Co-authored-by: J. Nick Koston <nick@koston.org>
pull/58994/head
Dave T 2021-10-28 22:47:49 +01:00 committed by Franck Nijhof
parent ae99b678dd
commit 0a27b0f353
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
3 changed files with 58 additions and 46 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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):