mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +00:00
Add config flow for utility_meter (#68457)
This commit is contained in:
parent
d81ee9b2da
commit
bdb61e0222
@ -7,10 +7,12 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
|
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_NAME, Platform
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.core import HomeAssistant, split_entity_id
|
||||||
|
from homeassistant.helpers import discovery, entity_registry as er
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
@ -30,6 +32,8 @@ from .const import (
|
|||||||
DATA_UTILITY,
|
DATA_UTILITY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
METER_TYPES,
|
METER_TYPES,
|
||||||
|
SERVICE_RESET,
|
||||||
|
SIGNAL_RESET_METER,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -100,6 +104,34 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
|
|
||||||
hass.data[DATA_UTILITY] = {}
|
hass.data[DATA_UTILITY] = {}
|
||||||
|
|
||||||
|
async def async_reset_meters(service_call):
|
||||||
|
"""Reset all sensors of a meter."""
|
||||||
|
entity_id = service_call.data["entity_id"]
|
||||||
|
|
||||||
|
domain = split_entity_id(entity_id)[0]
|
||||||
|
if domain == DOMAIN:
|
||||||
|
for entity in hass.data[DATA_LEGACY_COMPONENT].entities:
|
||||||
|
if entity_id == entity.entity_id:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"forward reset meter from %s to %s",
|
||||||
|
entity_id,
|
||||||
|
entity.tracked_entity_id,
|
||||||
|
)
|
||||||
|
entity_id = entity.tracked_entity_id
|
||||||
|
|
||||||
|
_LOGGER.debug("reset meter %s", entity_id)
|
||||||
|
async_dispatcher_send(hass, SIGNAL_RESET_METER, entity_id)
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_RESET,
|
||||||
|
async_reset_meters,
|
||||||
|
vol.Schema({ATTR_ENTITY_ID: cv.entity_id}),
|
||||||
|
)
|
||||||
|
|
||||||
|
if DOMAIN not in config:
|
||||||
|
return True
|
||||||
|
|
||||||
for meter, conf in config[DOMAIN].items():
|
for meter, conf in config[DOMAIN].items():
|
||||||
_LOGGER.debug("Setup %s.%s", DOMAIN, meter)
|
_LOGGER.debug("Setup %s.%s", DOMAIN, meter)
|
||||||
|
|
||||||
@ -151,3 +183,59 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Set up Utility Meter from a config entry."""
|
||||||
|
entity_registry = er.async_get(hass)
|
||||||
|
hass.data[DATA_UTILITY][entry.entry_id] = {}
|
||||||
|
hass.data[DATA_UTILITY][entry.entry_id][DATA_TARIFF_SENSORS] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
er.async_validate_entity_id(entity_registry, entry.options[CONF_SOURCE_SENSOR])
|
||||||
|
except vol.Invalid:
|
||||||
|
# The entity is identified by an unknown entity registry ID
|
||||||
|
_LOGGER.error(
|
||||||
|
"Failed to setup utility_meter for unknown entity %s",
|
||||||
|
entry.options[CONF_SOURCE_SENSOR],
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not entry.options.get(CONF_TARIFFS):
|
||||||
|
# Only a single meter sensor is required
|
||||||
|
hass.data[DATA_UTILITY][entry.entry_id][CONF_TARIFF_ENTITY] = None
|
||||||
|
hass.config_entries.async_setup_platforms(entry, (Platform.SENSOR,))
|
||||||
|
else:
|
||||||
|
# Create tariff selection + one meter sensor for each tariff
|
||||||
|
entity_entry = entity_registry.async_get_or_create(
|
||||||
|
Platform.SELECT, DOMAIN, entry.entry_id, suggested_object_id=entry.title
|
||||||
|
)
|
||||||
|
hass.data[DATA_UTILITY][entry.entry_id][
|
||||||
|
CONF_TARIFF_ENTITY
|
||||||
|
] = entity_entry.entity_id
|
||||||
|
hass.config_entries.async_setup_platforms(
|
||||||
|
entry, (Platform.SELECT, Platform.SENSOR)
|
||||||
|
)
|
||||||
|
|
||||||
|
entry.async_on_unload(entry.add_update_listener(config_entry_update_listener))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
|
"""Update listener, called when the config entry options are changed."""
|
||||||
|
await hass.config_entries.async_reload(entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Unload a config entry."""
|
||||||
|
if unload_ok := await hass.config_entries.async_unload_platforms(
|
||||||
|
entry,
|
||||||
|
(
|
||||||
|
Platform.SELECT,
|
||||||
|
Platform.SENSOR,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
hass.data[DATA_UTILITY].pop(entry.entry_id)
|
||||||
|
|
||||||
|
return unload_ok
|
||||||
|
119
homeassistant/components/utility_meter/config_flow.py
Normal file
119
homeassistant/components/utility_meter/config_flow.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
"""Config flow for Utility Meter integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from typing import Any, cast
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT
|
||||||
|
from homeassistant.helpers import selector
|
||||||
|
from homeassistant.helpers.helper_config_entry_flow import (
|
||||||
|
HelperConfigFlowHandler,
|
||||||
|
HelperFlowError,
|
||||||
|
HelperFlowFormStep,
|
||||||
|
HelperFlowMenuStep,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
BIMONTHLY,
|
||||||
|
CONF_METER_DELTA_VALUES,
|
||||||
|
CONF_METER_NET_CONSUMPTION,
|
||||||
|
CONF_METER_OFFSET,
|
||||||
|
CONF_METER_TYPE,
|
||||||
|
CONF_SOURCE_SENSOR,
|
||||||
|
CONF_TARIFFS,
|
||||||
|
DAILY,
|
||||||
|
DOMAIN,
|
||||||
|
HOURLY,
|
||||||
|
MONTHLY,
|
||||||
|
QUARTER_HOURLY,
|
||||||
|
QUARTERLY,
|
||||||
|
WEEKLY,
|
||||||
|
YEARLY,
|
||||||
|
)
|
||||||
|
|
||||||
|
METER_TYPES = [
|
||||||
|
{"value": "none", "label": "No cycle"},
|
||||||
|
{"value": QUARTER_HOURLY, "label": "Every 15 minutes"},
|
||||||
|
{"value": HOURLY, "label": "Hourly"},
|
||||||
|
{"value": DAILY, "label": "Daily"},
|
||||||
|
{"value": WEEKLY, "label": "Weekly"},
|
||||||
|
{"value": MONTHLY, "label": "Monthly"},
|
||||||
|
{"value": BIMONTHLY, "label": "Every two months"},
|
||||||
|
{"value": QUARTERLY, "label": "Quarterly"},
|
||||||
|
{"value": YEARLY, "label": "Yearly"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_config(data: Any) -> Any:
|
||||||
|
"""Validate config."""
|
||||||
|
tariffs: list[str]
|
||||||
|
if not data[CONF_TARIFFS]:
|
||||||
|
tariffs = []
|
||||||
|
else:
|
||||||
|
tariffs = data[CONF_TARIFFS].split(",")
|
||||||
|
try:
|
||||||
|
vol.Unique()(tariffs)
|
||||||
|
except vol.Invalid as exc:
|
||||||
|
raise HelperFlowError("tariffs_not_unique") from exc
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
OPTIONS_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_SOURCE_SENSOR): selector.selector(
|
||||||
|
{"entity": {"domain": "sensor"}},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_NAME): selector.selector({"text": {}}),
|
||||||
|
vol.Required(CONF_SOURCE_SENSOR): selector.selector(
|
||||||
|
{"entity": {"domain": "sensor"}},
|
||||||
|
),
|
||||||
|
vol.Required(CONF_METER_TYPE): selector.selector(
|
||||||
|
{"select": {"options": METER_TYPES}}
|
||||||
|
),
|
||||||
|
vol.Required(CONF_METER_OFFSET, default=0): selector.selector(
|
||||||
|
{
|
||||||
|
"number": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 28,
|
||||||
|
"mode": "box",
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: "days",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
vol.Optional(CONF_TARIFFS): selector.selector({"text": {}}),
|
||||||
|
vol.Required(CONF_METER_NET_CONSUMPTION, default=False): selector.selector(
|
||||||
|
{"boolean": {}}
|
||||||
|
),
|
||||||
|
vol.Required(CONF_METER_DELTA_VALUES, default=False): selector.selector(
|
||||||
|
{"boolean": {}}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_FLOW: dict[str, HelperFlowFormStep | HelperFlowMenuStep] = {
|
||||||
|
"user": HelperFlowFormStep(CONFIG_SCHEMA, validate_user_input=_validate_config)
|
||||||
|
}
|
||||||
|
|
||||||
|
OPTIONS_FLOW: dict[str, HelperFlowFormStep | HelperFlowMenuStep] = {
|
||||||
|
"init": HelperFlowFormStep(OPTIONS_SCHEMA)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFlowHandler(HelperConfigFlowHandler, domain=DOMAIN):
|
||||||
|
"""Handle a config or options flow for Utility Meter."""
|
||||||
|
|
||||||
|
config_flow = CONFIG_FLOW
|
||||||
|
options_flow = OPTIONS_FLOW
|
||||||
|
|
||||||
|
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
||||||
|
"""Return config entry title."""
|
||||||
|
|
||||||
|
return cast(str, options[CONF_NAME])
|
@ -1,10 +1,18 @@
|
|||||||
{
|
{
|
||||||
"domain": "utility_meter",
|
"domain": "utility_meter",
|
||||||
|
"integration_type": "helper",
|
||||||
"name": "Utility Meter",
|
"name": "Utility Meter",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/utility_meter",
|
"documentation": "https://www.home-assistant.io/integrations/utility_meter",
|
||||||
"requirements": ["croniter==1.0.6"],
|
"requirements": [
|
||||||
"codeowners": ["@dgomes"],
|
"croniter==1.0.6"
|
||||||
|
],
|
||||||
|
"codeowners": [
|
||||||
|
"@dgomes"
|
||||||
|
],
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["croniter"]
|
"loggers": [
|
||||||
|
"croniter"
|
||||||
|
],
|
||||||
|
"config_flow": true
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,12 @@ from homeassistant.components.select.const import (
|
|||||||
DOMAIN as SELECT_DOMAIN,
|
DOMAIN as SELECT_DOMAIN,
|
||||||
SERVICE_SELECT_OPTION,
|
SERVICE_SELECT_OPTION,
|
||||||
)
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, STATE_UNAVAILABLE
|
from homeassistant.const import ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, STATE_UNAVAILABLE
|
||||||
from homeassistant.core import Event, callback, split_entity_id
|
from homeassistant.core import Event, HomeAssistant, callback, split_entity_id
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_track_state_change_event
|
from homeassistant.helpers.event import async_track_state_change_event
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
|
|
||||||
@ -26,17 +27,34 @@ from .const import (
|
|||||||
CONF_METER,
|
CONF_METER,
|
||||||
CONF_TARIFFS,
|
CONF_TARIFFS,
|
||||||
DATA_LEGACY_COMPONENT,
|
DATA_LEGACY_COMPONENT,
|
||||||
DOMAIN,
|
|
||||||
SERVICE_RESET,
|
|
||||||
SERVICE_SELECT_NEXT_TARIFF,
|
SERVICE_SELECT_NEXT_TARIFF,
|
||||||
SERVICE_SELECT_TARIFF,
|
SERVICE_SELECT_TARIFF,
|
||||||
SIGNAL_RESET_METER,
|
|
||||||
TARIFF_ICON,
|
TARIFF_ICON,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize Utility Meter config entry."""
|
||||||
|
name = config_entry.title
|
||||||
|
|
||||||
|
# Remove when frontend list selector is available
|
||||||
|
if not config_entry.options.get(CONF_TARIFFS):
|
||||||
|
tariffs = []
|
||||||
|
else:
|
||||||
|
tariffs = config_entry.options[CONF_TARIFFS].split(",")
|
||||||
|
|
||||||
|
legacy_add_entities = None
|
||||||
|
unique_id = config_entry.entry_id
|
||||||
|
tariff_select = TariffSelect(name, tariffs, legacy_add_entities, unique_id)
|
||||||
|
async_add_entities([tariff_select])
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, conf, async_add_entities, discovery_info=None):
|
async def async_setup_platform(hass, conf, async_add_entities, discovery_info=None):
|
||||||
"""Set up the utility meter select."""
|
"""Set up the utility meter select."""
|
||||||
legacy_component = hass.data[DATA_LEGACY_COMPONENT]
|
legacy_component = hass.data[DATA_LEGACY_COMPONENT]
|
||||||
@ -46,35 +64,11 @@ async def async_setup_platform(hass, conf, async_add_entities, discovery_info=No
|
|||||||
discovery_info[CONF_METER],
|
discovery_info[CONF_METER],
|
||||||
discovery_info[CONF_TARIFFS],
|
discovery_info[CONF_TARIFFS],
|
||||||
legacy_component.async_add_entities,
|
legacy_component.async_add_entities,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_reset_meters(service_call):
|
|
||||||
"""Reset all sensors of a meter."""
|
|
||||||
entity_id = service_call.data["entity_id"]
|
|
||||||
|
|
||||||
domain = split_entity_id(entity_id)[0]
|
|
||||||
if domain == DOMAIN:
|
|
||||||
for entity in legacy_component.entities:
|
|
||||||
if entity_id == entity.entity_id:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"forward reset meter from %s to %s",
|
|
||||||
entity_id,
|
|
||||||
entity.tracked_entity_id,
|
|
||||||
)
|
|
||||||
entity_id = entity.tracked_entity_id
|
|
||||||
|
|
||||||
_LOGGER.debug("reset meter %s", entity_id)
|
|
||||||
async_dispatcher_send(hass, SIGNAL_RESET_METER, entity_id)
|
|
||||||
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_RESET,
|
|
||||||
async_reset_meters,
|
|
||||||
vol.Schema({ATTR_ENTITY_ID: cv.entity_id}),
|
|
||||||
)
|
|
||||||
|
|
||||||
legacy_component.async_register_entity_service(
|
legacy_component.async_register_entity_service(
|
||||||
SERVICE_SELECT_TARIFF,
|
SERVICE_SELECT_TARIFF,
|
||||||
{vol.Required(ATTR_TARIFF): cv.string},
|
{vol.Required(ATTR_TARIFF): cv.string},
|
||||||
@ -89,9 +83,10 @@ async def async_setup_platform(hass, conf, async_add_entities, discovery_info=No
|
|||||||
class TariffSelect(SelectEntity, RestoreEntity):
|
class TariffSelect(SelectEntity, RestoreEntity):
|
||||||
"""Representation of a Tariff selector."""
|
"""Representation of a Tariff selector."""
|
||||||
|
|
||||||
def __init__(self, name, tariffs, add_legacy_entities):
|
def __init__(self, name, tariffs, add_legacy_entities, unique_id):
|
||||||
"""Initialize a tariff selector."""
|
"""Initialize a tariff selector."""
|
||||||
self._attr_name = name
|
self._attr_name = name
|
||||||
|
self._attr_unique_id = unique_id
|
||||||
self._current_tariff = None
|
self._current_tariff = None
|
||||||
self._tariffs = tariffs
|
self._tariffs = tariffs
|
||||||
self._attr_icon = TARIFF_ICON
|
self._attr_icon = TARIFF_ICON
|
||||||
@ -112,7 +107,8 @@ class TariffSelect(SelectEntity, RestoreEntity):
|
|||||||
"""Run when entity about to be added."""
|
"""Run when entity about to be added."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
await self._add_legacy_entities([LegacyTariffSelect(self.entity_id)])
|
if self._add_legacy_entities:
|
||||||
|
await self._add_legacy_entities([LegacyTariffSelect(self.entity_id)])
|
||||||
|
|
||||||
state = await self.async_get_last_state()
|
state = await self.async_get_last_state()
|
||||||
if not state or state.state not in self._tariffs:
|
if not state or state.state not in self._tariffs:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Utility meter from sensors providing raw data."""
|
"""Utility meter from sensors providing raw data."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal, DecimalException, InvalidOperation
|
from decimal import Decimal, DecimalException, InvalidOperation
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -14,6 +14,7 @@ from homeassistant.components.sensor import (
|
|||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
@ -24,7 +25,7 @@ from homeassistant.const import (
|
|||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import entity_platform
|
from homeassistant.helpers import entity_platform, entity_registry as er
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import (
|
from homeassistant.helpers.event import (
|
||||||
@ -49,6 +50,7 @@ from .const import (
|
|||||||
CONF_SOURCE_SENSOR,
|
CONF_SOURCE_SENSOR,
|
||||||
CONF_TARIFF,
|
CONF_TARIFF,
|
||||||
CONF_TARIFF_ENTITY,
|
CONF_TARIFF_ENTITY,
|
||||||
|
CONF_TARIFFS,
|
||||||
DAILY,
|
DAILY,
|
||||||
DATA_TARIFF_SENSORS,
|
DATA_TARIFF_SENSORS,
|
||||||
DATA_UTILITY,
|
DATA_UTILITY,
|
||||||
@ -93,6 +95,84 @@ PAUSED = "paused"
|
|||||||
COLLECTING = "collecting"
|
COLLECTING = "collecting"
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize Utility Meter config entry."""
|
||||||
|
entry_id = config_entry.entry_id
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
# Validate + resolve entity registry id to entity_id
|
||||||
|
source_entity_id = er.async_validate_entity_id(
|
||||||
|
registry, config_entry.options[CONF_SOURCE_SENSOR]
|
||||||
|
)
|
||||||
|
|
||||||
|
cron_pattern = None
|
||||||
|
delta_values = config_entry.options[CONF_METER_DELTA_VALUES]
|
||||||
|
meter_offset = timedelta(days=config_entry.options[CONF_METER_OFFSET])
|
||||||
|
meter_type = config_entry.options[CONF_METER_TYPE]
|
||||||
|
if meter_type == "none":
|
||||||
|
meter_type = None
|
||||||
|
name = config_entry.title
|
||||||
|
net_consumption = config_entry.options[CONF_METER_NET_CONSUMPTION]
|
||||||
|
tariff_entity = hass.data[DATA_UTILITY][entry_id][CONF_TARIFF_ENTITY]
|
||||||
|
|
||||||
|
meters = []
|
||||||
|
|
||||||
|
# Remove when frontend list selector is available
|
||||||
|
if not config_entry.options.get(CONF_TARIFFS):
|
||||||
|
tariffs = []
|
||||||
|
else:
|
||||||
|
tariffs = config_entry.options[CONF_TARIFFS].split(",")
|
||||||
|
|
||||||
|
if not tariffs:
|
||||||
|
# Add single sensor, not gated by a tariff selector
|
||||||
|
meter_sensor = UtilityMeterSensor(
|
||||||
|
cron_pattern=cron_pattern,
|
||||||
|
delta_values=delta_values,
|
||||||
|
meter_offset=meter_offset,
|
||||||
|
meter_type=meter_type,
|
||||||
|
name=name,
|
||||||
|
net_consumption=net_consumption,
|
||||||
|
parent_meter=entry_id,
|
||||||
|
source_entity=source_entity_id,
|
||||||
|
tariff_entity=tariff_entity,
|
||||||
|
tariff=None,
|
||||||
|
unique_id=entry_id,
|
||||||
|
)
|
||||||
|
meters.append(meter_sensor)
|
||||||
|
hass.data[DATA_UTILITY][entry_id][DATA_TARIFF_SENSORS].append(meter_sensor)
|
||||||
|
else:
|
||||||
|
# Add sensors for each tariff
|
||||||
|
for tariff in tariffs:
|
||||||
|
meter_sensor = UtilityMeterSensor(
|
||||||
|
cron_pattern=cron_pattern,
|
||||||
|
delta_values=delta_values,
|
||||||
|
meter_offset=meter_offset,
|
||||||
|
meter_type=meter_type,
|
||||||
|
name=f"{name} {tariff}",
|
||||||
|
net_consumption=net_consumption,
|
||||||
|
parent_meter=entry_id,
|
||||||
|
source_entity=source_entity_id,
|
||||||
|
tariff_entity=tariff_entity,
|
||||||
|
tariff=tariff,
|
||||||
|
unique_id=f"{entry_id}_{tariff}",
|
||||||
|
)
|
||||||
|
meters.append(meter_sensor)
|
||||||
|
hass.data[DATA_UTILITY][entry_id][DATA_TARIFF_SENSORS].append(meter_sensor)
|
||||||
|
|
||||||
|
async_add_entities(meters)
|
||||||
|
|
||||||
|
platform = entity_platform.async_get_current_platform()
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_CALIBRATE_METER,
|
||||||
|
{vol.Required(ATTR_VALUE): vol.Coerce(Decimal)},
|
||||||
|
"async_calibrate",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
@ -121,16 +201,17 @@ async def async_setup_platform(
|
|||||||
)
|
)
|
||||||
conf_cron_pattern = hass.data[DATA_UTILITY][meter].get(CONF_CRON_PATTERN)
|
conf_cron_pattern = hass.data[DATA_UTILITY][meter].get(CONF_CRON_PATTERN)
|
||||||
meter_sensor = UtilityMeterSensor(
|
meter_sensor = UtilityMeterSensor(
|
||||||
meter,
|
cron_pattern=conf_cron_pattern,
|
||||||
conf_meter_source,
|
delta_values=conf_meter_delta_values,
|
||||||
conf.get(CONF_NAME),
|
meter_offset=conf_meter_offset,
|
||||||
conf_meter_type,
|
meter_type=conf_meter_type,
|
||||||
conf_meter_offset,
|
name=conf.get(CONF_NAME),
|
||||||
conf_meter_delta_values,
|
net_consumption=conf_meter_net_consumption,
|
||||||
conf_meter_net_consumption,
|
parent_meter=meter,
|
||||||
conf.get(CONF_TARIFF),
|
source_entity=conf_meter_source,
|
||||||
conf_meter_tariff_entity,
|
tariff_entity=conf_meter_tariff_entity,
|
||||||
conf_cron_pattern,
|
tariff=conf.get(CONF_TARIFF),
|
||||||
|
unique_id=None,
|
||||||
)
|
)
|
||||||
meters.append(meter_sensor)
|
meters.append(meter_sensor)
|
||||||
|
|
||||||
@ -152,18 +233,21 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
*,
|
||||||
|
cron_pattern,
|
||||||
|
delta_values,
|
||||||
|
meter_offset,
|
||||||
|
meter_type,
|
||||||
|
name,
|
||||||
|
net_consumption,
|
||||||
parent_meter,
|
parent_meter,
|
||||||
source_entity,
|
source_entity,
|
||||||
name,
|
tariff_entity,
|
||||||
meter_type,
|
tariff,
|
||||||
meter_offset,
|
unique_id,
|
||||||
delta_values,
|
|
||||||
net_consumption,
|
|
||||||
tariff=None,
|
|
||||||
tariff_entity=None,
|
|
||||||
cron_pattern=None,
|
|
||||||
):
|
):
|
||||||
"""Initialize the Utility Meter sensor."""
|
"""Initialize the Utility Meter sensor."""
|
||||||
|
self._attr_unique_id = unique_id
|
||||||
self._parent_meter = parent_meter
|
self._parent_meter = parent_meter
|
||||||
self._sensor_source_id = source_entity
|
self._sensor_source_id = source_entity
|
||||||
self._state = None
|
self._state = None
|
||||||
|
28
homeassistant/components/utility_meter/strings.json
Normal file
28
homeassistant/components/utility_meter/strings.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "New Utility Meter",
|
||||||
|
"description": "The utility meter sensor provides functionality to track consumptions of various utilities (e.g., energy, gas, water, heating) over a configured period of time, typically monthly. The utility meter sensor also supports splitting the consumption by tariffs.\nMeter reset offset allows offsetting the day of monthly meter reset.\nSupported tariffs is a comma separated list of supported tariffs, leave empty if only a single tariff is needed.",
|
||||||
|
"data": {
|
||||||
|
"cycle": "Meter reset cycle",
|
||||||
|
"delta_values": "Delta values",
|
||||||
|
"name": "Name",
|
||||||
|
"net_consumption": "Net consumption",
|
||||||
|
"offset": "Meter reset offset",
|
||||||
|
"source": "Input sensor",
|
||||||
|
"tariffs": "Supported tariffs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"source": "[%key:component::utility_meter::config::step::user::data::source%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
homeassistant/components/utility_meter/translations/en.json
Normal file
28
homeassistant/components/utility_meter/translations/en.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"cycle": "Meter reset cycle",
|
||||||
|
"delta_values": "Delta values",
|
||||||
|
"name": "Name",
|
||||||
|
"net_consumption": "Net consumption",
|
||||||
|
"offset": "Meter reset offset",
|
||||||
|
"source": "Input sensor",
|
||||||
|
"tariffs": "Supported tariffs"
|
||||||
|
},
|
||||||
|
"description": "The utility meter sensor provides functionality to track consumptions of various utilities (e.g., energy, gas, water, heating) over a configured period of time, typically monthly. The utility meter sensor also supports splitting the consumption by tariffs.\nMeter reset offset allows offsetting the day of monthly meter reset.\nSupported tariffs is a comma separated list of supported tariffs, leave empty if only a single tariff is needed.",
|
||||||
|
"title": "New Utility Meter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"source": "Input sensor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -405,6 +405,7 @@ FLOWS = {
|
|||||||
"min_max",
|
"min_max",
|
||||||
"switch_as_x",
|
"switch_as_x",
|
||||||
"threshold",
|
"threshold",
|
||||||
"tod"
|
"tod",
|
||||||
|
"utility_meter"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
209
tests/components/utility_meter/test_config_flow.py
Normal file
209
tests/components/utility_meter/test_config_flow.py
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
"""Test the Utility Meter config flow."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components.utility_meter.const import DOMAIN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("platform", ("sensor",))
|
||||||
|
async def test_config_flow(hass: HomeAssistant, platform) -> None:
|
||||||
|
"""Test the config flow."""
|
||||||
|
input_sensor_entity_id = "sensor.input"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.utility_meter.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"cycle": "monthly",
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor_entity_id,
|
||||||
|
"tariffs": "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "Electricity meter"
|
||||||
|
assert result["data"] == {}
|
||||||
|
assert result["options"] == {
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor_entity_id,
|
||||||
|
"tariffs": "",
|
||||||
|
}
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
|
assert config_entry.data == {}
|
||||||
|
assert config_entry.options == {
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor_entity_id,
|
||||||
|
"tariffs": "",
|
||||||
|
}
|
||||||
|
assert config_entry.title == "Electricity meter"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_tariffs(hass: HomeAssistant) -> None:
|
||||||
|
"""Test tariffs."""
|
||||||
|
input_sensor_entity_id = "sensor.input"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"cycle": "monthly",
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor_entity_id,
|
||||||
|
"tariffs": "cat,dog,horse,cow",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "Electricity meter"
|
||||||
|
assert result["data"] == {}
|
||||||
|
assert result["options"] == {
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor_entity_id,
|
||||||
|
"tariffs": "cat,dog,horse,cow",
|
||||||
|
}
|
||||||
|
|
||||||
|
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
|
assert config_entry.data == {}
|
||||||
|
assert config_entry.options == {
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor_entity_id,
|
||||||
|
"tariffs": "cat,dog,horse,cow",
|
||||||
|
}
|
||||||
|
assert config_entry.title == "Electricity meter"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"cycle": "monthly",
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor_entity_id,
|
||||||
|
"tariffs": "cat,cat,cat,cat",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"]["base"] == "tariffs_not_unique"
|
||||||
|
|
||||||
|
|
||||||
|
def get_suggested(schema, key):
|
||||||
|
"""Get suggested value for key in voluptuous schema."""
|
||||||
|
for k in schema.keys():
|
||||||
|
if k == key:
|
||||||
|
if k.description is None or "suggested_value" not in k.description:
|
||||||
|
return None
|
||||||
|
return k.description["suggested_value"]
|
||||||
|
# Wanted key absent from schema
|
||||||
|
raise Exception
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options(hass: HomeAssistant) -> None:
|
||||||
|
"""Test reconfiguring."""
|
||||||
|
input_sensor1_entity_id = "sensor.input1"
|
||||||
|
input_sensor2_entity_id = "sensor.input2"
|
||||||
|
|
||||||
|
# Setup the config entry
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options={
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor1_entity_id,
|
||||||
|
"tariffs": "",
|
||||||
|
},
|
||||||
|
title="Electricity meter",
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "init"
|
||||||
|
schema = result["data_schema"].schema
|
||||||
|
assert get_suggested(schema, "source") == input_sensor1_entity_id
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={"source": input_sensor2_entity_id},
|
||||||
|
)
|
||||||
|
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["data"] == {
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor2_entity_id,
|
||||||
|
"tariffs": "",
|
||||||
|
}
|
||||||
|
assert config_entry.data == {}
|
||||||
|
assert config_entry.options == {
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor2_entity_id,
|
||||||
|
"tariffs": "",
|
||||||
|
}
|
||||||
|
assert config_entry.title == "Electricity meter"
|
||||||
|
|
||||||
|
# Check config entry is reloaded with new options
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get("sensor.electricity_meter")
|
||||||
|
assert state.attributes["source"] == input_sensor2_entity_id
|
@ -1,7 +1,11 @@
|
|||||||
"""The tests for the utility_meter component."""
|
"""The tests for the utility_meter component."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.select.const import (
|
from homeassistant.components.select.const import (
|
||||||
DOMAIN as SELECT_DOMAIN,
|
DOMAIN as SELECT_DOMAIN,
|
||||||
SERVICE_SELECT_OPTION,
|
SERVICE_SELECT_OPTION,
|
||||||
@ -22,12 +26,14 @@ from homeassistant.const import (
|
|||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
Platform,
|
Platform,
|
||||||
)
|
)
|
||||||
from homeassistant.core import State
|
from homeassistant.core import HomeAssistant, State
|
||||||
|
from homeassistant.exceptions import ServiceNotFound
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from tests.common import mock_restore_cache
|
from tests.common import MockConfigEntry, mock_restore_cache
|
||||||
|
|
||||||
|
|
||||||
async def test_restore_state(hass):
|
async def test_restore_state(hass):
|
||||||
@ -168,8 +174,138 @@ async def test_services(hass):
|
|||||||
assert state.state == "4"
|
assert state.state == "4"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_services_config_entry(hass):
|
||||||
|
"""Test energy sensor reset service."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options={
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Energy bill",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": "peak,offpeak",
|
||||||
|
},
|
||||||
|
title="Energy bill",
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options={
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Energy bill2",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": "peak,offpeak",
|
||||||
|
},
|
||||||
|
title="Energy bill2",
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
entity_id = "sensor.energy"
|
||||||
|
hass.states.async_set(
|
||||||
|
entity_id, 1, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
now = dt_util.utcnow() + timedelta(seconds=10)
|
||||||
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
|
hass.states.async_set(
|
||||||
|
entity_id,
|
||||||
|
3,
|
||||||
|
{ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
|
force_update=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_peak")
|
||||||
|
assert state.state == "2"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
|
assert state.state == "0"
|
||||||
|
|
||||||
|
# Next tariff - only supported on legacy entity
|
||||||
|
with pytest.raises(ServiceNotFound):
|
||||||
|
data = {ATTR_ENTITY_ID: "utility_meter.energy_bill"}
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SELECT_NEXT_TARIFF, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Change tariff
|
||||||
|
data = {ATTR_ENTITY_ID: "select.energy_bill", "option": "offpeak"}
|
||||||
|
await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
now += timedelta(seconds=10)
|
||||||
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
|
hass.states.async_set(
|
||||||
|
entity_id,
|
||||||
|
4,
|
||||||
|
{ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
|
force_update=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_peak")
|
||||||
|
assert state.state == "2"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
|
assert state.state == "1"
|
||||||
|
|
||||||
|
# Change tariff
|
||||||
|
data = {ATTR_ENTITY_ID: "select.energy_bill", "option": "wrong_tariff"}
|
||||||
|
await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Inexisting tariff, ignoring
|
||||||
|
assert hass.states.get("select.energy_bill").state != "wrong_tariff"
|
||||||
|
|
||||||
|
data = {ATTR_ENTITY_ID: "select.energy_bill", "option": "peak"}
|
||||||
|
await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
now += timedelta(seconds=10)
|
||||||
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
|
hass.states.async_set(
|
||||||
|
entity_id,
|
||||||
|
5,
|
||||||
|
{ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
|
force_update=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_peak")
|
||||||
|
assert state.state == "3"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
|
assert state.state == "1"
|
||||||
|
|
||||||
|
# Reset meters
|
||||||
|
data = {ATTR_ENTITY_ID: "select.energy_bill"}
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_RESET, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_peak")
|
||||||
|
assert state.state == "0"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
|
assert state.state == "0"
|
||||||
|
|
||||||
|
# meanwhile energy_bill2_peak accumulated all kWh
|
||||||
|
state = hass.states.get("sensor.energy_bill2_peak")
|
||||||
|
assert state.state == "4"
|
||||||
|
|
||||||
|
|
||||||
async def test_cron(hass, legacy_patchable_time):
|
async def test_cron(hass, legacy_patchable_time):
|
||||||
"""Test cron pattern and offset fails."""
|
"""Test cron pattern."""
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
"utility_meter": {
|
"utility_meter": {
|
||||||
@ -327,3 +463,61 @@ async def test_legacy_support(hass):
|
|||||||
await hass.services.async_call(DOMAIN, SERVICE_RESET, data)
|
await hass.services.async_call(DOMAIN, SERVICE_RESET, data)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert reset_calls == ["select.energy_bill"]
|
assert reset_calls == ["select.energy_bill"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"tariffs,expected_entities",
|
||||||
|
(
|
||||||
|
(
|
||||||
|
"",
|
||||||
|
["sensor.electricity_meter"],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"high,low",
|
||||||
|
[
|
||||||
|
"sensor.electricity_meter_low",
|
||||||
|
"sensor.electricity_meter_high",
|
||||||
|
"select.electricity_meter",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_setup_and_remove_config_entry(
|
||||||
|
hass: HomeAssistant, tariffs: str, expected_entities: list[str]
|
||||||
|
) -> None:
|
||||||
|
"""Test setting up and removing a config entry."""
|
||||||
|
input_sensor_entity_id = "sensor.input"
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
# Setup the config entry
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options={
|
||||||
|
"cycle": "monthly",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Electricity meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": input_sensor_entity_id,
|
||||||
|
"tariffs": tariffs,
|
||||||
|
},
|
||||||
|
title="Electricity meter",
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(hass.states.async_all()) == len(expected_entities)
|
||||||
|
assert len(registry.entities) == len(expected_entities)
|
||||||
|
for entity in expected_entities:
|
||||||
|
assert hass.states.get(entity)
|
||||||
|
assert entity in registry.entities
|
||||||
|
|
||||||
|
# Remove the config entry
|
||||||
|
assert await hass.config_entries.async_remove(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Check the state and entity registry entry are removed
|
||||||
|
assert len(hass.states.async_all()) == 0
|
||||||
|
assert len(registry.entities) == 0
|
||||||
|
@ -3,6 +3,8 @@ from contextlib import contextmanager
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.select.const import (
|
from homeassistant.components.select.const import (
|
||||||
DOMAIN as SELECT_DOMAIN,
|
DOMAIN as SELECT_DOMAIN,
|
||||||
SERVICE_SELECT_OPTION,
|
SERVICE_SELECT_OPTION,
|
||||||
@ -39,7 +41,7 @@ from homeassistant.core import State
|
|||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed, mock_restore_cache
|
from tests.common import MockConfigEntry, async_fire_time_changed, mock_restore_cache
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@ -52,24 +54,55 @@ def alter_time(retval):
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
async def test_state(hass):
|
@pytest.mark.parametrize(
|
||||||
"""Test utility sensor state."""
|
"yaml_config,config_entry_config",
|
||||||
config = {
|
(
|
||||||
"utility_meter": {
|
(
|
||||||
"energy_bill": {
|
{
|
||||||
|
"utility_meter": {
|
||||||
|
"energy_bill": {
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": ["onpeak", "midpeak", "offpeak"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"cycle": "none",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Energy bill",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
"source": "sensor.energy",
|
"source": "sensor.energy",
|
||||||
"tariffs": ["onpeak", "midpeak", "offpeak"],
|
"tariffs": "onpeak,midpeak,offpeak",
|
||||||
}
|
},
|
||||||
}
|
),
|
||||||
}
|
),
|
||||||
|
)
|
||||||
assert await async_setup_component(hass, DOMAIN, config)
|
async def test_state(hass, yaml_config, config_entry_config):
|
||||||
await hass.async_block_till_done()
|
"""Test utility sensor state."""
|
||||||
|
if yaml_config:
|
||||||
|
assert await async_setup_component(hass, DOMAIN, yaml_config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id = yaml_config[DOMAIN]["energy_bill"]["source"]
|
||||||
|
else:
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options=config_entry_config,
|
||||||
|
title=config_entry_config["name"],
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id = config_entry_config["source"]
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
)
|
)
|
||||||
@ -175,7 +208,6 @@ async def test_state(hass):
|
|||||||
assert state.state == "0.123"
|
assert state.state == "0.123"
|
||||||
|
|
||||||
# test invalid state
|
# test invalid state
|
||||||
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id, "*", {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id, "*", {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
)
|
)
|
||||||
@ -185,7 +217,6 @@ async def test_state(hass):
|
|||||||
assert state.state == "0.123"
|
assert state.state == "0.123"
|
||||||
|
|
||||||
# test unavailable source
|
# test unavailable source
|
||||||
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id, STATE_UNAVAILABLE, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id, STATE_UNAVAILABLE, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
)
|
)
|
||||||
@ -195,19 +226,51 @@ async def test_state(hass):
|
|||||||
assert state.state == "0.123"
|
assert state.state == "0.123"
|
||||||
|
|
||||||
|
|
||||||
async def test_init(hass):
|
@pytest.mark.parametrize(
|
||||||
"""Test utility sensor state initializtion."""
|
"yaml_config,config_entry_config",
|
||||||
config = {
|
(
|
||||||
"utility_meter": {
|
(
|
||||||
"energy_bill": {
|
{
|
||||||
|
"utility_meter": {
|
||||||
|
"energy_bill": {
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": ["onpeak", "midpeak", "offpeak"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"cycle": "none",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Energy bill",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
"source": "sensor.energy",
|
"source": "sensor.energy",
|
||||||
"tariffs": ["onpeak", "midpeak", "offpeak"],
|
"tariffs": "onpeak,midpeak,offpeak",
|
||||||
}
|
},
|
||||||
}
|
),
|
||||||
}
|
),
|
||||||
|
)
|
||||||
assert await async_setup_component(hass, DOMAIN, config)
|
async def test_init(hass, yaml_config, config_entry_config):
|
||||||
await hass.async_block_till_done()
|
"""Test utility sensor state initializtion."""
|
||||||
|
if yaml_config:
|
||||||
|
assert await async_setup_component(hass, DOMAIN, yaml_config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id = yaml_config[DOMAIN]["energy_bill"]["source"]
|
||||||
|
else:
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options=config_entry_config,
|
||||||
|
title=config_entry_config["name"],
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id = config_entry_config["source"]
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -220,7 +283,6 @@ async def test_init(hass):
|
|||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
)
|
)
|
||||||
@ -238,31 +300,74 @@ async def test_init(hass):
|
|||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
|
||||||
|
|
||||||
async def test_device_class(hass):
|
@pytest.mark.parametrize(
|
||||||
|
"yaml_config,config_entry_configs",
|
||||||
|
(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"utility_meter": {
|
||||||
|
"energy_meter": {
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"net_consumption": True,
|
||||||
|
},
|
||||||
|
"gas_meter": {
|
||||||
|
"source": "sensor.gas",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cycle": "none",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Energy meter",
|
||||||
|
"net_consumption": True,
|
||||||
|
"offset": 0,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cycle": "none",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Gas meter",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": "sensor.gas",
|
||||||
|
"tariffs": "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_device_class(hass, yaml_config, config_entry_configs):
|
||||||
"""Test utility device_class."""
|
"""Test utility device_class."""
|
||||||
config = {
|
if yaml_config:
|
||||||
"utility_meter": {
|
assert await async_setup_component(hass, DOMAIN, yaml_config)
|
||||||
"energy_meter": {
|
await hass.async_block_till_done()
|
||||||
"source": "sensor.energy",
|
else:
|
||||||
"net_consumption": True,
|
for config_entry_config in config_entry_configs:
|
||||||
},
|
config_entry = MockConfigEntry(
|
||||||
"gas_meter": {
|
data={},
|
||||||
"source": "sensor.gas",
|
domain=DOMAIN,
|
||||||
},
|
options=config_entry_config,
|
||||||
}
|
title=config_entry_config["name"],
|
||||||
}
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert await async_setup_component(hass, DOMAIN, config)
|
entity_id_energy = "sensor.energy"
|
||||||
await hass.async_block_till_done()
|
entity_id_gas = "sensor.gas"
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
entity_id_energy = config[DOMAIN]["energy_meter"]["source"]
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id_energy, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id_energy, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
)
|
)
|
||||||
entity_id_gas = config[DOMAIN]["gas_meter"]["source"]
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id_gas, 2, {ATTR_UNIT_OF_MEASUREMENT: "some_archaic_unit"}
|
entity_id_gas, 2, {ATTR_UNIT_OF_MEASUREMENT: "some_archaic_unit"}
|
||||||
)
|
)
|
||||||
@ -283,17 +388,37 @@ async def test_device_class(hass):
|
|||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "some_archaic_unit"
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "some_archaic_unit"
|
||||||
|
|
||||||
|
|
||||||
async def test_restore_state(hass):
|
@pytest.mark.parametrize(
|
||||||
|
"yaml_config,config_entry_config",
|
||||||
|
(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"utility_meter": {
|
||||||
|
"energy_bill": {
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": ["onpeak", "midpeak", "offpeak"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"cycle": "none",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Energy bill",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": "onpeak,midpeak,offpeak",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_restore_state(hass, yaml_config, config_entry_config):
|
||||||
"""Test utility sensor restore state."""
|
"""Test utility sensor restore state."""
|
||||||
last_reset = "2020-12-21T00:00:00.013073+00:00"
|
last_reset = "2020-12-21T00:00:00.013073+00:00"
|
||||||
config = {
|
|
||||||
"utility_meter": {
|
|
||||||
"energy_bill": {
|
|
||||||
"source": "sensor.energy",
|
|
||||||
"tariffs": ["onpeak", "midpeak", "offpeak"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mock_restore_cache(
|
mock_restore_cache(
|
||||||
hass,
|
hass,
|
||||||
[
|
[
|
||||||
@ -322,8 +447,19 @@ async def test_restore_state(hass):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
assert await async_setup_component(hass, DOMAIN, config)
|
if yaml_config:
|
||||||
await hass.async_block_till_done()
|
assert await async_setup_component(hass, DOMAIN, yaml_config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
else:
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options=config_entry_config,
|
||||||
|
title=config_entry_config["name"],
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# restore from cache
|
# restore from cache
|
||||||
state = hass.states.get("sensor.energy_bill_onpeak")
|
state = hass.states.get("sensor.energy_bill_onpeak")
|
||||||
@ -355,19 +491,53 @@ async def test_restore_state(hass):
|
|||||||
assert state.attributes.get("status") == PAUSED
|
assert state.attributes.get("status") == PAUSED
|
||||||
|
|
||||||
|
|
||||||
async def test_net_consumption(hass):
|
@pytest.mark.parametrize(
|
||||||
|
"yaml_config,config_entry_config",
|
||||||
|
(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"utility_meter": {
|
||||||
|
"energy_bill": {
|
||||||
|
"net_consumption": True,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"cycle": "none",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Energy bill",
|
||||||
|
"net_consumption": True,
|
||||||
|
"offset": 0,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": "",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_net_consumption(hass, yaml_config, config_entry_config):
|
||||||
"""Test utility sensor state."""
|
"""Test utility sensor state."""
|
||||||
config = {
|
if yaml_config:
|
||||||
"utility_meter": {
|
assert await async_setup_component(hass, DOMAIN, yaml_config)
|
||||||
"energy_bill": {"source": "sensor.energy", "net_consumption": True}
|
await hass.async_block_till_done()
|
||||||
}
|
entity_id = yaml_config[DOMAIN]["energy_bill"]["source"]
|
||||||
}
|
else:
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
assert await async_setup_component(hass, DOMAIN, config)
|
data={},
|
||||||
await hass.async_block_till_done()
|
domain=DOMAIN,
|
||||||
|
options=config_entry_config,
|
||||||
|
title=config_entry_config["name"],
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id = config_entry_config["source"]
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
)
|
)
|
||||||
@ -389,19 +559,53 @@ async def test_net_consumption(hass):
|
|||||||
assert state.state == "-1"
|
assert state.state == "-1"
|
||||||
|
|
||||||
|
|
||||||
async def test_non_net_consumption(hass):
|
@pytest.mark.parametrize(
|
||||||
|
"yaml_config,config_entry_config",
|
||||||
|
(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"utility_meter": {
|
||||||
|
"energy_bill": {
|
||||||
|
"net_consumption": False,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"cycle": "none",
|
||||||
|
"delta_values": False,
|
||||||
|
"name": "Energy bill",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": "",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_non_net_consumption(hass, yaml_config, config_entry_config):
|
||||||
"""Test utility sensor state."""
|
"""Test utility sensor state."""
|
||||||
config = {
|
if yaml_config:
|
||||||
"utility_meter": {
|
assert await async_setup_component(hass, DOMAIN, yaml_config)
|
||||||
"energy_bill": {"source": "sensor.energy", "net_consumption": False}
|
await hass.async_block_till_done()
|
||||||
}
|
entity_id = yaml_config[DOMAIN]["energy_bill"]["source"]
|
||||||
}
|
else:
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
assert await async_setup_component(hass, DOMAIN, config)
|
data={},
|
||||||
await hass.async_block_till_done()
|
domain=DOMAIN,
|
||||||
|
options=config_entry_config,
|
||||||
|
title=config_entry_config["name"],
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id = config_entry_config["source"]
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
)
|
)
|
||||||
@ -423,21 +627,55 @@ async def test_non_net_consumption(hass):
|
|||||||
assert state.state == "0"
|
assert state.state == "0"
|
||||||
|
|
||||||
|
|
||||||
async def test_delta_values(hass):
|
@pytest.mark.parametrize(
|
||||||
|
"yaml_config,config_entry_config",
|
||||||
|
(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"utility_meter": {
|
||||||
|
"energy_bill": {
|
||||||
|
"delta_values": True,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"cycle": "none",
|
||||||
|
"delta_values": True,
|
||||||
|
"name": "Energy bill",
|
||||||
|
"net_consumption": False,
|
||||||
|
"offset": 0,
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": "",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_delta_values(hass, yaml_config, config_entry_config):
|
||||||
"""Test utility meter "delta_values" mode."""
|
"""Test utility meter "delta_values" mode."""
|
||||||
config = {
|
|
||||||
"utility_meter": {
|
|
||||||
"energy_bill": {"source": "sensor.energy", "delta_values": True}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
with alter_time(now):
|
with alter_time(now):
|
||||||
assert await async_setup_component(hass, DOMAIN, config)
|
if yaml_config:
|
||||||
await hass.async_block_till_done()
|
assert await async_setup_component(hass, DOMAIN, yaml_config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id = yaml_config[DOMAIN]["energy_bill"]["source"]
|
||||||
|
else:
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options=config_entry_config,
|
||||||
|
title=config_entry_config["name"],
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id = config_entry_config["source"]
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
|
||||||
|
|
||||||
async_fire_time_changed(hass, now)
|
async_fire_time_changed(hass, now)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user