From 4e2de2479a3f1ae861c7683fe3c5c0881a242c17 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Thu, 7 Jul 2022 15:25:44 +0100 Subject: [PATCH] Add SetSystemDateandTime Button (#66419) * add SetSystemDateandTime * fix * address review * follow recommendation to set date and time on start * add set date and time button test --- homeassistant/components/onvif/button.py | 19 ++++++++++++-- homeassistant/components/onvif/device.py | 32 ++++++++++++++++++++++++ tests/components/onvif/test_button.py | 30 ++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/onvif/button.py b/homeassistant/components/onvif/button.py index 23ea5124e61..0af4a16d269 100644 --- a/homeassistant/components/onvif/button.py +++ b/homeassistant/components/onvif/button.py @@ -1,5 +1,4 @@ """ONVIF Buttons.""" - from homeassistant.components.button import ButtonDeviceClass, ButtonEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -18,7 +17,7 @@ async def async_setup_entry( ) -> None: """Set up ONVIF button based on a config entry.""" device = hass.data[DOMAIN][config_entry.unique_id] - async_add_entities([RebootButton(device)]) + async_add_entities([RebootButton(device), SetSystemDateAndTimeButton(device)]) class RebootButton(ONVIFBaseEntity, ButtonEntity): @@ -39,3 +38,19 @@ class RebootButton(ONVIFBaseEntity, ButtonEntity): """Send out a SystemReboot command.""" device_mgmt = self.device.device.create_devicemgmt_service() await device_mgmt.SystemReboot() + + +class SetSystemDateAndTimeButton(ONVIFBaseEntity, ButtonEntity): + """Defines a ONVIF SetSystemDateAndTime button.""" + + _attr_entity_category = EntityCategory.CONFIG + + def __init__(self, device: ONVIFDevice) -> None: + """Initialize the button entity.""" + super().__init__(device) + self._attr_name = f"{self.device.name} Set System Date and Time" + self._attr_unique_id = f"{self.device.info.mac or self.device.info.serial_number}_setsystemdatetime" + + async def async_press(self) -> None: + """Send out a SetSystemDateAndTime command.""" + await self.device.async_manually_set_date_and_time() diff --git a/homeassistant/components/onvif/device.py b/homeassistant/components/onvif/device.py index d376b7fe258..5907ea90124 100644 --- a/homeassistant/components/onvif/device.py +++ b/homeassistant/components/onvif/device.py @@ -5,6 +5,7 @@ import asyncio from contextlib import suppress import datetime as dt import os +import time from httpx import RequestError import onvif @@ -148,6 +149,32 @@ class ONVIFDevice: await self.events.async_stop() await self.device.close() + async def async_manually_set_date_and_time(self) -> None: + """Set Date and Time Manually using SetSystemDateAndTime command.""" + device_mgmt = self.device.create_devicemgmt_service() + + # Retrieve DateTime object from camera to use as template for Set operation + device_time = await device_mgmt.GetSystemDateAndTime() + + system_date = dt_util.utcnow() + LOGGER.debug("System date (UTC): %s", system_date) + + dt_param = device_mgmt.create_type("SetSystemDateAndTime") + dt_param.DateTimeType = "Manual" + # Retrieve DST setting from system + dt_param.DaylightSavings = bool(time.localtime().tm_isdst) + dt_param.UTCDateTime = device_time.UTCDateTime + # Retrieve timezone from system + dt_param.TimeZone = str(system_date.astimezone().tzinfo) + dt_param.UTCDateTime.Date.Year = system_date.year + dt_param.UTCDateTime.Date.Month = system_date.month + dt_param.UTCDateTime.Date.Day = system_date.day + dt_param.UTCDateTime.Time.Hour = system_date.hour + dt_param.UTCDateTime.Time.Minute = system_date.minute + dt_param.UTCDateTime.Time.Second = system_date.second + LOGGER.debug("SetSystemDateAndTime: %s", dt_param) + await device_mgmt.SetSystemDateAndTime(dt_param) + async def async_check_date_and_time(self) -> None: """Warns if device and system date not synced.""" LOGGER.debug("Setting up the ONVIF device management service") @@ -165,6 +192,8 @@ class ONVIFDevice: ) return + LOGGER.debug("Device time: %s", device_time) + tzone = dt_util.DEFAULT_TIME_ZONE cdate = device_time.LocalDateTime if device_time.UTCDateTime: @@ -207,6 +236,9 @@ class ONVIFDevice: cam_date_utc, system_date, ) + if device_time.DateTimeType == "Manual": + # Set Date and Time ourselves if Date and Time is set manually in the camera. + await self.async_manually_set_date_and_time() except RequestError as err: LOGGER.warning( "Couldn't get device '%s' date/time. Error: %s", self.name, err diff --git a/tests/components/onvif/test_button.py b/tests/components/onvif/test_button.py index a8ac24da524..be418acd1e0 100644 --- a/tests/components/onvif/test_button.py +++ b/tests/components/onvif/test_button.py @@ -38,3 +38,33 @@ async def test_reboot_button_press(hass): await hass.async_block_till_done() devicemgmt.SystemReboot.assert_called_once() + + +async def test_set_dateandtime_button(hass): + """Test states of the SetDateAndTime button.""" + await setup_onvif_integration(hass) + + state = hass.states.get("button.testcamera_set_system_date_and_time") + assert state + assert state.state == STATE_UNKNOWN + + registry = er.async_get(hass) + entry = registry.async_get("button.testcamera_set_system_date_and_time") + assert entry + assert entry.unique_id == f"{MAC}_setsystemdatetime" + + +async def test_set_dateandtime_button_press(hass): + """Test SetDateAndTime button press.""" + _, camera, device = await setup_onvif_integration(hass) + device.async_manually_set_date_and_time = AsyncMock(return_value=True) + + await hass.services.async_call( + BUTTON_DOMAIN, + "press", + {ATTR_ENTITY_ID: "button.testcamera_set_system_date_and_time"}, + blocking=True, + ) + await hass.async_block_till_done() + + device.async_manually_set_date_and_time.assert_called_once()