1 """Support for LCN devices."""
3 from __future__
import annotations
5 from functools
import partial
9 from pypck.connection
import PchkConnectionManager
27 ADD_ENTITIES_CALLBACKS,
37 from .helpers
import (
40 async_update_config_entry,
42 register_lcn_address_devices,
43 register_lcn_host_device,
45 from .services
import register_services
46 from .websocket
import register_panel_and_ws_api
48 _LOGGER = logging.getLogger(__name__)
50 CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
53 async
def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
54 """Set up the LCN component."""
55 hass.data.setdefault(DOMAIN, {})
64 """Set up a connection to PCHK host from a config entry."""
65 if config_entry.entry_id
in hass.data[DOMAIN]:
69 "SK_NUM_TRIES": config_entry.data[CONF_SK_NUM_TRIES],
70 "DIM_MODE": pypck.lcn_defs.OutputPortDimMode[config_entry.data[CONF_DIM_MODE]],
71 "ACKNOWLEDGE": config_entry.data[CONF_ACKNOWLEDGE],
75 lcn_connection = PchkConnectionManager(
76 config_entry.data[CONF_IP_ADDRESS],
77 config_entry.data[CONF_PORT],
78 config_entry.data[CONF_USERNAME],
79 config_entry.data[CONF_PASSWORD],
81 connection_id=config_entry.entry_id,
85 await lcn_connection.async_connect(timeout=15)
86 except pypck.connection.PchkAuthenticationError:
87 _LOGGER.warning(
'Authentication on PCHK "%s" failed', config_entry.title)
89 except pypck.connection.PchkLicenseError:
92 'Maximum number of connections on PCHK "%s" was '
93 "reached. An additional license key is required"
99 _LOGGER.warning(
'Connection to PCHK "%s" failed', config_entry.title)
102 _LOGGER.debug(
'LCN connected to "%s"', config_entry.title)
103 hass.data[DOMAIN][config_entry.entry_id] = {
104 CONNECTION: lcn_connection,
105 ADD_ENTITIES_CALLBACKS: {},
115 await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
118 device_registry = dr.async_get(hass)
119 input_received = partial(
120 async_host_input_received, hass, config_entry, device_registry
122 lcn_connection.register_for_inputs(input_received)
128 """Migrate old entry."""
130 "Migrating configuration from version %s.%s",
131 config_entry.version,
132 config_entry.minor_version,
135 new_data = {**config_entry.data}
137 if config_entry.version == 1:
139 if config_entry.minor_version < 2:
140 new_data[CONF_ACKNOWLEDGE] =
False
143 new_entities_data = [*new_data[CONF_ENTITIES]]
144 for entity
in new_entities_data:
145 if entity[CONF_DOMAIN]
in [Platform.LIGHT, Platform.SCENE]:
146 if entity[CONF_DOMAIN_DATA][CONF_TRANSITION]
is None:
147 entity[CONF_DOMAIN_DATA][CONF_TRANSITION] = 0
148 entity[CONF_DOMAIN_DATA][CONF_TRANSITION] /= 1000.0
149 new_data[CONF_ENTITIES] = new_entities_data
151 hass.config_entries.async_update_entry(
152 config_entry, data=new_data, minor_version=1, version=2
156 "Migration to configuration version %s.%s successful",
157 config_entry.version,
158 config_entry.minor_version,
164 """Close connection to PCHK host represented by config_entry."""
166 unload_ok = await hass.config_entries.async_unload_platforms(
167 config_entry, PLATFORMS
170 if unload_ok
and config_entry.entry_id
in hass.data[DOMAIN]:
171 host = hass.data[DOMAIN].pop(config_entry.entry_id)
172 await host[CONNECTION].async_close()
179 config_entry: ConfigEntry,
180 device_registry: dr.DeviceRegistry,
181 inp: pypck.inputs.Input,
183 """Process received input object (command) from LCN bus."""
184 if not isinstance(inp, pypck.inputs.ModInput):
187 lcn_connection = hass.data[DOMAIN][config_entry.entry_id][CONNECTION]
188 logical_address = lcn_connection.physical_to_logical(inp.physical_source_addr)
190 logical_address.seg_id,
191 logical_address.addr_id,
192 logical_address.is_group,
195 device = device_registry.async_get_device(identifiers=identifiers)
199 if isinstance(inp, pypck.inputs.ModStatusAccessControl):
201 elif isinstance(inp, pypck.inputs.ModSendKeysHost):
206 hass: HomeAssistant, device: dr.DeviceEntry, address: AddressType, inp: InputType
208 """Fire access control event (transponder, transmitter, fingerprint, codelock)."""
210 "segment_id": address[0],
211 "module_id": address[1],
215 if device
is not None:
216 event_data.update({CONF_DEVICE_ID: device.id})
218 if inp.periphery == pypck.lcn_defs.AccessControlPeriphery.TRANSMITTER:
220 {
"level": inp.level,
"key": inp.key,
"action": inp.action.value}
223 event_name = f
"lcn_{inp.periphery.value.lower()}"
224 hass.bus.async_fire(event_name, event_data)
228 hass: HomeAssistant, device: dr.DeviceEntry, address: AddressType, inp: InputType
230 """Fire send_keys event."""
231 for table, action
in enumerate(inp.actions):
232 if action == pypck.lcn_defs.SendKeyCommand.DONTSEND:
235 for key, selected
in enumerate(inp.keys):
239 "segment_id": address[0],
240 "module_id": address[1],
241 "key": pypck.lcn_defs.Key(table * 8 + key).name.lower(),
242 "action": action.name.lower(),
245 if device
is not None:
246 event_data.update({CONF_DEVICE_ID: device.id})
248 hass.bus.async_fire(
"lcn_send_keys", event_data)
str generate_unique_id(list[int] dev_id, int channel)
None register_lcn_host_device(HomeAssistant hass, ConfigEntry config_entry)
None register_lcn_address_devices(HomeAssistant hass, ConfigEntry config_entry)
None async_update_config_entry(HomeAssistant hass, ConfigEntry config_entry)
None register_panel_and_ws_api(HomeAssistant hass)
None async_host_input_received(HomeAssistant hass, ConfigEntry config_entry, dr.DeviceRegistry device_registry, pypck.inputs.Input inp)
None _async_fire_send_keys_event(HomeAssistant hass, dr.DeviceEntry device, AddressType address, InputType inp)
bool async_setup(HomeAssistant hass, ConfigType config)
bool async_migrate_entry(HomeAssistant hass, ConfigEntry config_entry)
bool async_unload_entry(HomeAssistant hass, ConfigEntry config_entry)
bool async_setup_entry(HomeAssistant hass, ConfigEntry config_entry)
None _async_fire_access_control_event(HomeAssistant hass, dr.DeviceEntry device, AddressType address, InputType inp)
None register_services(HomeAssistant hass)