Add next alarm time sensor to Garmin (#40420)

* Add sensor for garmin next alarm(s)

* code-review changes

* type fix

* linter fix

* review fixes
This commit is contained in:
Schachar Levin 2020-10-24 00:01:29 +03:00 committed by GitHub
parent e997e3a2eb
commit af048715f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 4 deletions

View File

@ -304,6 +304,7 @@ omit =
homeassistant/components/garmin_connect/__init__.py
homeassistant/components/garmin_connect/const.py
homeassistant/components/garmin_connect/sensor.py
homeassistant/components/garmin_connect/alarm_util.py
homeassistant/components/gc100/*
homeassistant/components/geniushub/*
homeassistant/components/geizhals/sensor.py

View File

@ -90,6 +90,17 @@ class GarminConnectData:
self.client = client
self.data = None
async def _get_combined_alarms_of_all_devices(self):
"""Combine the list of active alarms from all garmin devices."""
alarms = []
devices = await self.hass.async_add_executor_job(self.client.get_devices)
for device in devices:
device_settings = await self.hass.async_add_executor_job(
self.client.get_device_settings, device["deviceId"]
)
alarms += device_settings["alarms"]
return alarms
@Throttle(MIN_SCAN_INTERVAL)
async def async_update(self):
"""Update data via library."""
@ -99,6 +110,7 @@ class GarminConnectData:
self.data = await self.hass.async_add_executor_job(
self.client.get_stats_and_body, today.isoformat()
)
self.data["nextAlarm"] = await self._get_combined_alarms_of_all_devices()
except (
GarminConnectAuthenticationError,
GarminConnectTooManyRequestsError,

View File

@ -0,0 +1,50 @@
"""Utility method for converting Garmin Connect alarms to python datetime."""
from datetime import date, datetime, timedelta
import logging
_LOGGER = logging.getLogger(__name__)
DAY_TO_NUMBER = {
"Mo": 1,
"M": 1,
"Tu": 2,
"We": 3,
"W": 3,
"Th": 4,
"Fr": 5,
"F": 5,
"Sa": 6,
"Su": 7,
}
def calculate_next_active_alarms(alarms):
"""
Calculate garmin next active alarms from settings.
Alarms are sorted by time
"""
active_alarms = []
_LOGGER.debug(alarms)
for alarm_setting in alarms:
if alarm_setting["alarmMode"] != "ON":
continue
for day in alarm_setting["alarmDays"]:
alarm_time = alarm_setting["alarmTime"]
if day == "ONCE":
midnight = datetime.combine(date.today(), datetime.min.time())
alarm = midnight + timedelta(minutes=alarm_time)
if alarm < datetime.now():
alarm += timedelta(days=1)
else:
start_of_week = datetime.combine(
date.today() - timedelta(days=datetime.today().isoweekday() % 7),
datetime.min.time(),
)
days_to_add = DAY_TO_NUMBER[day] % 7
alarm = start_of_week + timedelta(minutes=alarm_time, days=days_to_add)
if alarm < datetime.now():
alarm += timedelta(days=7)
active_alarms.append(alarm.isoformat())
return sorted(active_alarms) if active_alarms else None

View File

@ -348,4 +348,5 @@ GARMIN_ENTITY_LIST = {
"physiqueRating": ["Physique Rating", "", "mdi:numeric", None, False],
"visceralFat": ["Visceral Fat", "", "mdi:food", None, False],
"metabolicAge": ["Metabolic Age", "", "mdi:calendar-heart", None, False],
"nextAlarm": ["Next Alarm Time", "", "mdi:alarm", DEVICE_CLASS_TIMESTAMP, True],
}

View File

@ -2,7 +2,7 @@
"domain": "garmin_connect",
"name": "Garmin Connect",
"documentation": "https://www.home-assistant.io/integrations/garmin_connect",
"requirements": ["garminconnect==0.1.13"],
"requirements": ["garminconnect==0.1.16"],
"codeowners": ["@cyberjunky"],
"config_flow": true
}

View File

@ -13,6 +13,7 @@ from homeassistant.const import ATTR_ATTRIBUTION, CONF_ID
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType
from .alarm_util import calculate_next_active_alarms
from .const import ATTRIBUTION, DOMAIN, GARMIN_ENTITY_LIST
_LOGGER = logging.getLogger(__name__)
@ -123,11 +124,16 @@ class GarminConnectSensor(Entity):
"""Return attributes for sensor."""
if not self._data.data:
return {}
return {
attributes = {
"source": self._data.data["source"],
"last_synced": self._data.data["lastSyncTimestampGMT"],
ATTR_ATTRIBUTION: ATTRIBUTION,
}
if self._type == "nextAlarm":
attributes["next_alarms"] = calculate_next_active_alarms(
self._data.data[self._type]
)
return attributes
@property
def device_info(self) -> Dict[str, Any]:
@ -177,6 +183,12 @@ class GarminConnectSensor(Entity):
self._type == "bodyFat" or self._type == "bodyWater" or self._type == "bmi"
):
self._state = round(data[self._type], 2)
elif self._type == "nextAlarm":
active_alarms = calculate_next_active_alarms(data[self._type])
if active_alarms:
self._state = active_alarms[0]
else:
self._available = False
else:
self._state = data[self._type]

View File

@ -622,7 +622,7 @@ fritzconnection==1.2.0
gTTS-token==1.1.3
# homeassistant.components.garmin_connect
garminconnect==0.1.13
garminconnect==0.1.16
# homeassistant.components.geizhals
geizhals==0.0.9

View File

@ -305,7 +305,7 @@ foobot_async==0.3.2
gTTS-token==1.1.3
# homeassistant.components.garmin_connect
garminconnect==0.1.13
garminconnect==0.1.16
# homeassistant.components.geo_json_events
# homeassistant.components.usgs_earthquakes_feed