Skip to content

Commit

Permalink
Add options flow to disable live streaming
Browse files Browse the repository at this point in the history
  • Loading branch information
sdb9696 committed Oct 4, 2024
1 parent 104d35e commit 377b5b2
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 14 deletions.
22 changes: 21 additions & 1 deletion homeassistant/components/ring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import CONF_LISTEN_CREDENTIALS, DOMAIN, PLATFORMS
from .const import (
CONF_LISTEN_CREDENTIALS,
CONF_LIVE_STREAM,
DEFAULT_LIVE_STREAM,
DOMAIN,
PLATFORMS,
)
from .coordinator import RingDataCoordinator, RingListenCoordinator

_LOGGER = logging.getLogger(__name__)
Expand All @@ -33,6 +39,7 @@ class RingData:
devices: RingDevices
devices_coordinator: RingDataCoordinator
listen_coordinator: RingListenCoordinator
live_stream: bool


type RingConfigEntry = ConfigEntry[RingData]
Expand Down Expand Up @@ -90,18 +97,31 @@ def listen_credentials_updater(token: dict[str, Any]) -> None:

await devices_coordinator.async_config_entry_first_refresh()

live_stream = entry.options.get(CONF_LIVE_STREAM, DEFAULT_LIVE_STREAM)

entry.runtime_data = RingData(
api=ring,
devices=ring.devices(),
devices_coordinator=devices_coordinator,
listen_coordinator=listen_coordinator,
live_stream=live_stream,
)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

entry.async_on_unload(entry.add_update_listener(update_listener))

return True


async def update_listener(hass: HomeAssistant, entry: RingConfigEntry) -> None:
"""Handle options update."""
if entry.runtime_data.live_stream != entry.options.get(
CONF_LIVE_STREAM, DEFAULT_LIVE_STREAM
):
await hass.config_entries.async_reload(entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Ring entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
Expand Down
15 changes: 12 additions & 3 deletions homeassistant/components/ring/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,15 @@ async def async_setup_entry(
ring_data = entry.runtime_data
devices_coordinator = ring_data.devices_coordinator
ffmpeg_manager = ffmpeg.get_ffmpeg_manager(hass)
live_stream = ring_data.live_stream

cams = [
RingCam(camera, devices_coordinator, ffmpeg_manager)
RingCam(
camera,
devices_coordinator,
ffmpeg_manager=ffmpeg_manager,
live_stream=live_stream,
)
for camera in ring_data.devices.video_devices
if camera.has_subscription
]
Expand All @@ -60,7 +66,9 @@ def __init__(
self,
device: RingDoorBell,
coordinator: RingDataCoordinator,
*,
ffmpeg_manager: ffmpeg.FFmpegManager,
live_stream: bool,
) -> None:
"""Initialize a Ring Door Bell camera."""
super().__init__(device, coordinator)
Expand All @@ -74,8 +82,9 @@ def __init__(
self._attr_unique_id = str(device.id)
if device.has_capability(MOTION_DETECTION_CAPABILITY):
self._attr_motion_detection_enabled = device.motion_detection
self._attr_supported_features |= CameraEntityFeature.STREAM
self._attr_frontend_stream_type = StreamType.WEB_RTC
if live_stream:
self._attr_supported_features |= CameraEntityFeature.STREAM
self._attr_frontend_stream_type = StreamType.WEB_RTC

@callback
def _handle_coordinator_update(self) -> None:
Expand Down
43 changes: 40 additions & 3 deletions homeassistant/components/ring/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,33 @@
import voluptuous as vol

from homeassistant.components import dhcp
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.device_registry as dr
from homeassistant.helpers.selector import BooleanSelector

from . import get_auth_agent_id
from .const import CONF_2FA, DOMAIN
from .const import CONF_2FA, CONF_LIVE_STREAM, DEFAULT_LIVE_STREAM, DOMAIN

_LOGGER = logging.getLogger(__name__)

STEP_USER_DATA_SCHEMA = vol.Schema(
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
)
STEP_REAUTH_DATA_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str})
OPTIONS_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_LIVE_STREAM, default=DEFAULT_LIVE_STREAM): BooleanSelector(),
}
)

UNKNOWN_RING_ACCOUNT = "unknown_ring_account"

Expand Down Expand Up @@ -173,6 +184,32 @@ async def async_step_reauth_confirm(
},
)

@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
"""Create the options flow."""
return OptionsFlowHandler(config_entry)


class OptionsFlowHandler(OptionsFlow):
"""Handle an option flow for jellyfin."""

def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry

async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)

return self.async_show_form(
step_id="init",
data_schema=OPTIONS_DATA_SCHEMA,
)


