Fully mock the ring_doorbell api and remove requests_mock (#113140)
* Fully mock ring_doorbell library * Add comments and docstrings * Simplify devices_mocks and fake RingDevices * Update post review * Consolidate device filtering in conftest * Fix ruff lambda assignment failure * Fix ruff check fail * Update post reviewpull/118921/head
parent
99b85e16d1
commit
333ac56904
|
@ -1,17 +1,19 @@
|
|||
"""Configuration for Ring tests."""
|
||||
|
||||
import re
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
from itertools import chain
|
||||
from unittest.mock import AsyncMock, Mock, create_autospec, patch
|
||||
|
||||
import pytest
|
||||
import requests_mock
|
||||
import ring_doorbell
|
||||
from typing_extensions import Generator
|
||||
|
||||
from homeassistant.components.ring import DOMAIN
|
||||
from homeassistant.const import CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
from .device_mocks import get_active_alerts, get_devices_data, get_mock_devices
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.light.conftest import mock_light_profiles # noqa: F401
|
||||
|
||||
|
||||
|
@ -36,6 +38,67 @@ def mock_ring_auth():
|
|||
yield mock_ring_auth.return_value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_ring_devices():
|
||||
"""Mock Ring devices."""
|
||||
|
||||
devices = get_mock_devices()
|
||||
device_list = list(chain.from_iterable(devices.values()))
|
||||
|
||||
def filter_devices(device_api_ai: int, device_family: set | None = None):
|
||||
return next(
|
||||
iter(
|
||||
[
|
||||
device
|
||||
for device in device_list
|
||||
if device.id == device_api_ai
|
||||
and (not device_family or device.family in device_family)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
class FakeRingDevices:
|
||||
"""Class fakes the RingDevices class."""
|
||||
|
||||
all_devices = device_list
|
||||
video_devices = (
|
||||
devices["stickup_cams"]
|
||||
+ devices["doorbots"]
|
||||
+ devices["authorized_doorbots"]
|
||||
)
|
||||
stickup_cams = devices["stickup_cams"]
|
||||
other = devices["other"]
|
||||
chimes = devices["chimes"]
|
||||
|
||||
def get_device(self, id):
|
||||
return filter_devices(id)
|
||||
|
||||
def get_video_device(self, id):
|
||||
return filter_devices(
|
||||
id, {"stickup_cams", "doorbots", "authorized_doorbots"}
|
||||
)
|
||||
|
||||
def get_stickup_cam(self, id):
|
||||
return filter_devices(id, {"stickup_cams"})
|
||||
|
||||
def get_other(self, id):
|
||||
return filter_devices(id, {"other"})
|
||||
|
||||
return FakeRingDevices()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_ring_client(mock_ring_auth, mock_ring_devices):
|
||||
"""Mock ring client api."""
|
||||
mock_client = create_autospec(ring_doorbell.Ring)
|
||||
mock_client.return_value.devices_data = get_devices_data()
|
||||
mock_client.return_value.devices.return_value = mock_ring_devices
|
||||
mock_client.return_value.active_alerts.side_effect = get_active_alerts
|
||||
|
||||
with patch("homeassistant.components.ring.Ring", new=mock_client):
|
||||
yield mock_client.return_value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Mock ConfigEntry."""
|
||||
|
@ -55,91 +118,10 @@ async def mock_added_config_entry(
|
|||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_ring_auth: Mock,
|
||||
mock_ring_client: Mock,
|
||||
) -> MockConfigEntry:
|
||||
"""Mock ConfigEntry that's been added to HA."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert DOMAIN in hass.config_entries.async_domains()
|
||||
return mock_config_entry
|
||||
|
||||
|
||||
@pytest.fixture(name="requests_mock")
|
||||
def requests_mock_fixture():
|
||||
"""Fixture to provide a requests mocker."""
|
||||
with requests_mock.mock() as mock:
|
||||
# Note all devices have an id of 987652, but a different device_id.
|
||||
# the device_id is used as our unique_id, but the id is what is sent
|
||||
# to the APIs, which is why every mock uses that id.
|
||||
|
||||
# Mocks the response for authenticating
|
||||
mock.post(
|
||||
"https://oauth.ring.com/oauth/token",
|
||||
text=load_fixture("oauth.json", "ring"),
|
||||
)
|
||||
# Mocks the response for getting the login session
|
||||
mock.post(
|
||||
"https://api.ring.com/clients_api/session",
|
||||
text=load_fixture("session.json", "ring"),
|
||||
)
|
||||
# Mocks the response for getting all the devices
|
||||
mock.get(
|
||||
"https://api.ring.com/clients_api/ring_devices",
|
||||
text=load_fixture("devices.json", "ring"),
|
||||
)
|
||||
mock.get(
|
||||
"https://api.ring.com/clients_api/dings/active",
|
||||
text=load_fixture("ding_active.json", "ring"),
|
||||
)
|
||||
# Mocks the response for getting the history of a device
|
||||
mock.get(
|
||||
re.compile(
|
||||
r"https:\/\/api\.ring\.com\/clients_api\/doorbots\/\d+\/history"
|
||||
),
|
||||
text=load_fixture("doorbot_history.json", "ring"),
|
||||
)
|
||||
# Mocks the response for getting the health of a device
|
||||
mock.get(
|
||||
re.compile(r"https:\/\/api\.ring\.com\/clients_api\/doorbots\/\d+\/health"),
|
||||
text=load_fixture("doorboot_health_attrs.json", "ring"),
|
||||
)
|
||||
# Mocks the response for getting a chimes health
|
||||
mock.get(
|
||||
re.compile(r"https:\/\/api\.ring\.com\/clients_api\/chimes\/\d+\/health"),
|
||||
text=load_fixture("chime_health_attrs.json", "ring"),
|
||||
)
|
||||
mock.get(
|
||||
re.compile(
|
||||
r"https:\/\/api\.ring\.com\/clients_api\/dings\/\d+\/share/play"
|
||||
),
|
||||
status_code=200,
|
||||
json={"url": "http://127.0.0.1/foo"},
|
||||
)
|
||||
mock.get(
|
||||
"https://api.ring.com/groups/v1/locations/mock-location-id/groups",
|
||||
text=load_fixture("groups.json", "ring"),
|
||||
)
|
||||
# Mocks the response for getting the history of the intercom
|
||||
mock.get(
|
||||
"https://api.ring.com/clients_api/doorbots/185036587/history",
|
||||
text=load_fixture("intercom_history.json", "ring"),
|
||||
)
|
||||
# Mocks the response for setting properties in settings (i.e. motion_detection)
|
||||
mock.patch(
|
||||
re.compile(
|
||||
r"https:\/\/api\.ring\.com\/devices\/v1\/devices\/\d+\/settings"
|
||||
),
|
||||
text="ok",
|
||||
)
|
||||
# Mocks the open door command for intercom devices
|
||||
mock.put(
|
||||
"https://api.ring.com/commands/v1/devices/185036587/device_rpc",
|
||||
status_code=200,
|
||||
text="{}",
|
||||
)
|
||||
# Mocks the response for getting the history of the intercom
|
||||
mock.get(
|
||||
"https://api.ring.com/clients_api/doorbots/185036587/history",
|
||||
text=load_fixture("intercom_history.json", "ring"),
|
||||
)
|
||||
yield mock
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
"""Module for ring device mocks.
|
||||
|
||||
Creates a MagicMock for all device families, i.e. chimes, doorbells, stickup_cams and other.
|
||||
|
||||
Each device entry in the devices.json will have a MagicMock instead of the RingObject.
|
||||
|
||||
Mocks the api calls on the devices such as history() and health().
|
||||
"""
|
||||
|
||||
from copy import deepcopy
|
||||
from datetime import datetime
|
||||
from time import time
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ring_doorbell import (
|
||||
RingCapability,
|
||||
RingChime,
|
||||
RingDoorBell,
|
||||
RingOther,
|
||||
RingStickUpCam,
|
||||
)
|
||||
|
||||
from homeassistant.components.ring.const import DOMAIN
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from tests.common import load_json_value_fixture
|
||||
|
||||
DEVICES_FIXTURE = load_json_value_fixture("devices.json", DOMAIN)
|
||||
DOORBOT_HISTORY = load_json_value_fixture("doorbot_history.json", DOMAIN)
|
||||
INTERCOM_HISTORY = load_json_value_fixture("intercom_history.json", DOMAIN)
|
||||
DOORBOT_HEALTH = load_json_value_fixture("doorbot_health_attrs.json", DOMAIN)
|
||||
CHIME_HEALTH = load_json_value_fixture("chime_health_attrs.json", DOMAIN)
|
||||
DEVICE_ALERTS = load_json_value_fixture("ding_active.json", DOMAIN)
|
||||
|
||||
|
||||
def get_mock_devices():
|
||||
"""Return list of mock devices keyed by device_type."""
|
||||
devices = {}
|
||||
for device_family, device_class in DEVICE_TYPES.items():
|
||||
devices[device_family] = [
|
||||
_mocked_ring_device(
|
||||
device, device_family, device_class, DEVICE_CAPABILITIES[device_class]
|
||||
)
|
||||
for device in DEVICES_FIXTURE[device_family]
|
||||
]
|
||||
return devices
|
||||
|
||||
|
||||
def get_devices_data():
|
||||
"""Return devices raw json used by the diagnostics module."""
|
||||
return {
|
||||
device_type: {obj["id"]: obj for obj in devices}
|
||||
for device_type, devices in DEVICES_FIXTURE.items()
|
||||
}
|
||||
|
||||
|
||||
def get_active_alerts():
|
||||
"""Return active alerts set to now."""
|
||||
dings_fixture = deepcopy(DEVICE_ALERTS)
|
||||
for ding in dings_fixture:
|
||||
ding["now"] = time()
|
||||
return dings_fixture
|
||||
|
||||
|
||||
DEVICE_TYPES = {
|
||||
"doorbots": RingDoorBell,
|
||||
"authorized_doorbots": RingDoorBell,
|
||||
"stickup_cams": RingStickUpCam,
|
||||
"chimes": RingChime,
|
||||
"other": RingOther,
|
||||
}
|
||||
|
||||
DEVICE_CAPABILITIES = {
|
||||
RingDoorBell: [
|
||||
RingCapability.BATTERY,
|
||||
RingCapability.VOLUME,
|
||||
RingCapability.MOTION_DETECTION,
|
||||
RingCapability.VIDEO,
|
||||
RingCapability.HISTORY,
|
||||
],
|
||||
RingStickUpCam: [
|
||||
RingCapability.BATTERY,
|
||||
RingCapability.VOLUME,
|
||||
RingCapability.MOTION_DETECTION,
|
||||
RingCapability.VIDEO,
|
||||
RingCapability.HISTORY,
|
||||
RingCapability.SIREN,
|
||||
RingCapability.LIGHT,
|
||||
],
|
||||
RingChime: [RingCapability.VOLUME],
|
||||
RingOther: [RingCapability.OPEN, RingCapability.HISTORY],
|
||||
}
|
||||
|
||||
|
||||
def _mocked_ring_device(device_dict, device_family, device_class, capabilities):
|
||||
"""Return a mocked device."""
|
||||
mock_device = MagicMock(spec=device_class, name=f"Mocked {device_family!s}")
|
||||
|
||||
def has_capability(capability):
|
||||
return (
|
||||
capability in capabilities
|
||||
if isinstance(capability, RingCapability)
|
||||
else RingCapability.from_name(capability) in capabilities
|
||||
)
|
||||
|
||||
def update_health_data(fixture):
|
||||
mock_device.configure_mock(
|
||||
wifi_signal_category=fixture["device_health"].get("latest_signal_category"),
|
||||
wifi_signal_strength=fixture["device_health"].get("latest_signal_strength"),
|
||||
)
|
||||
|
||||
def update_history_data(fixture):
|
||||
for entry in fixture: # Mimic the api date parsing
|
||||
if isinstance(entry["created_at"], str):
|
||||
dt_at = datetime.strptime(entry["created_at"], "%Y-%m-%dT%H:%M:%S.%f%z")
|
||||
entry["created_at"] = dt_util.as_utc(dt_at)
|
||||
mock_device.configure_mock(last_history=fixture) # Set last_history
|
||||
return fixture
|
||||
|
||||
# Configure the device attributes
|
||||
mock_device.configure_mock(**device_dict)
|
||||
|
||||
# Configure the Properties on the device
|
||||
mock_device.configure_mock(
|
||||
model=device_family,
|
||||
device_api_id=device_dict["id"],
|
||||
name=device_dict["description"],
|
||||
wifi_signal_category=None,
|
||||
wifi_signal_strength=None,
|
||||
family=device_family,
|
||||
)
|
||||
|
||||
# Configure common methods
|
||||
mock_device.has_capability.side_effect = has_capability
|
||||
mock_device.update_health_data.side_effect = lambda: update_health_data(
|
||||
DOORBOT_HEALTH if device_family != "chimes" else CHIME_HEALTH
|
||||
)
|
||||
# Configure methods based on capability
|
||||
if has_capability(RingCapability.HISTORY):
|
||||
mock_device.configure_mock(last_history=[])
|
||||
mock_device.history.side_effect = lambda *_, **__: update_history_data(
|
||||
DOORBOT_HISTORY if device_family != "other" else INTERCOM_HISTORY
|
||||
)
|
||||
|
||||
if has_capability(RingCapability.MOTION_DETECTION):
|
||||
mock_device.configure_mock(
|
||||
motion_detection=device_dict["settings"].get("motion_detection_enabled"),
|
||||
)
|
||||
|
||||
if has_capability(RingCapability.LIGHT):
|
||||
mock_device.configure_mock(lights=device_dict.get("led_status"))
|
||||
|
||||
if has_capability(RingCapability.VOLUME):
|
||||
mock_device.configure_mock(
|
||||
volume=device_dict["settings"].get(
|
||||
"doorbell_volume", device_dict["settings"].get("volume")
|
||||
)
|
||||
)
|
||||
|
||||
if has_capability(RingCapability.SIREN):
|
||||
mock_device.configure_mock(
|
||||
siren=device_dict["siren_status"].get("seconds_remaining")
|
||||
)
|
||||
|
||||
if has_capability(RingCapability.BATTERY):
|
||||
mock_device.configure_mock(
|
||||
battery_life=min(
|
||||
100, device_dict.get("battery_life", device_dict.get("battery_life2"))
|
||||
)
|
||||
)
|
||||
|
||||
if device_family == "other":
|
||||
mock_device.configure_mock(
|
||||
doorbell_volume=device_dict["settings"].get("doorbell_volume"),
|
||||
mic_volume=device_dict["settings"].get("mic_volume"),
|
||||
voice_volume=device_dict["settings"].get("voice_volume"),
|
||||
)
|
||||
|
||||
return mock_device
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"authorized_doorbots": [],
|
||||
"chimes": [
|
||||
{
|
||||
"address": "123 Main St",
|
||||
"alerts": { "connection": "online" },
|
||||
"description": "Downstairs",
|
||||
"device_id": "abcdef123",
|
||||
"do_not_disturb": { "seconds_left": 0 },
|
||||
"features": { "ringtones_enabled": true },
|
||||
"firmware_version": "1.2.3",
|
||||
"id": 123456,
|
||||
"kind": "chime",
|
||||
"latitude": 12.0,
|
||||
"longitude": -70.12345,
|
||||
"owned": true,
|
||||
"owner": {
|
||||
"email": "foo@bar.org",
|
||||
"first_name": "Marcelo",
|
||||
"id": 999999,
|
||||
"last_name": "Assistant"
|
||||
},
|
||||
"settings": {
|
||||
"ding_audio_id": null,
|
||||
"ding_audio_user_id": null,
|
||||
"motion_audio_id": null,
|
||||
"motion_audio_user_id": null,
|
||||
"volume": 2
|
||||
},
|
||||
"time_zone": "America/New_York"
|
||||
}
|
||||
],
|
||||
"doorbots": [],
|
||||
"stickup_cams": []
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
"address": "123 Main St",
|
||||
"alerts": { "connection": "online" },
|
||||
"description": "Downstairs",
|
||||
"device_id": "abcdef123",
|
||||
"device_id": "abcdef123456",
|
||||
"do_not_disturb": { "seconds_left": 0 },
|
||||
"features": { "ringtones_enabled": true },
|
||||
"firmware_version": "1.2.3",
|
||||
|
@ -36,7 +36,7 @@
|
|||
"alerts": { "connection": "online" },
|
||||
"battery_life": 4081,
|
||||
"description": "Front Door",
|
||||
"device_id": "aacdef123",
|
||||
"device_id": "aacdef987654",
|
||||
"external_connection": false,
|
||||
"features": {
|
||||
"advanced_motion_enabled": false,
|
||||
|
@ -85,7 +85,7 @@
|
|||
"alerts": { "connection": "online" },
|
||||
"battery_life": 80,
|
||||
"description": "Front",
|
||||
"device_id": "aacdef123",
|
||||
"device_id": "aacdef765432",
|
||||
"external_connection": false,
|
||||
"features": {
|
||||
"advanced_motion_enabled": false,
|
||||
|
@ -234,7 +234,7 @@
|
|||
"alerts": { "connection": "online" },
|
||||
"battery_life": 80,
|
||||
"description": "Internal",
|
||||
"device_id": "aacdef124",
|
||||
"device_id": "aacdef345678",
|
||||
"external_connection": false,
|
||||
"features": {
|
||||
"advanced_motion_enabled": false,
|
||||
|
@ -395,7 +395,7 @@
|
|||
"last_name": "",
|
||||
"email": ""
|
||||
},
|
||||
"device_id": "124ba1b3fe1a",
|
||||
"device_id": "abcdef185036587",
|
||||
"time_zone": "Europe/Rome",
|
||||
"firmware_version": "Up to Date",
|
||||
"owned": true,
|
||||
|
|
|
@ -1,382 +0,0 @@
|
|||
{
|
||||
"authorized_doorbots": [],
|
||||
"chimes": [
|
||||
{
|
||||
"address": "123 Main St",
|
||||
"alerts": { "connection": "online" },
|
||||
"description": "Downstairs",
|
||||
"device_id": "abcdef123",
|
||||
"do_not_disturb": { "seconds_left": 0 },
|
||||
"features": { "ringtones_enabled": true },
|
||||
"firmware_version": "1.2.3",
|
||||
"id": 123456,
|
||||
"kind": "chime",
|
||||
"latitude": 12.0,
|
||||
"longitude": -70.12345,
|
||||
"owned": true,
|
||||
"owner": {
|
||||
"email": "foo@bar.org",
|
||||
"first_name": "Marcelo",
|
||||
"id": 999999,
|
||||
"last_name": "Assistant"
|
||||
},
|
||||
"settings": {
|
||||
"ding_audio_id": null,
|
||||
"ding_audio_user_id": null,
|
||||
"motion_audio_id": null,
|
||||
"motion_audio_user_id": null,
|
||||
"volume": 2
|
||||
},
|
||||
"time_zone": "America/New_York"
|
||||
}
|
||||
],
|
||||
"doorbots": [
|
||||
{
|
||||
"address": "123 Main St",
|
||||
"alerts": { "connection": "online" },
|
||||
"battery_life": 4081,
|
||||
"description": "Front Door",
|
||||
"device_id": "aacdef123",
|
||||
"external_connection": false,
|
||||
"features": {
|
||||
"advanced_motion_enabled": false,
|
||||
"motion_message_enabled": false,
|
||||
"motions_enabled": true,
|
||||
"people_only_enabled": false,
|
||||
"shadow_correction_enabled": false,
|
||||
"show_recordings": true
|
||||
},
|
||||
"firmware_version": "1.4.26",
|
||||
"id": 987654,
|
||||
"kind": "lpd_v1",
|
||||
"latitude": 12.0,
|
||||
"longitude": -70.12345,
|
||||
"motion_snooze": null,
|
||||
"owned": true,
|
||||
"owner": {
|
||||
"email": "foo@bar.org",
|
||||
"first_name": "Home",
|
||||
"id": 999999,
|
||||
"last_name": "Assistant"
|
||||
},
|
||||
"settings": {
|
||||
"chime_settings": {
|
||||
"duration": 3,
|
||||
"enable": true,
|
||||
"type": 0
|
||||
},
|
||||
"doorbell_volume": 1,
|
||||
"enable_vod": true,
|
||||
"live_view_preset_profile": "highest",
|
||||
"live_view_presets": ["low", "middle", "high", "highest"],
|
||||
"motion_detection_enabled": true,
|
||||
"motion_announcement": false,
|
||||
"motion_snooze_preset_profile": "low",
|
||||
"motion_snooze_presets": ["null", "low", "medium", "high"]
|
||||
},
|
||||
"subscribed": true,
|
||||
"subscribed_motions": true,
|
||||
"time_zone": "America/New_York"
|
||||
}
|
||||
],
|
||||
"stickup_cams": [
|
||||
{
|
||||
"address": "123 Main St",
|
||||
"alerts": { "connection": "online" },
|
||||
"battery_life": 80,
|
||||
"description": "Front",
|
||||
"device_id": "aacdef123",
|
||||
"external_connection": false,
|
||||
"features": {
|
||||
"advanced_motion_enabled": false,
|
||||
"motion_message_enabled": false,
|
||||
"motions_enabled": true,
|
||||
"night_vision_enabled": false,
|
||||
"people_only_enabled": false,
|
||||
"shadow_correction_enabled": false,
|
||||
"show_recordings": true
|
||||
},
|
||||
"firmware_version": "1.9.3",
|
||||
"id": 765432,
|
||||
"kind": "hp_cam_v1",
|
||||
"latitude": 12.0,
|
||||
"led_status": "on",
|
||||
"location_id": null,
|
||||
"longitude": -70.12345,
|
||||
"motion_snooze": { "scheduled": true },
|
||||
"night_mode_status": "false",
|
||||
"owned": true,
|
||||
"owner": {
|
||||
"email": "foo@bar.org",
|
||||
"first_name": "Foo",
|
||||
"id": 999999,
|
||||
"last_name": "Bar"
|
||||
},
|
||||
"ring_cam_light_installed": "false",
|
||||
"ring_id": null,
|
||||
"settings": {
|
||||
"chime_settings": {
|
||||
"duration": 10,
|
||||
"enable": true,
|
||||
"type": 0
|
||||
},
|
||||
"doorbell_volume": 11,
|
||||
"enable_vod": true,
|
||||
"floodlight_settings": {
|
||||
"duration": 30,
|
||||
"priority": 0
|
||||
},
|
||||
"light_schedule_settings": {
|
||||
"end_hour": 0,
|
||||
"end_minute": 0,
|
||||
"start_hour": 0,
|
||||
"start_minute": 0
|
||||
},
|
||||
"live_view_preset_profile": "highest",
|
||||
"live_view_presets": ["low", "middle", "high", "highest"],
|
||||
"motion_detection_enabled": true,
|
||||
"motion_announcement": false,
|
||||
"motion_snooze_preset_profile": "low",
|
||||
"motion_snooze_presets": ["none", "low", "medium", "high"],
|
||||
"motion_zones": {
|
||||
"active_motion_filter": 1,
|
||||
"advanced_object_settings": {
|
||||
"human_detection_confidence": {
|
||||
"day": 0.7,
|
||||
"night": 0.7
|
||||
},
|
||||
"motion_zone_overlap": {
|
||||
"day": 0.1,
|
||||
"night": 0.2
|
||||
},
|
||||
"object_size_maximum": {
|
||||
"day": 0.8,
|
||||
"night": 0.8
|
||||
},
|
||||
"object_size_minimum": {
|
||||
"day": 0.03,
|
||||
"night": 0.05
|
||||
},
|
||||
"object_time_overlap": {
|
||||
"day": 0.1,
|
||||
"night": 0.6
|
||||
}
|
||||
},
|
||||
"enable_audio": false,
|
||||
"pir_settings": {
|
||||
"sensitivity1": 1,
|
||||
"sensitivity2": 1,
|
||||
"sensitivity3": 1,
|
||||
"zone_mask": 6
|
||||
},
|
||||
"sensitivity": 5,
|
||||
"zone1": {
|
||||
"name": "Zone 1",
|
||||
"state": 2,
|
||||
"vertex1": { "x": 0.0, "y": 0.0 },
|
||||
"vertex2": { "x": 0.0, "y": 0.0 },
|
||||
"vertex3": { "x": 0.0, "y": 0.0 },
|
||||
"vertex4": { "x": 0.0, "y": 0.0 },
|
||||
"vertex5": { "x": 0.0, "y": 0.0 },
|
||||
"vertex6": { "x": 0.0, "y": 0.0 },
|
||||
"vertex7": { "x": 0.0, "y": 0.0 },
|
||||
"vertex8": { "x": 0.0, "y": 0.0 }
|
||||
},
|
||||
"zone2": {
|
||||
"name": "Zone 2",
|
||||
"state": 2,
|
||||
"vertex1": { "x": 0.0, "y": 0.0 },
|
||||
"vertex2": { "x": 0.0, "y": 0.0 },
|
||||
"vertex3": { "x": 0.0, "y": 0.0 },
|
||||
"vertex4": { "x": 0.0, "y": 0.0 },
|
||||
"vertex5": { "x": 0.0, "y": 0.0 },
|
||||
"vertex6": { "x": 0.0, "y": 0.0 },
|
||||
"vertex7": { "x": 0.0, "y": 0.0 },
|
||||
"vertex8": { "x": 0.0, "y": 0.0 }
|
||||
},
|
||||
"zone3": {
|
||||
"name": "Zone 3",
|
||||
"state": 2,
|
||||
"vertex1": { "x": 0.0, "y": 0.0 },
|
||||
"vertex2": { "x": 0.0, "y": 0.0 },
|
||||
"vertex3": { "x": 0.0, "y": 0.0 },
|
||||
"vertex4": { "x": 0.0, "y": 0.0 },
|
||||
"vertex5": { "x": 0.0, "y": 0.0 },
|
||||
"vertex6": { "x": 0.0, "y": 0.0 },
|
||||
"vertex7": { "x": 0.0, "y": 0.0 },
|
||||
"vertex8": { "x": 0.0, "y": 0.0 }
|
||||
}
|
||||
},
|
||||
"pir_motion_zones": [0, 1, 1],
|
||||
"pir_settings": {
|
||||
"sensitivity1": 1,
|
||||
"sensitivity2": 1,
|
||||
"sensitivity3": 1,
|
||||
"zone_mask": 6
|
||||
},
|
||||
"stream_setting": 0,
|
||||
"video_settings": {
|
||||
"ae_level": 0,
|
||||
"birton": null,
|
||||
"brightness": 0,
|
||||
"contrast": 64,
|
||||
"saturation": 80
|
||||
}
|
||||
},
|
||||
"siren_status": { "seconds_remaining": 30 },
|
||||
"stolen": false,
|
||||
"subscribed": true,
|
||||
"subscribed_motions": true,
|
||||
"time_zone": "America/New_York"
|
||||
},
|
||||
{
|
||||
"address": "123 Main St",
|
||||
"alerts": { "connection": "online" },
|
||||
"battery_life": 80,
|
||||
"description": "Internal",
|
||||
"device_id": "aacdef124",
|
||||
"external_connection": false,
|
||||
"features": {
|
||||
"advanced_motion_enabled": false,
|
||||
"motion_message_enabled": false,
|
||||
"motions_enabled": true,
|
||||
"night_vision_enabled": false,
|
||||
"people_only_enabled": false,
|
||||
"shadow_correction_enabled": false,
|
||||
"show_recordings": true
|
||||
},
|
||||
"firmware_version": "1.9.3",
|
||||
"id": 345678,
|
||||
"kind": "hp_cam_v1",
|
||||
"latitude": 12.0,
|
||||
"led_status": "off",
|
||||
"location_id": null,
|
||||
"longitude": -70.12345,
|
||||
"motion_snooze": { "scheduled": true },
|
||||
"night_mode_status": "false",
|
||||
"owned": true,
|
||||
"owner": {
|
||||
"email": "foo@bar.org",
|
||||
"first_name": "Foo",
|
||||
"id": 999999,
|
||||
"last_name": "Bar"
|
||||
},
|
||||
"ring_cam_light_installed": "false",
|
||||
"ring_id": null,
|
||||
"settings": {
|
||||
"chime_settings": {
|
||||
"duration": 10,
|
||||
"enable": true,
|
||||
"type": 0
|
||||
},
|
||||
"doorbell_volume": 11,
|
||||
"enable_vod": true,
|
||||
"floodlight_settings": {
|
||||
"duration": 30,
|
||||
"priority": 0
|
||||
},
|
||||
"light_schedule_settings": {
|
||||
"end_hour": 0,
|
||||
"end_minute": 0,
|
||||
"start_hour": 0,
|
||||
"start_minute": 0
|
||||
},
|
||||
"live_view_preset_profile": "highest",
|
||||
"live_view_presets": ["low", "middle", "high", "highest"],
|
||||
"motion_detection_enabled": false,
|
||||
"motion_announcement": false,
|
||||
"motion_snooze_preset_profile": "low",
|
||||
"motion_snooze_presets": ["none", "low", "medium", "high"],
|
||||
"motion_zones": {
|
||||
"active_motion_filter": 1,
|
||||
"advanced_object_settings": {
|
||||
"human_detection_confidence": {
|
||||
"day": 0.7,
|
||||
"night": 0.7
|
||||
},
|
||||
"motion_zone_overlap": {
|
||||
"day": 0.1,
|
||||
"night": 0.2
|
||||
},
|
||||
"object_size_maximum": {
|
||||
"day": 0.8,
|
||||
"night": 0.8
|
||||
},
|
||||
"object_size_minimum": {
|
||||
"day": 0.03,
|
||||
"night": 0.05
|
||||
},
|
||||
"object_time_overlap": {
|
||||
"day": 0.1,
|
||||
"night": 0.6
|
||||
}
|
||||
},
|
||||
"enable_audio": false,
|
||||
"pir_settings": {
|
||||
"sensitivity1": 1,
|
||||
"sensitivity2": 1,
|
||||
"sensitivity3": 1,
|
||||
"zone_mask": 6
|
||||
},
|
||||
"sensitivity": 5,
|
||||
"zone1": {
|
||||
"name": "Zone 1",
|
||||
"state": 2,
|
||||
"vertex1": { "x": 0.0, "y": 0.0 },
|
||||
"vertex2": { "x": 0.0, "y": 0.0 },
|
||||
"vertex3": { "x": 0.0, "y": 0.0 },
|
||||
"vertex4": { "x": 0.0, "y": 0.0 },
|
||||
"vertex5": { "x": 0.0, "y": 0.0 },
|
||||
"vertex6": { "x": 0.0, "y": 0.0 },
|
||||
"vertex7": { "x": 0.0, "y": 0.0 },
|
||||
"vertex8": { "x": 0.0, "y": 0.0 }
|
||||
},
|
||||
"zone2": {
|
||||
"name": "Zone 2",
|
||||
"state": 2,
|
||||
"vertex1": { "x": 0.0, "y": 0.0 },
|
||||
"vertex2": { "x": 0.0, "y": 0.0 },
|
||||
"vertex3": { "x": 0.0, "y": 0.0 },
|
||||
"vertex4": { "x": 0.0, "y": 0.0 },
|
||||
"vertex5": { "x": 0.0, "y": 0.0 },
|
||||
"vertex6": { "x": 0.0, "y": 0.0 },
|
||||
"vertex7": { "x": 0.0, "y": 0.0 },
|
||||
"vertex8": { "x": 0.0, "y": 0.0 }
|
||||
},
|
||||
"zone3": {
|
||||
"name": "Zone 3",
|
||||
"state": 2,
|
||||
"vertex1": { "x": 0.0, "y": 0.0 },
|
||||
"vertex2": { "x": 0.0, "y": 0.0 },
|
||||
"vertex3": { "x": 0.0, "y": 0.0 },
|
||||
"vertex4": { "x": 0.0, "y": 0.0 },
|
||||
"vertex5": { "x": 0.0, "y": 0.0 },
|
||||
"vertex6": { "x": 0.0, "y": 0.0 },
|
||||
"vertex7": { "x": 0.0, "y": 0.0 },
|
||||
"vertex8": { "x": 0.0, "y": 0.0 }
|
||||
}
|
||||
},
|
||||
"pir_motion_zones": [0, 1, 1],
|
||||
"pir_settings": {
|
||||
"sensitivity1": 1,
|
||||
"sensitivity2": 1,
|
||||
"sensitivity3": 1,
|
||||
"zone_mask": 6
|
||||
},
|
||||
"stream_setting": 0,
|
||||
"video_settings": {
|
||||
"ae_level": 0,
|
||||
"birton": null,
|
||||
"brightness": 0,
|
||||
"contrast": 64,
|
||||
"saturation": 80
|
||||
}
|
||||
},
|
||||
"siren_status": { "seconds_remaining": 30 },
|
||||
"stolen": false,
|
||||
"subscribed": true,
|
||||
"subscribed_motions": true,
|
||||
"time_zone": "America/New_York"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -24,5 +24,12 @@
|
|||
"snapshot_url": "",
|
||||
"state": "ringing",
|
||||
"video_jitter_buffer_ms": 0
|
||||
},
|
||||
{
|
||||
"kind": "motion",
|
||||
"doorbot_id": 987654,
|
||||
"state": "ringing",
|
||||
"now": 1490949469.5498993,
|
||||
"expires_in": 180
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
[
|
||||
{
|
||||
"answered": false,
|
||||
"created_at": "2018-03-05T15:03:40.000Z",
|
||||
"events": [],
|
||||
"favorite": false,
|
||||
"id": 987654321,
|
||||
"kind": "ding",
|
||||
"recording": { "status": "ready" },
|
||||
"snapshot_url": ""
|
||||
},
|
||||
{
|
||||
"answered": false,
|
||||
"created_at": "2017-03-05T15:03:40.000Z",
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"started_at": "2019-07-28T16:58:27.593+00:00",
|
||||
"duration": 30,
|
||||
"ends_at": "2019-07-28T16:58:57.593+00:00",
|
||||
"seconds_remaining": 30
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"device_groups": [
|
||||
{
|
||||
"device_group_id": "mock-group-id",
|
||||
"location_id": "mock-location-id",
|
||||
"name": "Landscape",
|
||||
"devices": [
|
||||
{
|
||||
"doorbot_id": 12345678,
|
||||
"location_id": "mock-location-id",
|
||||
"type": "beams_ct200_transformer",
|
||||
"mac_address": null,
|
||||
"hardware_id": "1234567890",
|
||||
"name": "Mock Transformer",
|
||||
"deleted_at": null
|
||||
}
|
||||
],
|
||||
"created_at": "2020-11-03T22:07:05Z",
|
||||
"updated_at": "2020-11-19T03:52:59Z",
|
||||
"deleted_at": null,
|
||||
"external_id": "12345678-1234-5678-90ab-1234567890ab"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"access_token": "eyJ0eWfvEQwqfJNKyQ9999",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 3600,
|
||||
"refresh_token": "67695a26bdefc1ac8999",
|
||||
"scope": "client",
|
||||
"created_at": 1529099870
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"profile": {
|
||||
"authentication_token": "12345678910",
|
||||
"email": "foo@bar.org",
|
||||
"features": {
|
||||
"chime_dnd_enabled": false,
|
||||
"chime_pro_enabled": true,
|
||||
"delete_all_enabled": true,
|
||||
"delete_all_settings_enabled": false,
|
||||
"device_health_alerts_enabled": true,
|
||||
"floodlight_cam_enabled": true,
|
||||
"live_view_settings_enabled": true,
|
||||
"lpd_enabled": true,
|
||||
"lpd_motion_announcement_enabled": false,
|
||||
"multiple_calls_enabled": true,
|
||||
"multiple_delete_enabled": true,
|
||||
"nw_enabled": true,
|
||||
"nw_larger_area_enabled": false,
|
||||
"nw_user_activated": false,
|
||||
"owner_proactive_snoozing_enabled": true,
|
||||
"power_cable_enabled": false,
|
||||
"proactive_snoozing_enabled": false,
|
||||
"reactive_snoozing_enabled": false,
|
||||
"remote_logging_format_storing": false,
|
||||
"remote_logging_level": 1,
|
||||
"ringplus_enabled": true,
|
||||
"starred_events_enabled": true,
|
||||
"stickupcam_setup_enabled": true,
|
||||
"subscriptions_enabled": true,
|
||||
"ujet_enabled": false,
|
||||
"video_search_enabled": false,
|
||||
"vod_enabled": false
|
||||
},
|
||||
"first_name": "Home",
|
||||
"id": 999999,
|
||||
"last_name": "Assistant"
|
||||
}
|
||||
}
|
|
@ -1,32 +1,14 @@
|
|||
"""The tests for the Ring binary sensor platform."""
|
||||
|
||||
from time import time
|
||||
from unittest.mock import patch
|
||||
|
||||
import requests_mock
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import setup_platform
|
||||
|
||||
|
||||
async def test_binary_sensor(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
) -> None:
|
||||
async def test_binary_sensor(hass: HomeAssistant, mock_ring_client) -> None:
|
||||
"""Test the Ring binary sensors."""
|
||||
with patch(
|
||||
"ring_doorbell.Ring.active_alerts",
|
||||
return_value=[
|
||||
{
|
||||
"kind": "motion",
|
||||
"doorbot_id": 987654,
|
||||
"state": "ringing",
|
||||
"now": time(),
|
||||
"expires_in": 180,
|
||||
}
|
||||
],
|
||||
):
|
||||
await setup_platform(hass, "binary_sensor")
|
||||
await setup_platform(hass, Platform.BINARY_SENSOR)
|
||||
|
||||
motion_state = hass.states.get("binary_sensor.front_door_motion")
|
||||
assert motion_state is not None
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
"""The tests for the Ring button platform."""
|
||||
|
||||
import requests_mock
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
@ -11,7 +9,7 @@ from .common import setup_platform
|
|||
|
||||
async def test_entity_registry(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
|
@ -22,21 +20,19 @@ async def test_entity_registry(
|
|||
|
||||
|
||||
async def test_button_opens_door(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
) -> None:
|
||||
"""Tests the door open button works correctly."""
|
||||
await setup_platform(hass, Platform.BUTTON)
|
||||
|
||||
# Mocks the response for opening door
|
||||
mock = requests_mock.put(
|
||||
"https://api.ring.com/commands/v1/devices/185036587/device_rpc",
|
||||
status_code=200,
|
||||
text="{}",
|
||||
)
|
||||
mock_intercom = mock_ring_devices.get_device(185036587)
|
||||
mock_intercom.open_door.assert_not_called()
|
||||
|
||||
await hass.services.async_call(
|
||||
"button", "press", {"entity_id": "button.ingress_open_door"}, blocking=True
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_count == 1
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
mock_intercom.open_door.assert_called_once()
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
"""The tests for the Ring switch platform."""
|
||||
|
||||
from unittest.mock import PropertyMock, patch
|
||||
from unittest.mock import PropertyMock
|
||||
|
||||
import pytest
|
||||
import requests_mock
|
||||
import ring_doorbell
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
|
@ -14,13 +13,11 @@ from homeassistant.helpers import entity_registry as er
|
|||
|
||||
from .common import setup_platform
|
||||
|
||||
from tests.common import load_fixture
|
||||
|
||||
|
||||
async def test_entity_registry(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, Platform.CAMERA)
|
||||
|
@ -42,7 +39,7 @@ async def test_entity_registry(
|
|||
)
|
||||
async def test_camera_motion_detection_state_reports_correctly(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
entity_name,
|
||||
expected_state,
|
||||
friendly_name,
|
||||
|
@ -56,7 +53,7 @@ async def test_camera_motion_detection_state_reports_correctly(
|
|||
|
||||
|
||||
async def test_camera_motion_detection_can_be_turned_on(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client
|
||||
) -> None:
|
||||
"""Tests the siren turns on correctly."""
|
||||
await setup_platform(hass, Platform.CAMERA)
|
||||
|
@ -78,17 +75,15 @@ async def test_camera_motion_detection_can_be_turned_on(
|
|||
|
||||
|
||||
async def test_updates_work(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client, mock_ring_devices
|
||||
) -> None:
|
||||
"""Tests the update service works correctly."""
|
||||
await setup_platform(hass, Platform.CAMERA)
|
||||
state = hass.states.get("camera.internal")
|
||||
assert state.attributes.get("motion_detection") is True
|
||||
# Changes the return to indicate that the switch is now on.
|
||||
requests_mock.get(
|
||||
"https://api.ring.com/clients_api/ring_devices",
|
||||
text=load_fixture("devices_updated.json", "ring"),
|
||||
)
|
||||
|
||||
internal_camera_mock = mock_ring_devices.get_device(345678)
|
||||
internal_camera_mock.motion_detection = False
|
||||
|
||||
await hass.services.async_call("ring", "update", {}, blocking=True)
|
||||
|
||||
|
@ -109,7 +104,8 @@ async def test_updates_work(
|
|||
)
|
||||
async def test_motion_detection_errors_when_turned_on(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
exception_type,
|
||||
reauth_expected,
|
||||
) -> None:
|
||||
|
@ -119,19 +115,19 @@ async def test_motion_detection_errors_when_turned_on(
|
|||
|
||||
assert not any(config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
|
||||
with patch.object(
|
||||
ring_doorbell.RingDoorBell, "motion_detection", new_callable=PropertyMock
|
||||
) as mock_motion_detection:
|
||||
mock_motion_detection.side_effect = exception_type
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"camera",
|
||||
"enable_motion_detection",
|
||||
{"entity_id": "camera.front"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_motion_detection.call_count == 1
|
||||
front_camera_mock = mock_ring_devices.get_device(765432)
|
||||
p = PropertyMock(side_effect=exception_type)
|
||||
type(front_camera_mock).motion_detection = p
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"camera",
|
||||
"enable_motion_detection",
|
||||
{"entity_id": "camera.front"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
p.assert_called_once()
|
||||
assert (
|
||||
any(
|
||||
flow
|
||||
|
|
|
@ -17,7 +17,7 @@ from tests.common import MockConfigEntry
|
|||
async def test_form(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_ring_auth: Mock,
|
||||
mock_ring_client: Mock,
|
||||
) -> None:
|
||||
"""Test we get the form."""
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
"""Test Ring diagnostics."""
|
||||
|
||||
import requests_mock
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
@ -14,7 +13,7 @@ async def test_entry_diagnostics(
|
|||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test Ring diagnostics."""
|
||||
|
|
|
@ -1,67 +1,69 @@
|
|||
"""The tests for the Ring component."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
import requests_mock
|
||||
from ring_doorbell import AuthenticationError, RingError, RingTimeout
|
||||
|
||||
from homeassistant.components import ring
|
||||
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
||||
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||
from homeassistant.components.ring import DOMAIN
|
||||
from homeassistant.components.ring.const import SCAN_INTERVAL
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er, issue_registry as ir
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def test_setup(hass: HomeAssistant, requests_mock: requests_mock.Mocker) -> None:
|
||||
async def test_setup(hass: HomeAssistant, mock_ring_client) -> None:
|
||||
"""Test the setup."""
|
||||
await async_setup_component(hass, ring.DOMAIN, {})
|
||||
|
||||
requests_mock.post(
|
||||
"https://oauth.ring.com/oauth/token", text=load_fixture("oauth.json", "ring")
|
||||
)
|
||||
requests_mock.post(
|
||||
"https://api.ring.com/clients_api/session",
|
||||
text=load_fixture("session.json", "ring"),
|
||||
)
|
||||
requests_mock.get(
|
||||
"https://api.ring.com/clients_api/ring_devices",
|
||||
text=load_fixture("devices.json", "ring"),
|
||||
)
|
||||
requests_mock.get(
|
||||
"https://api.ring.com/clients_api/chimes/999999/health",
|
||||
text=load_fixture("chime_health_attrs.json", "ring"),
|
||||
)
|
||||
requests_mock.get(
|
||||
"https://api.ring.com/clients_api/doorbots/987652/health",
|
||||
text=load_fixture("doorboot_health_attrs.json", "ring"),
|
||||
)
|
||||
|
||||
async def test_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
mock_ring_client,
|
||||
mock_added_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test setup entry."""
|
||||
assert mock_added_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
|
||||
async def test_setup_entry_device_update(
|
||||
hass: HomeAssistant,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_added_config_entry: MockConfigEntry,
|
||||
caplog,
|
||||
) -> None:
|
||||
"""Test devices are updating after setup entry."""
|
||||
|
||||
front_door_doorbell = mock_ring_devices.get_device(987654)
|
||||
front_door_doorbell.history.assert_not_called()
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
front_door_doorbell.history.assert_called_once()
|
||||
|
||||
|
||||
async def test_auth_failed_on_setup(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test auth failure on setup entry."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"ring_doorbell.Ring.update_data",
|
||||
side_effect=AuthenticationError,
|
||||
):
|
||||
assert not any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
|
||||
mock_ring_client.update_data.side_effect = AuthenticationError
|
||||
|
||||
assert not any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -80,37 +82,30 @@ async def test_auth_failed_on_setup(
|
|||
)
|
||||
async def test_error_on_setup(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
error_type,
|
||||
log_msg,
|
||||
) -> None:
|
||||
"""Test auth failure on setup entry."""
|
||||
"""Test non-auth errors on setup entry."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"ring_doorbell.Ring.update_data",
|
||||
side_effect=error_type,
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||
mock_ring_client.update_data.side_effect = error_type
|
||||
|
||||
assert [
|
||||
record.message
|
||||
for record in caplog.records
|
||||
if record.levelname == "DEBUG"
|
||||
and record.name == "homeassistant.config_entries"
|
||||
and log_msg in record.message
|
||||
and DOMAIN in record.message
|
||||
]
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
assert log_msg in caplog.text
|
||||
|
||||
|
||||
async def test_auth_failure_on_global_update(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test authentication failure on global data update."""
|
||||
|
@ -118,27 +113,24 @@ async def test_auth_failure_on_global_update(
|
|||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert not any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
with patch(
|
||||
"ring_doorbell.Ring.update_devices",
|
||||
side_effect=AuthenticationError,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "Authentication failed while fetching devices data: " in [
|
||||
record.message
|
||||
for record in caplog.records
|
||||
if record.levelname == "ERROR"
|
||||
and record.name == "homeassistant.components.ring.coordinator"
|
||||
]
|
||||
mock_ring_client.update_devices.side_effect = AuthenticationError
|
||||
|
||||
assert any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "Authentication failed while fetching devices data: " in caplog.text
|
||||
|
||||
assert any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
|
||||
|
||||
async def test_auth_failure_on_device_update(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test authentication failure on device data update."""
|
||||
|
@ -146,21 +138,17 @@ async def test_auth_failure_on_device_update(
|
|||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert not any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
with patch(
|
||||
"ring_doorbell.RingDoorBell.history",
|
||||
side_effect=AuthenticationError,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
assert "Authentication failed while fetching devices data: " in [
|
||||
record.message
|
||||
for record in caplog.records
|
||||
if record.levelname == "ERROR"
|
||||
and record.name == "homeassistant.components.ring.coordinator"
|
||||
]
|
||||
front_door_doorbell = mock_ring_devices.get_device(987654)
|
||||
front_door_doorbell.history.side_effect = AuthenticationError
|
||||
|
||||
assert any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
assert "Authentication failed while fetching devices data: " in caplog.text
|
||||
|
||||
assert any(mock_config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -179,29 +167,27 @@ async def test_auth_failure_on_device_update(
|
|||
)
|
||||
async def test_error_on_global_update(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
error_type,
|
||||
log_msg,
|
||||
) -> None:
|
||||
"""Test error on global data update."""
|
||||
"""Test non-auth errors on global data update."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"ring_doorbell.Ring.update_devices",
|
||||
side_effect=error_type,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
mock_ring_client.update_devices.side_effect = error_type
|
||||
|
||||
assert log_msg in [
|
||||
record.message for record in caplog.records if record.levelname == "ERROR"
|
||||
]
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
assert mock_config_entry.entry_id in hass.data[DOMAIN]
|
||||
assert log_msg in caplog.text
|
||||
|
||||
assert mock_config_entry.entry_id in hass.data[DOMAIN]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -220,35 +206,35 @@ async def test_error_on_global_update(
|
|||
)
|
||||
async def test_error_on_device_update(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
error_type,
|
||||
log_msg,
|
||||
) -> None:
|
||||
"""Test auth failure on data update."""
|
||||
"""Test non-auth errors on device update."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"ring_doorbell.RingDoorBell.history",
|
||||
side_effect=error_type,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
front_door_doorbell = mock_ring_devices.get_device(765432)
|
||||
front_door_doorbell.history.side_effect = error_type
|
||||
|
||||
assert log_msg in [
|
||||
record.message for record in caplog.records if record.levelname == "ERROR"
|
||||
]
|
||||
assert mock_config_entry.entry_id in hass.data[DOMAIN]
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
assert log_msg in caplog.text
|
||||
assert mock_config_entry.entry_id in hass.data[DOMAIN]
|
||||
|
||||
|
||||
async def test_issue_deprecated_service_ring_update(
|
||||
hass: HomeAssistant,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the issue is raised on deprecated service ring.update."""
|
||||
|
@ -288,7 +274,7 @@ async def test_update_unique_id(
|
|||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
domain: str,
|
||||
old_unique_id: int | str,
|
||||
) -> None:
|
||||
|
@ -324,7 +310,7 @@ async def test_update_unique_id_existing(
|
|||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
) -> None:
|
||||
"""Test unique_id update of integration."""
|
||||
old_unique_id = 123456
|
||||
|
@ -372,7 +358,7 @@ async def test_update_unique_id_no_update(
|
|||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
) -> None:
|
||||
"""Test unique_id update of integration."""
|
||||
correct_unique_id = "123456"
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
"""The tests for the Ring light platform."""
|
||||
|
||||
from unittest.mock import PropertyMock, patch
|
||||
from unittest.mock import PropertyMock
|
||||
|
||||
import pytest
|
||||
import requests_mock
|
||||
import ring_doorbell
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
|
@ -14,13 +13,11 @@ from homeassistant.helpers import entity_registry as er
|
|||
|
||||
from .common import setup_platform
|
||||
|
||||
from tests.common import load_fixture
|
||||
|
||||
|
||||
async def test_entity_registry(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, Platform.LIGHT)
|
||||
|
@ -33,7 +30,7 @@ async def test_entity_registry(
|
|||
|
||||
|
||||
async def test_light_off_reports_correctly(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client
|
||||
) -> None:
|
||||
"""Tests that the initial state of a device that should be off is correct."""
|
||||
await setup_platform(hass, Platform.LIGHT)
|
||||
|
@ -44,7 +41,7 @@ async def test_light_off_reports_correctly(
|
|||
|
||||
|
||||
async def test_light_on_reports_correctly(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client
|
||||
) -> None:
|
||||
"""Tests that the initial state of a device that should be on is correct."""
|
||||
await setup_platform(hass, Platform.LIGHT)
|
||||
|
@ -54,18 +51,10 @@ async def test_light_on_reports_correctly(
|
|||
assert state.attributes.get("friendly_name") == "Internal Light"
|
||||
|
||||
|
||||
async def test_light_can_be_turned_on(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
) -> None:
|
||||
async def test_light_can_be_turned_on(hass: HomeAssistant, mock_ring_client) -> None:
|
||||
"""Tests the light turns on correctly."""
|
||||
await setup_platform(hass, Platform.LIGHT)
|
||||
|
||||
# Mocks the response for turning a light on
|
||||
requests_mock.put(
|
||||
"https://api.ring.com/clients_api/doorbots/765432/floodlight_light_on",
|
||||
text=load_fixture("doorbot_siren_on_response.json", "ring"),
|
||||
)
|
||||
|
||||
state = hass.states.get("light.front_light")
|
||||
assert state.state == "off"
|
||||
|
||||
|
@ -79,17 +68,15 @@ async def test_light_can_be_turned_on(
|
|||
|
||||
|
||||
async def test_updates_work(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client, mock_ring_devices
|
||||
) -> None:
|
||||
"""Tests the update service works correctly."""
|
||||
await setup_platform(hass, Platform.LIGHT)
|
||||
state = hass.states.get("light.front_light")
|
||||
assert state.state == "off"
|
||||
# Changes the return to indicate that the light is now on.
|
||||
requests_mock.get(
|
||||
"https://api.ring.com/clients_api/ring_devices",
|
||||
text=load_fixture("devices_updated.json", "ring"),
|
||||
)
|
||||
|
||||
front_light_mock = mock_ring_devices.get_device(765432)
|
||||
front_light_mock.lights = "on"
|
||||
|
||||
await hass.services.async_call("ring", "update", {}, blocking=True)
|
||||
|
||||
|
@ -110,7 +97,8 @@ async def test_updates_work(
|
|||
)
|
||||
async def test_light_errors_when_turned_on(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
exception_type,
|
||||
reauth_expected,
|
||||
) -> None:
|
||||
|
@ -120,16 +108,17 @@ async def test_light_errors_when_turned_on(
|
|||
|
||||
assert not any(config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
|
||||
with patch.object(
|
||||
ring_doorbell.RingStickUpCam, "lights", new_callable=PropertyMock
|
||||
) as mock_lights:
|
||||
mock_lights.side_effect = exception_type
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"light", "turn_on", {"entity_id": "light.front_light"}, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_lights.call_count == 1
|
||||
front_light_mock = mock_ring_devices.get_device(765432)
|
||||
p = PropertyMock(side_effect=exception_type)
|
||||
type(front_light_mock).lights = p
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"light", "turn_on", {"entity_id": "light.front_light"}, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
p.assert_called_once()
|
||||
|
||||
assert (
|
||||
any(
|
||||
flow
|
||||
|
|
|
@ -4,21 +4,19 @@ import logging
|
|||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
import requests_mock
|
||||
|
||||
from homeassistant.components.ring.const import SCAN_INTERVAL
|
||||
from homeassistant.components.sensor import ATTR_STATE_CLASS, SensorStateClass
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
|
||||
from tests.common import async_fire_time_changed, load_fixture
|
||||
|
||||
WIFI_ENABLED = False
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
async def test_sensor(hass: HomeAssistant, requests_mock: requests_mock.Mocker) -> None:
|
||||
async def test_sensor(hass: HomeAssistant, mock_ring_client) -> None:
|
||||
"""Test the Ring sensors."""
|
||||
await setup_platform(hass, "sensor")
|
||||
|
||||
|
@ -41,10 +39,6 @@ async def test_sensor(hass: HomeAssistant, requests_mock: requests_mock.Mocker)
|
|||
assert downstairs_volume_state is not None
|
||||
assert downstairs_volume_state.state == "2"
|
||||
|
||||
downstairs_wifi_signal_strength_state = hass.states.get(
|
||||
"sensor.downstairs_wifi_signal_strength"
|
||||
)
|
||||
|
||||
ingress_mic_volume_state = hass.states.get("sensor.ingress_mic_volume")
|
||||
assert ingress_mic_volume_state.state == "11"
|
||||
|
||||
|
@ -54,56 +48,118 @@ async def test_sensor(hass: HomeAssistant, requests_mock: requests_mock.Mocker)
|
|||
ingress_voice_volume_state = hass.states.get("sensor.ingress_voice_volume")
|
||||
assert ingress_voice_volume_state.state == "11"
|
||||
|
||||
if not WIFI_ENABLED:
|
||||
return
|
||||
|
||||
assert downstairs_wifi_signal_strength_state is not None
|
||||
assert downstairs_wifi_signal_strength_state.state == "-39"
|
||||
|
||||
front_door_wifi_signal_category_state = hass.states.get(
|
||||
"sensor.front_door_wifi_signal_category"
|
||||
)
|
||||
assert front_door_wifi_signal_category_state is not None
|
||||
assert front_door_wifi_signal_category_state.state == "good"
|
||||
|
||||
front_door_wifi_signal_strength_state = hass.states.get(
|
||||
"sensor.front_door_wifi_signal_strength"
|
||||
)
|
||||
assert front_door_wifi_signal_strength_state is not None
|
||||
assert front_door_wifi_signal_strength_state.state == "-58"
|
||||
|
||||
|
||||
async def test_history(
|
||||
@pytest.mark.parametrize(
|
||||
("device_id", "device_name", "sensor_name", "expected_value"),
|
||||
[
|
||||
(987654, "front_door", "wifi_signal_category", "good"),
|
||||
(987654, "front_door", "wifi_signal_strength", "-58"),
|
||||
(123456, "downstairs", "wifi_signal_category", "good"),
|
||||
(123456, "downstairs", "wifi_signal_strength", "-39"),
|
||||
(765432, "front", "wifi_signal_category", "good"),
|
||||
(765432, "front", "wifi_signal_strength", "-58"),
|
||||
],
|
||||
ids=[
|
||||
"doorbell-category",
|
||||
"doorbell-strength",
|
||||
"chime-category",
|
||||
"chime-strength",
|
||||
"stickup_cam-category",
|
||||
"stickup_cam-strength",
|
||||
],
|
||||
)
|
||||
async def test_health_sensor(
|
||||
hass: HomeAssistant,
|
||||
mock_ring_client,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
entity_registry: er.EntityRegistry,
|
||||
device_id,
|
||||
device_name,
|
||||
sensor_name,
|
||||
expected_value,
|
||||
) -> None:
|
||||
"""Test history derived sensors."""
|
||||
await setup_platform(hass, Platform.SENSOR)
|
||||
"""Test the Ring health sensors."""
|
||||
entity_id = f"sensor.{device_name}_{sensor_name}"
|
||||
# Enable the sensor as the health sensors are disabled by default
|
||||
entity_entry = entity_registry.async_get_or_create(
|
||||
"sensor",
|
||||
"ring",
|
||||
f"{device_id}-{sensor_name}",
|
||||
suggested_object_id=f"{device_name}_{sensor_name}",
|
||||
disabled_by=None,
|
||||
)
|
||||
assert entity_entry.disabled is False
|
||||
assert entity_entry.entity_id == entity_id
|
||||
|
||||
await setup_platform(hass, "sensor")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
sensor_state = hass.states.get(entity_id)
|
||||
assert sensor_state is not None
|
||||
assert sensor_state.state == "unknown"
|
||||
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(True)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
sensor_state = hass.states.get(entity_id)
|
||||
assert sensor_state is not None
|
||||
assert sensor_state.state == expected_value
|
||||
|
||||
front_door_last_activity_state = hass.states.get("sensor.front_door_last_activity")
|
||||
assert front_door_last_activity_state.state == "2017-03-05T15:03:40+00:00"
|
||||
|
||||
ingress_last_activity_state = hass.states.get("sensor.ingress_last_activity")
|
||||
assert ingress_last_activity_state.state == "2024-02-02T11:21:24+00:00"
|
||||
@pytest.mark.parametrize(
|
||||
("device_name", "sensor_name", "expected_value"),
|
||||
[
|
||||
("front_door", "last_motion", "2017-03-05T15:03:40+00:00"),
|
||||
("front_door", "last_ding", "2018-03-05T15:03:40+00:00"),
|
||||
("front_door", "last_activity", "2018-03-05T15:03:40+00:00"),
|
||||
("front", "last_motion", "2017-03-05T15:03:40+00:00"),
|
||||
("ingress", "last_activity", "2024-02-02T11:21:24+00:00"),
|
||||
],
|
||||
ids=[
|
||||
"doorbell-motion",
|
||||
"doorbell-ding",
|
||||
"doorbell-activity",
|
||||
"stickup_cam-motion",
|
||||
"other-activity",
|
||||
],
|
||||
)
|
||||
async def test_history_sensor(
|
||||
hass: HomeAssistant,
|
||||
mock_ring_client,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
device_name,
|
||||
sensor_name,
|
||||
expected_value,
|
||||
) -> None:
|
||||
"""Test the Ring sensors."""
|
||||
await setup_platform(hass, "sensor")
|
||||
|
||||
entity_id = f"sensor.{device_name}_{sensor_name}"
|
||||
sensor_state = hass.states.get(entity_id)
|
||||
assert sensor_state is not None
|
||||
assert sensor_state.state == "unknown"
|
||||
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
sensor_state = hass.states.get(entity_id)
|
||||
assert sensor_state is not None
|
||||
assert sensor_state.state == expected_value
|
||||
|
||||
|
||||
async def test_only_chime_devices(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Tests the update service works correctly if only chimes are returned."""
|
||||
await hass.config.async_set_time_zone("UTC")
|
||||
freezer.move_to("2021-01-09 12:00:00+00:00")
|
||||
requests_mock.get(
|
||||
"https://api.ring.com/clients_api/ring_devices",
|
||||
text=load_fixture("chime_devices.json", "ring"),
|
||||
)
|
||||
|
||||
mock_ring_devices.all_devices = mock_ring_devices.chimes
|
||||
|
||||
await setup_platform(hass, Platform.SENSOR)
|
||||
await hass.async_block_till_done()
|
||||
caplog.set_level(logging.DEBUG)
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
"""The tests for the Ring button platform."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import requests_mock
|
||||
import ring_doorbell
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
|
@ -18,7 +15,7 @@ from .common import setup_platform
|
|||
async def test_entity_registry(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, Platform.SIREN)
|
||||
|
@ -27,9 +24,7 @@ async def test_entity_registry(
|
|||
assert entry.unique_id == "123456-siren"
|
||||
|
||||
|
||||
async def test_sirens_report_correctly(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
) -> None:
|
||||
async def test_sirens_report_correctly(hass: HomeAssistant, mock_ring_client) -> None:
|
||||
"""Tests that the initial state of a device that should be on is correct."""
|
||||
await setup_platform(hass, Platform.SIREN)
|
||||
|
||||
|
@ -39,16 +34,11 @@ async def test_sirens_report_correctly(
|
|||
|
||||
|
||||
async def test_default_ding_chime_can_be_played(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client, mock_ring_devices
|
||||
) -> None:
|
||||
"""Tests the play chime request is sent correctly."""
|
||||
await setup_platform(hass, Platform.SIREN)
|
||||
|
||||
# Mocks the response for playing a test sound
|
||||
requests_mock.post(
|
||||
"https://api.ring.com/clients_api/chimes/123456/play_sound",
|
||||
text="SUCCESS",
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"siren",
|
||||
"turn_on",
|
||||
|
@ -58,26 +48,19 @@ async def test_default_ding_chime_can_be_played(
|
|||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert requests_mock.request_history[-1].url.startswith(
|
||||
"https://api.ring.com/clients_api/chimes/123456/play_sound?"
|
||||
)
|
||||
assert "kind=ding" in requests_mock.request_history[-1].url
|
||||
downstairs_chime_mock = mock_ring_devices.get_device(123456)
|
||||
downstairs_chime_mock.test_sound.assert_called_once_with(kind="ding")
|
||||
|
||||
state = hass.states.get("siren.downstairs_siren")
|
||||
assert state.state == "unknown"
|
||||
|
||||
|
||||
async def test_turn_on_plays_default_chime(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client, mock_ring_devices
|
||||
) -> None:
|
||||
"""Tests the play chime request is sent correctly when turned on."""
|
||||
await setup_platform(hass, Platform.SIREN)
|
||||
|
||||
# Mocks the response for playing a test sound
|
||||
requests_mock.post(
|
||||
"https://api.ring.com/clients_api/chimes/123456/play_sound",
|
||||
text="SUCCESS",
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"siren",
|
||||
"turn_on",
|
||||
|
@ -87,26 +70,21 @@ async def test_turn_on_plays_default_chime(
|
|||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert requests_mock.request_history[-1].url.startswith(
|
||||
"https://api.ring.com/clients_api/chimes/123456/play_sound?"
|
||||
)
|
||||
assert "kind=ding" in requests_mock.request_history[-1].url
|
||||
downstairs_chime_mock = mock_ring_devices.get_device(123456)
|
||||
downstairs_chime_mock.test_sound.assert_called_once_with(kind="ding")
|
||||
|
||||
state = hass.states.get("siren.downstairs_siren")
|
||||
assert state.state == "unknown"
|
||||
|
||||
|
||||
async def test_explicit_ding_chime_can_be_played(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
) -> None:
|
||||
"""Tests the play chime request is sent correctly."""
|
||||
await setup_platform(hass, Platform.SIREN)
|
||||
|
||||
# Mocks the response for playing a test sound
|
||||
requests_mock.post(
|
||||
"https://api.ring.com/clients_api/chimes/123456/play_sound",
|
||||
text="SUCCESS",
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"siren",
|
||||
"turn_on",
|
||||
|
@ -116,26 +94,19 @@ async def test_explicit_ding_chime_can_be_played(
|
|||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert requests_mock.request_history[-1].url.startswith(
|
||||
"https://api.ring.com/clients_api/chimes/123456/play_sound?"
|
||||
)
|
||||
assert "kind=ding" in requests_mock.request_history[-1].url
|
||||
downstairs_chime_mock = mock_ring_devices.get_device(123456)
|
||||
downstairs_chime_mock.test_sound.assert_called_once_with(kind="ding")
|
||||
|
||||
state = hass.states.get("siren.downstairs_siren")
|
||||
assert state.state == "unknown"
|
||||
|
||||
|
||||
async def test_motion_chime_can_be_played(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client, mock_ring_devices
|
||||
) -> None:
|
||||
"""Tests the play chime request is sent correctly."""
|
||||
await setup_platform(hass, Platform.SIREN)
|
||||
|
||||
# Mocks the response for playing a test sound
|
||||
requests_mock.post(
|
||||
"https://api.ring.com/clients_api/chimes/123456/play_sound",
|
||||
text="SUCCESS",
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"siren",
|
||||
"turn_on",
|
||||
|
@ -145,10 +116,8 @@ async def test_motion_chime_can_be_played(
|
|||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert requests_mock.request_history[-1].url.startswith(
|
||||
"https://api.ring.com/clients_api/chimes/123456/play_sound?"
|
||||
)
|
||||
assert "kind=motion" in requests_mock.request_history[-1].url
|
||||
downstairs_chime_mock = mock_ring_devices.get_device(123456)
|
||||
downstairs_chime_mock.test_sound.assert_called_once_with(kind="motion")
|
||||
|
||||
state = hass.states.get("siren.downstairs_siren")
|
||||
assert state.state == "unknown"
|
||||
|
@ -165,7 +134,8 @@ async def test_motion_chime_can_be_played(
|
|||
)
|
||||
async def test_siren_errors_when_turned_on(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
exception_type,
|
||||
reauth_expected,
|
||||
) -> None:
|
||||
|
@ -175,18 +145,18 @@ async def test_siren_errors_when_turned_on(
|
|||
|
||||
assert not any(config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
|
||||
with patch.object(
|
||||
ring_doorbell.RingChime, "test_sound", side_effect=exception_type
|
||||
) as mock_siren:
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"siren",
|
||||
"turn_on",
|
||||
{"entity_id": "siren.downstairs_siren", "tone": "motion"},
|
||||
blocking=True,
|
||||
)
|
||||
downstairs_chime_mock = mock_ring_devices.get_device(123456)
|
||||
downstairs_chime_mock.test_sound.side_effect = exception_type
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"siren",
|
||||
"turn_on",
|
||||
{"entity_id": "siren.downstairs_siren", "tone": "motion"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_siren.call_count == 1
|
||||
downstairs_chime_mock.test_sound.assert_called_once_with(kind="motion")
|
||||
assert (
|
||||
any(
|
||||
flow
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
"""The tests for the Ring switch platform."""
|
||||
|
||||
from unittest.mock import PropertyMock, patch
|
||||
from unittest.mock import PropertyMock
|
||||
|
||||
import pytest
|
||||
import requests_mock
|
||||
import ring_doorbell
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
|
@ -15,13 +14,11 @@ from homeassistant.setup import async_setup_component
|
|||
|
||||
from .common import setup_platform
|
||||
|
||||
from tests.common import load_fixture
|
||||
|
||||
|
||||
async def test_entity_registry(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, Platform.SWITCH)
|
||||
|
@ -34,7 +31,7 @@ async def test_entity_registry(
|
|||
|
||||
|
||||
async def test_siren_off_reports_correctly(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client
|
||||
) -> None:
|
||||
"""Tests that the initial state of a device that should be off is correct."""
|
||||
await setup_platform(hass, Platform.SWITCH)
|
||||
|
@ -45,7 +42,7 @@ async def test_siren_off_reports_correctly(
|
|||
|
||||
|
||||
async def test_siren_on_reports_correctly(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client
|
||||
) -> None:
|
||||
"""Tests that the initial state of a device that should be on is correct."""
|
||||
await setup_platform(hass, Platform.SWITCH)
|
||||
|
@ -55,18 +52,10 @@ async def test_siren_on_reports_correctly(
|
|||
assert state.attributes.get("friendly_name") == "Internal Siren"
|
||||
|
||||
|
||||
async def test_siren_can_be_turned_on(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
) -> None:
|
||||
async def test_siren_can_be_turned_on(hass: HomeAssistant, mock_ring_client) -> None:
|
||||
"""Tests the siren turns on correctly."""
|
||||
await setup_platform(hass, Platform.SWITCH)
|
||||
|
||||
# Mocks the response for turning a siren on
|
||||
requests_mock.put(
|
||||
"https://api.ring.com/clients_api/doorbots/765432/siren_on",
|
||||
text=load_fixture("doorbot_siren_on_response.json", "ring"),
|
||||
)
|
||||
|
||||
state = hass.states.get("switch.front_siren")
|
||||
assert state.state == "off"
|
||||
|
||||
|
@ -80,17 +69,15 @@ async def test_siren_can_be_turned_on(
|
|||
|
||||
|
||||
async def test_updates_work(
|
||||
hass: HomeAssistant, requests_mock: requests_mock.Mocker
|
||||
hass: HomeAssistant, mock_ring_client, mock_ring_devices
|
||||
) -> None:
|
||||
"""Tests the update service works correctly."""
|
||||
await setup_platform(hass, Platform.SWITCH)
|
||||
state = hass.states.get("switch.front_siren")
|
||||
assert state.state == "off"
|
||||
# Changes the return to indicate that the siren is now on.
|
||||
requests_mock.get(
|
||||
"https://api.ring.com/clients_api/ring_devices",
|
||||
text=load_fixture("devices_updated.json", "ring"),
|
||||
)
|
||||
|
||||
front_siren_mock = mock_ring_devices.get_device(765432)
|
||||
front_siren_mock.siren = 20
|
||||
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
await hass.services.async_call(
|
||||
|
@ -117,7 +104,8 @@ async def test_updates_work(
|
|||
)
|
||||
async def test_switch_errors_when_turned_on(
|
||||
hass: HomeAssistant,
|
||||
requests_mock: requests_mock.Mocker,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
exception_type,
|
||||
reauth_expected,
|
||||
) -> None:
|
||||
|
@ -127,16 +115,16 @@ async def test_switch_errors_when_turned_on(
|
|||
|
||||
assert not any(config_entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
|
||||
with patch.object(
|
||||
ring_doorbell.RingStickUpCam, "siren", new_callable=PropertyMock
|
||||
) as mock_switch:
|
||||
mock_switch.side_effect = exception_type
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_on", {"entity_id": "switch.front_siren"}, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_switch.call_count == 1
|
||||
front_siren_mock = mock_ring_devices.get_device(765432)
|
||||
p = PropertyMock(side_effect=exception_type)
|
||||
type(front_siren_mock).siren = p
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_on", {"entity_id": "switch.front_siren"}, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
p.assert_called_once()
|
||||
assert (
|
||||
any(
|
||||
flow
|
||||
|
|
Loading…
Reference in New Issue