From 02e87e447f108c0b2bace78512a68ef463c3d8c9 Mon Sep 17 00:00:00 2001 From: MarBra <16831559+MarBra@users.noreply.github.com> Date: Sat, 23 May 2020 16:05:50 +0200 Subject: [PATCH] Add cron and next_schedule --- .../components/xiaomi_miio/manifest.json | 2 +- .../components/xiaomi_miio/vacuum.py | 21 ++++++- requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/xiaomi_miio/test_vacuum.py | 58 +++++++++++++------ 5 files changed, 68 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/manifest.json b/homeassistant/components/xiaomi_miio/manifest.json index 468389b46261a2..4489097b6090a9 100644 --- a/homeassistant/components/xiaomi_miio/manifest.json +++ b/homeassistant/components/xiaomi_miio/manifest.json @@ -3,6 +3,6 @@ "name": "Xiaomi Miio", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/xiaomi_miio", - "requirements": ["construct==2.9.45", "python-miio==0.5.0.1"], + "requirements": ["construct==2.9.45", "python-miio==0.5.0.1", "croniter==0.3.31"], "codeowners": ["@rytilahti", "@syssi"] } diff --git a/homeassistant/components/xiaomi_miio/vacuum.py b/homeassistant/components/xiaomi_miio/vacuum.py index 47676be602ff2d..77e47953d82655 100644 --- a/homeassistant/components/xiaomi_miio/vacuum.py +++ b/homeassistant/components/xiaomi_miio/vacuum.py @@ -1,9 +1,12 @@ """Support for the Xiaomi vacuum cleaner robot.""" import asyncio +from datetime import datetime from functools import partial import logging +from croniter import croniter from miio import DeviceException, Vacuum # pylint: disable=import-error +from pytz import all_timezones, timezone, utc import voluptuous as vol from homeassistant.components.vacuum import ( @@ -301,7 +304,23 @@ def fan_speed_list(self): @property def timers(self): """Get the list of added timers of the vacuum cleaner.""" - return [{"enabled": timer.enabled, "time": timer.ts} for timer in self._timers] + if self.timezone not in all_timezones: + return [] + + timers = [] + local_tz = timezone(self.timezone) + + for timer in self._timers: + cron = croniter(timer.cron, start_time=local_tz.localize(datetime.now())) + next_schedule = cron.get_next(ret_type=datetime) + timers.append( + { + "enabled": timer.enabled, + "cron": timer.cron, + "next_schedule": next_schedule.astimezone(utc), + } + ) + return timers @property def device_state_attributes(self): diff --git a/requirements_all.txt b/requirements_all.txt index c3b5be78d8e96d..f89f90d21074cc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -435,6 +435,9 @@ coronavirus==1.1.0 # homeassistant.components.crimereports crimereports==1.0.1 +# homeassistant.components.xiaomi_miio +croniter==0.3.31 + # homeassistant.components.datadog datadog==0.15.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a7f3c6075da0a1..e077c0fcca4b2e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -181,6 +181,9 @@ coronavirus==1.1.0 # homeassistant.scripts.credstash # credstash==1.15.0 +# homeassistant.components.xiaomi_miio +croniter==0.3.31 + # homeassistant.components.datadog datadog==0.15.0 diff --git a/tests/components/xiaomi_miio/test_vacuum.py b/tests/components/xiaomi_miio/test_vacuum.py index 24bd4b125de173..4506fb9f11192f 100644 --- a/tests/components/xiaomi_miio/test_vacuum.py +++ b/tests/components/xiaomi_miio/test_vacuum.py @@ -1,8 +1,9 @@ """The tests for the Xiaomi vacuum platform.""" -from datetime import time, timedelta +from datetime import datetime, time, timedelta from unittest import mock import pytest +from pytz import utc from homeassistant.components.vacuum import ( ATTR_BATTERY_ICON, @@ -61,6 +62,7 @@ mock.call.consumable_status(), mock.call.clean_history(), mock.call.dnd_status(), + mock.call.timezone(), mock.call.timer(), ] @@ -96,22 +98,25 @@ def mirobo_is_got_error_fixture(): mock_vacuum.dnd_status().start = time(hour=22, minute=0) mock_vacuum.dnd_status().end = time(hour=6, minute=0) + mock_vacuum.timezone.return_value = "Europe/Berlin" + mock_timer_1 = mock.MagicMock() mock_timer_1.enabled = True - mock_timer_1.ts = time(hour=9, minute=0) + mock_timer_1.cron = "5 5 1 8 1" mock_timer_2 = mock.MagicMock() mock_timer_2.enabled = False - mock_timer_2.ts = time(hour=11, minute=0) + mock_timer_2.cron = "5 5 1 8 2" mock_vacuum.timer.return_value = [mock_timer_1, mock_timer_2] - mock_vacuum.timezone.return_value = "Europe/Berlin" - with mock.patch( "homeassistant.components.xiaomi_miio.vacuum.Vacuum" - ) as mock_vaccum_cls: + ) as mock_vaccum_cls, mock.patch( + "homeassistant.components.xiaomi_miio.vacuum.croniter.get_next" + ) as mock_next_schedule: mock_vaccum_cls.return_value = mock_vacuum + mock_next_schedule.return_value = datetime(2020, 5, 23, 15, 21, 10) yield mock_vacuum @@ -174,22 +179,25 @@ def mirobo_is_on_fixture(): mock_vacuum.status().state_code = 5 mock_vacuum.dnd_status().enabled = False + mock_vacuum.timezone.return_value = "Europe/Berlin" + mock_timer_1 = mock.MagicMock() mock_timer_1.enabled = True - mock_timer_1.ts = time(hour=9, minute=0) + mock_timer_1.cron = "5 5 1 8 1" mock_timer_2 = mock.MagicMock() mock_timer_2.enabled = False - mock_timer_2.ts = time(hour=11, minute=0) + mock_timer_2.cron = "5 5 1 8 2" mock_vacuum.timer.return_value = [mock_timer_1, mock_timer_2] - mock_vacuum.timezone.return_value = "Europe/Berlin" - with mock.patch( "homeassistant.components.xiaomi_miio.vacuum.Vacuum" - ) as mock_vaccum_cls: + ) as mock_vaccum_cls, mock.patch( + "homeassistant.components.xiaomi_miio.vacuum.croniter.get_next" + ) as mock_next_schedule: mock_vaccum_cls.return_value = mock_vacuum + mock_next_schedule.return_value = datetime(2020, 5, 23, 15, 21, 10) yield mock_vacuum @@ -267,11 +275,19 @@ async def test_xiaomi_vacuum_services(hass, caplog, mock_mirobo_is_got_error): assert state.attributes.get(ATTR_CLEANING_COUNT) == 35 assert state.attributes.get(ATTR_CLEANED_TOTAL_AREA) == 123 assert state.attributes.get(ATTR_CLEANING_TOTAL_TIME) == 695 + assert state.attributes.get(ATTR_TIMEZONE) == "Europe/Berlin" assert state.attributes.get(ATTR_TIMERS) == [ - {"enabled": True, "time": time(hour=9, minute=0)}, - {"enabled": False, "time": time(hour=11, minute=0)}, + { + "enabled": True, + "cron": "5 5 1 8 1", + "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc), + }, + { + "enabled": False, + "cron": "5 5 1 8 2", + "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc), + }, ] - assert state.attributes.get(ATTR_TIMEZONE) == "Europe/Berlin" # Call services await hass.services.async_call( @@ -372,11 +388,19 @@ async def test_xiaomi_specific_services(hass, caplog, mock_mirobo_is_on): assert state.attributes.get(ATTR_CLEANING_COUNT) == 41 assert state.attributes.get(ATTR_CLEANED_TOTAL_AREA) == 323 assert state.attributes.get(ATTR_CLEANING_TOTAL_TIME) == 675 + assert state.attributes.get(ATTR_TIMEZONE) == "Europe/Berlin" assert state.attributes.get(ATTR_TIMERS) == [ - {"enabled": True, "time": time(hour=9, minute=0)}, - {"enabled": False, "time": time(hour=11, minute=0)}, + { + "enabled": True, + "cron": "5 5 1 8 1", + "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc), + }, + { + "enabled": False, + "cron": "5 5 1 8 2", + "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc), + }, ] - assert state.attributes.get(ATTR_TIMEZONE) == "Europe/Berlin" # Xiaomi vacuum specific services: await hass.services.async_call(