2019-02-19 13:09:06 +00:00
|
|
|
"""Support for INSTEON Modems (PLM and Hub)."""
|
2020-05-17 13:27:38 +00:00
|
|
|
import asyncio
|
2018-02-25 19:13:39 +00:00
|
|
|
import logging
|
2018-09-10 14:54:17 +00:00
|
|
|
|
2020-05-17 13:27:38 +00:00
|
|
|
from pyinsteon import async_close, async_connect, devices
|
2017-02-21 07:53:39 +00:00
|
|
|
|
2020-08-11 23:04:44 +00:00
|
|
|
from homeassistant.config_entries import SOURCE_IMPORT
|
|
|
|
from homeassistant.const import CONF_PLATFORM, EVENT_HOMEASSISTANT_STOP
|
|
|
|
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY
|
|
|
|
from homeassistant.exceptions import ConfigEntryNotReady
|
2018-09-10 14:54:17 +00:00
|
|
|
|
2020-01-30 09:47:44 +00:00
|
|
|
from .const import (
|
|
|
|
CONF_CAT,
|
|
|
|
CONF_DIM_STEPS,
|
|
|
|
CONF_HOUSECODE,
|
|
|
|
CONF_OVERRIDE,
|
|
|
|
CONF_SUBCAT,
|
|
|
|
CONF_UNITCODE,
|
|
|
|
CONF_X10,
|
|
|
|
DOMAIN,
|
2020-05-17 13:27:38 +00:00
|
|
|
INSTEON_COMPONENTS,
|
|
|
|
ON_OFF_EVENTS,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2020-08-11 23:04:44 +00:00
|
|
|
from .schemas import convert_yaml_to_config_flow
|
2020-05-17 13:27:38 +00:00
|
|
|
from .utils import (
|
|
|
|
add_on_off_event_device,
|
|
|
|
async_register_services,
|
|
|
|
get_device_platforms,
|
|
|
|
register_new_device_callback,
|
|
|
|
)
|
2017-02-21 07:53:39 +00:00
|
|
|
|
2020-01-30 09:47:44 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2018-12-14 13:02:06 +00:00
|
|
|
|
2017-02-21 07:53:39 +00:00
|
|
|
|
2020-05-17 13:27:38 +00:00
|
|
|
async def async_id_unknown_devices(config_dir):
|
|
|
|
"""Send device ID commands to all unidentified devices."""
|
|
|
|
await devices.async_load(id_devices=1)
|
|
|
|
for addr in devices:
|
|
|
|
device = devices[addr]
|
|
|
|
flags = True
|
|
|
|
for name in device.operating_flags:
|
|
|
|
if not device.operating_flags[name].is_loaded:
|
|
|
|
flags = False
|
|
|
|
break
|
|
|
|
if flags:
|
|
|
|
for name in device.properties:
|
|
|
|
if not device.properties[name].is_loaded:
|
|
|
|
flags = False
|
|
|
|
break
|
|
|
|
|
|
|
|
# Cannot be done concurrently due to issues with the underlying protocol.
|
|
|
|
if not device.aldb.is_loaded or not flags:
|
|
|
|
await device.async_read_config()
|
|
|
|
|
|
|
|
await devices.async_save(workdir=config_dir)
|
|
|
|
|
|
|
|
|
2020-08-11 23:04:44 +00:00
|
|
|
async def async_setup_platforms(hass, config_entry):
|
2020-05-17 13:27:38 +00:00
|
|
|
"""Initiate the connection and services."""
|
|
|
|
tasks = [
|
2020-08-11 23:04:44 +00:00
|
|
|
hass.config_entries.async_forward_entry_setup(config_entry, component)
|
2020-05-17 13:27:38 +00:00
|
|
|
for component in INSTEON_COMPONENTS
|
|
|
|
]
|
|
|
|
await asyncio.gather(*tasks)
|
|
|
|
|
|
|
|
for address in devices:
|
|
|
|
device = devices[address]
|
|
|
|
platforms = get_device_platforms(device)
|
|
|
|
if ON_OFF_EVENTS in platforms:
|
|
|
|
add_on_off_event_device(hass, device)
|
|
|
|
|
|
|
|
_LOGGER.debug("Insteon device count: %s", len(devices))
|
2020-08-11 23:04:44 +00:00
|
|
|
register_new_device_callback(hass)
|
2020-05-17 13:27:38 +00:00
|
|
|
async_register_services(hass)
|
|
|
|
|
2020-08-11 23:04:44 +00:00
|
|
|
device_registry = await hass.helpers.device_registry.async_get_registry()
|
|
|
|
device_registry.async_get_or_create(
|
|
|
|
config_entry_id=config_entry.entry_id,
|
|
|
|
identifiers={(DOMAIN, str(devices.modem.address))},
|
|
|
|
manufacturer="Smart Home",
|
|
|
|
name=f"{devices.modem.description} {devices.modem.address}",
|
|
|
|
model=f"{devices.modem.model} (0x{devices.modem.cat:02x}, 0x{devices.modem.subcat:02x})",
|
|
|
|
sw_version=f"{devices.modem.firmware:02x} Engine Version: {devices.modem.engine_version}",
|
|
|
|
)
|
|
|
|
|
|
|
|
# Make a copy of addresses due to edge case where the list of devices could change during status update
|
2020-05-17 13:27:38 +00:00
|
|
|
# Cannot be done concurrently due to issues with the underlying protocol.
|
2020-08-11 23:04:44 +00:00
|
|
|
for address in list(devices):
|
|
|
|
try:
|
|
|
|
await devices[address].async_status()
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
2020-05-17 13:27:38 +00:00
|
|
|
await async_id_unknown_devices(hass.config.config_dir)
|
|
|
|
|
|
|
|
|
|
|
|
async def close_insteon_connection(*args):
|
|
|
|
"""Close the Insteon connection."""
|
|
|
|
await async_close()
|
|
|
|
|
|
|
|
|
2020-08-11 23:04:44 +00:00
|
|
|
async def async_import_config(hass, conf):
|
|
|
|
"""Set up all of the config imported from yaml."""
|
|
|
|
data, options = convert_yaml_to_config_flow(conf)
|
|
|
|
# Create a config entry with the connection data
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": SOURCE_IMPORT}, data=data
|
|
|
|
)
|
|
|
|
# If this is the first time we ran, update the config options
|
|
|
|
if result["type"] == RESULT_TYPE_CREATE_ENTRY and options:
|
|
|
|
entry = result["result"]
|
|
|
|
hass.config_entries.async_update_entry(
|
2020-08-27 11:56:20 +00:00
|
|
|
entry=entry,
|
|
|
|
options=options,
|
2020-08-11 23:04:44 +00:00
|
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2018-10-01 06:52:42 +00:00
|
|
|
async def async_setup(hass, config):
|
2020-08-11 23:04:44 +00:00
|
|
|
"""Set up the Insteon platform."""
|
|
|
|
if DOMAIN not in config:
|
|
|
|
return True
|
2018-02-25 19:13:39 +00:00
|
|
|
|
2017-02-21 07:53:39 +00:00
|
|
|
conf = config[DOMAIN]
|
2020-08-11 23:04:44 +00:00
|
|
|
hass.async_create_task(async_import_config(hass, conf))
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
async def async_setup_entry(hass, entry):
|
|
|
|
"""Set up an Insteon entry."""
|
|
|
|
|
|
|
|
if not devices.modem:
|
|
|
|
try:
|
|
|
|
await async_connect(**entry.data)
|
|
|
|
except ConnectionError as exception:
|
|
|
|
_LOGGER.error("Could not connect to Insteon modem")
|
|
|
|
raise ConfigEntryNotReady from exception
|
2017-02-21 07:53:39 +00:00
|
|
|
|
2020-05-17 13:27:38 +00:00
|
|
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, close_insteon_connection)
|
2020-01-30 09:47:44 +00:00
|
|
|
|
2020-05-17 13:27:38 +00:00
|
|
|
await devices.async_load(
|
|
|
|
workdir=hass.config.config_dir, id_devices=0, load_modem_aldb=0
|
|
|
|
)
|
2020-01-30 09:47:44 +00:00
|
|
|
|
2020-08-11 23:04:44 +00:00
|
|
|
for device_override in entry.options.get(CONF_OVERRIDE, []):
|
2017-02-21 07:53:39 +00:00
|
|
|
# Override the device default capabilities for a specific address
|
2019-07-31 19:25:30 +00:00
|
|
|
address = device_override.get("address")
|
2020-05-17 13:27:38 +00:00
|
|
|
if not devices.get(address):
|
|
|
|
cat = device_override[CONF_CAT]
|
|
|
|
subcat = device_override[CONF_SUBCAT]
|
2020-08-11 23:04:44 +00:00
|
|
|
devices.set_id(address, cat, subcat, 0)
|
2020-05-17 13:27:38 +00:00
|
|
|
|
2020-08-11 23:04:44 +00:00
|
|
|
for device in entry.options.get(CONF_X10, []):
|
2018-06-21 01:44:05 +00:00
|
|
|
housecode = device.get(CONF_HOUSECODE)
|
|
|
|
unitcode = device.get(CONF_UNITCODE)
|
2020-05-17 13:27:38 +00:00
|
|
|
x10_type = "on_off"
|
2018-06-21 01:44:05 +00:00
|
|
|
steps = device.get(CONF_DIM_STEPS, 22)
|
2019-07-31 19:25:30 +00:00
|
|
|
if device.get(CONF_PLATFORM) == "light":
|
|
|
|
x10_type = "dimmable"
|
|
|
|
elif device.get(CONF_PLATFORM) == "binary_sensor":
|
|
|
|
x10_type = "sensor"
|
|
|
|
_LOGGER.debug(
|
|
|
|
"Adding X10 device to Insteon: %s %d %s", housecode, unitcode, x10_type
|
|
|
|
)
|
2020-05-17 13:27:38 +00:00
|
|
|
device = devices.add_x10_device(housecode, unitcode, x10_type, steps)
|
2018-06-21 01:44:05 +00:00
|
|
|
|
2020-08-11 23:04:44 +00:00
|
|
|
asyncio.create_task(async_setup_platforms(hass, entry))
|
2017-02-21 07:53:39 +00:00
|
|
|
return True
|