Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The EnergyFlip integration."""
2 
3 import asyncio
4 from datetime import timedelta
5 import logging
6 from typing import Any
7 
8 from energyflip import EnergyFlip, EnergyFlipException
9 
10 from homeassistant.config_entries import ConfigEntry
11 from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
12 from homeassistant.core import HomeAssistant
13 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
14 
15 from .const import (
16  DATA_COORDINATOR,
17  DOMAIN,
18  FETCH_TIMEOUT,
19  POLLING_INTERVAL,
20  SENSOR_TYPE_RATE,
21  SENSOR_TYPE_THIS_DAY,
22  SENSOR_TYPE_THIS_MONTH,
23  SENSOR_TYPE_THIS_WEEK,
24  SENSOR_TYPE_THIS_YEAR,
25  SOURCE_TYPES,
26 )
27 
28 PLATFORMS = [Platform.SENSOR]
29 
30 _LOGGER = logging.getLogger(__name__)
31 
32 
33 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
34  """Set up EnergyFlip from a config entry."""
35  # Create the EnergyFlip client
36  energyflip = EnergyFlip(
37  username=entry.data[CONF_USERNAME],
38  password=entry.data[CONF_PASSWORD],
39  source_types=SOURCE_TYPES,
40  request_timeout=FETCH_TIMEOUT,
41  )
42 
43  # Attempt authentication. If this fails, an exception is thrown
44  try:
45  await energyflip.authenticate()
46  except EnergyFlipException as exception:
47  _LOGGER.error("Authentication failed: %s", str(exception))
48  return False
49 
50  async def async_update_data() -> dict[str, dict[str, Any]]:
51  return await async_update_energyflip(energyflip)
52 
53  # Create a coordinator for polling updates
54  coordinator = DataUpdateCoordinator(
55  hass,
56  _LOGGER,
57  config_entry=entry,
58  name="sensor",
59  update_method=async_update_data,
60  update_interval=timedelta(seconds=POLLING_INTERVAL),
61  )
62 
63  await coordinator.async_config_entry_first_refresh()
64 
65  # Load the client in the data of home assistant
66  hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {DATA_COORDINATOR: coordinator}
67 
68  # Offload the loading of entities to the platform
69  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
70 
71  return True
72 
73 
74 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
75  """Unload a config entry."""
76  # Forward the unloading of the entry to the platform
77  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
78 
79  # If successful, unload the EnergyFlip client
80  if unload_ok:
81  hass.data[DOMAIN].pop(entry.entry_id)
82 
83  return unload_ok
84 
85 
86 async def async_update_energyflip(energyflip: EnergyFlip) -> dict[str, dict[str, Any]]:
87  """Update the data by performing a request to EnergyFlip."""
88  try:
89  # Note: TimeoutError and aiohttp.ClientError are already
90  # handled by the data update coordinator.
91  async with asyncio.timeout(FETCH_TIMEOUT):
92  if not energyflip.is_authenticated():
93  _LOGGER.warning("EnergyFlip is unauthenticated. Reauthenticating")
94  await energyflip.authenticate()
95 
96  current_measurements = await energyflip.current_measurements()
97 
98  return {
99  source_type: {
100  SENSOR_TYPE_RATE: _get_measurement_rate(
101  current_measurements, source_type
102  ),
103  SENSOR_TYPE_THIS_DAY: _get_cumulative_value(
104  current_measurements, source_type, SENSOR_TYPE_THIS_DAY
105  ),
106  SENSOR_TYPE_THIS_WEEK: _get_cumulative_value(
107  current_measurements, source_type, SENSOR_TYPE_THIS_WEEK
108  ),
109  SENSOR_TYPE_THIS_MONTH: _get_cumulative_value(
110  current_measurements, source_type, SENSOR_TYPE_THIS_MONTH
111  ),
112  SENSOR_TYPE_THIS_YEAR: _get_cumulative_value(
113  current_measurements, source_type, SENSOR_TYPE_THIS_YEAR
114  ),
115  }
116  for source_type in SOURCE_TYPES
117  }
118  except EnergyFlipException as exception:
119  raise UpdateFailed(f"Error communicating with API: {exception}") from exception
120 
121 
123  current_measurements: dict,
124  source_type: str,
125  period_type: str,
126 ):
127  """Get the cumulative energy consumption for a certain period.
128 
129  :param current_measurements: The result from the EnergyFlip client
130  :param source_type: The source of energy (electricity or gas)
131  :param period_type: The period for which cumulative value should be given.
132  """
133  if source_type in current_measurements:
134  if (
135  period_type in current_measurements[source_type]
136  and current_measurements[source_type][period_type] is not None
137  ):
138  return current_measurements[source_type][period_type]["value"]
139  else:
140  _LOGGER.error(
141  "Source type %s not present in %s", source_type, current_measurements
142  )
143  return None
144 
145 
146 def _get_measurement_rate(current_measurements: dict, source_type: str):
147  if source_type in current_measurements:
148  if (
149  "measurement" in current_measurements[source_type]
150  and current_measurements[source_type]["measurement"] is not None
151  ):
152  return current_measurements[source_type]["measurement"]["rate"]
153  else:
154  _LOGGER.error(
155  "Source type %s not present in %s", source_type, current_measurements
156  )
157  return None
def _get_measurement_rate(dict current_measurements, str source_type)
Definition: __init__.py:146
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:74
def _get_cumulative_value(dict current_measurements, str source_type, str period_type)
Definition: __init__.py:126
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:33
dict[str, dict[str, Any]] async_update_energyflip(EnergyFlip energyflip)
Definition: __init__.py:86