Add support for homekit camera motion notification (#35994)
* Add support for homekit camera motion notification A motion sensor can now be linked to the cameras. * Increase coverage
This commit is contained in:
parent
15539536ad
commit
8cbee76929
6 changed files with 253 additions and 14 deletions
|
@ -6,7 +6,10 @@ import pytest
|
|||
from zeroconf import InterfaceChoice
|
||||
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.components.binary_sensor import DEVICE_CLASS_BATTERY_CHARGING
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_BATTERY_CHARGING,
|
||||
DEVICE_CLASS_MOTION,
|
||||
)
|
||||
from homeassistant.components.homekit import (
|
||||
MAX_DEVICES,
|
||||
STATUS_READY,
|
||||
|
@ -1032,3 +1035,78 @@ async def test_homekit_ignored_missing_devices(
|
|||
"linked_battery_sensor": "sensor.powerwall_battery",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def test_homekit_finds_linked_motion_sensors(
|
||||
hass, hk_driver, debounce_patcher, device_reg, entity_reg
|
||||
):
|
||||
"""Test HomeKit start method."""
|
||||
entry = await async_init_integration(hass)
|
||||
|
||||
homekit = HomeKit(
|
||||
hass,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
{},
|
||||
{"camera.camera_demo": {}},
|
||||
DEFAULT_SAFE_MODE,
|
||||
advertise_ip=None,
|
||||
interface_choice=None,
|
||||
entry_id=entry.entry_id,
|
||||
)
|
||||
homekit.driver = hk_driver
|
||||
homekit._filter = Mock(return_value=True)
|
||||
homekit.bridge = HomeBridge(hass, hk_driver, "mock_bridge")
|
||||
|
||||
config_entry = MockConfigEntry(domain="test", data={})
|
||||
config_entry.add_to_hass(hass)
|
||||
device_entry = device_reg.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
sw_version="0.16.0",
|
||||
model="Camera Server",
|
||||
manufacturer="Ubq",
|
||||
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
)
|
||||
|
||||
binary_motion_sensor = entity_reg.async_get_or_create(
|
||||
"binary_sensor",
|
||||
"camera",
|
||||
"motion_sensor",
|
||||
device_id=device_entry.id,
|
||||
device_class=DEVICE_CLASS_MOTION,
|
||||
)
|
||||
camera = entity_reg.async_get_or_create(
|
||||
"camera", "camera", "demo", device_id=device_entry.id
|
||||
)
|
||||
|
||||
hass.states.async_set(
|
||||
binary_motion_sensor.entity_id,
|
||||
STATE_ON,
|
||||
{ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION},
|
||||
)
|
||||
hass.states.async_set(camera.entity_id, STATE_ON)
|
||||
|
||||
def _mock_get_accessory(*args, **kwargs):
|
||||
return [None, "acc", None]
|
||||
|
||||
with patch.object(homekit.bridge, "add_accessory"), patch(
|
||||
f"{PATH_HOMEKIT}.show_setup_message"
|
||||
), patch(f"{PATH_HOMEKIT}.get_accessory") as mock_get_acc, patch(
|
||||
"pyhap.accessory_driver.AccessoryDriver.start"
|
||||
):
|
||||
await homekit.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_get_acc.assert_called_with(
|
||||
hass,
|
||||
hk_driver,
|
||||
ANY,
|
||||
ANY,
|
||||
{
|
||||
"manufacturer": "Ubq",
|
||||
"model": "Camera Server",
|
||||
"sw_version": "0.16.0",
|
||||
"linked_motion_sensor": "binary_sensor.camera_motion_sensor",
|
||||
},
|
||||
)
|
||||
|
|
|
@ -9,16 +9,21 @@ from homeassistant.components import camera, ffmpeg
|
|||
from homeassistant.components.homekit.accessories import HomeBridge
|
||||
from homeassistant.components.homekit.const import (
|
||||
AUDIO_CODEC_COPY,
|
||||
CHAR_MOTION_DETECTED,
|
||||
CONF_AUDIO_CODEC,
|
||||
CONF_LINKED_MOTION_SENSOR,
|
||||
CONF_STREAM_SOURCE,
|
||||
CONF_SUPPORT_AUDIO,
|
||||
CONF_VIDEO_CODEC,
|
||||
DEVICE_CLASS_MOTION,
|
||||
SERV_MOTION_SENSOR,
|
||||
VIDEO_CODEC_COPY,
|
||||
VIDEO_CODEC_H264_OMX,
|
||||
)
|
||||
from homeassistant.components.homekit.img_util import TurboJPEGSingleton
|
||||
from homeassistant.components.homekit.type_cameras import Camera
|
||||
from homeassistant.components.homekit.type_switches import Switch
|
||||
from homeassistant.const import ATTR_DEVICE_CLASS, STATE_OFF, STATE_ON
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
@ -492,3 +497,91 @@ async def test_camera_streaming_fails_after_starting_ffmpeg(hass, run_driver, ev
|
|||
output=expected_output.format(**session_info),
|
||||
stdout_pipe=False,
|
||||
)
|
||||
|
||||
|
||||
async def test_camera_with_linked_motion_sensor(hass, run_driver, events):
|
||||
"""Test a camera with a linked motion sensor can update."""
|
||||
await async_setup_component(hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}})
|
||||
await async_setup_component(
|
||||
hass, camera.DOMAIN, {camera.DOMAIN: {"platform": "demo"}}
|
||||
)
|
||||
motion_entity_id = "binary_sensor.motion"
|
||||
|
||||
hass.states.async_set(
|
||||
motion_entity_id, STATE_ON, {ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
entity_id = "camera.demo_camera"
|
||||
|
||||
hass.states.async_set(entity_id, None)
|
||||
await hass.async_block_till_done()
|
||||
acc = Camera(
|
||||
hass,
|
||||
run_driver,
|
||||
"Camera",
|
||||
entity_id,
|
||||
2,
|
||||
{
|
||||
CONF_STREAM_SOURCE: "/dev/null",
|
||||
CONF_SUPPORT_AUDIO: True,
|
||||
CONF_VIDEO_CODEC: VIDEO_CODEC_H264_OMX,
|
||||
CONF_AUDIO_CODEC: AUDIO_CODEC_COPY,
|
||||
CONF_LINKED_MOTION_SENSOR: motion_entity_id,
|
||||
},
|
||||
)
|
||||
bridge = HomeBridge("hass", run_driver, "Test Bridge")
|
||||
bridge.add_accessory(acc)
|
||||
|
||||
await acc.run_handler()
|
||||
|
||||
assert acc.aid == 2
|
||||
assert acc.category == 17 # Camera
|
||||
|
||||
service = acc.get_service(SERV_MOTION_SENSOR)
|
||||
assert service
|
||||
char = service.get_characteristic(CHAR_MOTION_DETECTED)
|
||||
assert char
|
||||
|
||||
assert char.value is True
|
||||
|
||||
hass.states.async_set(
|
||||
motion_entity_id, STATE_OFF, {ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert char.value is False
|
||||
|
||||
char.set_value(True)
|
||||
hass.states.async_set(
|
||||
motion_entity_id, STATE_ON, {ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert char.value is True
|
||||
|
||||
|
||||
async def test_camera_with_a_missing_linked_motion_sensor(hass, run_driver, events):
|
||||
"""Test a camera with a configured linked motion sensor that is missing."""
|
||||
await async_setup_component(hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}})
|
||||
await async_setup_component(
|
||||
hass, camera.DOMAIN, {camera.DOMAIN: {"platform": "demo"}}
|
||||
)
|
||||
motion_entity_id = "binary_sensor.motion"
|
||||
entity_id = "camera.demo_camera"
|
||||
hass.states.async_set(entity_id, None)
|
||||
await hass.async_block_till_done()
|
||||
acc = Camera(
|
||||
hass,
|
||||
run_driver,
|
||||
"Camera",
|
||||
entity_id,
|
||||
2,
|
||||
{CONF_LINKED_MOTION_SENSOR: motion_entity_id},
|
||||
)
|
||||
bridge = HomeBridge("hass", run_driver, "Test Bridge")
|
||||
bridge.add_accessory(acc)
|
||||
|
||||
await acc.run_handler()
|
||||
|
||||
assert acc.aid == 2
|
||||
assert acc.category == 17 # Camera
|
||||
|
||||
assert not acc.get_service(SERV_MOTION_SENSOR)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue