Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for INSTEON Modems (PLM and Hub)."""
2 
3 from contextlib import suppress
4 import logging
5 
6 from pyinsteon import async_close, async_connect, devices
7 from pyinsteon.constants import ReadWriteMode
8 
9 from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
10 from homeassistant.const import CONF_PLATFORM, EVENT_HOMEASSISTANT_STOP
11 from homeassistant.core import HomeAssistant
12 from homeassistant.exceptions import ConfigEntryNotReady
13 from homeassistant.helpers import device_registry as dr
14 
15 from . import api
16 from .const import (
17  CONF_CAT,
18  CONF_DEV_PATH,
19  CONF_DIM_STEPS,
20  CONF_HOUSECODE,
21  CONF_OVERRIDE,
22  CONF_SUBCAT,
23  CONF_UNITCODE,
24  CONF_X10,
25  DOMAIN,
26  INSTEON_PLATFORMS,
27 )
28 from .utils import (
29  add_insteon_events,
30  async_register_services,
31  get_device_platforms,
32  register_new_device_callback,
33 )
34 
35 _LOGGER = logging.getLogger(__name__)
36 OPTIONS = "options"
37 
38 
39 async def async_get_device_config(hass, config_entry):
40  """Initiate the connection and services."""
41  # Make a copy of addresses due to edge case where the list of devices could
42  # change during status update
43  # Cannot be done concurrently due to issues with the underlying protocol.
44  for address in list(devices):
45  if devices[address].is_battery:
46  continue
47  with suppress(AttributeError):
48  await devices[address].async_status()
49 
50  load_aldb = 2 if devices.modem.aldb.read_write_mode == ReadWriteMode.UNKNOWN else 1
51  await devices.async_load(id_devices=1, load_modem_aldb=load_aldb)
52  for addr in list(devices):
53  device = devices[addr]
54  flags = True
55  for name in device.operating_flags:
56  if not device.operating_flags[name].is_loaded:
57  flags = False
58  break
59  if flags:
60  for name in device.properties:
61  if not device.properties[name].is_loaded:
62  flags = False
63  break
64 
65  # Cannot be done concurrently due to issues with the underlying protocol.
66  if not device.aldb.is_loaded or not flags:
67  await device.async_read_config()
68 
69  await devices.async_save(workdir=hass.config.config_dir)
70 
71 
72 async def close_insteon_connection(*args):
73  """Close the Insteon connection."""
74  await async_close()
75 
76 
77 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
78  """Set up an Insteon entry."""
79 
80  if dev_path := entry.options.get(CONF_DEV_PATH):
81  hass.data[DOMAIN] = {}
82  hass.data[DOMAIN][CONF_DEV_PATH] = dev_path
83 
84  api.async_load_api(hass)
85  await api.async_register_insteon_frontend(hass)
86 
87  if not devices.modem:
88  try:
89  await async_connect(**entry.data)
90  except ConnectionError as exception:
91  _LOGGER.error("Could not connect to Insteon modem")
92  raise ConfigEntryNotReady from exception
93 
94  entry.async_on_unload(
95  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, close_insteon_connection)
96  )
97 
98  await devices.async_load(
99  workdir=hass.config.config_dir, id_devices=0, load_modem_aldb=0
100  )
101 
102  # If options existed in YAML and have not already been saved to the config entry
103  # add them now
104  if (
105  not entry.options
106  and entry.source == SOURCE_IMPORT
107  and hass.data.get(DOMAIN)
108  and hass.data[DOMAIN].get(OPTIONS)
109  ):
110  hass.config_entries.async_update_entry(
111  entry=entry,
112  options=hass.data[DOMAIN][OPTIONS],
113  )
114 
115  for device_override in entry.options.get(CONF_OVERRIDE, []):
116  # Override the device default capabilities for a specific address
117  address = device_override.get("address")
118  if not devices.get(address):
119  cat = device_override[CONF_CAT]
120  subcat = device_override[CONF_SUBCAT]
121  devices.set_id(address, cat, subcat, 0)
122 
123  for device in entry.options.get(CONF_X10, []):
124  housecode = device.get(CONF_HOUSECODE)
125  unitcode = device.get(CONF_UNITCODE)
126  x10_type = "on_off"
127  steps = device.get(CONF_DIM_STEPS, 22)
128  if device.get(CONF_PLATFORM) == "light":
129  x10_type = "dimmable"
130  elif device.get(CONF_PLATFORM) == "binary_sensor":
131  x10_type = "sensor"
132  _LOGGER.debug(
133  "Adding X10 device to Insteon: %s %d %s", housecode, unitcode, x10_type
134  )
135  device = devices.add_x10_device(housecode, unitcode, x10_type, steps)
136 
137  await hass.config_entries.async_forward_entry_setups(entry, INSTEON_PLATFORMS)
138 
139  for address in devices:
140  device = devices[address]
141  platforms = get_device_platforms(device)
142  add_insteon_events(hass, device)
143  if not platforms:
144  create_insteon_device(hass, device, entry.entry_id)
145 
146  _LOGGER.debug("Insteon device count: %s", len(devices))
149 
150  create_insteon_device(hass, devices.modem, entry.entry_id)
151 
152  entry.async_create_background_task(
153  hass, async_get_device_config(hass, entry), "insteon-get-device-config"
154  )
155 
156  return True
157 
158 
159 def create_insteon_device(hass, device, config_entry_id):
160  """Create an Insteon device."""
161  device_registry = dr.async_get(hass)
162  device_registry.async_get_or_create(
163  config_entry_id=config_entry_id, # entry.entry_id,
164  identifiers={(DOMAIN, str(device.address))},
165  manufacturer="SmartLabs, Inc",
166  name=f"{device.description} {device.address}",
167  model=f"{device.model} ({device.cat!r}, 0x{device.subcat:02x})",
168  sw_version=f"{device.firmware:02x} Engine Version: {device.engine_version}",
169  )
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_register_services(HomeAssistant hass)
Definition: services.py:80
dict[Platform, Iterable[int]] get_device_platforms(device)
Definition: ipdb.py:112
None add_insteon_events(HomeAssistant hass, Device device)
Definition: utils.py:117
def create_insteon_device(hass, device, config_entry_id)
Definition: __init__.py:159
def async_get_device_config(hass, config_entry)
Definition: __init__.py:39
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:77