Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for HomeMatic devices."""
2 
3 from datetime import datetime
4 from functools import partial
5 import logging
6 
7 from pyhomematic import HMConnection
8 import voluptuous as vol
9 
10 from homeassistant.const import (
11  ATTR_ENTITY_ID,
12  ATTR_MODE,
13  ATTR_NAME,
14  ATTR_TIME,
15  CONF_HOST,
16  CONF_HOSTS,
17  CONF_PASSWORD,
18  CONF_PATH,
19  CONF_PLATFORM,
20  CONF_PORT,
21  CONF_SSL,
22  CONF_USERNAME,
23  CONF_VERIFY_SSL,
24  EVENT_HOMEASSISTANT_STOP,
25 )
26 from homeassistant.core import HomeAssistant, ServiceCall
27 from homeassistant.helpers import discovery
29 from homeassistant.helpers.typing import ConfigType
30 
31 from .const import (
32  ATTR_ADDRESS,
33  ATTR_CHANNEL,
34  ATTR_DEVICE_TYPE,
35  ATTR_DISCOVER_DEVICES,
36  ATTR_DISCOVERY_TYPE,
37  ATTR_ERRORCODE,
38  ATTR_INTERFACE,
39  ATTR_LOW_BAT,
40  ATTR_LOWBAT,
41  ATTR_MESSAGE,
42  ATTR_PARAM,
43  ATTR_PARAMSET,
44  ATTR_PARAMSET_KEY,
45  ATTR_RX_MODE,
46  ATTR_UNIQUE_ID,
47  ATTR_VALUE,
48  ATTR_VALUE_TYPE,
49  CONF_CALLBACK_IP,
50  CONF_CALLBACK_PORT,
51  CONF_INTERFACES,
52  CONF_JSONPORT,
53  CONF_LOCAL_IP,
54  CONF_LOCAL_PORT,
55  CONF_RESOLVENAMES,
56  CONF_RESOLVENAMES_OPTIONS,
57  DATA_CONF,
58  DATA_HOMEMATIC,
59  DATA_STORE,
60  DISCOVER_BATTERY,
61  DISCOVER_BINARY_SENSORS,
62  DISCOVER_CLIMATE,
63  DISCOVER_COVER,
64  DISCOVER_LIGHTS,
65  DISCOVER_LOCKS,
66  DISCOVER_SENSORS,
67  DISCOVER_SWITCHES,
68  DOMAIN,
69  EVENT_ERROR,
70  EVENT_IMPULSE,
71  EVENT_KEYPRESS,
72  HM_DEVICE_TYPES,
73  HM_IGNORE_DISCOVERY_NODE,
74  HM_IGNORE_DISCOVERY_NODE_EXCEPTIONS,
75  HM_IMPULSE_EVENTS,
76  HM_PRESS_EVENTS,
77  SERVICE_PUT_PARAMSET,
78  SERVICE_RECONNECT,
79  SERVICE_SET_DEVICE_VALUE,
80  SERVICE_SET_INSTALL_MODE,
81  SERVICE_SET_VARIABLE_VALUE,
82  SERVICE_VIRTUALKEY,
83 )
84 from .entity import HMHub
85 
86 _LOGGER = logging.getLogger(__name__)
87 
88 DEFAULT_LOCAL_IP = "0.0.0.0"
89 DEFAULT_LOCAL_PORT = 0
90 DEFAULT_RESOLVENAMES = False
91 DEFAULT_JSONPORT = 80
92 DEFAULT_PORT = 2001
93 DEFAULT_PATH = ""
94 DEFAULT_USERNAME = "Admin"
95 DEFAULT_PASSWORD = ""
96 DEFAULT_SSL = False
97 DEFAULT_VERIFY_SSL = False
98 DEFAULT_CHANNEL = 1
99 
100 
101 DEVICE_SCHEMA = vol.Schema(
102  {
103  vol.Required(CONF_PLATFORM): "homematic",
104  vol.Required(ATTR_NAME): cv.string,
105  vol.Required(ATTR_ADDRESS): cv.string,
106  vol.Required(ATTR_INTERFACE): cv.string,
107  vol.Optional(ATTR_DEVICE_TYPE): cv.string,
108  vol.Optional(ATTR_CHANNEL, default=DEFAULT_CHANNEL): vol.Coerce(int),
109  vol.Optional(ATTR_PARAM): cv.string,
110  vol.Optional(ATTR_UNIQUE_ID): cv.string,
111  }
112 )
113 
114 CONFIG_SCHEMA = vol.Schema(
115  {
116  DOMAIN: vol.Schema(
117  {
118  vol.Optional(CONF_INTERFACES, default={}): {
119  cv.match_all: {
120  vol.Required(CONF_HOST): cv.string,
121  vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
122  vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string,
123  vol.Optional(
124  CONF_RESOLVENAMES, default=DEFAULT_RESOLVENAMES
125  ): vol.In(CONF_RESOLVENAMES_OPTIONS),
126  vol.Optional(CONF_JSONPORT, default=DEFAULT_JSONPORT): cv.port,
127  vol.Optional(
128  CONF_USERNAME, default=DEFAULT_USERNAME
129  ): cv.string,
130  vol.Optional(
131  CONF_PASSWORD, default=DEFAULT_PASSWORD
132  ): cv.string,
133  vol.Optional(CONF_CALLBACK_IP): cv.string,
134  vol.Optional(CONF_CALLBACK_PORT): cv.port,
135  vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
136  vol.Optional(
137  CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL
138  ): cv.boolean,
139  }
140  },
141  vol.Optional(CONF_HOSTS, default={}): {
142  cv.match_all: {
143  vol.Required(CONF_HOST): cv.string,
144  vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
145  vol.Optional(
146  CONF_USERNAME, default=DEFAULT_USERNAME
147  ): cv.string,
148  vol.Optional(
149  CONF_PASSWORD, default=DEFAULT_PASSWORD
150  ): cv.string,
151  }
152  },
153  vol.Optional(CONF_LOCAL_IP, default=DEFAULT_LOCAL_IP): cv.string,
154  vol.Optional(CONF_LOCAL_PORT): cv.port,
155  }
156  )
157  },
158  extra=vol.ALLOW_EXTRA,
159 )
160 
161 SCHEMA_SERVICE_VIRTUALKEY = vol.Schema(
162  {
163  vol.Required(ATTR_ADDRESS): vol.All(cv.string, vol.Upper),
164  vol.Required(ATTR_CHANNEL): vol.Coerce(int),
165  vol.Required(ATTR_PARAM): cv.string,
166  vol.Optional(ATTR_INTERFACE): cv.string,
167  }
168 )
169 
170 SCHEMA_SERVICE_SET_VARIABLE_VALUE = vol.Schema(
171  {
172  vol.Required(ATTR_NAME): cv.string,
173  vol.Required(ATTR_VALUE): cv.match_all,
174  vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
175  }
176 )
177 
178 SCHEMA_SERVICE_SET_DEVICE_VALUE = vol.Schema(
179  {
180  vol.Required(ATTR_ADDRESS): vol.All(cv.string, vol.Upper),
181  vol.Required(ATTR_CHANNEL): vol.Coerce(int),
182  vol.Required(ATTR_PARAM): vol.All(cv.string, vol.Upper),
183  vol.Required(ATTR_VALUE): cv.match_all,
184  vol.Optional(ATTR_VALUE_TYPE): vol.In(
185  ["boolean", "dateTime.iso8601", "double", "int", "string"]
186  ),
187  vol.Optional(ATTR_INTERFACE): cv.string,
188  }
189 )
190 
191 SCHEMA_SERVICE_RECONNECT = vol.Schema({})
192 
193 SCHEMA_SERVICE_SET_INSTALL_MODE = vol.Schema(
194  {
195  vol.Required(ATTR_INTERFACE): cv.string,
196  vol.Optional(ATTR_TIME, default=60): cv.positive_int,
197  vol.Optional(ATTR_MODE, default=1): vol.All(vol.Coerce(int), vol.In([1, 2])),
198  vol.Optional(ATTR_ADDRESS): vol.All(cv.string, vol.Upper),
199  }
200 )
201 
202 SCHEMA_SERVICE_PUT_PARAMSET = vol.Schema(
203  {
204  vol.Required(ATTR_INTERFACE): cv.string,
205  vol.Required(ATTR_ADDRESS): vol.All(cv.string, vol.Upper),
206  vol.Required(ATTR_PARAMSET_KEY): vol.All(cv.string, vol.Upper),
207  vol.Required(ATTR_PARAMSET): dict,
208  vol.Optional(ATTR_RX_MODE): vol.All(cv.string, vol.Upper),
209  }
210 )
211 
212 
213 def setup(hass: HomeAssistant, config: ConfigType) -> bool:
214  """Set up the Homematic component."""
215  conf = config[DOMAIN]
216  hass.data[DATA_CONF] = remotes = {}
217  hass.data[DATA_STORE] = set()
218 
219  # Create hosts-dictionary for pyhomematic
220  for rname, rconfig in conf[CONF_INTERFACES].items():
221  remotes[rname] = {
222  "ip": rconfig.get(CONF_HOST),
223  "port": rconfig.get(CONF_PORT),
224  "path": rconfig.get(CONF_PATH),
225  "resolvenames": rconfig.get(CONF_RESOLVENAMES),
226  "jsonport": rconfig.get(CONF_JSONPORT),
227  "username": rconfig.get(CONF_USERNAME),
228  "password": rconfig.get(CONF_PASSWORD),
229  "callbackip": rconfig.get(CONF_CALLBACK_IP),
230  "callbackport": rconfig.get(CONF_CALLBACK_PORT),
231  "ssl": rconfig[CONF_SSL],
232  "verify_ssl": rconfig.get(CONF_VERIFY_SSL),
233  "connect": True,
234  }
235 
236  for sname, sconfig in conf[CONF_HOSTS].items():
237  remotes[sname] = {
238  "ip": sconfig.get(CONF_HOST),
239  "port": sconfig[CONF_PORT],
240  "username": sconfig.get(CONF_USERNAME),
241  "password": sconfig.get(CONF_PASSWORD),
242  "connect": False,
243  }
244 
245  # Create server thread
246  bound_system_callback = partial(_system_callback_handler, hass, config)
247  hass.data[DATA_HOMEMATIC] = homematic = HMConnection(
248  local=config[DOMAIN].get(CONF_LOCAL_IP),
249  localport=config[DOMAIN].get(CONF_LOCAL_PORT, DEFAULT_LOCAL_PORT),
250  remotes=remotes,
251  systemcallback=bound_system_callback,
252  interface_id="homeassistant",
253  )
254 
255  # Start server thread, connect to hosts, initialize to receive events
256  homematic.start()
257 
258  # Stops server when Home Assistant is shutting down
259  hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, hass.data[DATA_HOMEMATIC].stop)
260 
261  # Init homematic hubs
262  entity_hubs = [HMHub(hass, homematic, hub_name) for hub_name in conf[CONF_HOSTS]]
263 
264  def _hm_service_virtualkey(service: ServiceCall) -> None:
265  """Service to handle virtualkey servicecalls."""
266  address = service.data.get(ATTR_ADDRESS)
267  channel = service.data.get(ATTR_CHANNEL)
268  param = service.data.get(ATTR_PARAM)
269 
270  # Device not found
271  hmdevice = _device_from_servicecall(hass, service)
272  if hmdevice is None:
273  _LOGGER.error("%s not found for service virtualkey!", address)
274  return
275 
276  # Parameter doesn't exist for device
277  if param not in hmdevice.ACTIONNODE:
278  _LOGGER.error("%s not datapoint in hm device %s", param, address)
279  return
280 
281  # Channel doesn't exist for device
282  if channel not in hmdevice.ACTIONNODE[param]:
283  _LOGGER.error("%i is not a channel in hm device %s", channel, address)
284  return
285 
286  # Call parameter
287  hmdevice.actionNodeData(param, True, channel)
288 
289  hass.services.register(
290  DOMAIN,
291  SERVICE_VIRTUALKEY,
292  _hm_service_virtualkey,
293  schema=SCHEMA_SERVICE_VIRTUALKEY,
294  )
295 
296  def _service_handle_value(service: ServiceCall) -> None:
297  """Service to call setValue method for HomeMatic system variable."""
298  entity_ids = service.data.get(ATTR_ENTITY_ID)
299  name = service.data[ATTR_NAME]
300  value = service.data[ATTR_VALUE]
301 
302  if entity_ids:
303  entities = [
304  entity for entity in entity_hubs if entity.entity_id in entity_ids
305  ]
306  else:
307  entities = entity_hubs
308 
309  if not entities:
310  _LOGGER.error("No HomeMatic hubs available")
311  return
312 
313  for hub in entities:
314  hub.hm_set_variable(name, value)
315 
316  hass.services.register(
317  DOMAIN,
318  SERVICE_SET_VARIABLE_VALUE,
319  _service_handle_value,
320  schema=SCHEMA_SERVICE_SET_VARIABLE_VALUE,
321  )
322 
323  def _service_handle_reconnect(service: ServiceCall) -> None:
324  """Service to reconnect all HomeMatic hubs."""
325  homematic.reconnect()
326 
327  hass.services.register(
328  DOMAIN,
329  SERVICE_RECONNECT,
330  _service_handle_reconnect,
331  schema=SCHEMA_SERVICE_RECONNECT,
332  )
333 
334  def _service_handle_device(service: ServiceCall) -> None:
335  """Service to call setValue method for HomeMatic devices."""
336  address = service.data[ATTR_ADDRESS]
337  channel = service.data[ATTR_CHANNEL]
338  param = service.data[ATTR_PARAM]
339  value = service.data[ATTR_VALUE]
340  value_type = service.data.get(ATTR_VALUE_TYPE)
341 
342  # Convert value into correct XML-RPC Type.
343  # https://docs.python.org/3/library/xmlrpc.client.html#xmlrpc.client.ServerProxy
344  if value_type:
345  if value_type == "int":
346  value = int(value)
347  elif value_type == "double":
348  value = float(value)
349  elif value_type == "boolean":
350  value = bool(value)
351  elif value_type == "dateTime.iso8601":
352  value = datetime.strptime(value, "%Y%m%dT%H:%M:%S")
353  else:
354  # Default is 'string'
355  value = str(value)
356 
357  # Device not found
358  hmdevice = _device_from_servicecall(hass, service)
359  if hmdevice is None:
360  _LOGGER.error("%s not found!", address)
361  return
362 
363  hmdevice.setValue(param, value, channel)
364 
365  hass.services.register(
366  DOMAIN,
367  SERVICE_SET_DEVICE_VALUE,
368  _service_handle_device,
369  schema=SCHEMA_SERVICE_SET_DEVICE_VALUE,
370  )
371 
372  def _service_handle_install_mode(service: ServiceCall) -> None:
373  """Service to set interface into install mode."""
374  interface = service.data.get(ATTR_INTERFACE)
375  mode = service.data.get(ATTR_MODE)
376  time = service.data.get(ATTR_TIME)
377  address = service.data.get(ATTR_ADDRESS)
378 
379  homematic.setInstallMode(interface, t=time, mode=mode, address=address)
380 
381  hass.services.register(
382  DOMAIN,
383  SERVICE_SET_INSTALL_MODE,
384  _service_handle_install_mode,
385  schema=SCHEMA_SERVICE_SET_INSTALL_MODE,
386  )
387 
388  def _service_put_paramset(service: ServiceCall) -> None:
389  """Service to call the putParamset method on a HomeMatic connection."""
390  interface = service.data[ATTR_INTERFACE]
391  address = service.data[ATTR_ADDRESS]
392  paramset_key = service.data[ATTR_PARAMSET_KEY]
393  # When passing in the paramset from a YAML file we get an OrderedDict
394  # here instead of a dict, so add this explicit cast.
395  # The service schema makes sure that this cast works.
396  paramset = dict(service.data[ATTR_PARAMSET])
397  rx_mode = service.data.get(ATTR_RX_MODE)
398 
399  _LOGGER.debug(
400  "Calling putParamset: %s, %s, %s, %s, %s",
401  interface,
402  address,
403  paramset_key,
404  paramset,
405  rx_mode,
406  )
407  homematic.putParamset(interface, address, paramset_key, paramset, rx_mode)
408 
409  hass.services.register(
410  DOMAIN,
411  SERVICE_PUT_PARAMSET,
412  _service_put_paramset,
413  schema=SCHEMA_SERVICE_PUT_PARAMSET,
414  )
415 
416  return True
417 
418 
419 def _system_callback_handler(hass, config, src, *args):
420  """System callback handler."""
421  # New devices available at hub
422  if src == "newDevices":
423  (interface_id, dev_descriptions) = args
424  interface = interface_id.split("-")[-1]
425 
426  # Device support active?
427  if not hass.data[DATA_CONF][interface]["connect"]:
428  return
429 
430  addresses = []
431  for dev in dev_descriptions:
432  address = dev["ADDRESS"].split(":")[0]
433  if address not in hass.data[DATA_STORE]:
434  hass.data[DATA_STORE].add(address)
435  addresses.append(address)
436 
437  # Register EVENTS
438  # Search all devices with an EVENTNODE that includes data
439  bound_event_callback = partial(_hm_event_handler, hass, interface)
440  for dev in addresses:
441  hmdevice = hass.data[DATA_HOMEMATIC].devices[interface].get(dev)
442 
443  if hmdevice.EVENTNODE:
444  hmdevice.setEventCallback(callback=bound_event_callback, bequeath=True)
445 
446  # Create Home Assistant entities
447  if addresses:
448  for component_name, discovery_type in (
449  ("switch", DISCOVER_SWITCHES),
450  ("light", DISCOVER_LIGHTS),
451  ("cover", DISCOVER_COVER),
452  ("binary_sensor", DISCOVER_BINARY_SENSORS),
453  ("sensor", DISCOVER_SENSORS),
454  ("climate", DISCOVER_CLIMATE),
455  ("lock", DISCOVER_LOCKS),
456  ("binary_sensor", DISCOVER_BATTERY),
457  ):
458  # Get all devices of a specific type
459  found_devices = _get_devices(hass, discovery_type, addresses, interface)
460 
461  # When devices of this type are found
462  # they are setup in Home Assistant and a discovery event is fired
463  if found_devices:
464  discovery.load_platform(
465  hass,
466  component_name,
467  DOMAIN,
468  {
469  ATTR_DISCOVER_DEVICES: found_devices,
470  ATTR_DISCOVERY_TYPE: discovery_type,
471  },
472  config,
473  )
474 
475  # Homegear error message
476  elif src == "error":
477  _LOGGER.error("Error: %s", args)
478  (interface_id, errorcode, message) = args
479  hass.bus.fire(EVENT_ERROR, {ATTR_ERRORCODE: errorcode, ATTR_MESSAGE: message})
480 
481 
482 def _get_devices(hass, discovery_type, keys, interface):
483  """Get the HomeMatic devices for given discovery_type."""
484  device_arr = []
485 
486  for key in keys:
487  device = hass.data[DATA_HOMEMATIC].devices[interface][key]
488  class_name = device.__class__.__name__
489  metadata = {}
490 
491  # Class not supported by discovery type
492  if (
493  discovery_type != DISCOVER_BATTERY
494  and class_name not in HM_DEVICE_TYPES[discovery_type]
495  ):
496  continue
497 
498  # Load metadata needed to generate a parameter list
499  if discovery_type == DISCOVER_SENSORS:
500  metadata.update(device.SENSORNODE)
501  elif discovery_type == DISCOVER_BINARY_SENSORS:
502  metadata.update(device.BINARYNODE)
503  elif discovery_type == DISCOVER_BATTERY:
504  if ATTR_LOWBAT in device.ATTRIBUTENODE:
505  metadata.update({ATTR_LOWBAT: device.ATTRIBUTENODE[ATTR_LOWBAT]})
506  elif ATTR_LOW_BAT in device.ATTRIBUTENODE:
507  metadata.update({ATTR_LOW_BAT: device.ATTRIBUTENODE[ATTR_LOW_BAT]})
508  else:
509  continue
510  else:
511  metadata.update({None: device.ELEMENT})
512 
513  # Generate options for 1...n elements with 1...n parameters
514  for param, channels in metadata.items():
515  if (
516  param in HM_IGNORE_DISCOVERY_NODE
517  and class_name not in HM_IGNORE_DISCOVERY_NODE_EXCEPTIONS.get(param, [])
518  ):
519  continue
520  if discovery_type == DISCOVER_SWITCHES and class_name == "IPKeySwitchLevel":
521  channels.remove(8)
522  channels.remove(12)
523  if discovery_type == DISCOVER_LIGHTS and class_name == "IPKeySwitchLevel":
524  channels.remove(4)
525 
526  # Add devices
527  _LOGGER.debug(
528  "%s: Handling %s: %s: %s", discovery_type, key, param, channels
529  )
530  for channel in channels:
531  name = _create_ha_id(
532  name=device.NAME, channel=channel, param=param, count=len(channels)
533  )
534  unique_id = _create_ha_id(
535  name=key, channel=channel, param=param, count=len(channels)
536  )
537  device_dict = {
538  CONF_PLATFORM: "homematic",
539  ATTR_ADDRESS: key,
540  ATTR_INTERFACE: interface,
541  ATTR_NAME: name,
542  ATTR_DEVICE_TYPE: class_name,
543  ATTR_CHANNEL: channel,
544  ATTR_UNIQUE_ID: unique_id,
545  }
546  if param is not None:
547  device_dict[ATTR_PARAM] = param
548 
549  # Add new device
550  try:
551  DEVICE_SCHEMA(device_dict)
552  device_arr.append(device_dict)
553  except vol.MultipleInvalid as err:
554  _LOGGER.error("Invalid device config: %s", str(err))
555  return device_arr
556 
557 
558 def _create_ha_id(name, channel, param, count):
559  """Generate a unique entity id."""
560  # HMDevice is a simple device
561  if count == 1 and param is None:
562  return name
563 
564  # Has multiple elements/channels
565  if count > 1 and param is None:
566  return f"{name} {channel}"
567 
568  # With multiple parameters on first channel
569  if count == 1 and param is not None:
570  return f"{name} {param}"
571 
572  # Multiple parameters with multiple channels
573  if count > 1 and param is not None:
574  return f"{name} {channel} {param}"
575 
576  raise ValueError(f"Unable to create unique id for count:{count} and param:{param}")
577 
578 
579 def _hm_event_handler(hass, interface, device, caller, attribute, value):
580  """Handle all pyhomematic device events."""
581  try:
582  channel = int(device.split(":")[1])
583  address = device.split(":")[0]
584  hmdevice = hass.data[DATA_HOMEMATIC].devices[interface].get(address)
585  except (TypeError, ValueError):
586  _LOGGER.error("Event handling channel convert error!")
587  return
588 
589  # Return if not an event supported by device
590  if attribute not in hmdevice.EVENTNODE:
591  return
592 
593  _LOGGER.debug("Event %s for %s channel %i", attribute, hmdevice.NAME, channel)
594 
595  # Keypress event
596  if attribute in HM_PRESS_EVENTS:
597  hass.bus.fire(
598  EVENT_KEYPRESS,
599  {ATTR_NAME: hmdevice.NAME, ATTR_PARAM: attribute, ATTR_CHANNEL: channel},
600  )
601  return
602 
603  # Impulse event
604  if attribute in HM_IMPULSE_EVENTS:
605  hass.bus.fire(EVENT_IMPULSE, {ATTR_NAME: hmdevice.NAME, ATTR_CHANNEL: channel})
606  return
607 
608  _LOGGER.warning("Event is unknown and not forwarded")
609 
610 
611 def _device_from_servicecall(hass, service):
612  """Extract HomeMatic device from service call."""
613  address = service.data.get(ATTR_ADDRESS)
614  interface = service.data.get(ATTR_INTERFACE)
615  if address == "BIDCOS-RF":
616  address = "BidCoS-RF"
617  if address == "HMIP-RCV-1":
618  address = "HmIP-RCV-1"
619 
620  if interface:
621  return hass.data[DATA_HOMEMATIC].devices[interface].get(address)
622 
623  for devices in hass.data[DATA_HOMEMATIC].devices.values():
624  if address in devices:
625  return devices[address]
626  return None
bool add(self, _T matcher)
Definition: match.py:185
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
def _hm_event_handler(hass, interface, device, caller, attribute, value)
Definition: __init__.py:579
bool setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:213
def _system_callback_handler(hass, config, src, *args)
Definition: __init__.py:419
def _device_from_servicecall(hass, service)
Definition: __init__.py:611
def _get_devices(hass, discovery_type, keys, interface)
Definition: __init__.py:482
def _create_ha_id(name, channel, param, count)
Definition: __init__.py:558