Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for a Genius Hub system."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 import logging
7 
8 import aiohttp
9 from geniushubclient import GeniusHub
10 import voluptuous as vol
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import (
14  ATTR_ENTITY_ID,
15  ATTR_TEMPERATURE,
16  CONF_HOST,
17  CONF_MAC,
18  CONF_PASSWORD,
19  CONF_TOKEN,
20  CONF_USERNAME,
21  Platform,
22 )
23 from homeassistant.core import HomeAssistant, ServiceCall, callback
24 from homeassistant.helpers import config_validation as cv, entity_registry as er
25 from homeassistant.helpers.aiohttp_client import async_get_clientsession
26 from homeassistant.helpers.dispatcher import async_dispatcher_send
27 from homeassistant.helpers.event import async_track_time_interval
28 from homeassistant.helpers.service import verify_domain_control
29 
30 from .const import DOMAIN
31 
32 _LOGGER = logging.getLogger(__name__)
33 
34 
35 SCAN_INTERVAL = timedelta(seconds=60)
36 
37 MAC_ADDRESS_REGEXP = r"^([0-9A-F]{2}:){5}([0-9A-F]{2})$"
38 
39 ATTR_ZONE_MODE = "mode"
40 ATTR_DURATION = "duration"
41 
42 SVC_SET_ZONE_MODE = "set_zone_mode"
43 SVC_SET_ZONE_OVERRIDE = "set_zone_override"
44 
45 SET_ZONE_MODE_SCHEMA = vol.Schema(
46  {
47  vol.Required(ATTR_ENTITY_ID): cv.entity_id,
48  vol.Required(ATTR_ZONE_MODE): vol.In(["off", "timer", "footprint"]),
49  }
50 )
51 SET_ZONE_OVERRIDE_SCHEMA = vol.Schema(
52  {
53  vol.Required(ATTR_ENTITY_ID): cv.entity_id,
54  vol.Required(ATTR_TEMPERATURE): vol.All(
55  vol.Coerce(float), vol.Range(min=4, max=28)
56  ),
57  vol.Optional(ATTR_DURATION): vol.All(
58  cv.time_period,
59  vol.Range(min=timedelta(minutes=5), max=timedelta(days=1)),
60  ),
61  }
62 )
63 
64 PLATFORMS = [
65  Platform.BINARY_SENSOR,
66  Platform.CLIMATE,
67  Platform.SENSOR,
68  Platform.SWITCH,
69  Platform.WATER_HEATER,
70 ]
71 
72 
73 type GeniusHubConfigEntry = ConfigEntry[GeniusBroker]
74 
75 
76 async def async_setup_entry(hass: HomeAssistant, entry: GeniusHubConfigEntry) -> bool:
77  """Create a Genius Hub system."""
78  if CONF_TOKEN in entry.data and CONF_MAC in entry.data:
79  entity_registry = er.async_get(hass)
80  registry_entries = er.async_entries_for_config_entry(
81  entity_registry, entry.entry_id
82  )
83  for reg_entry in registry_entries:
84  if reg_entry.unique_id.startswith(entry.data[CONF_MAC]):
85  entity_registry.async_update_entity(
86  reg_entry.entity_id,
87  new_unique_id=reg_entry.unique_id.replace(
88  entry.data[CONF_MAC], entry.entry_id
89  ),
90  )
91 
92  session = async_get_clientsession(hass)
93  if CONF_HOST in entry.data:
94  client = GeniusHub(
95  entry.data[CONF_HOST],
96  username=entry.data[CONF_USERNAME],
97  password=entry.data[CONF_PASSWORD],
98  session=session,
99  )
100  else:
101  client = GeniusHub(entry.data[CONF_TOKEN], session=session)
102 
103  unique_id = entry.unique_id or entry.entry_id
104 
105  broker = entry.runtime_data = GeniusBroker(hass, client, unique_id)
106 
107  try:
108  await client.update()
109  except aiohttp.ClientResponseError as err:
110  _LOGGER.error("Setup failed, check your configuration, %s", err)
111  return False
112  broker.make_debug_log_entries()
113 
114  async_track_time_interval(hass, broker.async_update, SCAN_INTERVAL)
115 
116  setup_service_functions(hass, broker)
117 
118  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
119 
120  return True
121 
122 
123 @callback
124 def setup_service_functions(hass: HomeAssistant, broker):
125  """Set up the service functions."""
126 
127  @verify_domain_control(hass, DOMAIN)
128  async def set_zone_mode(call: ServiceCall) -> None:
129  """Set the system mode."""
130  entity_id = call.data[ATTR_ENTITY_ID]
131 
132  registry = er.async_get(hass)
133  registry_entry = registry.async_get(entity_id)
134 
135  if registry_entry is None or registry_entry.platform != DOMAIN:
136  raise ValueError(f"'{entity_id}' is not a known {DOMAIN} entity")
137 
138  if registry_entry.domain != "climate":
139  raise ValueError(f"'{entity_id}' is not an {DOMAIN} zone")
140 
141  payload = {
142  "unique_id": registry_entry.unique_id,
143  "service": call.service,
144  "data": call.data,
145  }
146 
147  async_dispatcher_send(hass, DOMAIN, payload)
148 
149  hass.services.async_register(
150  DOMAIN, SVC_SET_ZONE_MODE, set_zone_mode, schema=SET_ZONE_MODE_SCHEMA
151  )
152  hass.services.async_register(
153  DOMAIN, SVC_SET_ZONE_OVERRIDE, set_zone_mode, schema=SET_ZONE_OVERRIDE_SCHEMA
154  )
155 
156 
158  """Container for geniushub client and data."""
159 
160  def __init__(self, hass: HomeAssistant, client: GeniusHub, hub_uid: str) -> None:
161  """Initialize the geniushub client."""
162  self.hasshass = hass
163  self.clientclient = client
164  self.hub_uidhub_uid = hub_uid
165  self._connect_error_connect_error = False
166 
167  async def async_update(self, now, **kwargs) -> None:
168  """Update the geniushub client's data."""
169  try:
170  await self.clientclient.update()
171  if self._connect_error_connect_error:
172  self._connect_error_connect_error = False
173  _LOGGER.warning("Connection to geniushub re-established")
174  except (
175  aiohttp.ClientResponseError,
176  aiohttp.client_exceptions.ClientConnectorError,
177  ) as err:
178  if not self._connect_error_connect_error:
179  self._connect_error_connect_error = True
180  _LOGGER.error(
181  "Connection to geniushub failed (unable to update), message is: %s",
182  err,
183  )
184  return
185  self.make_debug_log_entriesmake_debug_log_entries()
186 
187  async_dispatcher_send(self.hasshass, DOMAIN)
188 
189  def make_debug_log_entries(self) -> None:
190  """Make any useful debug log entries."""
191  _LOGGER.debug(
192  "Raw JSON: \n\nclient._zones = %s \n\nclient._devices = %s",
193  self.clientclient._zones, # noqa: SLF001
194  self.clientclient._devices, # noqa: SLF001
195  )
None __init__(self, HomeAssistant hass, GeniusHub client, str hub_uid)
Definition: __init__.py:160
None async_update(self, now, **kwargs)
Definition: __init__.py:167
def setup_service_functions(HomeAssistant hass, broker)
Definition: __init__.py:124
bool async_setup_entry(HomeAssistant hass, GeniusHubConfigEntry entry)
Definition: __init__.py:76
IssData update(pyiss.ISS iss)
Definition: __init__.py:33
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
Definition: dispatcher.py:193
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)
Definition: event.py:1679