diff --git a/homeassistant/components/onvif/event.py b/homeassistant/components/onvif/event.py index fcb8e39cf54..ba8658f5fec 100644 --- a/homeassistant/components/onvif/event.py +++ b/homeassistant/components/onvif/event.py @@ -221,7 +221,7 @@ class EventManager: event = await parser(self.unique_id, msg) if not event: - LOGGER.warning("Unable to parse event from %s: %s", self.unique_id, msg) + LOGGER.info("Unable to parse event from %s: %s", self.unique_id, msg) return self._events[event.uid] = event diff --git a/homeassistant/components/onvif/parsers.py b/homeassistant/components/onvif/parsers.py index b20126235c7..65e5454699b 100644 --- a/homeassistant/components/onvif/parsers.py +++ b/homeassistant/components/onvif/parsers.py @@ -1,5 +1,6 @@ """ONVIF event parsers.""" from collections.abc import Callable, Coroutine +import datetime from typing import Any from homeassistant.helpers.entity import EntityCategory @@ -11,6 +12,18 @@ from .models import Event PARSERS: Registry[str, Callable[[str, Any], Coroutine[Any, Any, Event]]] = Registry() +def datetime_or_zero(value: str) -> datetime: + """Convert strings to datetimes, if invalid, return datetime.min.""" + # To handle cameras that return times like '0000-00-00T00:00:00Z' (e.g. hikvision) + try: + ret = dt_util.parse_datetime(value) + except ValueError: + return datetime.datetime.min + if ret is None: + return datetime.datetime.min + return ret + + @PARSERS.register("tns1:VideoSource/MotionAlarm") # pylint: disable=protected-access async def async_parse_motion_alarm(uid: str, msg) -> Event: @@ -246,7 +259,7 @@ async def async_parse_motion_region_detector(uid: str, msg) -> Event: "binary_sensor", "motion", None, - msg.Message._value_1.Data.SimpleItem[0].Value == "true", + msg.Message._value_1.Data.SimpleItem[0].Value in ["1", "true"], ) except (AttributeError, KeyError): return None @@ -339,18 +352,17 @@ async def async_parse_last_reboot(uid: str, msg) -> Event: Topic: tns1:Monitoring/OperatingTime/LastReboot """ try: + date_time = datetime_or_zero(msg.Message._value_1.Data.SimpleItem[0].Value) return Event( f"{uid}_{msg.Topic._value_1}", "Last Reboot", "sensor", "timestamp", None, - dt_util.as_local( - dt_util.parse_datetime(msg.Message._value_1.Data.SimpleItem[0].Value) - ), + dt_util.as_local(date_time), EntityCategory.DIAGNOSTIC, ) - except (AttributeError, KeyError, ValueError): + except (AttributeError, KeyError): return None @@ -362,19 +374,42 @@ async def async_parse_last_reset(uid: str, msg) -> Event: Topic: tns1:Monitoring/OperatingTime/LastReset """ try: + date_time = datetime_or_zero(msg.Message._value_1.Data.SimpleItem[0].Value) return Event( f"{uid}_{msg.Topic._value_1}", "Last Reset", "sensor", "timestamp", None, - dt_util.as_local( - dt_util.parse_datetime(msg.Message._value_1.Data.SimpleItem[0].Value) - ), + dt_util.as_local(date_time), EntityCategory.DIAGNOSTIC, entity_enabled=False, ) - except (AttributeError, KeyError, ValueError): + except (AttributeError, KeyError): + return None + + +@PARSERS.register("tns1:Monitoring/Backup/Last") +# pylint: disable=protected-access +async def async_parse_backup_last(uid: str, msg) -> Event: + """Handle parsing event message. + + Topic: tns1:Monitoring/Backup/Last + """ + + try: + date_time = datetime_or_zero(msg.Message._value_1.Data.SimpleItem[0].Value) + return Event( + f"{uid}_{msg.Topic._value_1}", + "Last Backup", + "sensor", + "timestamp", + None, + dt_util.as_local(date_time), + EntityCategory.DIAGNOSTIC, + entity_enabled=False, + ) + except (AttributeError, KeyError): return None @@ -386,19 +421,18 @@ async def async_parse_last_clock_sync(uid: str, msg) -> Event: Topic: tns1:Monitoring/OperatingTime/LastClockSynchronization """ try: + date_time = datetime_or_zero(msg.Message._value_1.Data.SimpleItem[0].Value) return Event( f"{uid}_{msg.Topic._value_1}", "Last Clock Synchronization", "sensor", "timestamp", None, - dt_util.as_local( - dt_util.parse_datetime(msg.Message._value_1.Data.SimpleItem[0].Value) - ), + dt_util.as_local(date_time), EntityCategory.DIAGNOSTIC, entity_enabled=False, ) - except (AttributeError, KeyError, ValueError): + except (AttributeError, KeyError): return None