Add config flow for efergy (#56890)
This commit is contained in:
parent
3825f80a2d
commit
c4eeebd7a7
27 changed files with 851 additions and 184 deletions
|
@ -1 +1,83 @@
|
|||
"""The efergy component."""
|
||||
"""The Efergy integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from pyefergy import Efergy, exceptions
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
ATTR_IDENTIFIERS,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_MODEL,
|
||||
ATTR_NAME,
|
||||
ATTR_SW_VERSION,
|
||||
CONF_API_KEY,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
||||
from .const import ATTRIBUTION, DATA_KEY_API, DEFAULT_NAME, DOMAIN
|
||||
|
||||
PLATFORMS = [SENSOR_DOMAIN]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Efergy from a config entry."""
|
||||
api = Efergy(
|
||||
entry.data[CONF_API_KEY],
|
||||
session=async_get_clientsession(hass),
|
||||
utc_offset=hass.config.time_zone,
|
||||
currency=hass.config.currency,
|
||||
)
|
||||
|
||||
try:
|
||||
await api.async_status(get_sids=True)
|
||||
except (exceptions.ConnectError, exceptions.DataError) as ex:
|
||||
raise ConfigEntryNotReady(f"Failed to connect to device: {ex}") from ex
|
||||
except exceptions.InvalidAuth as ex:
|
||||
raise ConfigEntryAuthFailed(
|
||||
"API Key is no longer valid. Please reauthenticate"
|
||||
) from ex
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {DATA_KEY_API: api}
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
||||
|
||||
|
||||
class EfergyEntity(Entity):
|
||||
"""Representation of a Efergy entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api: Efergy,
|
||||
server_unique_id: str,
|
||||
) -> None:
|
||||
"""Initialize an Efergy entity."""
|
||||
self.api = api
|
||||
self._server_unique_id = server_unique_id
|
||||
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device information of the entity."""
|
||||
return {
|
||||
"connections": {(dr.CONNECTION_NETWORK_MAC, self.api.info["mac"])},
|
||||
ATTR_IDENTIFIERS: {(DOMAIN, self._server_unique_id)},
|
||||
ATTR_MANUFACTURER: DEFAULT_NAME,
|
||||
ATTR_NAME: DEFAULT_NAME,
|
||||
ATTR_MODEL: self.api.info["type"],
|
||||
ATTR_SW_VERSION: self.api.info["version"],
|
||||
}
|
||||
|
|
85
homeassistant/components/efergy/config_flow.py
Normal file
85
homeassistant/components/efergy/config_flow.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
"""Config flow for Efergy integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from pyefergy import Efergy, exceptions
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import CONF_APPTOKEN, DEFAULT_NAME, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EfergyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Efergy."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle a flow initiated by the user."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
api_key = user_input[CONF_API_KEY]
|
||||
|
||||
self._async_abort_entries_match({CONF_API_KEY: api_key})
|
||||
hid, error = await self._async_try_connect(api_key)
|
||||
if error is None:
|
||||
entry = await self.async_set_unique_id(hid)
|
||||
if entry:
|
||||
self.hass.config_entries.async_update_entry(entry, data=user_input)
|
||||
await self.hass.config_entries.async_reload(entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(
|
||||
title=DEFAULT_NAME,
|
||||
data={CONF_API_KEY: api_key},
|
||||
)
|
||||
errors["base"] = error
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): str,
|
||||
}
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_import(self, import_config: ConfigType):
|
||||
"""Import a config entry from configuration.yaml."""
|
||||
for entry in self._async_current_entries():
|
||||
if entry.data[CONF_API_KEY] == import_config[CONF_APPTOKEN]:
|
||||
_part = import_config[CONF_APPTOKEN][0:4]
|
||||
_msg = f"Efergy yaml config with partial key {_part} has been imported. Please remove it"
|
||||
_LOGGER.warning(_msg)
|
||||
return self.async_abort(reason="already_configured")
|
||||
return await self.async_step_user({CONF_API_KEY: import_config[CONF_APPTOKEN]})
|
||||
|
||||
async def async_step_reauth(self, config: dict[str, Any]) -> FlowResult:
|
||||
"""Handle a reauthorization flow request."""
|
||||
return await self.async_step_user()
|
||||
|
||||
async def _async_try_connect(self, api_key: str) -> tuple[str | None, str | None]:
|
||||
"""Try connecting to Efergy servers."""
|
||||
api = Efergy(api_key, session=async_get_clientsession(self.hass))
|
||||
try:
|
||||
await api.async_status()
|
||||
except exceptions.ConnectError:
|
||||
return None, "cannot_connect"
|
||||
except exceptions.InvalidAuth:
|
||||
return None, "invalid_auth"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
return None, "unknown"
|
||||
return api.info["hid"], None
|
13
homeassistant/components/efergy/const.py
Normal file
13
homeassistant/components/efergy/const.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
"""Constants for the Efergy integration."""
|
||||
from datetime import timedelta
|
||||
|
||||
ATTRIBUTION = "Data provided by Efergy"
|
||||
|
||||
CONF_APPTOKEN = "app_token"
|
||||
CONF_CURRENT_VALUES = "current_values"
|
||||
|
||||
DATA_KEY_API = "api"
|
||||
DEFAULT_NAME = "Efergy"
|
||||
DOMAIN = "efergy"
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
|
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"domain": "efergy",
|
||||
"name": "Efergy",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/efergy",
|
||||
"requirements": ["pyefergy==0.0.3"],
|
||||
"requirements": ["pyefergy==0.1.2"],
|
||||
"codeowners": ["@tkdrob"],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
|
|
@ -2,15 +2,18 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from re import sub
|
||||
|
||||
from pyefergy import Efergy, exceptions
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.efergy import EfergyEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_CURRENCY,
|
||||
CONF_MONITORED_VARIABLES,
|
||||
|
@ -22,72 +25,103 @@ from homeassistant.const import (
|
|||
POWER_WATT,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers import entity_platform
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
CONF_APPTOKEN = "app_token"
|
||||
CONF_UTC_OFFSET = "utc_offset"
|
||||
|
||||
CONF_PERIOD = "period"
|
||||
|
||||
CONF_INSTANT = "instant_readings"
|
||||
CONF_AMOUNT = "amount"
|
||||
CONF_BUDGET = "budget"
|
||||
CONF_COST = "cost"
|
||||
CONF_CURRENT_VALUES = "current_values"
|
||||
|
||||
DEFAULT_PERIOD = "year"
|
||||
DEFAULT_UTC_OFFSET = "0"
|
||||
from .const import CONF_APPTOKEN, CONF_CURRENT_VALUES, DATA_KEY_API, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SENSOR_TYPES: dict[str, SensorEntityDescription] = {
|
||||
CONF_INSTANT: SensorEntityDescription(
|
||||
key=CONF_INSTANT,
|
||||
name="Energy Usage",
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key="instant_readings",
|
||||
name="Power Usage",
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
native_unit_of_measurement=POWER_WATT,
|
||||
),
|
||||
CONF_AMOUNT: SensorEntityDescription(
|
||||
key=CONF_AMOUNT,
|
||||
name="Energy Consumed",
|
||||
SensorEntityDescription(
|
||||
key="energy_day",
|
||||
name="Daily Consumption",
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="energy_week",
|
||||
name="Weekly Consumption",
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="energy_month",
|
||||
name="Monthly Consumption",
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||
),
|
||||
CONF_BUDGET: SensorEntityDescription(
|
||||
key=CONF_BUDGET,
|
||||
name="Energy Budget",
|
||||
SensorEntityDescription(
|
||||
key="energy_year",
|
||||
name="Yearly Consumption",
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
CONF_COST: SensorEntityDescription(
|
||||
key=CONF_COST,
|
||||
name="Energy Cost",
|
||||
SensorEntityDescription(
|
||||
key="budget",
|
||||
name="Energy Budget",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="cost_day",
|
||||
name="Daily Energy Cost",
|
||||
device_class=DEVICE_CLASS_MONETARY,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="cost_week",
|
||||
name="Weekly Energy Cost",
|
||||
device_class=DEVICE_CLASS_MONETARY,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="cost_month",
|
||||
name="Monthly Energy Cost",
|
||||
device_class=DEVICE_CLASS_MONETARY,
|
||||
),
|
||||
CONF_CURRENT_VALUES: SensorEntityDescription(
|
||||
SensorEntityDescription(
|
||||
key="cost_year",
|
||||
name="Yearly Energy Cost",
|
||||
device_class=DEVICE_CLASS_MONETARY,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=CONF_CURRENT_VALUES,
|
||||
name="Per-Device Usage",
|
||||
name="Power Usage",
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
native_unit_of_measurement=POWER_WATT,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
TYPES_SCHEMA = vol.In(
|
||||
["current_values", "instant_readings", "amount", "budget", "cost"]
|
||||
)
|
||||
|
||||
TYPES_SCHEMA = vol.In(SENSOR_TYPES)
|
||||
|
||||
SENSORS_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_TYPE): TYPES_SCHEMA,
|
||||
vol.Optional(CONF_CURRENCY, default=""): cv.string,
|
||||
vol.Optional(CONF_PERIOD, default=DEFAULT_PERIOD): cv.string,
|
||||
vol.Optional("period", default="year"): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
# Deprecated in Home Assistant 2021.11
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_APPTOKEN): cv.string,
|
||||
vol.Optional(CONF_UTC_OFFSET, default=DEFAULT_UTC_OFFSET): cv.string,
|
||||
vol.Optional("utc_offset", default="0"): cv.string,
|
||||
vol.Required(CONF_MONITORED_VARIABLES): [SENSORS_SCHEMA],
|
||||
}
|
||||
)
|
||||
|
@ -99,62 +133,69 @@ async def async_setup_platform(
|
|||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType = None,
|
||||
) -> None:
|
||||
"""Set up the Efergy sensor."""
|
||||
api = Efergy(
|
||||
config[CONF_APPTOKEN],
|
||||
async_get_clientsession(hass),
|
||||
utc_offset=config[CONF_UTC_OFFSET],
|
||||
"""Set up the Efergy sensor from yaml."""
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=config
|
||||
)
|
||||
)
|
||||
|
||||
dev = []
|
||||
try:
|
||||
sensors = await api.get_sids()
|
||||
except (exceptions.DataError, exceptions.ConnectTimeout) as ex:
|
||||
raise PlatformNotReady("Error getting data from Efergy:") from ex
|
||||
for variable in config[CONF_MONITORED_VARIABLES]:
|
||||
if variable[CONF_TYPE] == CONF_CURRENT_VALUES:
|
||||
for sensor in sensors:
|
||||
dev.append(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: entity_platform.AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Efergy sensors."""
|
||||
api: Efergy = hass.data[DOMAIN][entry.entry_id][DATA_KEY_API]
|
||||
sensors = []
|
||||
for description in SENSOR_TYPES:
|
||||
if description.key != CONF_CURRENT_VALUES:
|
||||
sensors.append(
|
||||
EfergySensor(
|
||||
api,
|
||||
description,
|
||||
entry.entry_id,
|
||||
period=sub("^energy_|^cost_", "", description.key),
|
||||
currency=hass.config.currency,
|
||||
)
|
||||
)
|
||||
else:
|
||||
description.entity_registry_enabled_default = len(api.info["sids"]) > 1
|
||||
for sid in api.info["sids"]:
|
||||
sensors.append(
|
||||
EfergySensor(
|
||||
api,
|
||||
variable[CONF_PERIOD],
|
||||
variable[CONF_CURRENCY],
|
||||
SENSOR_TYPES[variable[CONF_TYPE]],
|
||||
sid=sensor["sid"],
|
||||
description,
|
||||
entry.entry_id,
|
||||
sid=sid,
|
||||
)
|
||||
)
|
||||
dev.append(
|
||||
EfergySensor(
|
||||
api,
|
||||
variable[CONF_PERIOD],
|
||||
variable[CONF_CURRENCY],
|
||||
SENSOR_TYPES[variable[CONF_TYPE]],
|
||||
)
|
||||
)
|
||||
|
||||
add_entities(dev, True)
|
||||
async_add_entities(sensors, True)
|
||||
|
||||
|
||||
class EfergySensor(SensorEntity):
|
||||
class EfergySensor(EfergyEntity, SensorEntity):
|
||||
"""Implementation of an Efergy sensor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api: Efergy,
|
||||
period: str,
|
||||
currency: str,
|
||||
description: SensorEntityDescription,
|
||||
sid: str = None,
|
||||
server_unique_id: str,
|
||||
period: str = None,
|
||||
currency: str = None,
|
||||
sid: str = "",
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(api, server_unique_id)
|
||||
self.entity_description = description
|
||||
if description.key == CONF_CURRENT_VALUES:
|
||||
self._attr_name = f"{description.name}_{sid}"
|
||||
self._attr_unique_id = f"{server_unique_id}/{description.key}_{sid}"
|
||||
if "cost" in description.key:
|
||||
self._attr_native_unit_of_measurement = currency
|
||||
self.sid = sid
|
||||
self.api = api
|
||||
self.period = period
|
||||
if sid:
|
||||
self._attr_name = f"efergy_{sid}"
|
||||
if description.key == CONF_COST:
|
||||
self._attr_native_unit_of_measurement = f"{currency}/{period}"
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Get the Efergy monitor data from the web service."""
|
||||
|
@ -162,11 +203,11 @@ class EfergySensor(SensorEntity):
|
|||
self._attr_native_value = await self.api.async_get_reading(
|
||||
self.entity_description.key, period=self.period, sid=self.sid
|
||||
)
|
||||
except (exceptions.DataError, exceptions.ConnectTimeout) as ex:
|
||||
except (exceptions.DataError, exceptions.ConnectError) as ex:
|
||||
if self._attr_available:
|
||||
self._attr_available = False
|
||||
_LOGGER.error("Error getting data from Efergy: %s", ex)
|
||||
_LOGGER.error("Error getting data: %s", ex)
|
||||
return
|
||||
if not self._attr_available:
|
||||
self._attr_available = True
|
||||
_LOGGER.info("Connection to Efergy has resumed")
|
||||
_LOGGER.info("Connection has resumed")
|
||||
|
|
21
homeassistant/components/efergy/strings.json
Normal file
21
homeassistant/components/efergy/strings.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Efergy",
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
}
|
||||
}
|
21
homeassistant/components/efergy/translations/en.json
Normal file
21
homeassistant/components/efergy/translations/en.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured",
|
||||
"reauth_successful": "Re-authentication was successful"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect",
|
||||
"invalid_auth": "Invalid authentication",
|
||||
"unknown": "Unexpected error"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key"
|
||||
},
|
||||
"title": "Efergy"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -70,6 +70,7 @@ FLOWS = [
|
|||
"eafm",
|
||||
"ecobee",
|
||||
"econet",
|
||||
"efergy",
|
||||
"elgato",
|
||||
"elkm1",
|
||||
"emonitor",
|
||||
|
|
|
@ -1447,7 +1447,7 @@ pyeconet==0.1.14
|
|||
pyedimax==0.2.1
|
||||
|
||||
# homeassistant.components.efergy
|
||||
pyefergy==0.0.3
|
||||
pyefergy==0.1.2
|
||||
|
||||
# homeassistant.components.eight_sleep
|
||||
pyeight==0.1.9
|
||||
|
|
|
@ -844,7 +844,7 @@ pydispatcher==2.0.5
|
|||
pyeconet==0.1.14
|
||||
|
||||
# homeassistant.components.efergy
|
||||
pyefergy==0.0.3
|
||||
pyefergy==0.1.2
|
||||
|
||||
# homeassistant.components.everlights
|
||||
pyeverlights==0.1.0
|
||||
|
|
|
@ -1 +1,157 @@
|
|||
"""Tests for the efergy component."""
|
||||
"""Tests for Efergy integration."""
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from pyefergy import Efergy, exceptions
|
||||
|
||||
from homeassistant.components.efergy import DOMAIN
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
TOKEN = "9p6QGJ7dpZfO3fqPTBk1fyEmjV1cGoLT"
|
||||
MULTI_SENSOR_TOKEN = "9r6QGF7dpZfO3fqPTBl1fyRmjV1cGoLT"
|
||||
|
||||
CONF_DATA = {CONF_API_KEY: TOKEN}
|
||||
HID = "12345678901234567890123456789012"
|
||||
IMPORT_DATA = {"platform": "efergy", "app_token": TOKEN}
|
||||
|
||||
BASE_URL = "https://engage.efergy.com/mobile_proxy/"
|
||||
|
||||
|
||||
def create_entry(hass: HomeAssistant, token: str = TOKEN) -> MockConfigEntry:
|
||||
"""Create Efergy entry in Home Assistant."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id=HID,
|
||||
data={CONF_API_KEY: token},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
return entry
|
||||
|
||||
|
||||
async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
token: str = TOKEN,
|
||||
error: bool = False,
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the Efergy integration in Home Assistant."""
|
||||
entry = create_entry(hass, token=token)
|
||||
await mock_responses(hass, aioclient_mock, token=token, error=error)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return entry
|
||||
|
||||
|
||||
async def mock_responses(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
token: str = TOKEN,
|
||||
error: bool = False,
|
||||
):
|
||||
"""Mock responses from Efergy."""
|
||||
base_url = "https://engage.efergy.com/mobile_proxy/"
|
||||
api = Efergy(
|
||||
token, session=async_get_clientsession(hass), utc_offset=hass.config.time_zone
|
||||
)
|
||||
offset = api._utc_offset # pylint: disable=protected-access
|
||||
if error:
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getInstant?token={token}",
|
||||
exc=exceptions.ConnectError,
|
||||
)
|
||||
return
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getStatus?token={token}",
|
||||
text=load_fixture("efergy/status.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getInstant?token={token}",
|
||||
text=load_fixture("efergy/instant.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getEnergy?token={token}&offset={offset}&period=day",
|
||||
text=load_fixture("efergy/daily_energy.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getEnergy?token={token}&offset={offset}&period=week",
|
||||
text=load_fixture("efergy/weekly_energy.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getEnergy?token={token}&offset={offset}&period=month",
|
||||
text=load_fixture("efergy/monthly_energy.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getEnergy?token={token}&offset={offset}&period=year",
|
||||
text=load_fixture("efergy/yearly_energy.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getBudget?token={token}",
|
||||
text=load_fixture("efergy/budget.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCost?token={token}&offset={offset}&period=day",
|
||||
text=load_fixture("efergy/daily_cost.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCost?token={token}&offset={offset}&period=week",
|
||||
text=load_fixture("efergy/weekly_cost.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCost?token={token}&offset={offset}&period=month",
|
||||
text=load_fixture("efergy/monthly_cost.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCost?token={token}&offset={offset}&period=year",
|
||||
text=load_fixture("efergy/yearly_cost.json"),
|
||||
)
|
||||
if token == TOKEN:
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCurrentValuesSummary?token={token}",
|
||||
text=load_fixture("efergy/current_values_single.json"),
|
||||
)
|
||||
else:
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCurrentValuesSummary?token={token}",
|
||||
text=load_fixture("efergy/current_values_multi.json"),
|
||||
)
|
||||
|
||||
|
||||
def _patch_efergy():
|
||||
mocked_efergy = AsyncMock()
|
||||
mocked_efergy.info = {}
|
||||
mocked_efergy.info["hid"] = HID
|
||||
mocked_efergy.info["mac"] = "AA:BB:CC:DD:EE:FF"
|
||||
mocked_efergy.info["status"] = "on"
|
||||
mocked_efergy.info["type"] = "EEEHub"
|
||||
mocked_efergy.info["version"] = "2.3.7"
|
||||
return patch(
|
||||
"homeassistant.components.efergy.config_flow.Efergy",
|
||||
return_value=mocked_efergy,
|
||||
)
|
||||
|
||||
|
||||
def _patch_efergy_status():
|
||||
return patch("homeassistant.components.efergy.config_flow.Efergy.async_status")
|
||||
|
||||
|
||||
async def setup_platform(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
platform: str,
|
||||
token: str = TOKEN,
|
||||
error: bool = False,
|
||||
):
|
||||
"""Set up the platform."""
|
||||
entry = await init_integration(hass, aioclient_mock, token=token, error=error)
|
||||
|
||||
with patch("homeassistant.components.efergy.PLATFORMS", [platform]):
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
return entry
|
||||
|
|
134
tests/components/efergy/test_config_flow.py
Normal file
134
tests/components/efergy/test_config_flow.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
"""Test Efergy config flow."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyefergy import exceptions
|
||||
|
||||
from homeassistant.components.efergy.const import DEFAULT_NAME, DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH, SOURCE_USER
|
||||
from homeassistant.const import CONF_API_KEY, CONF_SOURCE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
RESULT_TYPE_CREATE_ENTRY,
|
||||
RESULT_TYPE_FORM,
|
||||
)
|
||||
|
||||
from . import (
|
||||
CONF_DATA,
|
||||
HID,
|
||||
IMPORT_DATA,
|
||||
_patch_efergy,
|
||||
_patch_efergy_status,
|
||||
create_entry,
|
||||
)
|
||||
|
||||
|
||||
def _patch_setup():
|
||||
return patch("homeassistant.components.efergy.async_setup_entry")
|
||||
|
||||
|
||||
async def test_flow_user(hass: HomeAssistant):
|
||||
"""Test user initialized flow."""
|
||||
with _patch_efergy(), _patch_setup():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={CONF_SOURCE: SOURCE_USER},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=CONF_DATA,
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == DEFAULT_NAME
|
||||
assert result["data"] == CONF_DATA
|
||||
assert result["result"].unique_id == HID
|
||||
|
||||
|
||||
async def test_flow_user_cannot_connect(hass: HomeAssistant):
|
||||
"""Test user initialized flow with unreachable service."""
|
||||
with _patch_efergy_status() as efergymock:
|
||||
efergymock.side_effect = exceptions.ConnectError
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data=CONF_DATA
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"]["base"] == "cannot_connect"
|
||||
|
||||
|
||||
async def test_flow_user_invalid_auth(hass: HomeAssistant):
|
||||
"""Test user initialized flow with invalid authentication."""
|
||||
with _patch_efergy_status() as efergymock:
|
||||
efergymock.side_effect = exceptions.InvalidAuth
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data=CONF_DATA
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"]["base"] == "invalid_auth"
|
||||
|
||||
|
||||
async def test_flow_user_unknown(hass: HomeAssistant):
|
||||
"""Test user initialized flow with unknown error."""
|
||||
with _patch_efergy_status() as efergymock:
|
||||
efergymock.side_effect = Exception
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data=CONF_DATA
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"]["base"] == "unknown"
|
||||
|
||||
|
||||
async def test_flow_import(hass: HomeAssistant):
|
||||
"""Test import step."""
|
||||
with _patch_efergy(), _patch_setup():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_IMPORT}, data=IMPORT_DATA
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == DEFAULT_NAME
|
||||
assert result["data"] == CONF_DATA
|
||||
assert result["result"].unique_id == HID
|
||||
|
||||
|
||||
async def test_flow_import_already_configured(hass: HomeAssistant):
|
||||
"""Test import step already configured."""
|
||||
create_entry(hass)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_IMPORT}, data=IMPORT_DATA
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_flow_reauth(hass: HomeAssistant):
|
||||
"""Test reauth step."""
|
||||
entry = create_entry(hass)
|
||||
with _patch_efergy(), _patch_setup():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
CONF_SOURCE: SOURCE_REAUTH,
|
||||
"entry_id": entry.entry_id,
|
||||
"unique_id": entry.unique_id,
|
||||
},
|
||||
data=CONF_DATA,
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
new_conf = {CONF_API_KEY: "1234567890"}
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=new_conf,
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
assert entry.data == new_conf
|
61
tests/components/efergy/test_init.py
Normal file
61
tests/components/efergy/test_init.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
"""Test Efergy integration."""
|
||||
from pyefergy import exceptions
|
||||
|
||||
from homeassistant.components.efergy.const import DEFAULT_NAME, DOMAIN
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from . import _patch_efergy_status, create_entry, init_integration, setup_platform
|
||||
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
async def test_setup(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker):
|
||||
"""Test unload."""
|
||||
entry = await init_integration(hass, aioclient_mock)
|
||||
assert entry.state == ConfigEntryState.LOADED
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.NOT_LOADED
|
||||
assert not hass.data.get(DOMAIN)
|
||||
|
||||
|
||||
async def test_async_setup_entry_not_ready(hass: HomeAssistant):
|
||||
"""Test that it throws ConfigEntryNotReady when exception occurs during setup."""
|
||||
entry = create_entry(hass)
|
||||
with _patch_efergy_status() as efergymock:
|
||||
efergymock.side_effect = (exceptions.ConnectError, exceptions.DataError)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert not hass.data.get(DOMAIN)
|
||||
|
||||
|
||||
async def test_async_setup_entry_auth_failed(hass: HomeAssistant):
|
||||
"""Test that it throws ConfigEntryAuthFailed when authentication fails."""
|
||||
entry = create_entry(hass)
|
||||
with _patch_efergy_status() as efergymock:
|
||||
efergymock.side_effect = exceptions.InvalidAuth
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert entry.state == ConfigEntryState.SETUP_ERROR
|
||||
assert not hass.data.get(DOMAIN)
|
||||
|
||||
|
||||
async def test_device_info(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker):
|
||||
"""Test device info."""
|
||||
entry = await setup_platform(hass, aioclient_mock, SENSOR_DOMAIN)
|
||||
device_registry = await dr.async_get_registry(hass)
|
||||
|
||||
device = device_registry.async_get_device({(DOMAIN, entry.entry_id)})
|
||||
|
||||
assert device.connections == {("mac", "ff:ff:ff:ff:ff:ff")}
|
||||
assert device.identifiers == {(DOMAIN, entry.entry_id)}
|
||||
assert device.manufacturer == DEFAULT_NAME
|
||||
assert device.model == "EEEHub"
|
||||
assert device.name == DEFAULT_NAME
|
||||
assert device.sw_version == "2.3.7"
|
|
@ -1,135 +1,125 @@
|
|||
"""The tests for Efergy sensor platform."""
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.components.efergy.sensor import SENSOR_TYPES
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_MONETARY,
|
||||
DEVICE_CLASS_POWER,
|
||||
ENERGY_KILO_WATT_HOUR,
|
||||
POWER_WATT,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_registry import EntityRegistry
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import async_fire_time_changed, load_fixture
|
||||
from . import MULTI_SENSOR_TOKEN, mock_responses, setup_platform
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
token = "9p6QGJ7dpZfO3fqPTBk1fyEmjV1cGoLT"
|
||||
multi_sensor_token = "9r6QGF7dpZfO3fqPTBl1fyRmjV1cGoLT"
|
||||
|
||||
ONE_SENSOR_CONFIG = {
|
||||
"platform": "efergy",
|
||||
"app_token": token,
|
||||
"utc_offset": "300",
|
||||
"monitored_variables": [
|
||||
{"type": "amount", "period": "day"},
|
||||
{"type": "instant_readings"},
|
||||
{"type": "budget"},
|
||||
{"type": "cost", "period": "day", "currency": "$"},
|
||||
{"type": "current_values"},
|
||||
],
|
||||
}
|
||||
|
||||
MULTI_SENSOR_CONFIG = {
|
||||
"platform": "efergy",
|
||||
"app_token": multi_sensor_token,
|
||||
"utc_offset": "300",
|
||||
"monitored_variables": [{"type": "current_values"}],
|
||||
}
|
||||
|
||||
|
||||
def mock_responses(aioclient_mock: AiohttpClientMocker, error: bool = False):
|
||||
"""Mock responses for Efergy."""
|
||||
base_url = "https://engage.efergy.com/mobile_proxy/"
|
||||
if error:
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCurrentValuesSummary?token={token}", exc=asyncio.TimeoutError
|
||||
)
|
||||
return
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getInstant?token={token}",
|
||||
text=load_fixture("efergy/efergy_instant.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getEnergy?token={token}&offset=300&period=day",
|
||||
text=load_fixture("efergy/efergy_energy.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getBudget?token={token}",
|
||||
text=load_fixture("efergy/efergy_budget.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCost?token={token}&offset=300&period=day",
|
||||
text=load_fixture("efergy/efergy_cost.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCurrentValuesSummary?token={token}",
|
||||
text=load_fixture("efergy/efergy_current_values_single.json"),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"{base_url}getCurrentValuesSummary?token={multi_sensor_token}",
|
||||
text=load_fixture("efergy/efergy_current_values_multi.json"),
|
||||
)
|
||||
|
||||
|
||||
async def test_single_sensor_readings(
|
||||
async def test_sensor_readings(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
):
|
||||
"""Test for successfully setting up the Efergy platform."""
|
||||
mock_responses(aioclient_mock)
|
||||
assert await async_setup_component(
|
||||
hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: ONE_SENSOR_CONFIG}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
for description in SENSOR_TYPES:
|
||||
description.entity_registry_enabled_default = True
|
||||
entry = await setup_platform(hass, aioclient_mock, SENSOR_DOMAIN)
|
||||
ent_reg: EntityRegistry = er.async_get(hass)
|
||||
|
||||
assert hass.states.get("sensor.energy_consumed").state == "38.21"
|
||||
assert hass.states.get("sensor.energy_usage").state == "1580"
|
||||
assert hass.states.get("sensor.energy_budget").state == "ok"
|
||||
assert hass.states.get("sensor.energy_cost").state == "5.27"
|
||||
assert hass.states.get("sensor.efergy_728386").state == "1628"
|
||||
state = hass.states.get("sensor.power_usage")
|
||||
assert state.state == "1580"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT
|
||||
state = hass.states.get("sensor.energy_budget")
|
||||
assert state.state == "ok"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) is None
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None
|
||||
state = hass.states.get("sensor.daily_consumption")
|
||||
assert state.state == "38.21"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||
state = hass.states.get("sensor.weekly_consumption")
|
||||
assert state.state == "267.47"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||
state = hass.states.get("sensor.monthly_consumption")
|
||||
assert state.state == "1069.88"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||
state = hass.states.get("sensor.yearly_consumption")
|
||||
assert state.state == "13373.50"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||
state = hass.states.get("sensor.daily_energy_cost")
|
||||
assert state.state == "5.27"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_MONETARY
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "EUR"
|
||||
state = hass.states.get("sensor.weekly_energy_cost")
|
||||
assert state.state == "36.89"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_MONETARY
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "EUR"
|
||||
state = hass.states.get("sensor.monthly_energy_cost")
|
||||
assert state.state == "147.56"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_MONETARY
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "EUR"
|
||||
state = hass.states.get("sensor.yearly_energy_cost")
|
||||
assert state.state == "1844.50"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_MONETARY
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "EUR"
|
||||
entity = ent_reg.async_get("sensor.power_usage_728386")
|
||||
assert entity.disabled_by == er.DISABLED_INTEGRATION
|
||||
ent_reg.async_update_entity(entity.entity_id, **{"disabled_by": None})
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.power_usage_728386")
|
||||
assert state.state == "1628"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT
|
||||
|
||||
|
||||
async def test_multi_sensor_readings(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
):
|
||||
"""Test for multiple sensors in one household."""
|
||||
mock_responses(aioclient_mock)
|
||||
assert await async_setup_component(
|
||||
hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: MULTI_SENSOR_CONFIG}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.efergy_728386").state == "218"
|
||||
assert hass.states.get("sensor.efergy_0").state == "1808"
|
||||
assert hass.states.get("sensor.efergy_728387").state == "312"
|
||||
|
||||
|
||||
async def test_failed_getting_sids(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
):
|
||||
"""Test failed gettings sids."""
|
||||
mock_responses(aioclient_mock, error=True)
|
||||
assert await async_setup_component(
|
||||
hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: ONE_SENSOR_CONFIG}
|
||||
)
|
||||
assert not hass.states.async_all("sensor")
|
||||
for description in SENSOR_TYPES:
|
||||
description.entity_registry_enabled_default = True
|
||||
await setup_platform(hass, aioclient_mock, SENSOR_DOMAIN, MULTI_SENSOR_TOKEN)
|
||||
state = hass.states.get("sensor.power_usage_728386")
|
||||
assert state.state == "218"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT
|
||||
state = hass.states.get("sensor.power_usage_0")
|
||||
assert state.state == "1808"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT
|
||||
state = hass.states.get("sensor.power_usage_728387")
|
||||
assert state.state == "312"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT
|
||||
|
||||
|
||||
async def test_failed_update_and_reconnection(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
):
|
||||
"""Test failed update and reconnection."""
|
||||
mock_responses(aioclient_mock)
|
||||
assert await async_setup_component(
|
||||
hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: ONE_SENSOR_CONFIG}
|
||||
)
|
||||
await setup_platform(hass, aioclient_mock, SENSOR_DOMAIN)
|
||||
assert hass.states.get("sensor.power_usage").state == "1580"
|
||||
aioclient_mock.clear_requests()
|
||||
mock_responses(aioclient_mock, error=True)
|
||||
next_update = dt_util.utcnow() + timedelta(seconds=3)
|
||||
async_fire_time_changed(hass, next_update)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get("sensor.efergy_728386").state == STATE_UNAVAILABLE
|
||||
aioclient_mock.clear_requests()
|
||||
mock_responses(aioclient_mock)
|
||||
await mock_responses(hass, aioclient_mock, error=True)
|
||||
next_update = dt_util.utcnow() + timedelta(seconds=30)
|
||||
async_fire_time_changed(hass, next_update)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get("sensor.efergy_728386").state == "1628"
|
||||
assert hass.states.get("sensor.power_usage").state == STATE_UNAVAILABLE
|
||||
aioclient_mock.clear_requests()
|
||||
await mock_responses(hass, aioclient_mock)
|
||||
next_update = dt_util.utcnow() + timedelta(seconds=30)
|
||||
async_fire_time_changed(hass, next_update)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get("sensor.power_usage").state == "1580"
|
||||
|
|
5
tests/fixtures/efergy/monthly_cost.json
vendored
Normal file
5
tests/fixtures/efergy/monthly_cost.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"sum": "147.56",
|
||||
"duration": 2537340,
|
||||
"units": "GBP"
|
||||
}
|
5
tests/fixtures/efergy/monthly_energy.json
vendored
Normal file
5
tests/fixtures/efergy/monthly_energy.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"sum": "1069.88",
|
||||
"duration": 2537340,
|
||||
"units": "kWh"
|
||||
}
|
31
tests/fixtures/efergy/status.json
vendored
Normal file
31
tests/fixtures/efergy/status.json
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"hid":"1234567890abcdef1234567890abcdef",
|
||||
"listOfMacs":[
|
||||
{
|
||||
"listofchannels":[
|
||||
{
|
||||
"assoc":1,
|
||||
"cid":"cid.ffffffffffff",
|
||||
"reading":null,
|
||||
"ts":1632961265,
|
||||
"tsDelta":1,
|
||||
"tsHuman":"Thu Sep 30 00:00:00 2021",
|
||||
"type":{
|
||||
"battery":5,
|
||||
"falseBattery":0,
|
||||
"id":null,
|
||||
"name":"EFCT"
|
||||
}
|
||||
}
|
||||
],
|
||||
"mac":"ffffffffffff",
|
||||
"personality":"E1",
|
||||
"status":"on",
|
||||
"ts":1632961265,
|
||||
"tsDelta":1,
|
||||
"tsHuman":"Thu Sep 30 00:00:00 2021",
|
||||
"type":"EEEHub",
|
||||
"version":"2.3.7"
|
||||
}
|
||||
]
|
||||
}
|
5
tests/fixtures/efergy/weekly_cost.json
vendored
Normal file
5
tests/fixtures/efergy/weekly_cost.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"sum": "36.89",
|
||||
"duration": 377280,
|
||||
"units": "GBP"
|
||||
}
|
5
tests/fixtures/efergy/weekly_energy.json
vendored
Normal file
5
tests/fixtures/efergy/weekly_energy.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"sum": "267.47",
|
||||
"duration": 377280,
|
||||
"units": "kWh"
|
||||
}
|
5
tests/fixtures/efergy/yearly_cost.json
vendored
Normal file
5
tests/fixtures/efergy/yearly_cost.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"sum": "1844.50",
|
||||
"duration": 23532540,
|
||||
"units": "GBP"
|
||||
}
|
5
tests/fixtures/efergy/yearly_energy.json
vendored
Normal file
5
tests/fixtures/efergy/yearly_energy.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"sum": "13373.50",
|
||||
"duration": 23532540,
|
||||
"units": "kWh"
|
||||
}
|
Loading…
Add table
Reference in a new issue