Example #1
0
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,
    )
Example #2
0
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,
    )