Skip to content

Commit de93d1d

Browse files
ptarjanclaude
andauthored
Skip unmapped and watchdog event types in Hikvision NVR event injection (#165009)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c67438c commit de93d1d

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

homeassistant/components/hikvision/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,21 @@ def fetch_and_inject_nvr_events() -> None:
117117
# Map raw event type names to friendly names using SENSOR_MAP
118118
mapped_events: dict[str, list[int]] = {}
119119
for event_type, channels in nvr_events.items():
120-
friendly_name = SENSOR_MAP.get(event_type.lower(), event_type)
120+
event_key = event_type.lower()
121+
# Skip videoloss - used as watchdog by pyhik, not a real sensor
122+
if event_key == "videoloss":
123+
continue
124+
friendly_name = SENSOR_MAP.get(event_key)
125+
if friendly_name is None:
126+
_LOGGER.debug("Skipping unmapped event type: %s", event_type)
127+
continue
121128
if friendly_name in mapped_events:
122129
mapped_events[friendly_name].extend(channels)
123130
else:
124131
mapped_events[friendly_name] = list(channels)
125132
_LOGGER.debug("Mapped NVR events: %s", mapped_events)
126-
camera.inject_events(mapped_events)
133+
if mapped_events:
134+
camera.inject_events(mapped_events)
127135
else:
128136
_LOGGER.debug(
129137
"No event triggers returned from %s. "

tests/components/hikvision/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,5 @@ def mock_hik_nvr(mock_hikcamera: MagicMock) -> MagicMock:
141141
camera = mock_hikcamera.return_value
142142
camera.get_type = "NVR"
143143
camera.current_event_states = {}
144-
camera.get_event_triggers.return_value = {"Motion": [1, 2]}
144+
camera.get_event_triggers.return_value = {"VMD": [1, 2]}
145145
return mock_hikcamera

tests/components/hikvision/test_init.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Test Hikvision integration setup and unload."""
22

3+
import logging
34
from unittest.mock import MagicMock
45
from xml.etree.ElementTree import ParseError
56

@@ -106,6 +107,60 @@ async def test_setup_entry_nvr_fetches_events(
106107
mock_hik_nvr.return_value.inject_events.assert_called_once()
107108

108109

110+
async def test_setup_entry_nvr_skips_videoloss(
111+
hass: HomeAssistant,
112+
mock_config_entry: MockConfigEntry,
113+
mock_hik_nvr: MagicMock,
114+
) -> None:
115+
"""Test NVR event injection skips videoloss (watchdog event)."""
116+
mock_hik_nvr.return_value.get_event_triggers.return_value = {
117+
"VMD": [1, 2],
118+
"videoloss": [1, 2, 3],
119+
}
120+
121+
await setup_integration(hass, mock_config_entry)
122+
123+
assert mock_config_entry.state is ConfigEntryState.LOADED
124+
mock_hik_nvr.return_value.inject_events.assert_called_once_with({"Motion": [1, 2]})
125+
126+
127+
async def test_setup_entry_nvr_skips_unmapped_events(
128+
hass: HomeAssistant,
129+
mock_config_entry: MockConfigEntry,
130+
mock_hik_nvr: MagicMock,
131+
caplog: pytest.LogCaptureFixture,
132+
) -> None:
133+
"""Test NVR event injection skips events not in SENSOR_MAP."""
134+
mock_hik_nvr.return_value.get_event_triggers.return_value = {
135+
"VMD": [1],
136+
"audioexception": [1, 2],
137+
}
138+
139+
with caplog.at_level(logging.DEBUG):
140+
await setup_integration(hass, mock_config_entry)
141+
142+
assert mock_config_entry.state is ConfigEntryState.LOADED
143+
mock_hik_nvr.return_value.inject_events.assert_called_once_with({"Motion": [1]})
144+
assert "Skipping unmapped event type: audioexception" in caplog.text
145+
146+
147+
async def test_setup_entry_nvr_skips_all_unknown_events(
148+
hass: HomeAssistant,
149+
mock_config_entry: MockConfigEntry,
150+
mock_hik_nvr: MagicMock,
151+
) -> None:
152+
"""Test NVR event injection with only unknown events does not inject."""
153+
mock_hik_nvr.return_value.get_event_triggers.return_value = {
154+
"videoloss": [1],
155+
"audioexception": [2],
156+
}
157+
158+
await setup_integration(hass, mock_config_entry)
159+
160+
assert mock_config_entry.state is ConfigEntryState.LOADED
161+
mock_hik_nvr.return_value.inject_events.assert_not_called()
162+
163+
109164
async def test_setup_entry_nvr_event_fetch_request_error(
110165
hass: HomeAssistant,
111166
mock_config_entry: MockConfigEntry,

0 commit comments

Comments
 (0)