1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-02 16:36:08 +01:00
Files
core/tests/components/zoneminder/test_sensor.py
nic bc324a1a6e Add ZoneMinder integration test suite (#163115)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-24 23:27:13 +01:00

497 lines
14 KiB
Python

"""Tests for ZoneMinder sensor entities."""
from __future__ import annotations
from datetime import timedelta
from unittest.mock import MagicMock, PropertyMock
from freezegun.api import FrozenDateTimeFactory
import pytest
from zoneminder.monitor import MonitorState, TimePeriod
from homeassistant.components.zoneminder.const import DOMAIN
from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .conftest import create_mock_monitor
from tests.common import async_fire_time_changed
async def _setup_zm_with_sensors(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
zm_config: dict,
monitors: list,
freezer: FrozenDateTimeFactory,
sensor_config: dict | None = None,
is_available: bool = True,
active_state: str | None = "Running",
) -> None:
"""Set up ZM component with sensor platform and trigger first poll."""
mock_zoneminder_client.get_monitors.return_value = monitors
type(mock_zoneminder_client).is_available = PropertyMock(return_value=is_available)
mock_zoneminder_client.get_active_state.return_value = active_state
assert await async_setup_component(hass, DOMAIN, zm_config)
await hass.async_block_till_done(wait_background_tasks=True)
if sensor_config is None:
sensor_config = {
"sensor": [
{
"platform": DOMAIN,
"monitored_conditions": [
"all",
"hour",
"day",
"week",
"month",
],
}
]
}
assert await async_setup_component(hass, "sensor", sensor_config)
await hass.async_block_till_done(wait_background_tasks=True)
# Trigger first poll to update entity state
freezer.tick(timedelta(seconds=60))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
# --- Monitor Status Sensor ---
async def test_monitor_status_sensor_exists(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test monitor status sensor is created."""
monitors = [create_mock_monitor(name="Front Door", function=MonitorState.MODECT)]
await _setup_zm_with_sensors(
hass, mock_zoneminder_client, single_server_config, monitors, freezer
)
state = hass.states.get("sensor.front_door_status")
assert state is not None
async def test_monitor_status_sensor_value(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test monitor status sensor shows MonitorState value."""
monitors = [create_mock_monitor(name="Front Door", function=MonitorState.RECORD)]
await _setup_zm_with_sensors(
hass, mock_zoneminder_client, single_server_config, monitors, freezer
)
state = hass.states.get("sensor.front_door_status")
assert state is not None
assert state.state == "Record"
@pytest.mark.parametrize(
("monitor_state", "expected_value"),
[
(MonitorState.NONE, "None"),
(MonitorState.MONITOR, "Monitor"),
(MonitorState.MODECT, "Modect"),
(MonitorState.RECORD, "Record"),
(MonitorState.MOCORD, "Mocord"),
(MonitorState.NODECT, "Nodect"),
],
)
async def test_monitor_status_sensor_all_states(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
monitor_state: MonitorState,
expected_value: str,
) -> None:
"""Test monitor status sensor with all MonitorState values."""
monitors = [create_mock_monitor(name="Cam", function=monitor_state)]
await _setup_zm_with_sensors(
hass, mock_zoneminder_client, single_server_config, monitors, freezer
)
state = hass.states.get("sensor.cam_status")
assert state is not None
assert state.state == expected_value
async def test_monitor_status_sensor_unavailable(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test monitor status sensor when monitor is unavailable."""
monitors = [
create_mock_monitor(name="Front Door", is_available=False, function=None)
]
await _setup_zm_with_sensors(
hass, mock_zoneminder_client, single_server_config, monitors, freezer
)
state = hass.states.get("sensor.front_door_status")
assert state is not None
assert state.state == STATE_UNAVAILABLE
async def test_monitor_status_sensor_null_function(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test monitor status sensor when function is falsy."""
monitors = [
create_mock_monitor(name="Front Door", function=None, is_available=True)
]
await _setup_zm_with_sensors(
hass, mock_zoneminder_client, single_server_config, monitors, freezer
)
state = hass.states.get("sensor.front_door_status")
assert state is not None
# --- Event Sensors ---
@pytest.mark.parametrize(
("condition", "expected_name_suffix", "expected_value"),
[
("all", "Events", "100"),
("hour", "Events Last Hour", "5"),
("day", "Events Last Day", "20"),
("week", "Events Last Week", "50"),
("month", "Events Last Month", "80"),
],
)
async def test_event_sensor_for_each_time_period(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
condition: str,
expected_name_suffix: str,
expected_value: str,
) -> None:
"""Test event sensors for all 5 time periods."""
monitors = [create_mock_monitor(name="Front Door")]
sensor_config = {
"sensor": [
{
"platform": DOMAIN,
"monitored_conditions": [condition],
}
]
}
await _setup_zm_with_sensors(
hass,
mock_zoneminder_client,
single_server_config,
monitors,
freezer,
sensor_config=sensor_config,
)
entity_id = f"sensor.front_door_{expected_name_suffix.lower().replace(' ', '_')}"
state = hass.states.get(entity_id)
assert state is not None
assert state.state == expected_value
async def test_event_sensor_unit_of_measurement(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test event sensors have 'Events' unit of measurement."""
monitors = [create_mock_monitor(name="Front Door")]
await _setup_zm_with_sensors(
hass, mock_zoneminder_client, single_server_config, monitors, freezer
)
state = hass.states.get("sensor.front_door_events")
assert state is not None
assert state.attributes.get("unit_of_measurement") == "Events"
async def test_event_sensor_name_format(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test event sensor name format is '{monitor_name} {time_period_title}'."""
monitors = [create_mock_monitor(name="Back Yard")]
sensor_config = {
"sensor": [
{
"platform": DOMAIN,
"monitored_conditions": ["hour"],
}
]
}
await _setup_zm_with_sensors(
hass,
mock_zoneminder_client,
single_server_config,
monitors,
freezer,
sensor_config=sensor_config,
)
state = hass.states.get("sensor.back_yard_events_last_hour")
assert state is not None
assert state.name == "Back Yard Events Last Hour"
async def test_event_sensor_none_handling(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test event sensor handles None event count."""
monitors = [
create_mock_monitor(
name="Front Door",
events=dict.fromkeys(TimePeriod),
)
]
await _setup_zm_with_sensors(
hass, mock_zoneminder_client, single_server_config, monitors, freezer
)
state = hass.states.get("sensor.front_door_events")
assert state is not None
# --- Run State Sensor ---
async def test_run_state_sensor_exists(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test run state sensor is created."""
monitors = [create_mock_monitor(name="Cam")]
await _setup_zm_with_sensors(
hass, mock_zoneminder_client, single_server_config, monitors, freezer
)
state = hass.states.get("sensor.run_state")
assert state is not None
async def test_run_state_sensor_value(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test run state sensor shows state name."""
monitors = [create_mock_monitor(name="Cam")]
await _setup_zm_with_sensors(
hass,
mock_zoneminder_client,
single_server_config,
monitors,
freezer,
active_state="Home",
)
state = hass.states.get("sensor.run_state")
assert state is not None
assert state.state == "Home"
async def test_run_state_sensor_unavailable(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test run state sensor when server unavailable."""
monitors = [create_mock_monitor(name="Cam")]
await _setup_zm_with_sensors(
hass,
mock_zoneminder_client,
single_server_config,
monitors,
freezer,
is_available=False,
active_state=None,
)
state = hass.states.get("sensor.run_state")
assert state is not None
assert state.state == STATE_UNAVAILABLE
# --- Platform behavior ---
async def test_platform_not_ready_empty_monitors(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
) -> None:
"""Test PlatformNotReady on empty monitors."""
mock_zoneminder_client.get_monitors.return_value = []
assert await async_setup_component(hass, DOMAIN, single_server_config)
await hass.async_block_till_done()
await async_setup_component(
hass,
"sensor",
{"sensor": [{"platform": DOMAIN}]},
)
await hass.async_block_till_done()
# No sensor entities should exist (PlatformNotReady caught by HA)
states = hass.states.async_all("sensor")
assert len(states) == 0
async def test_subset_condition_filtering(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test only selected monitored_conditions get event sensors."""
monitors = [create_mock_monitor(name="Cam")]
sensor_config = {
"sensor": [
{
"platform": DOMAIN,
"monitored_conditions": ["hour", "day"],
}
]
}
await _setup_zm_with_sensors(
hass,
mock_zoneminder_client,
single_server_config,
monitors,
freezer,
sensor_config=sensor_config,
)
# Should have: 1 status + 2 event + 1 run state = 4 sensors
states = hass.states.async_all("sensor")
assert len(states) == 4
# These should exist
assert hass.states.get("sensor.cam_events_last_hour") is not None
assert hass.states.get("sensor.cam_events_last_day") is not None
# These should NOT exist
assert hass.states.get("sensor.cam_events") is None
assert hass.states.get("sensor.cam_events_last_week") is None
assert hass.states.get("sensor.cam_events_last_month") is None
async def test_default_conditions_only_all(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test default monitored_conditions is only 'all'."""
monitors = [create_mock_monitor(name="Cam")]
sensor_config = {"sensor": [{"platform": DOMAIN}]}
await _setup_zm_with_sensors(
hass,
mock_zoneminder_client,
single_server_config,
monitors,
freezer,
sensor_config=sensor_config,
)
# Should have: 1 status + 1 event (all) + 1 run state = 3 sensors
states = hass.states.async_all("sensor")
assert len(states) == 3
async def test_include_archived_flag(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test include_archived flag is passed correctly to get_events."""
monitors = [create_mock_monitor(name="Cam")]
sensor_config = {
"sensor": [
{
"platform": DOMAIN,
"include_archived": True,
"monitored_conditions": ["all"],
}
]
}
await _setup_zm_with_sensors(
hass,
mock_zoneminder_client,
single_server_config,
monitors,
freezer,
sensor_config=sensor_config,
)
# Verify get_events was called with include_archived=True
monitors[0].get_events.assert_called_with(TimePeriod.ALL, True)
async def test_sensor_count_calculation(
hass: HomeAssistant,
mock_zoneminder_client: MagicMock,
single_server_config: dict,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test correct number of sensors created per monitor and client.
For each monitor: 1 status + N event sensors
Plus: 1 run state sensor per client
"""
monitors = [
create_mock_monitor(monitor_id=1, name="Cam1"),
create_mock_monitor(monitor_id=2, name="Cam2"),
]
sensor_config = {
"sensor": [
{
"platform": DOMAIN,
"monitored_conditions": ["all", "hour"],
"include_archived": False,
}
]
}
await _setup_zm_with_sensors(
hass,
mock_zoneminder_client,
single_server_config,
monitors,
freezer,
sensor_config=sensor_config,
)
# 2 monitors * (1 status + 2 events) + 1 run state = 7
assert len(hass.states.async_all("sensor")) == 7