Add Rainforest Eagle tests and price (#54887)
parent
6eadc0c303
commit
f1a4ba8bb0
|
@ -838,9 +838,6 @@ omit =
|
|||
homeassistant/components/rainmachine/binary_sensor.py
|
||||
homeassistant/components/rainmachine/sensor.py
|
||||
homeassistant/components/rainmachine/switch.py
|
||||
homeassistant/components/rainforest_eagle/__init__.py
|
||||
homeassistant/components/rainforest_eagle/data.py
|
||||
homeassistant/components/rainforest_eagle/sensor.py
|
||||
homeassistant/components/raspihats/*
|
||||
homeassistant/components/raspyrfm/*
|
||||
homeassistant/components/recollect_waste/__init__.py
|
||||
|
|
|
@ -147,14 +147,14 @@ class EagleDataCoordinator(DataUpdateCoordinator):
|
|||
async def _async_update_data_100(self):
|
||||
"""Get the latest data from the Eagle-100 device."""
|
||||
try:
|
||||
data = await self.hass.async_add_executor_job(self._fetch_data)
|
||||
data = await self.hass.async_add_executor_job(self._fetch_data_100)
|
||||
except UPDATE_100_ERRORS as error:
|
||||
raise UpdateFailed from error
|
||||
|
||||
_LOGGER.debug("API data: %s", data)
|
||||
return data
|
||||
|
||||
def _fetch_data(self):
|
||||
def _fetch_data_100(self):
|
||||
"""Fetch and return the four sensor values in a dict."""
|
||||
if self.eagle100_reader is None:
|
||||
self.eagle100_reader = Eagle100Reader(
|
||||
|
|
|
@ -9,6 +9,7 @@ import voluptuous as vol
|
|||
from homeassistant.components.sensor import (
|
||||
DEVICE_CLASS_ENERGY,
|
||||
PLATFORM_SCHEMA,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
|
@ -39,6 +40,7 @@ SENSORS = (
|
|||
name="Meter Power Demand",
|
||||
native_unit_of_measurement=POWER_KILO_WATT,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="zigbee:CurrentSummationDelivered",
|
||||
|
@ -95,7 +97,22 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up a config entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(EagleSensor(coordinator, description) for description in SENSORS)
|
||||
entities = [EagleSensor(coordinator, description) for description in SENSORS]
|
||||
|
||||
if coordinator.data.get("zigbee:Price") not in (None, "invalid"):
|
||||
entities.append(
|
||||
EagleSensor(
|
||||
coordinator,
|
||||
SensorEntityDescription(
|
||||
key="zigbee:Price",
|
||||
name="Meter Price",
|
||||
native_unit_of_measurement=f"{coordinator.data['zigbee:PriceCurrency']}/{ENERGY_KILO_WATT_HOUR}",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class EagleSensor(CoordinatorEntity, SensorEntity):
|
||||
|
@ -111,7 +128,7 @@ class EagleSensor(CoordinatorEntity, SensorEntity):
|
|||
@property
|
||||
def unique_id(self) -> str | None:
|
||||
"""Return unique ID of entity."""
|
||||
return f"{self.coordinator.cloud_id}-{self.entity_description.key}"
|
||||
return f"{self.coordinator.cloud_id}-${self.coordinator.hardware_address}-{self.entity_description.key}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
|
|
|
@ -242,10 +242,9 @@ class DataUpdateCoordinator(Generic[T]):
|
|||
except Exception as err: # pylint: disable=broad-except
|
||||
self.last_exception = err
|
||||
self.last_update_success = False
|
||||
if log_failures:
|
||||
self.logger.exception(
|
||||
"Unexpected error fetching %s data: %s", self.name, err
|
||||
)
|
||||
self.logger.exception(
|
||||
"Unexpected error fetching %s data: %s", self.name, err
|
||||
)
|
||||
|
||||
else:
|
||||
if not self.last_update_success:
|
||||
|
|
|
@ -1 +1,64 @@
|
|||
"""Tests for the Rainforest Eagle integration."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant import config_entries, setup
|
||||
from homeassistant.components.rainforest_eagle.const import (
|
||||
CONF_CLOUD_ID,
|
||||
CONF_HARDWARE_ADDRESS,
|
||||
CONF_INSTALL_CODE,
|
||||
DOMAIN,
|
||||
TYPE_EAGLE_200,
|
||||
)
|
||||
from homeassistant.const import CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import RESULT_TYPE_ABORT
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
async def test_import(hass: HomeAssistant) -> None:
|
||||
"""Test we get the form."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.rainforest_eagle.data.async_get_type",
|
||||
return_value=(TYPE_EAGLE_200, "mock-hw"),
|
||||
), patch(
|
||||
"homeassistant.components.rainforest_eagle.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
await async_setup_component(
|
||||
hass,
|
||||
"sensor",
|
||||
{
|
||||
"sensor": {
|
||||
"platform": DOMAIN,
|
||||
"ip_address": "192.168.1.55",
|
||||
CONF_CLOUD_ID: "abcdef",
|
||||
CONF_INSTALL_CODE: "123456",
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entries = hass.config_entries.async_entries(DOMAIN)
|
||||
assert len(entries) == 1
|
||||
entry = entries[0]
|
||||
|
||||
assert entry.title == "abcdef"
|
||||
assert entry.data == {
|
||||
CONF_TYPE: TYPE_EAGLE_200,
|
||||
CONF_CLOUD_ID: "abcdef",
|
||||
CONF_INSTALL_CODE: "123456",
|
||||
CONF_HARDWARE_ADDRESS: "mock-hw",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
# Second time we should get already_configured
|
||||
result2 = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
data={CONF_CLOUD_ID: "abcdef", CONF_INSTALL_CODE: "123456"},
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
)
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_ABORT
|
||||
assert result2["reason"] == "already_configured"
|
||||
|
|
|
@ -12,11 +12,7 @@ from homeassistant.components.rainforest_eagle.const import (
|
|||
from homeassistant.components.rainforest_eagle.data import CannotConnect, InvalidAuth
|
||||
from homeassistant.const import CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
RESULT_TYPE_CREATE_ENTRY,
|
||||
RESULT_TYPE_FORM,
|
||||
)
|
||||
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
||||
|
||||
|
||||
async def test_form(hass: HomeAssistant) -> None:
|
||||
|
@ -88,42 +84,3 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
|||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_import(hass: HomeAssistant) -> None:
|
||||
"""Test we get the form."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.rainforest_eagle.data.async_get_type",
|
||||
return_value=(TYPE_EAGLE_200, "mock-hw"),
|
||||
), patch(
|
||||
"homeassistant.components.rainforest_eagle.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
data={CONF_CLOUD_ID: "abcdef", CONF_INSTALL_CODE: "123456"},
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "abcdef"
|
||||
assert result["data"] == {
|
||||
CONF_TYPE: TYPE_EAGLE_200,
|
||||
CONF_CLOUD_ID: "abcdef",
|
||||
CONF_INSTALL_CODE: "123456",
|
||||
CONF_HARDWARE_ADDRESS: "mock-hw",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
# Second time we should get already_configured
|
||||
result2 = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
data={CONF_CLOUD_ID: "abcdef", CONF_INSTALL_CODE: "123456"},
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
)
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_ABORT
|
||||
assert result2["reason"] == "already_configured"
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
"""Tests for rainforest eagle sensors."""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.rainforest_eagle.const import (
|
||||
CONF_CLOUD_ID,
|
||||
CONF_HARDWARE_ADDRESS,
|
||||
CONF_INSTALL_CODE,
|
||||
DOMAIN,
|
||||
TYPE_EAGLE_100,
|
||||
TYPE_EAGLE_200,
|
||||
)
|
||||
from homeassistant.const import CONF_TYPE
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
MOCK_CLOUD_ID = "12345"
|
||||
MOCK_200_RESPONSE_WITH_PRICE = {
|
||||
"zigbee:InstantaneousDemand": {
|
||||
"Name": "zigbee:InstantaneousDemand",
|
||||
"Value": "1.152000",
|
||||
},
|
||||
"zigbee:CurrentSummationDelivered": {
|
||||
"Name": "zigbee:CurrentSummationDelivered",
|
||||
"Value": "45251.285000",
|
||||
},
|
||||
"zigbee:CurrentSummationReceived": {
|
||||
"Name": "zigbee:CurrentSummationReceived",
|
||||
"Value": "232.232000",
|
||||
},
|
||||
"zigbee:Price": {"Name": "zigbee:Price", "Value": "0.053990"},
|
||||
"zigbee:PriceCurrency": {"Name": "zigbee:PriceCurrency", "Value": "USD"},
|
||||
}
|
||||
MOCK_200_RESPONSE_WITHOUT_PRICE = {
|
||||
"zigbee:InstantaneousDemand": {
|
||||
"Name": "zigbee:InstantaneousDemand",
|
||||
"Value": "1.152000",
|
||||
},
|
||||
"zigbee:CurrentSummationDelivered": {
|
||||
"Name": "zigbee:CurrentSummationDelivered",
|
||||
"Value": "45251.285000",
|
||||
},
|
||||
"zigbee:CurrentSummationReceived": {
|
||||
"Name": "zigbee:CurrentSummationReceived",
|
||||
"Value": "232.232000",
|
||||
},
|
||||
"zigbee:Price": {"Name": "zigbee:Price", "Value": "invalid"},
|
||||
"zigbee:PriceCurrency": {"Name": "zigbee:PriceCurrency", "Value": "USD"},
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_rainforest_200(hass):
|
||||
"""Set up rainforest."""
|
||||
MockConfigEntry(
|
||||
domain="rainforest_eagle",
|
||||
data={
|
||||
CONF_CLOUD_ID: MOCK_CLOUD_ID,
|
||||
CONF_INSTALL_CODE: "abcdefgh",
|
||||
CONF_HARDWARE_ADDRESS: "mock-hw-address",
|
||||
CONF_TYPE: TYPE_EAGLE_200,
|
||||
},
|
||||
).add_to_hass(hass)
|
||||
with patch(
|
||||
"aioeagle.ElectricMeter.get_device_query",
|
||||
return_value=MOCK_200_RESPONSE_WITHOUT_PRICE,
|
||||
) as mock_update:
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
yield mock_update
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_rainforest_100(hass):
|
||||
"""Set up rainforest."""
|
||||
MockConfigEntry(
|
||||
domain="rainforest_eagle",
|
||||
data={
|
||||
CONF_CLOUD_ID: MOCK_CLOUD_ID,
|
||||
CONF_INSTALL_CODE: "abcdefgh",
|
||||
CONF_HARDWARE_ADDRESS: None,
|
||||
CONF_TYPE: TYPE_EAGLE_100,
|
||||
},
|
||||
).add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.rainforest_eagle.data.Eagle100Reader",
|
||||
return_value=Mock(
|
||||
get_instantaneous_demand=Mock(
|
||||
return_value={"InstantaneousDemand": {"Demand": "1.152000"}}
|
||||
),
|
||||
get_current_summation=Mock(
|
||||
return_value={
|
||||
"CurrentSummation": {
|
||||
"SummationDelivered": "45251.285000",
|
||||
"SummationReceived": "232.232000",
|
||||
}
|
||||
}
|
||||
),
|
||||
),
|
||||
) as mock_update:
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
yield mock_update
|
||||
|
||||
|
||||
async def test_sensors_200(hass, setup_rainforest_200):
|
||||
"""Test the sensors."""
|
||||
assert len(hass.states.async_all()) == 3
|
||||
|
||||
demand = hass.states.get("sensor.meter_power_demand")
|
||||
assert demand is not None
|
||||
assert demand.state == "1.152000"
|
||||
assert demand.attributes["unit_of_measurement"] == "kW"
|
||||
|
||||
delivered = hass.states.get("sensor.total_meter_energy_delivered")
|
||||
assert delivered is not None
|
||||
assert delivered.state == "45251.285000"
|
||||
assert delivered.attributes["unit_of_measurement"] == "kWh"
|
||||
|
||||
received = hass.states.get("sensor.total_meter_energy_received")
|
||||
assert received is not None
|
||||
assert received.state == "232.232000"
|
||||
assert received.attributes["unit_of_measurement"] == "kWh"
|
||||
|
||||
setup_rainforest_200.return_value = MOCK_200_RESPONSE_WITH_PRICE
|
||||
|
||||
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(hass.states.async_all()) == 4
|
||||
|
||||
price = hass.states.get("sensor.meter_price")
|
||||
assert price is not None
|
||||
assert price.state == "0.053990"
|
||||
assert price.attributes["unit_of_measurement"] == "USD/kWh"
|
||||
|
||||
|
||||
async def test_sensors_100(hass, setup_rainforest_100):
|
||||
"""Test the sensors."""
|
||||
assert len(hass.states.async_all()) == 3
|
||||
|
||||
demand = hass.states.get("sensor.meter_power_demand")
|
||||
assert demand is not None
|
||||
assert demand.state == "1.152000"
|
||||
assert demand.attributes["unit_of_measurement"] == "kW"
|
||||
|
||||
delivered = hass.states.get("sensor.total_meter_energy_delivered")
|
||||
assert delivered is not None
|
||||
assert delivered.state == "45251.285000"
|
||||
assert delivered.attributes["unit_of_measurement"] == "kWh"
|
||||
|
||||
received = hass.states.get("sensor.total_meter_energy_received")
|
||||
assert received is not None
|
||||
assert received.state == "232.232000"
|
||||
assert received.attributes["unit_of_measurement"] == "kWh"
|
Loading…
Reference in New Issue