async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): """Set up Point from a config entry.""" from pypoint import PointSession def token_saver(token): _LOGGER.debug('Saving updated token') entry.data[CONF_TOKEN] = token hass.config_entries.async_update_entry(entry, data={**entry.data}) # Force token update. entry.data[CONF_TOKEN]['expires_in'] = -1 session = PointSession( entry.data['refresh_args']['client_id'], token=entry.data[CONF_TOKEN], auto_refresh_kwargs=entry.data['refresh_args'], token_saver=token_saver, ) if not session.is_authorized: _LOGGER.error('Authentication Error') return False await async_setup_webhook(hass, entry, session) client = MinutPointClient(hass, entry, session) hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: client}) await client.update() return True
async def async_setup_webhook(hass: HomeAssistantType, entry: ConfigEntry, session): """Set up a webhook to handle binary sensor events.""" if CONF_WEBHOOK_ID not in entry.data: entry.data[CONF_WEBHOOK_ID] = \ hass.components.webhook.async_generate_id() entry.data[CONF_WEBHOOK_URL] = \ hass.components.webhook.async_generate_url( entry.data[CONF_WEBHOOK_ID]) _LOGGER.info('Registering new webhook at: %s', entry.data[CONF_WEBHOOK_URL]) hass.config_entries.async_update_entry( entry, data={ **entry.data, }) session.update_webhook(entry.data[CONF_WEBHOOK_URL], entry.data[CONF_WEBHOOK_ID]) hass.components.webhook.async_register( DOMAIN, 'Point', entry.data[CONF_WEBHOOK_ID], handle_webhook)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.config_entries.async_setup_platforms(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(update_listener)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the AVM FRITZ!SmartHome platforms.""" fritz = Fritzhome( host=entry.data[CONF_HOST], user=entry.data[CONF_USERNAME], password=entry.data[CONF_PASSWORD], ) try: await hass.async_add_executor_job(fritz.login) except LoginError as err: raise ConfigEntryAuthFailed from err hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { CONF_CONNECTIONS: fritz, } def _update_fritz_devices() -> dict[str, FritzhomeDevice]: """Update all fritzbox device data.""" try: devices = fritz.get_devices() except requests.exceptions.HTTPError: # If the device rebooted, login again try: fritz.login() except requests.exceptions.HTTPError as ex: raise ConfigEntryAuthFailed from ex devices = fritz.get_devices() data = {} for device in devices: device.update() data[device.ain] = device return data async def async_update_coordinator() -> dict[str, FritzhomeDevice]: """Fetch all device data.""" return await hass.async_add_executor_job(_update_fritz_devices) hass.data[DOMAIN][entry.entry_id][ CONF_COORDINATOR ] = coordinator = DataUpdateCoordinator( hass, LOGGER, name=f"{entry.entry_id}", update_method=async_update_coordinator, update_interval=timedelta(seconds=30), ) await coordinator.async_config_entry_first_refresh() hass.config_entries.async_setup_platforms(entry, PLATFORMS) def logout_fritzbox(event: Event) -> None: """Close connections to this fritzbox.""" fritz.logout() entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, logout_fritzbox) ) return True
async def async_setup_gateway_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: """Set up the Xiaomi Gateway component from a config entry.""" host = entry.data[CONF_HOST] token = entry.data[CONF_TOKEN] name = entry.title gateway_id = entry.unique_id # For backwards compat if entry.unique_id.endswith("-gateway"): hass.config_entries.async_update_entry(entry, unique_id=entry.data["mac"]) entry.async_on_unload(entry.add_update_listener(update_listener)) # Connect to gateway gateway = ConnectXiaomiGateway(hass, entry) try: await gateway.async_connect_gateway(host, token) except AuthException as error: raise ConfigEntryAuthFailed() from error except SetupException as error: raise ConfigEntryNotReady() from error gateway_info = gateway.gateway_info device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, connections={(dr.CONNECTION_NETWORK_MAC, gateway_info.mac_address)}, identifiers={(DOMAIN, gateway_id)}, manufacturer="Xiaomi", name=name, model=gateway_info.model, sw_version=gateway_info.firmware_version, hw_version=gateway_info.hardware_version, ) def update_data(): """Fetch data from the subdevice.""" data = {} for sub_device in gateway.gateway_device.devices.values(): try: sub_device.update() except GatewayException as ex: _LOGGER.error("Got exception while fetching the state: %s", ex) data[sub_device.sid] = {ATTR_AVAILABLE: False} else: data[sub_device.sid] = {ATTR_AVAILABLE: True} return data async def async_update_data(): """Fetch data from the subdevice using async_add_executor_job.""" return await hass.async_add_executor_job(update_data) # Create update coordinator coordinator = DataUpdateCoordinator( hass, _LOGGER, name=name, update_method=async_update_data, # Polling interval. Will only be polled if there are subscribers. update_interval=UPDATE_INTERVAL, ) hass.data[DOMAIN][entry.entry_id] = { CONF_GATEWAY: gateway.gateway_device, KEY_COORDINATOR: coordinator, } for platform in GATEWAY_PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform))
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Perform Volkswagen WeConnect component setup.""" def register_services(): cs = ChargerService(hass) ss = SchedulerService(hass) hass.services.async_register( domain=DOMAIN, service=SERVICE_SET_TIMER_BASIC_SETTINGS, service_func=ss.set_timer_basic_settings, schema=SERVICE_SET_TIMER_BASIC_SETTINGS_SCHEMA, ) hass.services.async_register( domain=DOMAIN, service=SERVICE_UPDATE_SCHEDULE, service_func=ss.update_schedule, schema=SERVICE_UPDATE_SCHEDULE_SCHEMA, ) hass.services.async_register( domain=DOMAIN, service=SERVICE_UPDATE_PROFILE, service_func=ss.update_profile, schema=SERVICE_UPDATE_PROFILE_SCHEMA, ) hass.services.async_register( domain=DOMAIN, service=SERVICE_SET_CHARGER_MAX_CURRENT, service_func=cs.set_charger_max_current, schema=SERVICE_SET_CHARGER_MAX_CURRENT_SCHEMA, ) if entry.options.get(CONF_SCAN_INTERVAL): update_interval = timedelta(minutes=entry.options[CONF_SCAN_INTERVAL]) else: update_interval = timedelta(minutes=DEFAULT_UPDATE_INTERVAL) coordinator = VolkswagenCoordinator(hass, entry, update_interval) coordinator.async_add_listener(coordinator.async_update_listener) if not await coordinator.async_login(): await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH}, data=entry, ) return False hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, coordinator.async_logout) # First refresh, with retry on errors await coordinator.async_config_entry_first_refresh() data: VolkswagenData = VolkswagenData(entry.data, coordinator) instruments = coordinator.data def is_enabled(attr): """Return true if the user has enabled the resource.""" return attr in entry.options.get(CONF_RESOURCES, [attr]) def is_new(attr): """Return true if the resource is new.""" return attr not in entry.options.get(CONF_AVAILABLE_RESOURCES, [attr]) components = set() for instrument in (instrument for instrument in instruments if instrument.component in COMPONENTS): # Add resource if it's enabled or new if is_enabled(instrument.slug_attr) or (is_new( instrument.slug_attr) and not entry.pref_disable_new_entities): data.instruments.add(instrument) components.add(COMPONENTS[instrument.component]) for component in components: coordinator.platforms.append(component) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) hass.data[DOMAIN][entry.entry_id] = { UPDATE_CALLBACK: update_callback, DATA: data, UNDO_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener), } register_services() return True
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Sonos from a config entry.""" platform = entity_platform.async_get_current_platform() @callback def async_create_entities(speaker: SonosSpeaker) -> None: """Handle device discovery and create entities.""" async_add_entities([SonosMediaPlayerEntity(speaker)]) @service.verify_domain_control(hass, SONOS_DOMAIN) async def async_service_handle(service_call: ServiceCall) -> None: """Handle dispatched services.""" assert platform is not None entities = await platform.async_extract_from_service(service_call) if not entities: return speakers = [] for entity in entities: assert isinstance(entity, SonosMediaPlayerEntity) speakers.append(entity.speaker) if service_call.service == SERVICE_JOIN: master = platform.entities.get(service_call.data[ATTR_MASTER]) if master: await SonosSpeaker.join_multi(hass, master.speaker, speakers) # type: ignore[arg-type] else: _LOGGER.error( "Invalid master specified for join service: %s", service_call.data[ATTR_MASTER], ) elif service_call.service == SERVICE_UNJOIN: await SonosSpeaker.unjoin_multi(hass, speakers) # type: ignore[arg-type] elif service_call.service == SERVICE_SNAPSHOT: await SonosSpeaker.snapshot_multi( hass, speakers, service_call.data[ATTR_WITH_GROUP] # type: ignore[arg-type] ) elif service_call.service == SERVICE_RESTORE: await SonosSpeaker.restore_multi( hass, speakers, service_call.data[ATTR_WITH_GROUP] # type: ignore[arg-type] ) config_entry.async_on_unload( async_dispatcher_connect(hass, SONOS_CREATE_MEDIA_PLAYER, async_create_entities) ) hass.services.async_register( SONOS_DOMAIN, SERVICE_JOIN, async_service_handle, cv.make_entity_service_schema({vol.Required(ATTR_MASTER): cv.entity_id}), ) hass.services.async_register( SONOS_DOMAIN, SERVICE_UNJOIN, async_service_handle, cv.make_entity_service_schema({}), ) join_unjoin_schema = cv.make_entity_service_schema( {vol.Optional(ATTR_WITH_GROUP, default=True): cv.boolean} ) hass.services.async_register( SONOS_DOMAIN, SERVICE_SNAPSHOT, async_service_handle, join_unjoin_schema ) hass.services.async_register( SONOS_DOMAIN, SERVICE_RESTORE, async_service_handle, join_unjoin_schema ) platform.async_register_entity_service( # type: ignore SERVICE_SET_TIMER, { vol.Required(ATTR_SLEEP_TIME): vol.All( vol.Coerce(int), vol.Range(min=0, max=86399) ) }, "set_sleep_timer", ) platform.async_register_entity_service(SERVICE_CLEAR_TIMER, {}, "clear_sleep_timer") # type: ignore platform.async_register_entity_service( # type: ignore SERVICE_UPDATE_ALARM, { vol.Required(ATTR_ALARM_ID): cv.positive_int, vol.Optional(ATTR_TIME): cv.time, vol.Optional(ATTR_VOLUME): cv.small_float, vol.Optional(ATTR_ENABLED): cv.boolean, vol.Optional(ATTR_INCLUDE_LINKED_ZONES): cv.boolean, }, "set_alarm", ) platform.async_register_entity_service( # type: ignore SERVICE_SET_OPTION, { vol.Optional(ATTR_EQ_BASS): vol.All( vol.Coerce(int), vol.Range(min=-10, max=10) ), vol.Optional(ATTR_EQ_TREBLE): vol.All( vol.Coerce(int), vol.Range(min=-10, max=10) ), }, "set_option", ) platform.async_register_entity_service( # type: ignore SERVICE_PLAY_QUEUE, {vol.Optional(ATTR_QUEUE_POSITION): cv.positive_int}, "play_queue", ) platform.async_register_entity_service( # type: ignore SERVICE_REMOVE_FROM_QUEUE, {vol.Optional(ATTR_QUEUE_POSITION): cv.positive_int}, "remove_from_queue", )
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Control4 from a config entry.""" hass.data.setdefault(DOMAIN, {}) entry_data = hass.data[DOMAIN].setdefault(entry.entry_id, {}) account_session = aiohttp_client.async_get_clientsession(hass) config = entry.data account = C4Account(config[CONF_USERNAME], config[CONF_PASSWORD], account_session) try: await account.getAccountBearerToken() except client_exceptions.ClientError as exception: _LOGGER.error("Error connecting to Control4 account API: %s", exception) raise ConfigEntryNotReady from exception except BadCredentials as exception: _LOGGER.error( "Error authenticating with Control4 account API, incorrect username or password: %s", exception, ) return False entry_data[CONF_ACCOUNT] = account controller_unique_id = config[CONF_CONTROLLER_UNIQUE_ID] entry_data[CONF_CONTROLLER_UNIQUE_ID] = controller_unique_id director_token_dict = await account.getDirectorBearerToken( controller_unique_id) director_session = aiohttp_client.async_get_clientsession(hass, verify_ssl=False) director = C4Director(config[CONF_HOST], director_token_dict[CONF_TOKEN], director_session) entry_data[CONF_DIRECTOR] = director entry_data[CONF_DIRECTOR_TOKEN_EXPIRATION] = director_token_dict[ "token_expiration"] # Add Control4 controller to device registry controller_href = (await account.getAccountControllers())["href"] entry_data[ CONF_DIRECTOR_SW_VERSION] = await account.getControllerOSVersion( controller_href) _, model, mac_address = controller_unique_id.split("_", 3) entry_data[CONF_DIRECTOR_MODEL] = model.upper() device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, controller_unique_id)}, connections={(dr.CONNECTION_NETWORK_MAC, mac_address)}, manufacturer="Control4", name=controller_unique_id, model=entry_data[CONF_DIRECTOR_MODEL], sw_version=entry_data[CONF_DIRECTOR_SW_VERSION], ) # Store all items found on controller for platforms to use director_all_items = await director.getAllItemInfo() director_all_items = json.loads(director_all_items) entry_data[CONF_DIRECTOR_ALL_ITEMS] = director_all_items # Load options from config entry entry_data[CONF_SCAN_INTERVAL] = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) entry_data[CONF_CONFIG_LISTENER] = entry.add_update_listener( update_listener) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None: """Set up device tracker for Netgear component.""" router = hass.data[DOMAIN][entry.entry_id][KEY_ROUTER] coordinator = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR] coordinator_traffic = hass.data[DOMAIN][ entry.entry_id][KEY_COORDINATOR_TRAFFIC] coordinator_speed = hass.data[DOMAIN][ entry.entry_id][KEY_COORDINATOR_SPEED] coordinator_utilization = hass.data[DOMAIN][ entry.entry_id][KEY_COORDINATOR_UTIL] coordinator_link = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR_LINK] # Router entities router_entities = [] for description in SENSOR_TRAFFIC_TYPES: router_entities.append( NetgearRouterSensorEntity(coordinator_traffic, router, description)) for description in SENSOR_SPEED_TYPES: router_entities.append( NetgearRouterSensorEntity(coordinator_speed, router, description)) for description in SENSOR_UTILIZATION: router_entities.append( NetgearRouterSensorEntity(coordinator_utilization, router, description)) for description in SENSOR_LINK_TYPES: router_entities.append( NetgearRouterSensorEntity(coordinator_link, router, description)) async_add_entities(router_entities) # Entities per network device tracked = set() sensors = ["type", "link_rate", "signal"] if router.method_version == 2: sensors.extend(["ssid", "conn_ap_mac"]) @callback def new_device_callback() -> None: """Add new devices if needed.""" if not coordinator.data: return new_entities = [] for mac, device in router.devices.items(): if mac in tracked: continue new_entities.extend([ NetgearSensorEntity(coordinator, router, device, attribute) for attribute in sensors ]) tracked.add(mac) if new_entities: async_add_entities(new_entities) entry.async_on_unload(coordinator.async_add_listener(new_device_callback)) coordinator.data = True new_device_callback()
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up System Bridge from a config entry.""" bridge = Bridge( BridgeClient(aiohttp_client.async_get_clientsession(hass)), f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}", entry.data[CONF_API_KEY], ) try: async with async_timeout.timeout(30): await bridge.async_get_information() except BridgeAuthenticationException as exception: raise ConfigEntryAuthFailed( f"Authentication failed for {entry.title} ({entry.data[CONF_HOST]})" ) from exception except BRIDGE_CONNECTION_ERRORS as exception: raise ConfigEntryNotReady( f"Could not connect to {entry.title} ({entry.data[CONF_HOST]})." ) from exception coordinator = SystemBridgeDataUpdateCoordinator(hass, bridge, _LOGGER, entry=entry) await coordinator.async_config_entry_first_refresh() # Wait for initial data try: async with async_timeout.timeout(60): while (coordinator.bridge.battery is None or coordinator.bridge.cpu is None or coordinator.bridge.display is None or coordinator.bridge.filesystem is None or coordinator.bridge.graphics is None or coordinator.bridge.information is None or coordinator.bridge.memory is None or coordinator.bridge.network is None or coordinator.bridge.os is None or coordinator.bridge.processes is None or coordinator.bridge.system is None): _LOGGER.debug( "Waiting for initial data from %s (%s)", entry.title, entry.data[CONF_HOST], ) await asyncio.sleep(1) except asyncio.TimeoutError as exception: raise ConfigEntryNotReady( f"Timed out waiting for {entry.title} ({entry.data[CONF_HOST]})." ) from exception hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) if hass.services.has_service(DOMAIN, SERVICE_SEND_COMMAND): return True def valid_device(device: str): """Check device is valid.""" device_registry = dr.async_get(hass) device_entry = device_registry.async_get(device) if device_entry is not None: try: return next( entry.entry_id for entry in hass.config_entries.async_entries(DOMAIN) if entry.entry_id in device_entry.config_entries) except StopIteration as exception: raise vol.Invalid from exception raise vol.Invalid(f"Device {device} does not exist") async def handle_send_command(call): """Handle the send_command service call.""" coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][ call.data[CONF_BRIDGE]] bridge: Bridge = coordinator.bridge command = call.data[CONF_COMMAND] arguments = shlex.split(call.data[CONF_ARGUMENTS]) _LOGGER.debug( "Command payload: %s", { CONF_COMMAND: command, CONF_ARGUMENTS: arguments, CONF_WAIT: False }, ) try: response: CommandResponse = await bridge.async_send_command({ CONF_COMMAND: command, CONF_ARGUMENTS: arguments, CONF_WAIT: False }) if not response.success: raise HomeAssistantError( f"Error sending command. Response message was: {response.message}" ) except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception: raise HomeAssistantError("Error sending command") from exception _LOGGER.debug("Sent command. Response message was: %s", response.message) async def handle_open(call): """Handle the open service call.""" coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][ call.data[CONF_BRIDGE]] bridge: Bridge = coordinator.bridge path = call.data[CONF_PATH] _LOGGER.debug("Open payload: %s", {CONF_PATH: path}) try: await bridge.async_open({CONF_PATH: path}) except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception: raise HomeAssistantError("Error sending") from exception _LOGGER.debug("Sent open request") async def handle_send_keypress(call): """Handle the send_keypress service call.""" coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][ call.data[CONF_BRIDGE]] bridge: Bridge = coordinator.data keyboard_payload: KeyboardPayload = { CONF_KEY: call.data[CONF_KEY], CONF_MODIFIERS: shlex.split(call.data.get(CONF_MODIFIERS, "")), } _LOGGER.debug("Keypress payload: %s", keyboard_payload) try: await bridge.async_send_keypress(keyboard_payload) except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception: raise HomeAssistantError("Error sending") from exception _LOGGER.debug("Sent keypress request") async def handle_send_text(call): """Handle the send_keypress service call.""" coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][ call.data[CONF_BRIDGE]] bridge: Bridge = coordinator.data keyboard_payload: KeyboardPayload = {CONF_TEXT: call.data[CONF_TEXT]} _LOGGER.debug("Text payload: %s", keyboard_payload) try: await bridge.async_send_keypress(keyboard_payload) except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception: raise HomeAssistantError("Error sending") from exception _LOGGER.debug("Sent text request") hass.services.async_register( DOMAIN, SERVICE_SEND_COMMAND, handle_send_command, schema=vol.Schema( { vol.Required(CONF_BRIDGE): valid_device, vol.Required(CONF_COMMAND): cv.string, vol.Optional(CONF_ARGUMENTS, ""): cv.string, }, ), ) hass.services.async_register( DOMAIN, SERVICE_OPEN, handle_open, schema=vol.Schema( { vol.Required(CONF_BRIDGE): valid_device, vol.Required(CONF_PATH): cv.string, }, ), ) hass.services.async_register( DOMAIN, SERVICE_SEND_KEYPRESS, handle_send_keypress, schema=vol.Schema( { vol.Required(CONF_BRIDGE): valid_device, vol.Required(CONF_KEY): cv.string, vol.Optional(CONF_MODIFIERS): cv.string, }, ), ) hass.services.async_register( DOMAIN, SERVICE_SEND_TEXT, handle_send_text, schema=vol.Schema( { vol.Required(CONF_BRIDGE): valid_device, vol.Required(CONF_TEXT): cv.string, }, ), ) # Reload entry when its updated. entry.async_on_unload(entry.add_update_listener(async_reload_entry)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up the Tapo: Cameras Control component from a config entry.""" hass.data.setdefault(DOMAIN, {}) host = entry.data.get(CONF_IP_ADDRESS) username = entry.data.get(CONF_USERNAME) password = entry.data.get(CONF_PASSWORD) motionSensor = entry.data.get(ENABLE_MOTION_SENSOR) cloud_password = entry.data.get(CLOUD_PASSWORD) enableTimeSync = entry.data.get(ENABLE_TIME_SYNC) try: if cloud_password != "": tapoController = await hass.async_add_executor_job( registerController, host, "admin", cloud_password) else: tapoController = await hass.async_add_executor_job( registerController, host, username, password) async def async_update_data(): LOGGER.debug("async_update_data - entry") host = entry.data.get(CONF_IP_ADDRESS) username = entry.data.get(CONF_USERNAME) password = entry.data.get(CONF_PASSWORD) motionSensor = entry.data.get(ENABLE_MOTION_SENSOR) enableTimeSync = entry.data.get(ENABLE_TIME_SYNC) # motion detection retries if motionSensor or enableTimeSync: LOGGER.debug("Motion sensor or time sync is enabled.") if (not hass.data[DOMAIN][entry.entry_id]["eventsDevice"] or not hass.data[DOMAIN][ entry.entry_id]["onvifManagement"]): LOGGER.debug("Setting up subscription to motion sensor...") # retry if connection to onvif failed LOGGER.debug("Initiating onvif.") onvifDevice = await initOnvifEvents( hass, host, username, password) LOGGER.debug(onvifDevice) hass.data[DOMAIN][ entry.entry_id]["eventsDevice"] = onvifDevice["device"] hass.data[DOMAIN][entry.entry_id][ "onvifManagement"] = onvifDevice["device_mgmt"] if motionSensor: await setupOnvif(hass, entry) elif (not hass.data[DOMAIN][entry.entry_id]["eventsSetup"] and motionSensor): LOGGER.debug( "Setting up subcription to motion sensor events...") # retry if subscription to events failed hass.data[DOMAIN][ entry.entry_id]["eventsSetup"] = await setupEvents( hass, entry) else: LOGGER.debug("Motion sensor: OK") if (hass.data[DOMAIN][entry.entry_id]["onvifManagement"] and enableTimeSync): ts = datetime.datetime.utcnow().timestamp() if (ts - hass.data[DOMAIN][entry.entry_id]["lastTimeSync"] > TIME_SYNC_PERIOD): await syncTime(hass, entry) # cameras state someCameraEnabled = False for entity in hass.data[DOMAIN][entry.entry_id]["entities"]: if entity._enabled: someCameraEnabled = True if someCameraEnabled: try: camData = await getCamData(hass, tapoController) except Exception as e: camData = False LOGGER.error(e) hass.data[DOMAIN][entry.entry_id]["camData"] = camData for entity in hass.data[DOMAIN][entry.entry_id]["entities"]: if entity._enabled: entity.updateCam(camData) entity.async_schedule_update_ha_state(True) if (not hass.data[DOMAIN][ entry.entry_id]["noiseSensorStarted"] and entity._enable_sound_detection): await entity.startNoiseDetection() tapoCoordinator = DataUpdateCoordinator( hass, LOGGER, name="Tapo resource status", update_method=async_update_data, ) camData = await getCamData(hass, tapoController) hass.data[DOMAIN][entry.entry_id] = { "controller": tapoController, "update_listener": entry.add_update_listener(update_listener), "coordinator": tapoCoordinator, "camData": camData, "lastTimeSync": 0, "motionSensorCreated": False, "eventsDevice": False, "onvifManagement": False, "eventsSetup": False, "events": False, "name": camData["basic_info"]["device_alias"], } if motionSensor or enableTimeSync: onvifDevice = await initOnvifEvents(hass, host, username, password) hass.data[DOMAIN][ entry.entry_id]["eventsDevice"] = onvifDevice["device"] hass.data[DOMAIN][ entry.entry_id]["onvifManagement"] = onvifDevice["device_mgmt"] if motionSensor: LOGGER.debug("Seting up motion sensor for the first time.") await setupOnvif(hass, entry) if enableTimeSync: await syncTime(hass, entry) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, "camera")) async def unsubscribe(event): if hass.data[DOMAIN][entry.entry_id]["events"]: await hass.data[DOMAIN][entry.entry_id]["events"].async_stop() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unsubscribe) except Exception as e: LOGGER.error( "Unable to connect to Tapo: Cameras Control controller: %s", str(e)) raise ConfigEntryNotReady return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up RainMachine as config entry.""" hass.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = {} entry_updates = {} if not entry.unique_id: # If the config entry doesn't already have a unique ID, set one: entry_updates["unique_id"] = entry.data[CONF_IP_ADDRESS] if CONF_ZONE_RUN_TIME in entry.data: # If a zone run time exists in the config entry's data, pop it and move it to # options: data = {**entry.data} entry_updates["data"] = data entry_updates["options"] = { **entry.options, CONF_ZONE_RUN_TIME: data.pop(CONF_ZONE_RUN_TIME), } if entry_updates: hass.config_entries.async_update_entry(entry, **entry_updates) websession = aiohttp_client.async_get_clientsession(hass) client = Client(session=websession) try: await client.load_local( entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD], port=entry.data[CONF_PORT], ssl=entry.data.get(CONF_SSL, DEFAULT_SSL), ) except RainMachineError as err: LOGGER.error("An error occurred: %s", err) raise ConfigEntryNotReady from err # regenmaschine can load multiple controllers at once, but we only grab the one # we loaded above: controller = hass.data[DOMAIN][DATA_CONTROLLER][entry.entry_id] = next( iter(client.controllers.values())) async def async_update(api_category: str) -> dict: """Update the appropriate API data based on a category.""" try: if api_category == DATA_PROGRAMS: return await controller.programs.all(include_inactive=True) if api_category == DATA_PROVISION_SETTINGS: return await controller.provisioning.settings() if api_category == DATA_RESTRICTIONS_CURRENT: return await controller.restrictions.current() if api_category == DATA_RESTRICTIONS_UNIVERSAL: return await controller.restrictions.universal() return await controller.zones.all(details=True, include_inactive=True) except RainMachineError as err: raise UpdateFailed(err) from err controller_init_tasks = [] for api_category in [ DATA_PROGRAMS, DATA_PROVISION_SETTINGS, DATA_RESTRICTIONS_CURRENT, DATA_RESTRICTIONS_UNIVERSAL, DATA_ZONES, ]: coordinator = hass.data[DOMAIN][DATA_COORDINATOR][ entry.entry_id][api_category] = DataUpdateCoordinator( hass, LOGGER, name=f'{controller.name} ("{api_category}")', update_interval=DEFAULT_UPDATE_INTERVAL, update_method=partial(async_update, api_category), ) controller_init_tasks.append(coordinator.async_refresh()) await asyncio.gather(*controller_init_tasks) for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) hass.data[DOMAIN][DATA_LISTENER] = entry.add_update_listener( async_reload_entry) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up TDAmeritrade from a config entry.""" _LOGGER.debug("Setting up entry") config_flow.OAuth2FlowHandler.async_register_implementation( hass, config_entry_oauth2_flow.LocalOAuth2Implementation( hass, entry.domain, entry.data["consumer_key"], None, OAUTH2_AUTHORIZE, OAUTH2_TOKEN, ), ) implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry)) session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) auth = api.AsyncConfigEntryAuth( aiohttp_client.async_get_clientsession(hass), session) client = AmeritradeAPI(auth) async def place_order_service(call): """Handle a place trade service call.""" price = call.data["price"] instruction = call.data["instruction"] quantity = call.data["quantity"] symbol = call.data["symbol"] account_id = call.data["account_id"] order_type = call.data["order_type"] session = call.data["session"] duration = call.data["duration"] order_strategy_type = call.data["orderStrategyType"] asset_type = call.data["assetType"] return await client.async_place_order( price, instruction, quantity, symbol, account_id, order_type=order_type, session=session, duration=duration, orderStrategyType=order_strategy_type, assetType=asset_type, ) async def get_quote_service(call): """Handle a place trade service call.""" symbol = call.data["symbol"] res = await client.async_get_quote(ticker=symbol) hass.states.async_set( f"get_quote_service.{symbol}", res[symbol]["lastPrice"], attributes=res[symbol], ) return True _LOGGER.debug("Registering Services") hass.services.async_register(DOMAIN, "place_order", place_order_service) hass.services.async_register(DOMAIN, "get_quote", get_quote_service) hass.data.setdefault(DOMAIN, {}) hass_data = dict(entry.data) entry.update_listeners = [] hass_data["unsub"] = entry.add_update_listener(options_update_listener) hass_data["client"] = AmeritradeAPI(auth) hass.data[DOMAIN][entry.entry_id] = hass_data if entry.state == "not_loaded": for component in PLATFORMS: _LOGGER.debug("Setting up %s component", component) hass.async_create_task( hass.config_entries.async_forward_entry_setup( entry, component)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Yeelight from a config entry.""" async def _initialize(host: str, capabilities: dict | None = None) -> None: remove_dispatcher = async_dispatcher_connect( hass, DEVICE_INITIALIZED.format(host), _load_platforms, ) hass.data[DOMAIN][DATA_CONFIG_ENTRIES][ entry.entry_id][DATA_REMOVE_INIT_DISPATCHER] = remove_dispatcher device = await _async_get_device(hass, host, entry, capabilities) hass.data[DOMAIN][DATA_CONFIG_ENTRIES][ entry.entry_id][DATA_DEVICE] = device await device.async_setup() async def _load_platforms(): for platform in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform)) # Move options from data for imported entries # Initialize options with default values for other entries if not entry.options: hass.config_entries.async_update_entry( entry, data={ CONF_HOST: entry.data.get(CONF_HOST), CONF_ID: entry.data.get(CONF_ID), }, options={ CONF_NAME: entry.data.get(CONF_NAME, ""), CONF_MODEL: entry.data.get(CONF_MODEL, ""), CONF_TRANSITION: entry.data.get(CONF_TRANSITION, DEFAULT_TRANSITION), CONF_MODE_MUSIC: entry.data.get(CONF_MODE_MUSIC, DEFAULT_MODE_MUSIC), CONF_SAVE_ON_CHANGE: entry.data.get(CONF_SAVE_ON_CHANGE, DEFAULT_SAVE_ON_CHANGE), CONF_NIGHTLIGHT_SWITCH: entry.data.get(CONF_NIGHTLIGHT_SWITCH, DEFAULT_NIGHTLIGHT_SWITCH), }, ) hass.data[DOMAIN][DATA_CONFIG_ENTRIES][entry.entry_id] = { DATA_UNSUB_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener) } if entry.data.get(CONF_HOST): # manually added device await _initialize(entry.data[CONF_HOST]) else: # discovery scanner = YeelightScanner.async_get(hass) scanner.async_register_callback(entry.data[CONF_ID], _initialize) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Netgear component.""" router = NetgearRouter(hass, entry) try: if not await router.async_setup(): raise ConfigEntryNotReady except CannotLoginException as ex: raise ConfigEntryNotReady from ex port = entry.data.get(CONF_PORT) ssl = entry.data.get(CONF_SSL) if port != router.port or ssl != router.ssl: data = {**entry.data, CONF_PORT: router.port, CONF_SSL: router.ssl} hass.config_entries.async_update_entry(entry, data=data) _LOGGER.info( "Netgear port-SSL combination updated from (%i, %r) to (%i, %r), " "this should only occur after a firmware update", port, ssl, router.port, router.ssl, ) hass.data.setdefault(DOMAIN, {}) entry.async_on_unload(entry.add_update_listener(update_listener)) assert entry.unique_id device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, entry.unique_id)}, manufacturer="Netgear", name=router.device_name, model=router.model, sw_version=router.firmware_version, hw_version=router.hardware_version, configuration_url=f"http://{entry.data[CONF_HOST]}/", ) async def async_update_devices() -> bool: """Fetch data from the router.""" if router.mode == MODE_ROUTER: return await router.async_update_device_trackers() return False async def async_update_traffic_meter() -> dict[str, Any] | None: """Fetch data from the router.""" return await router.async_get_traffic_meter() # Create update coordinators coordinator = DataUpdateCoordinator( hass, _LOGGER, name=f"{router.device_name} Devices", update_method=async_update_devices, update_interval=SCAN_INTERVAL, ) coordinator_traffic_meter = DataUpdateCoordinator( hass, _LOGGER, name=f"{router.device_name} Traffic meter", update_method=async_update_traffic_meter, update_interval=SCAN_INTERVAL, ) if router.mode == MODE_ROUTER: await coordinator.async_config_entry_first_refresh() await coordinator_traffic_meter.async_config_entry_first_refresh() hass.data[DOMAIN][entry.entry_id] = { KEY_ROUTER: router, KEY_COORDINATOR: coordinator, KEY_COORDINATOR_TRAFFIC: coordinator_traffic_meter, } hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up RainMachine as config entry.""" hass.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = {} entry_updates = {} if not entry.unique_id: # If the config entry doesn't already have a unique ID, set one: entry_updates["unique_id"] = entry.data[CONF_IP_ADDRESS] if CONF_ZONE_RUN_TIME in entry.data: # If a zone run time exists in the config entry's data, pop it and move it to # options: data = {**entry.data} entry_updates["data"] = data entry_updates["options"] = { **entry.options, CONF_ZONE_RUN_TIME: data.pop(CONF_ZONE_RUN_TIME), } if entry_updates: hass.config_entries.async_update_entry(entry, **entry_updates) _verify_domain_control = verify_domain_control(hass, DOMAIN) websession = aiohttp_client.async_get_clientsession(hass) client = Client(session=websession) try: await client.load_local( entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD], port=entry.data[CONF_PORT], ssl=entry.data.get(CONF_SSL, DEFAULT_SSL), ) except RainMachineError as err: LOGGER.error("An error occurred: %s", err) raise ConfigEntryNotReady from err # regenmaschine can load multiple controllers at once, but we only grab the one # we loaded above: controller = hass.data[DOMAIN][DATA_CONTROLLER][entry.entry_id] = next( iter(client.controllers.values())) async def async_update(api_category: str) -> dict: """Update the appropriate API data based on a category.""" try: if api_category == DATA_PROGRAMS: return await controller.programs.all(include_inactive=True) if api_category == DATA_PROVISION_SETTINGS: return await controller.provisioning.settings() if api_category == DATA_RESTRICTIONS_CURRENT: return await controller.restrictions.current() if api_category == DATA_RESTRICTIONS_UNIVERSAL: return await controller.restrictions.universal() return await controller.zones.all(details=True, include_inactive=True) except RainMachineError as err: raise UpdateFailed(err) from err controller_init_tasks = [] for api_category in [ DATA_PROGRAMS, DATA_PROVISION_SETTINGS, DATA_RESTRICTIONS_CURRENT, DATA_RESTRICTIONS_UNIVERSAL, DATA_ZONES, ]: coordinator = hass.data[DOMAIN][DATA_COORDINATOR][ entry.entry_id][api_category] = DataUpdateCoordinator( hass, LOGGER, name=f'{controller.name} ("{api_category}")', update_interval=DEFAULT_UPDATE_INTERVAL, update_method=partial(async_update, api_category), ) controller_init_tasks.append(coordinator.async_refresh()) await asyncio.gather(*controller_init_tasks) for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) @_verify_domain_control async def disable_program(call: ServiceCall): """Disable a program.""" await controller.programs.disable(call.data[CONF_PROGRAM_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def disable_zone(call: ServiceCall): """Disable a zone.""" await controller.zones.disable(call.data[CONF_ZONE_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def enable_program(call: ServiceCall): """Enable a program.""" await controller.programs.enable(call.data[CONF_PROGRAM_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def enable_zone(call: ServiceCall): """Enable a zone.""" await controller.zones.enable(call.data[CONF_ZONE_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def pause_watering(call: ServiceCall): """Pause watering for a set number of seconds.""" await controller.watering.pause_all(call.data[CONF_SECONDS]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def start_program(call: ServiceCall): """Start a particular program.""" await controller.programs.start(call.data[CONF_PROGRAM_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def start_zone(call: ServiceCall): """Start a particular zone for a certain amount of time.""" await controller.zones.start(call.data[CONF_ZONE_ID], call.data[CONF_ZONE_RUN_TIME]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def stop_all(call: ServiceCall): """Stop all watering.""" await controller.watering.stop_all() await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def stop_program(call: ServiceCall): """Stop a program.""" await controller.programs.stop(call.data[CONF_PROGRAM_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def stop_zone(call: ServiceCall): """Stop a zone.""" await controller.zones.stop(call.data[CONF_ZONE_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def unpause_watering(call: ServiceCall): """Unpause watering.""" await controller.watering.unpause_all() await async_update_programs_and_zones(hass, entry) for service, method, schema in [ ("disable_program", disable_program, SERVICE_ALTER_PROGRAM), ("disable_zone", disable_zone, SERVICE_ALTER_ZONE), ("enable_program", enable_program, SERVICE_ALTER_PROGRAM), ("enable_zone", enable_zone, SERVICE_ALTER_ZONE), ("pause_watering", pause_watering, SERVICE_PAUSE_WATERING), ("start_program", start_program, SERVICE_START_PROGRAM_SCHEMA), ("start_zone", start_zone, SERVICE_START_ZONE_SCHEMA), ("stop_all", stop_all, {}), ("stop_program", stop_program, SERVICE_STOP_PROGRAM_SCHEMA), ("stop_zone", stop_zone, SERVICE_STOP_ZONE_SCHEMA), ("unpause_watering", unpause_watering, {}), ]: hass.services.async_register(DOMAIN, service, method, schema=schema) hass.data[DOMAIN][DATA_LISTENER] = entry.add_update_listener( async_reload_entry) return True
async def async_setup_entry(hass: HomeAssistantType, config_entry: ConfigEntry): """Set up UPnP/IGD device from a config entry.""" domain_data = hass.data[DOMAIN] conf = domain_data['config'] # discover and construct device = await async_discover_and_construct(hass, config_entry.data.get('udn')) if not device: _LOGGER.info('Unable to create UPnP/IGD, aborting') return False # 'register'/save UDN config_entry.data['udn'] = device.udn hass.data[DOMAIN]['devices'][device.udn] = device hass.config_entries.async_update_entry(entry=config_entry, data=config_entry.data) # create device registry entry device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, connections={ (dr.CONNECTION_UPNP, device.udn) }, identifiers={ (DOMAIN, device.udn) }, name=device.name, manufacturer=device.manufacturer, ) # set up sensors if conf.get(CONF_ENABLE_SENSORS): _LOGGER.debug('Enabling sensors') # register sensor setup handlers hass.async_create_task(hass.config_entries.async_forward_entry_setup( config_entry, 'sensor')) # set up port mapping if conf.get(CONF_ENABLE_PORT_MAPPING): _LOGGER.debug('Enabling port mapping') local_ip = domain_data['local_ip'] ports = conf.get('ports', {}) hass_port = None if hasattr(hass, 'http'): hass_port = hass.http.server_port ports = _substitute_hass_ports(ports, hass_port=hass_port) await device.async_add_port_mappings(ports, local_ip) # set up port mapping deletion on stop-hook async def delete_port_mapping(event): """Delete port mapping on quit.""" _LOGGER.debug('Deleting port mappings') await device.async_delete_port_mappings() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, delete_port_mapping) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None: """Set up the Netatmo weather and homecoach platform.""" data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER] platform_not_ready = True async def find_entities(data_class_name: str) -> list: """Find all entities.""" all_module_infos = {} data = data_handler.data if data_class_name not in data: return [] if data[data_class_name] is None: return [] data_class = data[data_class_name] for station_id in data_class.stations: for module_id in data_class.get_modules(station_id): all_module_infos[module_id] = data_class.get_module(module_id) all_module_infos[station_id] = data_class.get_station(station_id) entities = [] for module in all_module_infos.values(): if "_id" not in module: _LOGGER.debug("Skipping module %s", module.get("module_name")) continue conditions = [ c.lower() for c in data_class.get_monitored_conditions( module_id=module["_id"]) if c.lower() in SENSOR_TYPES_KEYS ] for condition in conditions: if f"{condition}_value" in SENSOR_TYPES_KEYS: conditions.append(f"{condition}_value") elif f"{condition}_lvl" in SENSOR_TYPES_KEYS: conditions.append(f"{condition}_lvl") entities.extend([ NetatmoSensor(data_handler, data_class_name, module, description) for description in SENSOR_TYPES if description.key in conditions ]) _LOGGER.debug("Adding weather sensors %s", entities) return entities for data_class_name in ( WEATHERSTATION_DATA_CLASS_NAME, HOMECOACH_DATA_CLASS_NAME, ): await data_handler.register_data_class(data_class_name, data_class_name, None) data_class = data_handler.data.get(data_class_name) if data_class and data_class.raw_data: platform_not_ready = False async_add_entities(await find_entities(data_class_name), True) device_registry = await hass.helpers.device_registry.async_get_registry() async def add_public_entities(update: bool = True) -> None: """Retrieve Netatmo public weather entities.""" entities = { device.name: device.id for device in async_entries_for_config_entry( device_registry, entry.entry_id) if device.model == "Public Weather stations" } new_entities = [] for area in [ NetatmoArea(**i) for i in entry.options.get(CONF_WEATHER_AREAS, {}).values() ]: signal_name = f"{PUBLICDATA_DATA_CLASS_NAME}-{area.uuid}" if area.area_name in entities: entities.pop(area.area_name) if update: async_dispatcher_send( hass, f"netatmo-config-{area.area_name}", area, ) continue await data_handler.register_data_class( PUBLICDATA_DATA_CLASS_NAME, signal_name, None, lat_ne=area.lat_ne, lon_ne=area.lon_ne, lat_sw=area.lat_sw, lon_sw=area.lon_sw, ) data_class = data_handler.data.get(signal_name) if data_class and data_class.raw_data: nonlocal platform_not_ready platform_not_ready = False new_entities.extend([ NetatmoPublicSensor(data_handler, area, description) for description in SENSOR_TYPES if description.key in SUPPORTED_PUBLIC_SENSOR_TYPES ]) for device_id in entities.values(): device_registry.async_remove_device(device_id) if new_entities: async_add_entities(new_entities) async_dispatcher_connect( hass, f"signal-{DOMAIN}-public-update-{entry.entry_id}", add_public_entities) @callback def _create_entity(netatmo_device: NetatmoDevice) -> None: entity = NetatmoClimateBatterySensor(netatmo_device) _LOGGER.debug("Adding climate battery sensor %s", entity) async_add_entities([entity]) entry.async_on_unload( async_dispatcher_connect(hass, NETATMO_CREATE_BATTERY, _create_entity)) await add_public_entities(False) if platform_not_ready: raise PlatformNotReady
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the Goodwe components from a config entry.""" hass.data.setdefault(DOMAIN, {}) name = entry.title host = entry.data[CONF_HOST] model_family = entry.data[CONF_MODEL_FAMILY] # Connect to Goodwe inverter try: inverter = await connect( host=host, family=model_family, retries=10, ) except InverterError as err: raise ConfigEntryNotReady from err device_info = DeviceInfo( configuration_url="https://www.semsportal.com", identifiers={(DOMAIN, inverter.serial_number)}, name=entry.title, manufacturer="GoodWe", model=inverter.model_name, sw_version=f"{inverter.software_version} ({inverter.arm_version})", ) async def async_update_data(): """Fetch data from the inverter.""" try: return await inverter.read_runtime_data() except RequestFailedException as ex: # UDP communication with inverter is by definition unreliable. # It is rather normal in many environments to fail to receive # proper response in usual time, so we intentionally ignore isolated # failures and report problem with availability only after # consecutive streak of 3 of failed requests. if ex.consecutive_failures_count < 3: _LOGGER.debug("No response received (streak of %d)", ex.consecutive_failures_count) # return empty dictionary, sensors will keep their previous values return {} # Inverter does not respond anymore (e.g. it went to sleep mode) _LOGGER.debug("Inverter not responding (streak of %d)", ex.consecutive_failures_count) raise UpdateFailed(ex) from ex except InverterError as ex: raise UpdateFailed(ex) from ex # Create update coordinator coordinator = DataUpdateCoordinator( hass, _LOGGER, name=name, update_method=async_update_data, # Polling interval. Will only be polled if there are subscribers. update_interval=SCAN_INTERVAL, ) # Fetch initial data so we have data when entities subscribe await coordinator.async_config_entry_first_refresh() hass.data[DOMAIN][entry.entry_id] = { KEY_INVERTER: inverter, KEY_COORDINATOR: coordinator, KEY_DEVICE_INFO: device_info, } entry.async_on_unload(entry.add_update_listener(update_listener)) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry( hass: HomeAssistant, entry: config_entries.ConfigEntry ) -> bool: """Set up the ISY 994 integration.""" # As there currently is no way to import options from yaml # when setting up a config entry, we fallback to adding # the options to the config entry and pull them out here if # they are missing from the options _async_import_options_from_data_if_missing(hass, entry) hass.data[DOMAIN][entry.entry_id] = {} hass_isy_data = hass.data[DOMAIN][entry.entry_id] hass_isy_data[ISY994_NODES] = {} for platform in PLATFORMS: hass_isy_data[ISY994_NODES][platform] = [] hass_isy_data[ISY994_PROGRAMS] = {} for platform in SUPPORTED_PROGRAM_PLATFORMS: hass_isy_data[ISY994_PROGRAMS][platform] = [] hass_isy_data[ISY994_VARIABLES] = [] isy_config = entry.data isy_options = entry.options # Required user = isy_config[CONF_USERNAME] password = isy_config[CONF_PASSWORD] host = urlparse(isy_config[CONF_HOST]) # Optional tls_version = isy_config.get(CONF_TLS_VER) ignore_identifier = isy_options.get(CONF_IGNORE_STRING, DEFAULT_IGNORE_STRING) sensor_identifier = isy_options.get(CONF_SENSOR_STRING, DEFAULT_SENSOR_STRING) variable_identifier = isy_options.get( CONF_VAR_SENSOR_STRING, DEFAULT_VAR_SENSOR_STRING ) if host.scheme == "http": https = False port = host.port or 80 elif host.scheme == "https": https = True port = host.port or 443 else: _LOGGER.error("isy994 host value in configuration is invalid") return False # Connect to ISY controller. isy = await hass.async_add_executor_job( partial( ISY, host.hostname, port, username=user, password=password, use_https=https, tls_ver=tls_version, webroot=host.path, ) ) if not isy.connected: return False # Trigger a status update for all nodes, not done automatically in PyISY v2.x await hass.async_add_executor_job(isy.nodes.update) _categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier, sensor_identifier) _categorize_programs(hass_isy_data, isy.programs) _categorize_variables(hass_isy_data, isy.variables, variable_identifier) # Dump ISY Clock Information. Future: Add ISY as sensor to Hass with attrs _LOGGER.info(repr(isy.clock)) hass_isy_data[ISY994_ISY] = isy await _async_get_or_create_isy_device_in_registry(hass, entry, isy) # Load platforms for the devices in the ISY controller that we support. for platform in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform) ) def _start_auto_update() -> None: """Start isy auto update.""" _LOGGER.debug("ISY Starting Event Stream and automatic updates") isy.auto_update = True await hass.async_add_executor_job(_start_auto_update) undo_listener = entry.add_update_listener(_async_update_listener) hass_isy_data[UNDO_UPDATE_LISTENER] = undo_listener # Register Integration-wide Services: async_setup_services(hass) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up SimpliSafe as config entry.""" hass.data.setdefault(DOMAIN, {DATA_CLIENT: {}}) hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = [] _async_standardize_config_entry(hass, config_entry) _verify_domain_control = verify_domain_control(hass, DOMAIN) client_id = await async_get_client_id(hass) websession = aiohttp_client.async_get_clientsession(hass) try: api = await get_api( config_entry.data[CONF_USERNAME], config_entry.data[CONF_PASSWORD], client_id=client_id, session=websession, ) except InvalidCredentialsError as err: raise ConfigEntryAuthFailed from err except SimplipyError as err: LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady from err simplisafe = SimpliSafe(hass, config_entry, api) try: await simplisafe.async_init() except SimplipyError as err: raise ConfigEntryNotReady from err hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) @callback def verify_system_exists( coro: Callable[..., Awaitable] ) -> Callable[..., Awaitable]: """Log an error if a service call uses an invalid system ID.""" async def decorator(call: ServiceCall) -> None: """Decorate.""" system_id = int(call.data[ATTR_SYSTEM_ID]) if system_id not in simplisafe.systems: LOGGER.error("Unknown system ID in service call: %s", system_id) return await coro(call) return decorator @callback def v3_only(coro: Callable[..., Awaitable]) -> Callable[..., Awaitable]: """Log an error if the decorated coroutine is called with a v2 system.""" async def decorator(call: ServiceCall) -> None: """Decorate.""" system = simplisafe.systems[int(call.data[ATTR_SYSTEM_ID])] if system.version != 3: LOGGER.error("Service only available on V3 systems") return await coro(call) return decorator @verify_system_exists @_verify_domain_control async def clear_notifications(call: ServiceCall) -> None: """Clear all active notifications.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.clear_notifications() except SimplipyError as err: LOGGER.error("Error during service call: %s", err) @verify_system_exists @_verify_domain_control async def remove_pin(call: ServiceCall) -> None: """Remove a PIN.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE]) except SimplipyError as err: LOGGER.error("Error during service call: %s", err) @verify_system_exists @_verify_domain_control async def set_pin(call: ServiceCall) -> None: """Set a PIN.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.set_pin(call.data[ATTR_PIN_LABEL], call.data[ATTR_PIN_VALUE]) except SimplipyError as err: LOGGER.error("Error during service call: %s", err) @verify_system_exists @v3_only @_verify_domain_control async def set_system_properties(call: ServiceCall) -> None: """Set one or more system parameters.""" system = cast(SystemV3, simplisafe.systems[call.data[ATTR_SYSTEM_ID]]) try: await system.set_properties( { prop: value for prop, value in call.data.items() if prop != ATTR_SYSTEM_ID } ) except SimplipyError as err: LOGGER.error("Error during service call: %s", err) for service, method, schema in ( ("clear_notifications", clear_notifications, None), ("remove_pin", remove_pin, SERVICE_REMOVE_PIN_SCHEMA), ("set_pin", set_pin, SERVICE_SET_PIN_SCHEMA), ( "set_system_properties", set_system_properties, SERVICE_SET_SYSTEM_PROPERTIES_SCHEMA, ), ): async_register_admin_service(hass, DOMAIN, service, method, schema=schema) config_entry.async_on_unload(config_entry.add_update_listener(async_reload_entry)) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry): """Set up BLE Monitor from a config entry.""" _LOGGER.debug("Initializing BLE Monitor entry (config entry): %s", config_entry) # Prevent unload to be triggered each time we update the config entry global UPDATE_UNLISTENER if UPDATE_UNLISTENER: UPDATE_UNLISTENER() if not config_entry.unique_id: hass.config_entries.async_update_entry(config_entry, unique_id=config_entry.title) _LOGGER.debug("async_setup_entry: domain %s", CONFIG_YAML) config = {} if not CONFIG_YAML: for key, value in config_entry.data.items(): config[key] = value for key, value in config_entry.options.items(): config[key] = value config[CONFIG_IS_FLOW] = True if CONF_DEVICES not in config: config[CONF_DEVICES] = [] else: # device configuration is taken from yaml, but yaml config already removed # save unique IDs (only once) if "ids_from_name" in config: devlist = config[CONF_DEVICES] for dev_idx, dev_conf in enumerate(devlist): if CONF_NAME in dev_conf: devlist[dev_idx][CONF_UNIQUE_ID] = dev_conf[CONF_NAME] del config["ids_from_name"] else: for key, value in CONFIG_YAML.items(): config[key] = value if CONF_HCI_INTERFACE in CONFIG_YAML: hci_list = [] if isinstance(CONFIG_YAML[CONF_HCI_INTERFACE], list): for hci in CONFIG_YAML[CONF_HCI_INTERFACE]: hci_list.append(str(hci)) else: hci_list.append(str(CONFIG_YAML[CONF_HCI_INTERFACE])) config[CONF_HCI_INTERFACE] = hci_list hass.config_entries.async_update_entry(config_entry, data={}, options=config) _LOGGER.debug("async_setup_entry: %s", config) UPDATE_UNLISTENER = config_entry.add_update_listener( _async_update_listener) if CONF_HCI_INTERFACE not in config: config[CONF_HCI_INTERFACE] = [DEFAULT_HCI_INTERFACE] else: hci_list = config_entry.options.get(CONF_HCI_INTERFACE) for i, hci in enumerate(hci_list): hci_list[i] = int(hci) config[CONF_HCI_INTERFACE] = hci_list _LOGGER.debug("HCI interface is %s", config[CONF_HCI_INTERFACE]) blemonitor = BLEmonitor(config) hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, blemonitor.shutdown_handler) blemonitor.start() hass.data[DOMAIN] = {} hass.data[DOMAIN]["blemonitor"] = blemonitor hass.data[DOMAIN]["config_entry_id"] = config_entry.entry_id for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, component)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Initialize config entry which represents the HEOS controller.""" # For backwards compat if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=DOMAIN) host = entry.data[CONF_HOST] # Setting all_progress_events=False ensures that we only receive a # media position update upon start of playback or when media changes controller = Heos(host, all_progress_events=False) try: await controller.connect(auto_reconnect=True) # Auto reconnect only operates if initial connection was successful. except HeosError as error: await controller.disconnect() _LOGGER.debug("Unable to connect to controller %s: %s", host, error) raise ConfigEntryNotReady from error # Disconnect when shutting down async def disconnect_controller(event): await controller.disconnect() entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, disconnect_controller) ) # Get players and sources try: players = await controller.get_players() favorites = {} if controller.is_signed_in: favorites = await controller.get_favorites() else: _LOGGER.warning( "%s is not logged in to a HEOS account and will be unable to retrieve " "HEOS favorites: Use the 'heos.sign_in' service to sign-in to a HEOS account", host, ) inputs = await controller.get_input_sources() except HeosError as error: await controller.disconnect() _LOGGER.debug("Unable to retrieve players and sources: %s", error) raise ConfigEntryNotReady from error controller_manager = ControllerManager(hass, controller) await controller_manager.connect_listeners() source_manager = SourceManager(favorites, inputs) source_manager.connect_update(hass, controller) hass.data[DOMAIN] = { DATA_CONTROLLER_MANAGER: controller_manager, DATA_SOURCE_MANAGER: source_manager, MEDIA_PLAYER_DOMAIN: players, } services.register(hass, controller) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry): """Set up BLE Monitor from a config entry.""" _LOGGER.debug("Initializing BLE Monitor entry (config entry): %s", config_entry) # Prevent unload to be triggered each time we update the config entry global UPDATE_UNLISTENER if UPDATE_UNLISTENER: UPDATE_UNLISTENER() if not config_entry.unique_id: hass.config_entries.async_update_entry(config_entry, unique_id=config_entry.title) _LOGGER.debug("async_setup_entry: domain %s", CONFIG_YAML) config = {} hci_list = [] bt_mac_list = [] if not CONFIG_YAML: # Configuration in UI for key, value in config_entry.data.items(): config[key] = value for key, value in config_entry.options.items(): config[key] = value config[CONFIG_IS_FLOW] = True if CONF_DEVICES not in config: config[CONF_DEVICES] = [] else: # device configuration is taken from yaml, but yaml config already removed # save unique IDs (only once) if "ids_from_name" in config: devlist = config[CONF_DEVICES] for dev_idx, dev_conf in enumerate(devlist): if CONF_NAME in dev_conf: devlist[dev_idx][CONF_UNIQUE_ID] = dev_conf[CONF_NAME] del config["ids_from_name"] if not config[CONF_BT_INTERFACE]: default_hci = list(BT_INTERFACES.keys())[list( BT_INTERFACES.values()).index(DEFAULT_BT_INTERFACE)] hci_list.append(int(default_hci)) bt_mac_list.append(str(DEFAULT_BT_INTERFACE)) else: bt_interface_list = config[CONF_BT_INTERFACE] for bt_mac in bt_interface_list: hci = list(BT_INTERFACES.keys())[list( BT_INTERFACES.values()).index(bt_mac)] hci_list.append(int(hci)) bt_mac_list.append(str(bt_mac)) else: # Configuration in YAML for key, value in CONFIG_YAML.items(): config[key] = value _LOGGER.warning("Available Bluetooth interfaces for BLE monitor: %s", BT_MAC_INTERFACES) if config[CONF_HCI_INTERFACE]: # Configuration of BT interface with hci number for hci in CONFIG_YAML[CONF_HCI_INTERFACE]: try: hci_list.append(int(hci)) bt_mac = BT_INTERFACES.get(hci) if bt_mac: bt_mac_list.append(str(bt_mac)) else: _LOGGER.error( "Bluetooth interface hci%i is not available", hci) except ValueError: _LOGGER.error("Bluetooth interface hci%i is not available", hci) else: # Configuration of BT interface with mac address CONF_BT_INTERFACES = [ x.upper() for x in CONFIG_YAML[CONF_BT_INTERFACE] ] for bt_mac in CONF_BT_INTERFACES: try: hci = list(BT_INTERFACES.keys())[list( BT_INTERFACES.values()).index(bt_mac)] hci_list.append(int(hci)) bt_mac_list.append(str(bt_mac)) except ValueError: _LOGGER.error( "Bluetooth interface with MAC address %s is not available", bt_mac) if not hci_list: # Fall back in case no hci interfaces are added default_hci = list(BT_INTERFACES.keys())[list( BT_INTERFACES.values()).index(DEFAULT_BT_INTERFACE)] hci_list.append(int(default_hci)) bt_mac_list.append(str(DEFAULT_BT_INTERFACE)) _LOGGER.warning( "No configured Bluetooth interfaces was found, using default interface instead" ) config[CONF_HCI_INTERFACE] = hci_list config[CONF_BT_INTERFACE] = bt_mac_list hass.config_entries.async_update_entry(config_entry, data={}, options=config) _LOGGER.debug("async_setup_entry: %s", config) UPDATE_UNLISTENER = config_entry.add_update_listener( _async_update_listener) _LOGGER.debug("HCI interface is %s", config[CONF_HCI_INTERFACE]) blemonitor = BLEmonitor(config) hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, blemonitor.shutdown_handler) blemonitor.start() hass.data[DOMAIN] = {} hass.data[DOMAIN]["blemonitor"] = blemonitor hass.data[DOMAIN]["config_entry_id"] = config_entry.entry_id for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, component)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Sense from a config entry.""" entry_data = entry.data email = entry_data[CONF_EMAIL] timeout = entry_data[CONF_TIMEOUT] access_token = entry_data.get("access_token", "") user_id = entry_data.get("user_id", "") monitor_id = entry_data.get("monitor_id", "") client_session = async_get_clientsession(hass) gateway = ASyncSenseable(api_timeout=timeout, wss_timeout=timeout, client_session=client_session) gateway.rate_limit = ACTIVE_UPDATE_RATE try: gateway.load_auth(access_token, user_id, monitor_id) await gateway.get_monitor_data() except (SenseAuthenticationException, SenseMFARequiredException) as err: _LOGGER.warning("Sense authentication expired") raise ConfigEntryAuthFailed(err) from err except SENSE_TIMEOUT_EXCEPTIONS as err: raise ConfigEntryNotReady( str(err) or "Timed out during authentication") from err sense_devices_data = SenseDevicesData() try: sense_discovered_devices = await gateway.get_discovered_device_data() await gateway.update_realtime() except SENSE_TIMEOUT_EXCEPTIONS as err: raise ConfigEntryNotReady( str(err) or "Timed out during realtime update") from err except SENSE_EXCEPTIONS as err: raise ConfigEntryNotReady(str(err) or "Error during realtime update") from err async def _async_update_trend(): """Update the trend data.""" try: await gateway.update_trend_data() except (SenseAuthenticationException, SenseMFARequiredException) as err: _LOGGER.warning("Sense authentication expired") raise ConfigEntryAuthFailed(err) from err trends_coordinator: DataUpdateCoordinator[None] = DataUpdateCoordinator( hass, _LOGGER, name=f"Sense Trends {email}", update_method=_async_update_trend, update_interval=timedelta(seconds=300), ) # Start out as unavailable so we do not report 0 data # until the update happens trends_coordinator.last_update_success = False # This can take longer than 60s and we already know # sense is online since get_discovered_device_data was # successful so we do it later. asyncio.create_task(trends_coordinator.async_request_refresh()) hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { SENSE_DATA: gateway, SENSE_DEVICES_DATA: sense_devices_data, SENSE_TRENDS_COORDINATOR: trends_coordinator, SENSE_DISCOVERED_DEVICES_DATA: sense_discovered_devices, } hass.config_entries.async_setup_platforms(entry, PLATFORMS) async def async_sense_update(_): """Retrieve latest state.""" try: await gateway.update_realtime() except SENSE_TIMEOUT_EXCEPTIONS as ex: _LOGGER.error("Timeout retrieving data: %s", ex) except SENSE_EXCEPTIONS as ex: _LOGGER.error("Failed to update data: %s", ex) data = gateway.get_realtime() if "devices" in data: sense_devices_data.set_devices_data(data["devices"]) async_dispatcher_send( hass, f"{SENSE_DEVICE_UPDATE}-{gateway.sense_monitor_id}") remove_update_callback = async_track_time_interval( hass, async_sense_update, timedelta(seconds=ACTIVE_UPDATE_RATE)) @callback def _remove_update_callback_at_stop(event): remove_update_callback() entry.async_on_unload(remove_update_callback) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _remove_update_callback_at_stop)) return True
async def async_setup_entry( # noqa: C901 hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Synology DSM sensors.""" # Migrate old unique_id @callback def _async_migrator( entity_entry: entity_registry.RegistryEntry, ) -> dict[str, str] | None: """Migrate away from ID using label.""" # Reject if new unique_id if "SYNO." in entity_entry.unique_id: return None entries = { **STORAGE_DISK_BINARY_SENSORS, **STORAGE_DISK_SENSORS, **STORAGE_VOL_SENSORS, **UTILISATION_SENSORS, } infos = entity_entry.unique_id.split("_") serial = infos.pop(0) label = infos.pop(0) device_id = "_".join(infos) # Removed entity if ("Type" in entity_entry.unique_id or "Device" in entity_entry.unique_id or "Name" in entity_entry.unique_id): return None entity_type: str | None = None for entity_key, entity_attrs in entries.items(): if (device_id and entity_attrs[ATTR_NAME] == "Status" and "Status" in entity_entry.unique_id and "(Smart)" not in entity_entry.unique_id): if "sd" in device_id and "disk" in entity_key: entity_type = entity_key continue if "volume" in device_id and "volume" in entity_key: entity_type = entity_key continue if entity_attrs[ATTR_NAME] == label: entity_type = entity_key if entity_type is None: return None new_unique_id = "_".join([serial, entity_type]) if device_id: new_unique_id += f"_{device_id}" _LOGGER.info( "Migrating unique_id from [%s] to [%s]", entity_entry.unique_id, new_unique_id, ) return {"new_unique_id": new_unique_id} await entity_registry.async_migrate_entries(hass, entry.entry_id, _async_migrator) # migrate device indetifiers dev_reg = await get_dev_reg(hass) devices: list[ DeviceEntry] = device_registry.async_entries_for_config_entry( dev_reg, entry.entry_id) for device in devices: old_identifier = list(next(iter(device.identifiers))) if len(old_identifier) > 2: new_identifier = {(old_identifier.pop(0), "_".join([str(x) for x in old_identifier]))} _LOGGER.debug("migrate identifier '%s' to '%s'", device.identifiers, new_identifier) dev_reg.async_update_device(device.id, new_identifiers=new_identifier) # Migrate existing entry configuration if entry.data.get(CONF_VERIFY_SSL) is None: hass.config_entries.async_update_entry( entry, data={ **entry.data, CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL }) # Continue setup api = SynoApi(hass, entry) try: await api.async_setup() except ( SynologyDSMLogin2SARequiredException, SynologyDSMLoginDisabledAccountException, SynologyDSMLoginInvalidException, SynologyDSMLoginPermissionDeniedException, ) as err: if err.args[0] and isinstance(err.args[0], dict): # pylint: disable=no-member details = err.args[0].get(EXCEPTION_DETAILS, EXCEPTION_UNKNOWN) else: details = EXCEPTION_UNKNOWN _LOGGER.debug( "Reauthentication for DSM '%s' needed - reason: %s", entry.unique_id, details, ) hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={ "source": SOURCE_REAUTH, "data": { **entry.data }, EXCEPTION_DETAILS: details, }, )) return False except (SynologyDSMLoginFailedException, SynologyDSMRequestException) as err: _LOGGER.debug("Unable to connect to DSM '%s' during setup: %s", entry.unique_id, err) raise ConfigEntryNotReady from err hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.unique_id] = { UNDO_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener), SYNO_API: api, SYSTEM_LOADED: True, } # Services await _async_setup_services(hass) # For SSDP compat if not entry.data.get(CONF_MAC): network = await hass.async_add_executor_job(getattr, api.dsm, "network") hass.config_entries.async_update_entry(entry, data={ **entry.data, CONF_MAC: network.macs }) async def async_coordinator_update_data_cameras( ) -> dict[str, dict[str, SynoCamera]] | None: """Fetch all camera data from api.""" if not hass.data[DOMAIN][entry.unique_id][SYSTEM_LOADED]: raise UpdateFailed("System not fully loaded") if SynoSurveillanceStation.CAMERA_API_KEY not in api.dsm.apis: return None surveillance_station = api.surveillance_station try: async with async_timeout.timeout(10): await hass.async_add_executor_job(surveillance_station.update) except SynologyDSMAPIErrorException as err: raise UpdateFailed(f"Error communicating with API: {err}") from err return { "cameras": { camera.id: camera for camera in surveillance_station.get_all_cameras() } } async def async_coordinator_update_data_central() -> None: """Fetch all device and sensor data from api.""" try: await api.async_update() except Exception as err: raise UpdateFailed(f"Error communicating with API: {err}") from err return None async def async_coordinator_update_data_switches( ) -> dict[str, dict[str, Any]] | None: """Fetch all switch data from api.""" if not hass.data[DOMAIN][entry.unique_id][SYSTEM_LOADED]: raise UpdateFailed("System not fully loaded") if SynoSurveillanceStation.HOME_MODE_API_KEY not in api.dsm.apis: return None surveillance_station = api.surveillance_station return { "switches": { "home_mode": await hass.async_add_executor_job( surveillance_station.get_home_mode_status) } } hass.data[DOMAIN][ entry.unique_id][COORDINATOR_CAMERAS] = DataUpdateCoordinator( hass, _LOGGER, name=f"{entry.unique_id}_cameras", update_method=async_coordinator_update_data_cameras, update_interval=timedelta(seconds=30), ) hass.data[DOMAIN][ entry.unique_id][COORDINATOR_CENTRAL] = DataUpdateCoordinator( hass, _LOGGER, name=f"{entry.unique_id}_central", update_method=async_coordinator_update_data_central, update_interval=timedelta(minutes=entry.options.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)), ) hass.data[DOMAIN][ entry.unique_id][COORDINATOR_SWITCHES] = DataUpdateCoordinator( hass, _LOGGER, name=f"{entry.unique_id}_switches", update_method=async_coordinator_update_data_switches, update_interval=timedelta(seconds=30), ) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up the deCONZ lights and groups from a config entry.""" gateway = get_gateway_from_config_entry(hass, config_entry) gateway.entities[DOMAIN] = set() @callback def async_add_light( lights: list[Light] | ValuesView[Light] = gateway.api.lights.values(), ) -> None: """Add light from deCONZ.""" entities = [] for light in lights: if ( isinstance(light, Light) and light.type not in POWER_PLUGS and light.unique_id not in gateway.entities[DOMAIN] ): entities.append(DeconzLight(light, gateway)) if entities: async_add_entities(entities) config_entry.async_on_unload( async_dispatcher_connect( hass, gateway.signal_new_light, async_add_light, ) ) @callback def async_add_group( groups: list[Group] | ValuesView[Group] = gateway.api.groups.values(), ) -> None: """Add group from deCONZ.""" if not gateway.option_allow_deconz_groups: return entities = [] for group in groups: if not group.lights: continue known_groups = set(gateway.entities[DOMAIN]) new_group = DeconzGroup(group, gateway) if new_group.unique_id not in known_groups: entities.append(new_group) if entities: async_add_entities(entities) config_entry.async_on_unload( async_dispatcher_connect( hass, gateway.signal_new_group, async_add_group, ) ) async_add_light() async_add_group()
async def async_setup_entry(hass: HomeAssistantType, config_entry: ConfigEntry) -> bool: """Set up Huawei LTE component from config entry.""" url = config_entry.data[CONF_URL] # Override settings from YAML config, but only if they're changed in it # Old values are stored as *_from_yaml in the config entry yaml_config = hass.data[DOMAIN].config.get(url) if yaml_config: # Config values new_data = {} for key in CONF_USERNAME, CONF_PASSWORD: if key in yaml_config: value = yaml_config[key] if value != config_entry.data.get(f"{key}_from_yaml"): new_data[f"{key}_from_yaml"] = value new_data[key] = value # Options new_options = {} yaml_recipient = yaml_config.get(NOTIFY_DOMAIN, {}).get(CONF_RECIPIENT) if yaml_recipient is not None and yaml_recipient != config_entry.options.get( f"{CONF_RECIPIENT}_from_yaml" ): new_options[f"{CONF_RECIPIENT}_from_yaml"] = yaml_recipient new_options[CONF_RECIPIENT] = yaml_recipient yaml_notify_name = yaml_config.get(NOTIFY_DOMAIN, {}).get(CONF_NAME) if ( yaml_notify_name is not None and yaml_notify_name != config_entry.options.get(f"{CONF_NAME}_from_yaml") ): new_options[f"{CONF_NAME}_from_yaml"] = yaml_notify_name new_options[CONF_NAME] = yaml_notify_name # Update entry if overrides were found if new_data or new_options: hass.config_entries.async_update_entry( config_entry, data={**config_entry.data, **new_data}, options={**config_entry.options, **new_options}, ) # Get MAC address for use in unique ids. Being able to use something # from the API would be nice, but all of that seems to be available only # through authenticated calls (e.g. device_information.SerialNumber), and # we want this available and the same when unauthenticated too. host = urlparse(url).hostname try: if ipaddress.ip_address(host).version == 6: mode = "ip6" else: mode = "ip" except ValueError: mode = "hostname" mac = await hass.async_add_executor_job(partial(get_mac_address, **{mode: host})) def get_connection() -> Connection: """ Set up a connection. Authorized one if username/pass specified (even if empty), unauthorized one otherwise. """ username = config_entry.data.get(CONF_USERNAME) password = config_entry.data.get(CONF_PASSWORD) if username or password: connection: Connection = AuthorizedConnection( url, username=username, password=password, timeout=CONNECTION_TIMEOUT ) else: connection = Connection(url, timeout=CONNECTION_TIMEOUT) return connection def signal_update() -> None: """Signal updates to data.""" dispatcher_send(hass, UPDATE_SIGNAL, url) try: connection = await hass.async_add_executor_job(get_connection) except Timeout as ex: raise ConfigEntryNotReady from ex # Set up router and store reference to it router = Router(connection, url, mac, signal_update) hass.data[DOMAIN].routers[url] = router # Do initial data update await hass.async_add_executor_job(router.update) # Clear all subscriptions, enabled entities will push back theirs router.subscriptions.clear() # Set up device registry device_data = {} sw_version = None if router.data.get(KEY_DEVICE_INFORMATION): device_info = router.data[KEY_DEVICE_INFORMATION] sw_version = device_info.get("SoftwareVersion") if device_info.get("DeviceName"): device_data["model"] = device_info["DeviceName"] if not sw_version and router.data.get(KEY_DEVICE_BASIC_INFORMATION): sw_version = router.data[KEY_DEVICE_BASIC_INFORMATION].get("SoftwareVersion") if sw_version: device_data["sw_version"] = sw_version device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, connections=router.device_connections, identifiers=router.device_identifiers, name=router.device_name, manufacturer="Huawei", **device_data, ) # Forward config entry setup to platforms for domain in CONFIG_ENTRY_PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, domain) ) # Notify doesn't support config entry setup yet, load with discovery for now await discovery.async_load_platform( hass, NOTIFY_DOMAIN, DOMAIN, { CONF_URL: url, CONF_NAME: config_entry.options.get(CONF_NAME, DEFAULT_NOTIFY_SERVICE_NAME), CONF_RECIPIENT: config_entry.options.get(CONF_RECIPIENT), }, hass.data[DOMAIN].hass_config, ) # Add config entry options update listener router.unload_handlers.append( config_entry.add_update_listener(async_signal_options_update) ) def _update_router(*_: Any) -> None: """ Update router data. Separate passthrough function because lambdas don't work with track_time_interval. """ router.update() # Set up periodic update router.unload_handlers.append( async_track_time_interval(hass, _update_router, SCAN_INTERVAL) ) # Clean up at end hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, router.cleanup) return True
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Z-Wave sensor from config entry.""" client: ZwaveClient = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT] @callback def async_add_sensor(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Sensor.""" entities: list[ZWaveBaseEntity] = [] entity_description = ENTITY_DESCRIPTION_KEY_MAP.get( info.platform_data ) or ZwaveSensorEntityDescription("base_sensor") entity_description.info = info if info.platform_hint == "string_sensor": entities.append(ZWaveStringSensor(config_entry, client, entity_description)) elif info.platform_hint == "numeric_sensor": entities.append( ZWaveNumericSensor(config_entry, client, entity_description) ) elif info.platform_hint == "list_sensor": entities.append(ZWaveListSensor(config_entry, client, entity_description)) elif info.platform_hint == "config_parameter": entities.append( ZWaveConfigParameterSensor(config_entry, client, entity_description) ) elif info.platform_hint == "meter": entities.append(ZWaveMeterSensor(config_entry, client, entity_description)) else: LOGGER.warning( "Sensor not implemented for %s/%s", info.platform_hint, info.primary_value.propertyname, ) return async_add_entities(entities) @callback def async_add_node_status_sensor(node: ZwaveNode) -> None: """Add node status sensor.""" async_add_entities([ZWaveNodeStatusSensor(config_entry, client, node)]) config_entry.async_on_unload( async_dispatcher_connect( hass, f"{DOMAIN}_{config_entry.entry_id}_add_{SENSOR_DOMAIN}", async_add_sensor, ) ) config_entry.async_on_unload( async_dispatcher_connect( hass, f"{DOMAIN}_{config_entry.entry_id}_add_node_status_sensor", async_add_node_status_sensor, ) ) platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( SERVICE_RESET_METER, { vol.Optional(ATTR_METER_TYPE): vol.Coerce(int), vol.Optional(ATTR_VALUE): vol.Coerce(int), }, "async_reset_meter", )
async def async_setup_entry(hass: core.HomeAssistant, entry: config_entries.ConfigEntry): """Set up the motion_blinds components from a config entry.""" hass.data.setdefault(DOMAIN, {}) host = entry.data[CONF_HOST] key = entry.data[CONF_API_KEY] multicast_interface = entry.data.get(CONF_INTERFACE, DEFAULT_INTERFACE) wait_for_push = entry.options.get(CONF_WAIT_FOR_PUSH, DEFAULT_WAIT_FOR_PUSH) entry.async_on_unload(entry.add_update_listener(update_listener)) # Create multicast Listener if KEY_MULTICAST_LISTENER not in hass.data[DOMAIN]: multicast = AsyncMotionMulticast(interface=multicast_interface) hass.data[DOMAIN][KEY_MULTICAST_LISTENER] = multicast # start listening for local pushes (only once) await multicast.Start_listen() # register stop callback to shutdown listening for local pushes def stop_motion_multicast(event): """Stop multicast thread.""" _LOGGER.debug("Shutting down Motion Listener") multicast.Stop_listen() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_motion_multicast) # Connect to motion gateway multicast = hass.data[DOMAIN][KEY_MULTICAST_LISTENER] connect_gateway_class = ConnectMotionGateway(hass, multicast) if not await connect_gateway_class.async_connect_gateway(host, key): raise ConfigEntryNotReady motion_gateway = connect_gateway_class.gateway_device coordinator_info = { KEY_GATEWAY: motion_gateway, CONF_WAIT_FOR_PUSH: wait_for_push, } coordinator = DataUpdateCoordinatorMotionBlinds( hass, _LOGGER, coordinator_info, # Name of the data. For logging purposes. name=entry.title, # Polling interval. Will only be polled if there are subscribers. update_interval=timedelta(seconds=UPDATE_INTERVAL), ) # Fetch initial data so we have data when entities subscribe await coordinator.async_config_entry_first_refresh() hass.data[DOMAIN][entry.entry_id] = { KEY_GATEWAY: motion_gateway, KEY_COORDINATOR: coordinator, } if motion_gateway.firmware is not None: version = f"{motion_gateway.firmware}, protocol: {motion_gateway.protocol}" else: version = f"Protocol: {motion_gateway.protocol}" device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, connections={(dr.CONNECTION_NETWORK_MAC, motion_gateway.mac)}, identifiers={(DOMAIN, entry.unique_id)}, manufacturer=MANUFACTURER, name=entry.title, model="Wi-Fi bridge", sw_version=version, ) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, ) -> bool: """Create a gateway.""" tradfri_data: dict[str, Any] = {} hass.data.setdefault(DOMAIN, {})[entry.entry_id] = tradfri_data factory = await APIFactory.init( entry.data[CONF_HOST], psk_id=entry.data[CONF_IDENTITY], psk=entry.data[CONF_KEY], ) tradfri_data[FACTORY] = factory # Used for async_unload_entry async def on_hass_stop(event: Event) -> None: """Close connection when hass stops.""" await factory.shutdown() # Setup listeners entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)) api = factory.request gateway = Gateway() try: gateway_info = await api(gateway.get_gateway_info(), timeout=TIMEOUT_API) devices_commands: Command = await api(gateway.get_devices(), timeout=TIMEOUT_API) devices: list[Device] = await api(devices_commands, timeout=TIMEOUT_API) except RequestError as exc: await factory.shutdown() raise ConfigEntryNotReady from exc dev_reg = dr.async_get(hass) dev_reg.async_get_or_create( config_entry_id=entry.entry_id, connections=set(), identifiers={(DOMAIN, entry.data[CONF_GATEWAY_ID])}, manufacturer="IKEA of Sweden", name="Gateway", # They just have 1 gateway model. Type is not exposed yet. model="E1526", sw_version=gateway_info.firmware_version, ) remove_stale_devices(hass, entry, devices) # Setup the device coordinators coordinator_data = { CONF_GATEWAY_ID: gateway, KEY_API: api, COORDINATOR_LIST: [], } for device in devices: coordinator = TradfriDeviceDataUpdateCoordinator(hass=hass, api=api, device=device) await coordinator.async_config_entry_first_refresh() entry.async_on_unload( async_dispatcher_connect(hass, SIGNAL_GW, coordinator.set_hub_available)) coordinator_data[COORDINATOR_LIST].append(coordinator) tradfri_data[COORDINATOR] = coordinator_data async def async_keep_alive(now: datetime) -> None: if hass.is_stopping: return gw_status = True try: await api(gateway.get_gateway_info()) except RequestError: LOGGER.error("Keep-alive failed") gw_status = False async_dispatcher_send(hass, SIGNAL_GW, gw_status) entry.async_on_unload( async_track_time_interval(hass, async_keep_alive, timedelta(seconds=60))) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Plex from a config entry.""" server_config = entry.data[PLEX_SERVER_CONFIG] if entry.unique_id is None: hass.config_entries.async_update_entry( entry, unique_id=entry.data[CONF_SERVER_IDENTIFIER]) if MP_DOMAIN not in entry.options: options = dict(entry.options) options.setdefault(MP_DOMAIN, {}) hass.config_entries.async_update_entry(entry, options=options) plex_server = PlexServer( hass, server_config, entry.data[CONF_SERVER_IDENTIFIER], entry.options, entry.entry_id, ) try: await hass.async_add_executor_job(plex_server.connect) except ShouldUpdateConfigEntry: new_server_data = { **entry.data[PLEX_SERVER_CONFIG], CONF_URL: plex_server.url_in_use, CONF_SERVER: plex_server.friendly_name, } hass.config_entries.async_update_entry( entry, data={ **entry.data, PLEX_SERVER_CONFIG: new_server_data }) except requests.exceptions.ConnectionError as error: if entry.state is not ConfigEntryState.SETUP_RETRY: _LOGGER.error( "Plex server (%s) could not be reached: [%s]", server_config[CONF_URL], error, ) raise ConfigEntryNotReady from error except plexapi.exceptions.Unauthorized as ex: raise ConfigEntryAuthFailed( f"Token not accepted, please reauthenticate Plex server '{entry.data[CONF_SERVER]}'" ) from ex except ( plexapi.exceptions.BadRequest, plexapi.exceptions.NotFound, ) as error: _LOGGER.error( "Login to %s failed, verify token and SSL settings: [%s]", entry.data[CONF_SERVER], error, ) return False _LOGGER.debug("Connected to: %s (%s)", plex_server.friendly_name, plex_server.url_in_use) server_id = plex_server.machine_identifier hass.data[PLEX_DOMAIN][SERVERS][server_id] = plex_server hass.data[PLEX_DOMAIN][PLATFORMS_COMPLETED][server_id] = set() entry.add_update_listener(async_options_updated) unsub = async_dispatcher_connect( hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id), plex_server.async_update_platforms, ) hass.data[PLEX_DOMAIN][DISPATCHERS].setdefault(server_id, []) hass.data[PLEX_DOMAIN][DISPATCHERS][server_id].append(unsub) @callback def plex_websocket_callback(msgtype, data, error): """Handle callbacks from plexwebsocket library.""" if msgtype == SIGNAL_CONNECTION_STATE: if data == STATE_CONNECTED: _LOGGER.debug("Websocket to %s successful", entry.data[CONF_SERVER]) hass.async_create_task(plex_server.async_update_platforms()) elif data == STATE_DISCONNECTED: _LOGGER.debug("Websocket to %s disconnected, retrying", entry.data[CONF_SERVER]) # Stopped websockets without errors are expected during shutdown and ignored elif data == STATE_STOPPED and error: _LOGGER.error( "Websocket to %s failed, aborting [Error: %s]", entry.data[CONF_SERVER], error, ) hass.async_create_task( hass.config_entries.async_reload(entry.entry_id)) elif msgtype == "playing": hass.async_create_task(plex_server.async_update_session(data)) elif msgtype == "status": if data["StatusNotification"][0][ "title"] == "Library scan complete": async_dispatcher_send( hass, PLEX_UPDATE_LIBRARY_SIGNAL.format(server_id), ) session = async_get_clientsession(hass) subscriptions = ["playing", "status"] verify_ssl = server_config.get(CONF_VERIFY_SSL) websocket = PlexWebsocket( plex_server.plex_server, plex_websocket_callback, subscriptions=subscriptions, session=session, verify_ssl=verify_ssl, ) hass.data[PLEX_DOMAIN][WEBSOCKETS][server_id] = websocket def start_websocket_session(platform, _): hass.data[PLEX_DOMAIN][PLATFORMS_COMPLETED][server_id].add(platform) if hass.data[PLEX_DOMAIN][PLATFORMS_COMPLETED][server_id] == PLATFORMS: hass.loop.create_task(websocket.listen()) def close_websocket_session(_): websocket.close() unsub = hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, close_websocket_session) hass.data[PLEX_DOMAIN][DISPATCHERS][server_id].append(unsub) for platform in PLATFORMS: task = hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform)) task.add_done_callback(partial(start_websocket_session, platform)) async_cleanup_plex_devices(hass, entry) def get_plex_account(plex_server): try: return plex_server.account except (plexapi.exceptions.BadRequest, plexapi.exceptions.Unauthorized): return None await hass.async_add_executor_job(get_plex_account, plex_server) return True
async def async_get_config_entry_diagnostics( hass: HomeAssistant, config_entry: ConfigEntry) -> dict[str, Any]: """Return diagnostics for a config entry.""" diagnostics = {"config_entry": config_entry.as_dict()} return diagnostics
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): """Initialize config entry which represents an installed SmartApp.""" from pysmartthings import SmartThings if not hass.config.api.base_url.lower().startswith('https://'): _LOGGER.warning("The 'base_url' of the 'http' component must be " "configured and start with 'https://'") return False api = SmartThings(async_get_clientsession(hass), entry.data[CONF_ACCESS_TOKEN]) remove_entry = False try: # See if the app is already setup. This occurs when there are # installs in multiple SmartThings locations (valid use-case) manager = hass.data[DOMAIN][DATA_MANAGER] smart_app = manager.smartapps.get(entry.data[CONF_APP_ID]) if not smart_app: # Validate and setup the app. app = await api.app(entry.data[CONF_APP_ID]) smart_app = setup_smartapp(hass, app) # Validate and retrieve the installed app. installed_app = await validate_installed_app( api, entry.data[CONF_INSTALLED_APP_ID]) # Get scenes scenes = await async_get_entry_scenes(entry, api) # Get SmartApp token to sync subscriptions token = await api.generate_tokens( entry.data[CONF_OAUTH_CLIENT_ID], entry.data[CONF_OAUTH_CLIENT_SECRET], entry.data[CONF_REFRESH_TOKEN]) entry.data[CONF_REFRESH_TOKEN] = token.refresh_token hass.config_entries.async_update_entry(entry) # Get devices and their current status devices = await api.devices( location_ids=[installed_app.location_id]) async def retrieve_device_status(device): try: await device.status.refresh() except ClientResponseError: _LOGGER.debug("Unable to update status for device: %s (%s), " "the device will be excluded", device.label, device.device_id, exc_info=True) devices.remove(device) await asyncio.gather(*[retrieve_device_status(d) for d in devices.copy()]) # Sync device subscriptions await smartapp_sync_subscriptions( hass, token.access_token, installed_app.location_id, installed_app.installed_app_id, devices) # Setup device broker broker = DeviceBroker(hass, entry, token, smart_app, devices, scenes) broker.connect() hass.data[DOMAIN][DATA_BROKERS][entry.entry_id] = broker except ClientResponseError as ex: if ex.status in (401, 403): _LOGGER.exception("Unable to setup config entry '%s' - please " "reconfigure the integration", entry.title) remove_entry = True else: _LOGGER.debug(ex, exc_info=True) raise ConfigEntryNotReady except (ClientConnectionError, RuntimeWarning) as ex: _LOGGER.debug(ex, exc_info=True) raise ConfigEntryNotReady if remove_entry: hass.async_create_task( hass.config_entries.async_remove(entry.entry_id)) # only create new flow if there isn't a pending one for SmartThings. flows = hass.config_entries.flow.async_progress() if not [flow for flow in flows if flow['handler'] == DOMAIN]: hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={'source': 'import'})) return False for component in SUPPORTED_PLATFORMS: hass.async_create_task(hass.config_entries.async_forward_entry_setup( entry, component)) return True