class Require2FA(HomeAssistantError):
"""Error to indicate we require 2FA."""
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/ring/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

from datetime import timedelta
from typing import Final

from homeassistant.const import Platform

Expand Down Expand Up @@ -31,3 +32,5 @@

CONF_2FA = "2fa"
CONF_LISTEN_CREDENTIALS = "listen_token"
CONF_LIVE_STREAM = "live_stream"
DEFAULT_LIVE_STREAM: Final[bool] = True
9 changes: 9 additions & 0 deletions homeassistant/components/ring/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
},
"options": {
"step": {
"init": {
"data": {
"live_stream": "Open camera with live stream"
}
}
}
},
"entity": {
"binary_sensor": {
"ding": {
Expand Down
15 changes: 9 additions & 6 deletions tests/components/ring/snapshots/test_camera.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
'original_name': None,
'platform': 'ring',
'previous_unique_id': None,
'supported_features': 0,
'supported_features': <CameraEntityFeature: 2>,
'translation_key': None,
'unique_id': '765432',
'unit_of_measurement': None,
Expand All @@ -39,8 +39,9 @@
'attribution': 'Data provided by Ring.com',
'entity_picture': '/api/camera_proxy/camera.front?token=1caab5c3b3',
'friendly_name': 'Front',
'frontend_stream_type': <StreamType.WEB_RTC: 'web_rtc'>,
'last_video_id': None,
'supported_features': <CameraEntityFeature: 0>,
'supported_features': <CameraEntityFeature: 2>,
'video_url': None,
}),
'context': <ANY>,
Expand Down Expand Up @@ -78,7 +79,7 @@
'original_name': None,
'platform': 'ring',
'previous_unique_id': None,
'supported_features': 0,
'supported_features': <CameraEntityFeature: 2>,
'translation_key': None,
'unique_id': '987654',
'unit_of_measurement': None,
Expand All @@ -91,9 +92,10 @@
'attribution': 'Data provided by Ring.com',
'entity_picture': '/api/camera_proxy/camera.front_door?token=1caab5c3b3',
'friendly_name': 'Front Door',
'frontend_stream_type': <StreamType.WEB_RTC: 'web_rtc'>,
'last_video_id': None,
'motion_detection': True,
'supported_features': <CameraEntityFeature: 0>,
'supported_features': <CameraEntityFeature: 2>,
'video_url': None,
}),
'context': <ANY>,
Expand Down Expand Up @@ -131,7 +133,7 @@
'original_name': None,
'platform': 'ring',
'previous_unique_id': None,
'supported_features': 0,
'supported_features': <CameraEntityFeature: 2>,
'translation_key': None,
'unique_id': '345678',
'unit_of_measurement': None,
Expand All @@ -144,9 +146,10 @@
'attribution': 'Data provided by Ring.com',
'entity_picture': '/api/camera_proxy/camera.internal?token=1caab5c3b3',
'friendly_name': 'Internal',
'frontend_stream_type': <StreamType.WEB_RTC: 'web_rtc'>,
'last_video_id': None,
'motion_detection': True,
'supported_features': <CameraEntityFeature: 0>,
'supported_features': <CameraEntityFeature: 2>,
'video_url': None,
}),
'context': <ANY>,
Expand Down
41 changes: 40 additions & 1 deletion tests/components/ring/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from homeassistant import config_entries
from homeassistant.components import dhcp
from homeassistant.components.ring import DOMAIN
from homeassistant.components.ring import CONF_LIVE_STREAM, DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
Expand Down Expand Up @@ -298,3 +298,42 @@ async def test_dhcp_discovery(
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"


async def test_options_flow(
hass: HomeAssistant,
mock_added_config_entry: MockConfigEntry,
) -> None:
"""Test config flow options."""
assert mock_added_config_entry.options == {}
assert mock_added_config_entry.runtime_data.live_stream is True

# Set the option to False
result = await hass.config_entries.options.async_init(
mock_added_config_entry.entry_id
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "init"

result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={CONF_LIVE_STREAM: False}
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert CONF_LIVE_STREAM in mock_added_config_entry.options
assert mock_added_config_entry.options[CONF_LIVE_STREAM] is False
assert mock_added_config_entry.runtime_data.live_stream is False

# Set the option back to True
result = await hass.config_entries.options.async_init(
mock_added_config_entry.entry_id
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "init"

result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={CONF_LIVE_STREAM: True}
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert CONF_LIVE_STREAM in mock_added_config_entry.options
assert mock_added_config_entry.options[CONF_LIVE_STREAM] is True
assert mock_added_config_entry.runtime_data.live_stream is True

0 comments on commit 377b5b2

Please sign in to comment.