Add easyEnergy integration (#86266)
This commit is contained in:
parent
4c1147e62b
commit
3723241937
|
@ -286,6 +286,8 @@ build.json @home-assistant/supervisor
|
|||
/tests/components/dynalite/ @ziv1234
|
||||
/homeassistant/components/eafm/ @Jc2k
|
||||
/tests/components/eafm/ @Jc2k
|
||||
/homeassistant/components/easyenergy/ @klaasnicolaas
|
||||
/tests/components/easyenergy/ @klaasnicolaas
|
||||
/homeassistant/components/ecobee/ @marthoc
|
||||
/tests/components/ecobee/ @marthoc
|
||||
/homeassistant/components/econet/ @vangorra @w1ll1am23
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
"""The easyEnergy integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EasyEnergyDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up easyEnergy from a config entry."""
|
||||
|
||||
coordinator = EasyEnergyDataUpdateCoordinator(hass)
|
||||
try:
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
except ConfigEntryNotReady:
|
||||
await coordinator.easyenergy.close()
|
||||
raise
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload easyEnergy config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
|
@ -0,0 +1,31 @@
|
|||
"""Config flow for easyEnergy integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
class EasyEnergyFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Config flow for easyEnergy integration."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step."""
|
||||
|
||||
await self.async_set_unique_id(DOMAIN)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
if user_input is None:
|
||||
return self.async_show_form(step_id="user")
|
||||
|
||||
return self.async_create_entry(
|
||||
title="easyEnergy",
|
||||
data={},
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
"""Constants for the easyEnergy integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Final
|
||||
|
||||
DOMAIN: Final = "easyenergy"
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
SCAN_INTERVAL = timedelta(minutes=10)
|
||||
THRESHOLD_HOUR: Final = 14
|
||||
|
||||
SERVICE_TYPE_DEVICE_NAMES = {
|
||||
"today_energy_usage": "Energy market price - Usage",
|
||||
"today_energy_return": "Energy market price - Return",
|
||||
"today_gas": "Gas market price",
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
"""The Coordinator for easyEnergy."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import NamedTuple
|
||||
|
||||
from easyenergy import (
|
||||
EasyEnergy,
|
||||
EasyEnergyConnectionError,
|
||||
EasyEnergyNoDataError,
|
||||
Electricity,
|
||||
Gas,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import dt
|
||||
|
||||
from .const import DOMAIN, LOGGER, SCAN_INTERVAL, THRESHOLD_HOUR
|
||||
|
||||
|
||||
class EasyEnergyData(NamedTuple):
|
||||
"""Class for defining data in dict."""
|
||||
|
||||
energy_today: Electricity
|
||||
energy_tomorrow: Electricity | None
|
||||
gas_today: Gas | None
|
||||
|
||||
|
||||
class EasyEnergyDataUpdateCoordinator(DataUpdateCoordinator[EasyEnergyData]):
|
||||
"""Class to manage fetching easyEnergy data from single endpoint."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(self, hass) -> None:
|
||||
"""Initialize global easyEnergy data updater."""
|
||||
super().__init__(
|
||||
hass,
|
||||
LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=SCAN_INTERVAL,
|
||||
)
|
||||
|
||||
self.easyenergy = EasyEnergy(session=async_get_clientsession(hass))
|
||||
|
||||
async def _async_update_data(self) -> EasyEnergyData:
|
||||
"""Fetch data from easyEnergy."""
|
||||
today = dt.now().date()
|
||||
gas_today = None
|
||||
energy_tomorrow = None
|
||||
|
||||
try:
|
||||
energy_today = await self.easyenergy.energy_prices(
|
||||
start_date=today, end_date=today
|
||||
)
|
||||
try:
|
||||
gas_today = await self.easyenergy.gas_prices(
|
||||
start_date=today, end_date=today
|
||||
)
|
||||
except EasyEnergyNoDataError:
|
||||
LOGGER.debug("No data for gas prices for easyEnergy integration")
|
||||
# Energy for tomorrow only after 14:00 UTC
|
||||
if dt.utcnow().hour >= THRESHOLD_HOUR:
|
||||
tomorrow = today + timedelta(days=1)
|
||||
try:
|
||||
energy_tomorrow = await self.easyenergy.energy_prices(
|
||||
start_date=tomorrow, end_date=tomorrow
|
||||
)
|
||||
except EasyEnergyNoDataError:
|
||||
LOGGER.debug(
|
||||
"No electricity data for tomorrow for easyEnergy integration"
|
||||
)
|
||||
|
||||
except EasyEnergyConnectionError as err:
|
||||
raise UpdateFailed("Error communicating with easyEnergy API") from err
|
||||
|
||||
return EasyEnergyData(
|
||||
energy_today=energy_today,
|
||||
energy_tomorrow=energy_tomorrow,
|
||||
gas_today=gas_today,
|
||||
)
|
|
@ -0,0 +1,70 @@
|
|||
"""Diagnostics support for easyEnergy."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import EasyEnergyDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EasyEnergyData
|
||||
|
||||
|
||||
def get_gas_price(data: EasyEnergyData, hours: int) -> float | None:
|
||||
"""Get the gas price for a given hour.
|
||||
|
||||
Args:
|
||||
data: The data object.
|
||||
hours: The number of hours to add to the current time.
|
||||
|
||||
Returns:
|
||||
The gas market price value.
|
||||
"""
|
||||
if not data.gas_today:
|
||||
return None
|
||||
return data.gas_today.price_at_time(
|
||||
data.gas_today.utcnow() + timedelta(hours=hours)
|
||||
)
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: EasyEnergyDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
return {
|
||||
"entry": {
|
||||
"title": entry.title,
|
||||
},
|
||||
"energy_usage": {
|
||||
"current_hour_price": coordinator.data.energy_today.current_usage_price,
|
||||
"next_hour_price": coordinator.data.energy_today.price_at_time(
|
||||
coordinator.data.energy_today.utcnow() + timedelta(hours=1)
|
||||
),
|
||||
"average_price": coordinator.data.energy_today.average_usage_price,
|
||||
"max_price": coordinator.data.energy_today.extreme_usage_prices[1],
|
||||
"min_price": coordinator.data.energy_today.extreme_usage_prices[0],
|
||||
"highest_price_time": coordinator.data.energy_today.highest_usage_price_time,
|
||||
"lowest_price_time": coordinator.data.energy_today.lowest_usage_price_time,
|
||||
"percentage_of_max": coordinator.data.energy_today.pct_of_max_usage,
|
||||
},
|
||||
"energy_return": {
|
||||
"current_hour_price": coordinator.data.energy_today.current_return_price,
|
||||
"next_hour_price": coordinator.data.energy_today.price_at_time(
|
||||
coordinator.data.energy_today.utcnow() + timedelta(hours=1), "return"
|
||||
),
|
||||
"average_price": coordinator.data.energy_today.average_return_price,
|
||||
"max_price": coordinator.data.energy_today.extreme_return_prices[1],
|
||||
"min_price": coordinator.data.energy_today.extreme_return_prices[0],
|
||||
"highest_price_time": coordinator.data.energy_today.highest_return_price_time,
|
||||
"lowest_price_time": coordinator.data.energy_today.lowest_return_price_time,
|
||||
"percentage_of_max": coordinator.data.energy_today.pct_of_max_return,
|
||||
},
|
||||
"gas": {
|
||||
"current_hour_price": get_gas_price(coordinator.data, 0),
|
||||
"next_hour_price": get_gas_price(coordinator.data, 1),
|
||||
},
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"domain": "easyenergy",
|
||||
"name": "easyEnergy",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/easyenergy",
|
||||
"requirements": ["easyenergy==0.1.2"],
|
||||
"codeowners": ["@klaasnicolaas"],
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "platinum"
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
"""Support for easyEnergy sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
DOMAIN as SENSOR_DOMAIN,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CURRENCY_EURO, PERCENTAGE, UnitOfEnergy, UnitOfVolume
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN, SERVICE_TYPE_DEVICE_NAMES
|
||||
from .coordinator import EasyEnergyData, EasyEnergyDataUpdateCoordinator
|
||||
|
||||
|
||||
@dataclass
|
||||
class EasyEnergySensorEntityDescriptionMixin:
|
||||
"""Mixin for required keys."""
|
||||
|
||||
value_fn: Callable[[EasyEnergyData], float | datetime | None]
|
||||
service_type: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class EasyEnergySensorEntityDescription(
|
||||
SensorEntityDescription, EasyEnergySensorEntityDescriptionMixin
|
||||
):
|
||||
"""Describes easyEnergy sensor entity."""
|
||||
|
||||
|
||||
SENSORS: tuple[EasyEnergySensorEntityDescription, ...] = (
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="current_hour_price",
|
||||
name="Current hour",
|
||||
service_type="today_gas",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfVolume.CUBIC_METERS}",
|
||||
value_fn=lambda data: data.gas_today.current_price if data.gas_today else None,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="next_hour_price",
|
||||
name="Next hour",
|
||||
service_type="today_gas",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfVolume.CUBIC_METERS}",
|
||||
value_fn=lambda data: get_gas_price(data, 1),
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="current_hour_price",
|
||||
name="Current hour",
|
||||
service_type="today_energy_usage",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.current_usage_price,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="next_hour_price",
|
||||
name="Next hour",
|
||||
service_type="today_energy_usage",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.price_at_time(
|
||||
data.energy_today.utcnow() + timedelta(hours=1)
|
||||
),
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="average_price",
|
||||
name="Average - today",
|
||||
service_type="today_energy_usage",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.average_usage_price,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="max_price",
|
||||
name="Highest price - today",
|
||||
service_type="today_energy_usage",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.extreme_usage_prices[1],
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="min_price",
|
||||
name="Lowest price - today",
|
||||
service_type="today_energy_usage",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.extreme_usage_prices[0],
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="highest_price_time",
|
||||
name="Time of highest price - today",
|
||||
service_type="today_energy_usage",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=lambda data: data.energy_today.highest_usage_price_time,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="lowest_price_time",
|
||||
name="Time of lowest price - today",
|
||||
service_type="today_energy_usage",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=lambda data: data.energy_today.lowest_usage_price_time,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="percentage_of_max",
|
||||
name="Current percentage of highest price - today",
|
||||
service_type="today_energy_usage",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:percent",
|
||||
value_fn=lambda data: data.energy_today.pct_of_max_usage,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="current_hour_price",
|
||||
name="Current hour",
|
||||
service_type="today_energy_return",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.current_return_price,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="next_hour_price",
|
||||
name="Next hour",
|
||||
service_type="today_energy_return",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.price_at_time(
|
||||
data.energy_today.utcnow() + timedelta(hours=1), "return"
|
||||
),
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="average_price",
|
||||
name="Average - today",
|
||||
service_type="today_energy_return",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.average_return_price,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="max_price",
|
||||
name="Highest price - today",
|
||||
service_type="today_energy_return",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.extreme_return_prices[1],
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="min_price",
|
||||
name="Lowest price - today",
|
||||
service_type="today_energy_return",
|
||||
native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}",
|
||||
value_fn=lambda data: data.energy_today.extreme_return_prices[0],
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="highest_price_time",
|
||||
name="Time of highest price - today",
|
||||
service_type="today_energy_return",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=lambda data: data.energy_today.highest_return_price_time,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="lowest_price_time",
|
||||
name="Time of lowest price - today",
|
||||
service_type="today_energy_return",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=lambda data: data.energy_today.lowest_return_price_time,
|
||||
),
|
||||
EasyEnergySensorEntityDescription(
|
||||
key="percentage_of_max",
|
||||
name="Current percentage of highest price - today",
|
||||
service_type="today_energy_return",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:percent",
|
||||
value_fn=lambda data: data.energy_today.pct_of_max_return,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def get_gas_price(data: EasyEnergyData, hours: int) -> float | None:
|
||||
"""Return the gas value.
|
||||
|
||||
Args:
|
||||
data: The data object.
|
||||
hours: The number of hours to add to the current time.
|
||||
|
||||
Returns:
|
||||
The gas market price value.
|
||||
"""
|
||||
if data.gas_today is None:
|
||||
return None
|
||||
return data.gas_today.price_at_time(
|
||||
data.gas_today.utcnow() + timedelta(hours=hours)
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up easyEnergy sensors based on a config entry."""
|
||||
coordinator: EasyEnergyDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
EasyEnergySensorEntity(coordinator=coordinator, description=description)
|
||||
for description in SENSORS
|
||||
)
|
||||
|
||||
|
||||
class EasyEnergySensorEntity(
|
||||
CoordinatorEntity[EasyEnergyDataUpdateCoordinator], SensorEntity
|
||||
):
|
||||
"""Defines a easyEnergy sensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_attribution = "Data provided by easyEnergy"
|
||||
entity_description: EasyEnergySensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
coordinator: EasyEnergyDataUpdateCoordinator,
|
||||
description: EasyEnergySensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize easyEnergy sensor."""
|
||||
super().__init__(coordinator=coordinator)
|
||||
self.entity_description = description
|
||||
self.entity_id = (
|
||||
f"{SENSOR_DOMAIN}.{DOMAIN}_{description.service_type}_{description.key}"
|
||||
)
|
||||
self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{description.service_type}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={
|
||||
(
|
||||
DOMAIN,
|
||||
f"{coordinator.config_entry.entry_id}_{description.service_type}",
|
||||
)
|
||||
},
|
||||
configuration_url="https://www.easyenergy.com",
|
||||
manufacturer="easyEnergy",
|
||||
name=SERVICE_TYPE_DEVICE_NAMES[self.entity_description.service_type],
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | datetime | None:
|
||||
"""Return the state of the sensor."""
|
||||
return self.entity_description.value_fn(self.coordinator.data)
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:common::config_flow::description::confirm_setup%]"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "Do you want to start setup?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -102,6 +102,7 @@ FLOWS = {
|
|||
"dunehd",
|
||||
"dynalite",
|
||||
"eafm",
|
||||
"easyenergy",
|
||||
"ecobee",
|
||||
"econet",
|
||||
"ecowitt",
|
||||
|
|
|
@ -1203,6 +1203,12 @@
|
|||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
},
|
||||
"easyenergy": {
|
||||
"name": "easyEnergy",
|
||||
"integration_type": "hub",
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
},
|
||||
"ebox": {
|
||||
"name": "EBox",
|
||||
"integration_type": "hub",
|
||||
|
|
|
@ -624,6 +624,9 @@ dynalite_devices==0.1.47
|
|||
# homeassistant.components.rainforest_eagle
|
||||
eagle100==0.1.1
|
||||
|
||||
# homeassistant.components.easyenergy
|
||||
easyenergy==0.1.2
|
||||
|
||||
# homeassistant.components.ebusd
|
||||
ebusdpy==0.0.17
|
||||
|
||||
|
|
|
@ -489,6 +489,9 @@ dynalite_devices==0.1.47
|
|||
# homeassistant.components.rainforest_eagle
|
||||
eagle100==0.1.1
|
||||
|
||||
# homeassistant.components.easyenergy
|
||||
easyenergy==0.1.2
|
||||
|
||||
# homeassistant.components.elgato
|
||||
elgato==3.0.0
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
"""Tests for the easyEnergy integration."""
|
|
@ -0,0 +1,61 @@
|
|||
"""Fixtures for easyEnergy integration tests."""
|
||||
from collections.abc import Generator
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from easyenergy import Electricity, Gas
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.easyenergy.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Mock setting up a config entry."""
|
||||
with patch(
|
||||
"homeassistant.components.easyenergy.async_setup_entry", return_value=True
|
||||
) as mock_setup:
|
||||
yield mock_setup
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return the default mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
title="energy",
|
||||
domain=DOMAIN,
|
||||
data={},
|
||||
unique_id="unique_thingy",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_easyenergy() -> Generator[MagicMock, None, None]:
|
||||
"""Return a mocked easyEnergy client."""
|
||||
with patch(
|
||||
"homeassistant.components.easyenergy.coordinator.EasyEnergy", autospec=True
|
||||
) as easyenergy_mock:
|
||||
client = easyenergy_mock.return_value
|
||||
client.energy_prices.return_value = Electricity.from_dict(
|
||||
json.loads(load_fixture("today_energy.json", DOMAIN))
|
||||
)
|
||||
client.gas_prices.return_value = Gas.from_dict(
|
||||
json.loads(load_fixture("today_gas.json", DOMAIN))
|
||||
)
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def init_integration(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_easyenergy: MagicMock
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the easyEnergy integration for testing."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return mock_config_entry
|
|
@ -0,0 +1,146 @@
|
|||
[
|
||||
{
|
||||
"Timestamp": "2023-01-18T23:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1349513,
|
||||
"TariffReturn": 0.11153
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T00:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1294458,
|
||||
"TariffReturn": 0.10698
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T01:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1270137,
|
||||
"TariffReturn": 0.10497
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T02:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1230812,
|
||||
"TariffReturn": 0.10172
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T03:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1297483,
|
||||
"TariffReturn": 0.10723
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T04:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1386902,
|
||||
"TariffReturn": 0.11462
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T05:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1439174,
|
||||
"TariffReturn": 0.11894
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T06:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.193479,
|
||||
"TariffReturn": 0.1599
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T07:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.19844,
|
||||
"TariffReturn": 0.164
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T08:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.2077449,
|
||||
"TariffReturn": 0.17169
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T09:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.16819,
|
||||
"TariffReturn": 0.139
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T10:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1649835,
|
||||
"TariffReturn": 0.13635
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T11:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.156816,
|
||||
"TariffReturn": 0.1296
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T12:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1873927,
|
||||
"TariffReturn": 0.15487
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T13:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1941929,
|
||||
"TariffReturn": 0.16049
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T14:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.2129116,
|
||||
"TariffReturn": 0.17596
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T15:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.2254109,
|
||||
"TariffReturn": 0.18629
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T16:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.2467674,
|
||||
"TariffReturn": 0.20394
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T17:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.2390597,
|
||||
"TariffReturn": 0.19757
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T18:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.2074303,
|
||||
"TariffReturn": 0.17143
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T19:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1815,
|
||||
"TariffReturn": 0.15
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T20:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1795761,
|
||||
"TariffReturn": 0.14841
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T21:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.1807014,
|
||||
"TariffReturn": 0.14934
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T22:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.16819,
|
||||
"TariffReturn": 0.139
|
||||
}
|
||||
]
|
|
@ -0,0 +1,146 @@
|
|||
[
|
||||
{
|
||||
"Timestamp": "2023-01-19T05:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T06:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T07:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T08:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T09:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T10:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T11:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T12:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T13:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T14:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T15:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T16:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T17:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T18:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T19:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T20:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T21:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T22:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-19T23:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-20T00:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-20T01:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-20T02:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-20T03:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
},
|
||||
{
|
||||
"Timestamp": "2023-01-20T04:00:00+00:00",
|
||||
"SupplierId": 0,
|
||||
"TariffUsage": 0.7252982,
|
||||
"TariffReturn": 0.7252982
|
||||
}
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
"""Test the easyEnergy config flow."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from homeassistant.components.easyenergy.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
|
||||
async def test_full_user_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: MagicMock,
|
||||
) -> None:
|
||||
"""Test the full user configuration flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result.get("type") == FlowResultType.FORM
|
||||
assert result.get("step_id") == SOURCE_USER
|
||||
assert "flow_id" in result
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={},
|
||||
)
|
||||
|
||||
assert result2.get("type") == FlowResultType.CREATE_ENTRY
|
||||
assert result2.get("title") == "easyEnergy"
|
||||
assert result2.get("data") == {}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
|
@ -0,0 +1,106 @@
|
|||
"""Tests for the diagnostics data provided by the easyEnergy integration."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from easyenergy import EasyEnergyNoDataError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.homeassistant import SERVICE_UPDATE_ENTITY
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-01-19 15:00:00")
|
||||
async def test_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSession,
|
||||
init_integration: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test diagnostics."""
|
||||
assert await get_diagnostics_for_config_entry(
|
||||
hass, hass_client, init_integration
|
||||
) == {
|
||||
"entry": {
|
||||
"title": "energy",
|
||||
},
|
||||
"energy_usage": {
|
||||
"current_hour_price": 0.22541,
|
||||
"next_hour_price": 0.24677,
|
||||
"average_price": 0.17665,
|
||||
"max_price": 0.24677,
|
||||
"min_price": 0.12308,
|
||||
"highest_price_time": "2023-01-19T16:00:00+00:00",
|
||||
"lowest_price_time": "2023-01-19T02:00:00+00:00",
|
||||
"percentage_of_max": 91.34,
|
||||
},
|
||||
"energy_return": {
|
||||
"current_hour_price": 0.18629,
|
||||
"next_hour_price": 0.20394,
|
||||
"average_price": 0.14599,
|
||||
"max_price": 0.20394,
|
||||
"min_price": 0.10172,
|
||||
"highest_price_time": "2023-01-19T16:00:00+00:00",
|
||||
"lowest_price_time": "2023-01-19T02:00:00+00:00",
|
||||
"percentage_of_max": 91.35,
|
||||
},
|
||||
"gas": {
|
||||
"current_hour_price": 0.7253,
|
||||
"next_hour_price": 0.7253,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-01-19 15:00:00")
|
||||
async def test_diagnostics_no_gas_today(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSession,
|
||||
mock_easyenergy: MagicMock,
|
||||
init_integration: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test diagnostics, no gas sensors available."""
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
mock_easyenergy.gas_prices.side_effect = EasyEnergyNoDataError
|
||||
|
||||
await hass.services.async_call(
|
||||
"homeassistant",
|
||||
SERVICE_UPDATE_ENTITY,
|
||||
{ATTR_ENTITY_ID: ["sensor.easyenergy_today_gas_current_hour_price"]},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert await get_diagnostics_for_config_entry(
|
||||
hass, hass_client, init_integration
|
||||
) == {
|
||||
"entry": {
|
||||
"title": "energy",
|
||||
},
|
||||
"energy_usage": {
|
||||
"current_hour_price": 0.22541,
|
||||
"next_hour_price": 0.24677,
|
||||
"average_price": 0.17665,
|
||||
"max_price": 0.24677,
|
||||
"min_price": 0.12308,
|
||||
"highest_price_time": "2023-01-19T16:00:00+00:00",
|
||||
"lowest_price_time": "2023-01-19T02:00:00+00:00",
|
||||
"percentage_of_max": 91.34,
|
||||
},
|
||||
"energy_return": {
|
||||
"current_hour_price": 0.18629,
|
||||
"next_hour_price": 0.20394,
|
||||
"average_price": 0.14599,
|
||||
"max_price": 0.20394,
|
||||
"min_price": 0.10172,
|
||||
"highest_price_time": "2023-01-19T16:00:00+00:00",
|
||||
"lowest_price_time": "2023-01-19T02:00:00+00:00",
|
||||
"percentage_of_max": 91.35,
|
||||
},
|
||||
"gas": {
|
||||
"current_hour_price": None,
|
||||
"next_hour_price": None,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
"""Tests for the easyEnergy integration."""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from easyenergy import EasyEnergyConnectionError
|
||||
|
||||
from homeassistant.components.easyenergy.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_load_unload_config_entry(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_easyenergy: MagicMock
|
||||
) -> None:
|
||||
"""Test the easyEnergy configuration entry loading/unloading."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert not hass.data.get(DOMAIN)
|
||||
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.components.easyenergy.coordinator.EasyEnergy._request",
|
||||
side_effect=EasyEnergyConnectionError,
|
||||
)
|
||||
async def test_config_flow_entry_not_ready(
|
||||
mock_request: MagicMock,
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the easyEnergy configuration entry not ready."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_request.call_count == 1
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
|
@ -0,0 +1,278 @@
|
|||
"""Tests for the sensors provided by the easyEnergy integration."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from easyenergy import EasyEnergyNoDataError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.easyenergy.const import DOMAIN
|
||||
from homeassistant.components.homeassistant import SERVICE_UPDATE_ENTITY
|
||||
from homeassistant.components.sensor import (
|
||||
ATTR_STATE_CLASS,
|
||||
SensorDeviceClass,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_ICON,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
CURRENCY_EURO,
|
||||
STATE_UNKNOWN,
|
||||
UnitOfEnergy,
|
||||
UnitOfVolume,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-01-19 15:00:00")
|
||||
async def test_energy_usage_today(
|
||||
hass: HomeAssistant, init_integration: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test the easyEnergy - Energy usage sensors."""
|
||||
entry_id = init_integration.entry_id
|
||||
entity_registry = er.async_get(hass)
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
# Current usage energy price sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_energy_usage_current_hour_price")
|
||||
entry = entity_registry.async_get(
|
||||
"sensor.easyenergy_today_energy_usage_current_hour_price"
|
||||
)
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_energy_usage_current_hour_price"
|
||||
assert state.state == "0.22541"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== "Energy market price - Usage Current hour"
|
||||
)
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}"
|
||||
)
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
# Average usage energy price sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_energy_usage_average_price")
|
||||
entry = entity_registry.async_get(
|
||||
"sensor.easyenergy_today_energy_usage_average_price"
|
||||
)
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_energy_usage_average_price"
|
||||
assert state.state == "0.17665"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== "Energy market price - Usage Average - today"
|
||||
)
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}"
|
||||
)
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
# Highest usage energy price sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_energy_usage_max_price")
|
||||
entry = entity_registry.async_get("sensor.easyenergy_today_energy_usage_max_price")
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_energy_usage_max_price"
|
||||
assert state.state == "0.24677"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== "Energy market price - Usage Highest price - today"
|
||||
)
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}"
|
||||
)
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
# Highest usage price time sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_energy_usage_highest_price_time")
|
||||
entry = entity_registry.async_get(
|
||||
"sensor.easyenergy_today_energy_usage_highest_price_time"
|
||||
)
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_energy_usage_highest_price_time"
|
||||
assert state.state == "2023-01-19T16:00:00+00:00"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== "Energy market price - Usage Time of highest price - today"
|
||||
)
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TIMESTAMP
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
assert entry.device_id
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.identifiers == {(DOMAIN, f"{entry_id}_today_energy_usage")}
|
||||
assert device_entry.manufacturer == "easyEnergy"
|
||||
assert device_entry.name == "Energy market price - Usage"
|
||||
assert device_entry.entry_type is dr.DeviceEntryType.SERVICE
|
||||
assert not device_entry.model
|
||||
assert not device_entry.sw_version
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-01-19 15:00:00")
|
||||
async def test_energy_return_today(
|
||||
hass: HomeAssistant, init_integration: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test the easyEnergy - Energy return sensors."""
|
||||
entry_id = init_integration.entry_id
|
||||
entity_registry = er.async_get(hass)
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
# Current return energy price sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_energy_return_current_hour_price")
|
||||
entry = entity_registry.async_get(
|
||||
"sensor.easyenergy_today_energy_return_current_hour_price"
|
||||
)
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_energy_return_current_hour_price"
|
||||
assert state.state == "0.18629"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== "Energy market price - Return Current hour"
|
||||
)
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}"
|
||||
)
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
# Average return energy price sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_energy_return_average_price")
|
||||
entry = entity_registry.async_get(
|
||||
"sensor.easyenergy_today_energy_return_average_price"
|
||||
)
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_energy_return_average_price"
|
||||
assert state.state == "0.14599"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== "Energy market price - Return Average - today"
|
||||
)
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}"
|
||||
)
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
# Highest return energy price sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_energy_return_max_price")
|
||||
entry = entity_registry.async_get("sensor.easyenergy_today_energy_return_max_price")
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_energy_return_max_price"
|
||||
assert state.state == "0.20394"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== "Energy market price - Return Highest price - today"
|
||||
)
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}"
|
||||
)
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
# Highest return price time sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_energy_return_highest_price_time")
|
||||
entry = entity_registry.async_get(
|
||||
"sensor.easyenergy_today_energy_return_highest_price_time"
|
||||
)
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_energy_return_highest_price_time"
|
||||
assert state.state == "2023-01-19T16:00:00+00:00"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== "Energy market price - Return Time of highest price - today"
|
||||
)
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TIMESTAMP
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
assert entry.device_id
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.identifiers == {(DOMAIN, f"{entry_id}_today_energy_return")}
|
||||
assert device_entry.manufacturer == "easyEnergy"
|
||||
assert device_entry.name == "Energy market price - Return"
|
||||
assert device_entry.entry_type is dr.DeviceEntryType.SERVICE
|
||||
assert not device_entry.model
|
||||
assert not device_entry.sw_version
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-01-19 10:00:00")
|
||||
async def test_gas_today(
|
||||
hass: HomeAssistant, init_integration: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test the easyEnergy - Gas sensors."""
|
||||
entry_id = init_integration.entry_id
|
||||
entity_registry = er.async_get(hass)
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
# Current gas price sensor
|
||||
state = hass.states.get("sensor.easyenergy_today_gas_current_hour_price")
|
||||
entry = entity_registry.async_get("sensor.easyenergy_today_gas_current_hour_price")
|
||||
assert entry
|
||||
assert state
|
||||
assert entry.unique_id == f"{entry_id}_today_gas_current_hour_price"
|
||||
assert state.state == "0.7253"
|
||||
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Gas market price Current hour"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== f"{CURRENCY_EURO}/{UnitOfVolume.CUBIC_METERS}"
|
||||
)
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_ICON not in state.attributes
|
||||
|
||||
assert entry.device_id
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.identifiers == {(DOMAIN, f"{entry_id}_today_gas")}
|
||||
assert device_entry.manufacturer == "easyEnergy"
|
||||
assert device_entry.name == "Gas market price"
|
||||
assert device_entry.entry_type is dr.DeviceEntryType.SERVICE
|
||||
assert not device_entry.model
|
||||
assert not device_entry.sw_version
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-01-19 15:00:00")
|
||||
async def test_no_gas_today(
|
||||
hass: HomeAssistant, mock_easyenergy: MagicMock, init_integration: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test the easyEnergy - No gas data available."""
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
|
||||
mock_easyenergy.gas_prices.side_effect = EasyEnergyNoDataError
|
||||
|
||||
await hass.services.async_call(
|
||||
"homeassistant",
|
||||
SERVICE_UPDATE_ENTITY,
|
||||
{ATTR_ENTITY_ID: "sensor.easyenergy_today_gas_current_hour_price"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.easyenergy_today_gas_current_hour_price")
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
Loading…
Reference in New Issue