def _add_camera( hass: HomeAssistant, device_registry: dr.DeviceRegistry, client: MotionEyeClient, entry: ConfigEntry, camera_id: int, camera: dict[str, Any], device_identifier: tuple[str, str], ) -> None: """Add a motionEye camera to hass.""" def _is_recognized_web_hook(url: str) -> bool: """Determine whether this integration set a web hook.""" return f"{WEB_HOOK_SENTINEL_KEY}={WEB_HOOK_SENTINEL_VALUE}" in url def _set_webhook( url: str, key_url: str, key_method: str, key_enabled: str, camera: dict[str, Any], ) -> bool: """Set a web hook.""" if (entry.options.get( CONF_WEBHOOK_SET_OVERWRITE, DEFAULT_WEBHOOK_SET_OVERWRITE, ) or not camera.get(key_url) or _is_recognized_web_hook(camera[key_url])) and ( not camera.get(key_enabled, False) or camera.get(key_method) != KEY_HTTP_METHOD_POST_JSON or camera.get(key_url) != url): camera[key_enabled] = True camera[key_method] = KEY_HTTP_METHOD_POST_JSON camera[key_url] = url return True return False def _build_url(device: dr.DeviceEntry, base: str, event_type: str, keys: list[str]) -> str: """Build a motionEye webhook URL.""" # This URL-surgery cannot use YARL because the output must NOT be # url-encoded. This is because motionEye will do further string # manipulation/substitution on this value before ultimately fetching it, # and it cannot deal with URL-encoded input to that string manipulation. return urljoin( base, "?" + urlencode( { **{ k: KEY_WEB_HOOK_CONVERSION_SPECIFIERS[k] for k in sorted(keys) }, WEB_HOOK_SENTINEL_KEY: WEB_HOOK_SENTINEL_VALUE, ATTR_EVENT_TYPE: event_type, ATTR_DEVICE_ID: device.id, }, safe="%{}", ), ) device = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={device_identifier}, manufacturer=MOTIONEYE_MANUFACTURER, model=MOTIONEYE_MANUFACTURER, name=camera[KEY_NAME], ) if entry.options.get(CONF_WEBHOOK_SET, DEFAULT_WEBHOOK_SET): url = async_generate_motioneye_webhook(hass, entry.data[CONF_WEBHOOK_ID]) if url: set_motion_event = _set_webhook( _build_url( device, url, EVENT_MOTION_DETECTED, EVENT_MOTION_DETECTED_KEYS, ), KEY_WEB_HOOK_NOTIFICATIONS_URL, KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD, KEY_WEB_HOOK_NOTIFICATIONS_ENABLED, camera, ) set_storage_event = _set_webhook( _build_url( device, url, EVENT_FILE_STORED, EVENT_FILE_STORED_KEYS, ), KEY_WEB_HOOK_STORAGE_URL, KEY_WEB_HOOK_STORAGE_HTTP_METHOD, KEY_WEB_HOOK_STORAGE_ENABLED, camera, ) if set_motion_event or set_storage_event: hass.async_create_task( client.async_set_camera(camera_id, camera)) async_dispatcher_send( hass, SIGNAL_CAMERA_ADD.format(entry.entry_id), camera, )
def _add_camera( hass: HomeAssistant, device_registry: dr.DeviceRegistry, client: MotionEyeClient, entry: ConfigEntry, camera_id: int, camera: dict[str, Any], device_identifier: tuple[str, str], ) -> None: """Add a motionEye camera to hass.""" def _is_recognized_web_hook(url: str) -> bool: """Determine whether this integration set a web hook.""" return f"{WEB_HOOK_SENTINEL_KEY}={WEB_HOOK_SENTINEL_VALUE}" in url def _set_webhook( url: str, key_url: str, key_method: str, key_enabled: str, camera: dict[str, Any], ) -> bool: """Set a web hook.""" if (entry.options.get( CONF_WEBHOOK_SET_OVERWRITE, DEFAULT_WEBHOOK_SET_OVERWRITE, ) or not camera.get(key_url) or _is_recognized_web_hook(camera[key_url])) and ( not camera.get(key_enabled, False) or camera.get(key_method) != KEY_HTTP_METHOD_GET or camera.get(key_url) != url): camera[key_enabled] = True camera[key_method] = KEY_HTTP_METHOD_GET camera[key_url] = url return True return False def _build_url(base: str, keys: list[str]) -> str: """Build a motionEye webhook URL.""" return (base + "?" + urlencode( { **{ k: KEY_WEB_HOOK_CONVERSION_SPECIFIERS[k] for k in sorted(keys) }, WEB_HOOK_SENTINEL_KEY: WEB_HOOK_SENTINEL_VALUE, }, safe="%{}", )) device = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={device_identifier}, manufacturer=MOTIONEYE_MANUFACTURER, model=MOTIONEYE_MANUFACTURER, name=camera[KEY_NAME], ) if entry.options.get(CONF_WEBHOOK_SET, DEFAULT_WEBHOOK_SET): url = None try: url = get_url(hass) except NoURLAvailableError: pass if url: if _set_webhook( _build_url( f"{url}{API_PATH_DEVICE_ROOT}{device.id}/{EVENT_MOTION_DETECTED}", EVENT_MOTION_DETECTED_KEYS, ), KEY_WEB_HOOK_NOTIFICATIONS_URL, KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD, KEY_WEB_HOOK_NOTIFICATIONS_ENABLED, camera, ) | _set_webhook( _build_url( f"{url}{API_PATH_DEVICE_ROOT}{device.id}/{EVENT_FILE_STORED}", EVENT_FILE_STORED_KEYS, ), KEY_WEB_HOOK_STORAGE_URL, KEY_WEB_HOOK_STORAGE_HTTP_METHOD, KEY_WEB_HOOK_STORAGE_ENABLED, camera, ): hass.async_create_task( client.async_set_camera(camera_id, camera)) async_dispatcher_send( hass, SIGNAL_CAMERA_ADD.format(entry.entry_id), camera, )