Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,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
Expand Down
35 changes: 35 additions & 0 deletions homeassistant/components/easyenergy/__init__.py
Original file line number Diff line number Diff line change
@@ -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
31 changes: 31 additions & 0 deletions homeassistant/components/easyenergy/config_flow.py
Original file line number Diff line number Diff line change
@@ -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={},
)
17 changes: 17 additions & 0 deletions homeassistant/components/easyenergy/const.py
Original file line number Diff line number Diff line change
@@ -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",
}
82 changes: 82 additions & 0 deletions homeassistant/components/easyenergy/coordinator.py
Original file line number Diff line number Diff line change
@@ -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,
)
70 changes: 70 additions & 0 deletions homeassistant/components/easyenergy/diagnostics.py
Original file line number Diff line number Diff line change
@@ -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),
},
}
10 changes: 10 additions & 0 deletions homeassistant/components/easyenergy/manifest.json
Original file line number Diff line number Diff line change
@@ -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"
}
Loading