async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a mobile_app entry.""" registration = entry.data webhook_id = registration[CONF_WEBHOOK_ID] hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] = entry device_registry = dr.async_get(hass) device = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, registration[ATTR_DEVICE_ID])}, manufacturer=registration[ATTR_MANUFACTURER], model=registration[ATTR_MODEL], name=registration[ATTR_DEVICE_NAME], sw_version=registration[ATTR_OS_VERSION], ) hass.data[DOMAIN][DATA_DEVICES][webhook_id] = device registration_name = f"Mobile App: {registration[ATTR_DEVICE_NAME]}" webhook_register(hass, DOMAIN, registration_name, webhook_id, handle_webhook) hass.config_entries.async_setup_platforms(entry, PLATFORMS) await hass_notify.async_reload(hass, DOMAIN) return True
async def async_setup_entry(hass, entry): """Set up a mobile_app entry.""" registration = entry.data webhook_id = registration[CONF_WEBHOOK_ID] hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] = entry device_registry = await dr.async_get_registry(hass) device = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, registration[ATTR_DEVICE_ID])}, manufacturer=registration[ATTR_MANUFACTURER], model=registration[ATTR_MODEL], name=registration[ATTR_DEVICE_NAME], sw_version=registration[ATTR_OS_VERSION], ) hass.data[DOMAIN][DATA_DEVICES][webhook_id] = device registration_name = f"Mobile App: {registration[ATTR_DEVICE_NAME]}" webhook_register(hass, DOMAIN, registration_name, webhook_id, handle_webhook) for domain in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, domain)) return True
async def register_webhook(event): if CONF_WEBHOOK_ID not in entry.data: data = {**entry.data, CONF_WEBHOOK_ID: secrets.token_hex()} hass.config_entries.async_update_entry(entry, data=data) if hass.components.cloud.async_active_subscription(): if CONF_CLOUDHOOK_URL not in entry.data: webhook_url = await hass.components.cloud.async_create_cloudhook( entry.data[CONF_WEBHOOK_ID]) data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url} hass.config_entries.async_update_entry(entry, data=data) else: webhook_url = entry.data[CONF_CLOUDHOOK_URL] else: webhook_url = hass.components.webhook.async_generate_url( entry.data[CONF_WEBHOOK_ID]) if entry.data[ "auth_implementation"] == "cloud" and not webhook_url.startswith( "https://"): _LOGGER.warning( "Webhook not registered - " "https and port 443 is required to register the webhook") return try: webhook_register( hass, DOMAIN, "Netatmo", entry.data[CONF_WEBHOOK_ID], async_handle_webhook, ) async def handle_event(event): """Handle webhook events.""" if event["data"]["push_type"] == "webhook_activation": if activation_listener is not None: activation_listener() if activation_timeout is not None: activation_timeout() activation_listener = async_dispatcher_connect( hass, f"signal-{DOMAIN}-webhook-None", handle_event, ) activation_timeout = async_call_later(hass, 10, unregister_webhook) await hass.async_add_executor_job( hass.data[DOMAIN][entry.entry_id][AUTH].addwebhook, webhook_url) _LOGGER.info("Register Netatmo webhook: %s", webhook_url) except pyatmo.ApiError as err: _LOGGER.error("Error during webhook registration - %s", err) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook)
async def async_setup(hass: HomeAssistantType, config: ConfigType): """Set up the mobile app component.""" store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) app_config = await store.async_load() if app_config is None: app_config = { DATA_BINARY_SENSOR: {}, DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_SENSOR: {}, } hass.data[DOMAIN] = { DATA_BINARY_SENSOR: app_config.get(DATA_BINARY_SENSOR, {}), DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: app_config.get(DATA_DELETED_IDS, []), DATA_DEVICES: {}, DATA_SENSOR: app_config.get(DATA_SENSOR, {}), DATA_STORE: store, } hass.http.register_view(RegistrationsView()) for deleted_id in hass.data[DOMAIN][DATA_DELETED_IDS]: try: webhook_register(hass, DOMAIN, "Deleted Webhook", deleted_id, handle_webhook) except ValueError: pass hass.async_create_task( discovery.async_load_platform(hass, "notify", DOMAIN, {}, config)) return True
async def register_webhook(event): # Wait for the cloud integration to be ready await asyncio.sleep(WAIT_FOR_CLOUD) if CONF_WEBHOOK_ID not in entry.data: data = {**entry.data, CONF_WEBHOOK_ID: secrets.token_hex()} hass.config_entries.async_update_entry(entry, data=data) if hass.components.cloud.async_active_subscription(): # Wait for cloud connection to be established await asyncio.sleep(WAIT_FOR_CLOUD) if CONF_CLOUDHOOK_URL not in entry.data: webhook_url = await hass.components.cloud.async_create_cloudhook( entry.data[CONF_WEBHOOK_ID]) data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url} hass.config_entries.async_update_entry(entry, data=data) else: webhook_url = entry.data[CONF_CLOUDHOOK_URL] else: webhook_url = hass.components.webhook.async_generate_url( entry.data[CONF_WEBHOOK_ID]) try: await hass.async_add_executor_job( hass.data[DOMAIN][entry.entry_id][AUTH].addwebhook, webhook_url) webhook_register(hass, DOMAIN, "Netatmo", entry.data[CONF_WEBHOOK_ID], handle_webhook) _LOGGER.info("Register Netatmo webhook: %s", webhook_url) except pyatmo.ApiError as err: _LOGGER.error("Error during webhook registration - %s", err) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook)
async def async_setup_entry(hass, entry): """Set up a mobile_app entry.""" registration = entry.data webhook_id = registration[CONF_WEBHOOK_ID] hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] = entry device_registry = await dr.async_get_registry(hass) identifiers = {(ATTR_DEVICE_ID, registration[ATTR_DEVICE_ID]), (CONF_WEBHOOK_ID, registration[CONF_WEBHOOK_ID])} device = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers=identifiers, manufacturer=registration[ATTR_MANUFACTURER], model=registration[ATTR_MODEL], name=registration[ATTR_DEVICE_NAME], sw_version=registration[ATTR_OS_VERSION]) hass.data[DOMAIN][DATA_DEVICES][webhook_id] = device registration_name = 'Mobile App: {}'.format(registration[ATTR_DEVICE_NAME]) webhook_register(hass, DOMAIN, registration_name, webhook_id, handle_webhook) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, DATA_BINARY_SENSOR)) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, DATA_SENSOR)) return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the mobile app component.""" store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) app_config = await store.async_load() if app_config is None: app_config = { DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], } hass.data[DOMAIN] = { DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: app_config.get(DATA_DELETED_IDS, []), DATA_DEVICES: {}, DATA_PUSH_CHANNEL: {}, DATA_STORE: store, } hass.http.register_view(RegistrationsView()) for deleted_id in hass.data[DOMAIN][DATA_DELETED_IDS]: with suppress(ValueError): webhook_register(hass, DOMAIN, "Deleted Webhook", deleted_id, handle_webhook) hass.async_create_task( discovery.async_load_platform(hass, "notify", DOMAIN, {}, config)) websocket_api.async_setup_commands(hass) return True
async def async_setup(hass: HomeAssistantType, config: ConfigType): """Set up the mobile app component.""" store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) app_config = await store.async_load() if app_config is None: app_config = { DATA_BINARY_SENSOR: {}, DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_DEVICES: {}, DATA_SENSOR: {} } hass.data[DOMAIN] = { DATA_BINARY_SENSOR: app_config.get(DATA_BINARY_SENSOR, {}), DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: app_config.get(DATA_DELETED_IDS, []), DATA_DEVICES: {}, DATA_SENSOR: app_config.get(DATA_SENSOR, {}), DATA_STORE: store, } hass.http.register_view(RegistrationsView()) register_websocket_handlers(hass) for deleted_id in hass.data[DOMAIN][DATA_DELETED_IDS]: try: webhook_register(hass, DOMAIN, "Deleted Webhook", deleted_id, handle_webhook) except ValueError: pass return True
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): """Set up Thermosmart from a config entry.""" implementation = await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry) thermo_api = api.ConfigEntryThermosmartApi(hass, entry, implementation) thermo_id = await hass.async_add_executor_job(thermo_api.get_thermostat_id) hass.data[DOMAIN][entry.entry_id] = { API: thermo_api, DEVICE: ThermosmartDevice(hass, entry.entry_id, thermo_api, thermo_id), } if CONF_WEBHOOK_ID in hass.data[DOMAIN][CONFIG]: webhook_id = hass.data[DOMAIN][CONFIG][CONF_WEBHOOK_ID] do_update = False else: webhook_id = None do_update = True hass.config_entries.async_update_entry(entry, data={ **entry.data, CONF_WEBHOOK_ID: webhook_id, "do_update": do_update }) # Register a webhook if entry.data[CONF_WEBHOOK_ID]: webhook_id = entry.data[CONF_WEBHOOK_ID] webhook_register( hass, DOMAIN, "Thermosmart", webhook_id, hass.data[DOMAIN][entry.entry_id][DEVICE].handle_webhook, ) async def unregister_webhook(event): _LOGGER.debug("Unregister Thermosmart webhook (%s)", entry.data[CONF_WEBHOOK_ID]) webhook_unregister(hass, entry.data[CONF_WEBHOOK_ID]) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook) await hass.data[DOMAIN][entry.entry_id][DEVICE].update() for platform in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform)) return True
async def register_webhook(self, event: Event | None = None) -> None: """Register a webhook with Toon to get live updates.""" if CONF_WEBHOOK_ID not in self.entry.data: data = {**self.entry.data, CONF_WEBHOOK_ID: secrets.token_hex()} self.hass.config_entries.async_update_entry(self.entry, data=data) if cloud.async_active_subscription(self.hass): if CONF_CLOUDHOOK_URL not in self.entry.data: try: webhook_url = await cloud.async_create_cloudhook( self.hass, self.entry.data[CONF_WEBHOOK_ID] ) except cloud.CloudNotConnected: webhook_url = webhook.async_generate_url( self.hass, self.entry.data[CONF_WEBHOOK_ID] ) else: data = {**self.entry.data, CONF_CLOUDHOOK_URL: webhook_url} self.hass.config_entries.async_update_entry(self.entry, data=data) else: webhook_url = self.entry.data[CONF_CLOUDHOOK_URL] else: webhook_url = webhook.async_generate_url( self.hass, self.entry.data[CONF_WEBHOOK_ID] ) # Ensure the webhook is not registered already webhook_unregister(self.hass, self.entry.data[CONF_WEBHOOK_ID]) webhook_register( self.hass, DOMAIN, "Toon", self.entry.data[CONF_WEBHOOK_ID], self.handle_webhook, ) try: await self.toon.subscribe_webhook( application_id=self.entry.entry_id, url=webhook_url ) _LOGGER.info("Registered Toon webhook: %s", webhook_url) except ToonError as err: _LOGGER.error("Error during webhook registration - %s", err) self.hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STOP, self.unregister_webhook )
async def register_webhook(event): if CONF_WEBHOOK_ID not in entry.data: data = {**entry.data, CONF_WEBHOOK_ID: secrets.token_hex()} hass.config_entries.async_update_entry(entry, data=data) if hass.components.cloud.async_active_subscription(): if CONF_CLOUDHOOK_URL not in entry.data: webhook_url = await hass.components.cloud.async_create_cloudhook( entry.data[CONF_WEBHOOK_ID] ) data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url} hass.config_entries.async_update_entry(entry, data=data) else: webhook_url = entry.data[CONF_CLOUDHOOK_URL] else: webhook_url = hass.components.webhook.async_generate_url( entry.data[CONF_WEBHOOK_ID] ) if entry.data["auth_implementation"] == "cloud" and not webhook_url.startswith( "https://" ): _LOGGER.warning( "Webhook not registered - " "https and port 443 is required to register the webhook" ) return try: webhook_register( hass, DOMAIN, "Netatmo", entry.data[CONF_WEBHOOK_ID], handle_webhook ) await hass.async_add_executor_job( hass.data[DOMAIN][entry.entry_id][AUTH].addwebhook, webhook_url ) _LOGGER.info("Register Netatmo webhook: %s", webhook_url) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, "light") ) except pyatmo.ApiError as err: _LOGGER.error("Error during webhook registration - %s", err) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook)
async def register_webhook( call_or_event_or_dt: ServiceCall | Event | datetime | None, ) -> None: if CONF_WEBHOOK_ID not in entry.data: data = {**entry.data, CONF_WEBHOOK_ID: secrets.token_hex()} hass.config_entries.async_update_entry(entry, data=data) if cloud.async_active_subscription(hass): webhook_url = await async_cloudhook_generate_url(hass, entry) else: webhook_url = webhook_generate_url(hass, entry.data[CONF_WEBHOOK_ID]) if entry.data[ "auth_implementation" ] == cloud.DOMAIN and not webhook_url.startswith("https://"): _LOGGER.warning( "Webhook not registered - " "https and port 443 is required to register the webhook" ) return webhook_register( hass, DOMAIN, "Netatmo", entry.data[CONF_WEBHOOK_ID], async_handle_webhook, ) try: await hass.data[DOMAIN][entry.entry_id][AUTH].async_addwebhook(webhook_url) _LOGGER.info("Register Netatmo webhook: %s", webhook_url) except pyatmo.ApiError as err: _LOGGER.error("Error during webhook registration - %s", err) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook) )
async def async_setup_entry(hass, entry): """Set up a mobile_app entry.""" registration = entry.data webhook_id = registration[CONF_WEBHOOK_ID] hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] = entry device_registry = await dr.async_get_registry(hass) identifiers = { (ATTR_DEVICE_ID, registration[ATTR_DEVICE_ID]), (CONF_WEBHOOK_ID, registration[CONF_WEBHOOK_ID]) } device = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers=identifiers, manufacturer=registration[ATTR_MANUFACTURER], model=registration[ATTR_MODEL], name=registration[ATTR_DEVICE_NAME], sw_version=registration[ATTR_OS_VERSION] ) hass.data[DOMAIN][DATA_DEVICES][webhook_id] = device registration_name = 'Mobile App: {}'.format(registration[ATTR_DEVICE_NAME]) webhook_register(hass, DOMAIN, registration_name, webhook_id, handle_webhook) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, DATA_BINARY_SENSOR)) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, DATA_SENSOR)) return True
DATA_DELETED_IDS: [], } hass.data[DOMAIN] = { DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: app_config.get(DATA_DELETED_IDS, []), DATA_DEVICES: {}, DATA_PUSH_CHANNEL: {}, DATA_STORE: store, } hass.http.register_view(RegistrationsView()) for deleted_id in hass.data[DOMAIN][DATA_DELETED_IDS]: with suppress(ValueError): webhook_register(hass, DOMAIN, "Deleted Webhook", deleted_id, handle_webhook) hass.async_create_task( discovery.async_load_platform(hass, Platform.NOTIFY, DOMAIN, {}, config)) websocket_api.async_setup_commands(hass) return True async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a mobile_app entry.""" registration = entry.data webhook_id = registration[CONF_WEBHOOK_ID]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up motionEye from a config entry.""" hass.data.setdefault(DOMAIN, {}) client = create_motioneye_client( entry.data[CONF_URL], admin_username=entry.data.get(CONF_ADMIN_USERNAME), admin_password=entry.data.get(CONF_ADMIN_PASSWORD), surveillance_username=entry.data.get(CONF_SURVEILLANCE_USERNAME), surveillance_password=entry.data.get(CONF_SURVEILLANCE_PASSWORD), session=async_get_clientsession(hass), ) try: await client.async_client_login() except MotionEyeClientInvalidAuthError as exc: await client.async_client_close() raise ConfigEntryAuthFailed from exc except MotionEyeClientError as exc: await client.async_client_close() raise ConfigEntryNotReady from exc # Ensure every loaded entry has a registered webhook id. if CONF_WEBHOOK_ID not in entry.data: hass.config_entries.async_update_entry( entry, data={ **entry.data, CONF_WEBHOOK_ID: async_generate_id() }) webhook_register(hass, DOMAIN, "motionEye", entry.data[CONF_WEBHOOK_ID], handle_webhook) @callback async def async_update_data() -> dict[str, Any] | None: try: return await client.async_get_cameras() except MotionEyeClientError as exc: raise UpdateFailed("Error communicating with API") from exc coordinator = DataUpdateCoordinator( hass, _LOGGER, name=DOMAIN, update_method=async_update_data, update_interval=DEFAULT_SCAN_INTERVAL, ) hass.data[DOMAIN][entry.entry_id] = { CONF_CLIENT: client, CONF_COORDINATOR: coordinator, } current_cameras: set[tuple[str, str]] = set() device_registry = dr.async_get(hass) @callback def _async_process_motioneye_cameras() -> None: """Process motionEye camera additions and removals.""" inbound_camera: set[tuple[str, str]] = set() if coordinator.data is None or KEY_CAMERAS not in coordinator.data: return for camera in coordinator.data[KEY_CAMERAS]: if not is_acceptable_camera(camera): return camera_id = camera[KEY_ID] device_identifier = get_motioneye_device_identifier( entry.entry_id, camera_id) inbound_camera.add(device_identifier) if device_identifier in current_cameras: continue current_cameras.add(device_identifier) _add_camera( hass, device_registry, client, entry, camera_id, camera, device_identifier, ) # Ensure every device associated with this config entry is still in the # list of motionEye cameras, otherwise remove the device (and thus # entities). for device_entry in dr.async_entries_for_config_entry( device_registry, entry.entry_id): for identifier in device_entry.identifiers: if identifier in inbound_camera: break else: device_registry.async_remove_device(device_entry.id) async def setup_then_listen() -> None: await asyncio.gather( *(hass.config_entries.async_forward_entry_setup(entry, platform) for platform in PLATFORMS)) entry.async_on_unload( coordinator.async_add_listener(_async_process_motioneye_cameras)) await coordinator.async_refresh() entry.async_on_unload(entry.add_update_listener(_async_entry_updated)) hass.async_create_task(setup_then_listen()) return True
async def register_webhook( call_or_event_or_dt: ServiceCall | Event | datetime | None, ) -> None: if CONF_WEBHOOK_ID not in entry.data: data = {**entry.data, CONF_WEBHOOK_ID: secrets.token_hex()} hass.config_entries.async_update_entry(entry, data=data) if cloud.async_active_subscription(hass): if CONF_CLOUDHOOK_URL not in entry.data: webhook_url = await cloud.async_create_cloudhook( hass, entry.data[CONF_WEBHOOK_ID]) data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url} hass.config_entries.async_update_entry(entry, data=data) else: webhook_url = entry.data[CONF_CLOUDHOOK_URL] else: webhook_url = webhook_generate_url(hass, entry.data[CONF_WEBHOOK_ID]) if entry.data[ "auth_implementation"] == cloud.DOMAIN and not webhook_url.startswith( "https://"): _LOGGER.warning( "Webhook not registered - " "https and port 443 is required to register the webhook") return try: webhook_register( hass, DOMAIN, "Netatmo", entry.data[CONF_WEBHOOK_ID], async_handle_webhook, ) async def handle_event(event: dict) -> None: """Handle webhook events.""" if event["data"][WEBHOOK_PUSH_TYPE] == WEBHOOK_ACTIVATION: if activation_listener is not None: activation_listener() if activation_timeout is not None: activation_timeout() activation_listener = async_dispatcher_connect( hass, f"signal-{DOMAIN}-webhook-None", handle_event, ) activation_timeout = async_call_later(hass, 30, unregister_webhook) await hass.data[DOMAIN][entry.entry_id ][AUTH].async_addwebhook(webhook_url) _LOGGER.info("Register Netatmo webhook: %s", webhook_url) except pyatmo.ApiError as err: _LOGGER.error("Error during webhook registration - %s", err) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook))