Compare commits

...

35 Commits

Author SHA1 Message Date
Ron
17fa2bcc63 Update version 2023-05-22 10:17:21 +02:00
Ron
b26f36c41f Merge pull request #72 from ViPeR5000/patch-2
Update translation
2023-05-22 10:14:53 +02:00
Ron
845cce3bda Merge pull request #49 from ray0711/gear_sensor
Added gear sensors & gear service
2023-05-22 10:14:12 +02:00
Ron
15800b00d5 Merge pull request #80 from gorzelak/main
Adding last activities sensor
2023-05-22 10:11:49 +02:00
Ron
c9e9740137 Merge pull request #79 from pailloM/main
Remove pytz dependency in sensor.py
2023-05-22 10:10:23 +02:00
Krzysztof Gorzelak
b7f2a6e653 Adding last activities sensor 2023-05-05 10:09:46 +02:00
Paillomams
be150124b7 remove pytz dependency 2023-04-23 00:46:24 -04:00
Ron
28876a8709 Update manifest.json 2023-04-19 08:23:38 +02:00
ray0711
287a9a04c3 Merge branch 'main' of https://github.com/cyberjunky/home-assistant-garmin_connect into gear_sensor 2023-04-16 10:15:05 +02:00
Ron
de51403c43 Update manifest.json 2023-04-05 17:39:31 +02:00
Ron
36e5b7c6b8 Add support for China 2023-04-05 17:39:16 +02:00
Ron
9a6aea06c4 Delete .github/workflows directory 2023-03-28 12:18:31 +02:00
Ron
bc1ff88af2 Update hassfest.yml 2023-03-28 12:07:57 +02:00
Ron
48d738d1dc Update manifest.json 2023-03-28 11:44:38 +02:00
Rui Melo
d57b8e0223 update
fix word
2023-03-03 11:25:43 +00:00
Ron
0688f89be1 Fix manifest validation errors 2023-03-03 10:08:37 +01:00
Ron
985468d7a2 Update manifest.json 2023-03-03 09:52:56 +01:00
Ron
ff0ccb7e99 Update manifest.json 2023-03-03 09:52:34 +01:00
Ron
f59b41e7c4 Fixed errors for weight and stress sensors 2023-03-03 09:52:23 +01:00
Ron
0074b94723 Fixed syntax error 2023-02-03 09:14:27 +01:00
Ron
ee777314f9 Update manifest.json 2023-02-02 18:13:12 +01:00
Ron
58f5dd49db Merge pull request #62 from misa1515/patch-2
Create sk.json
2023-02-02 18:12:30 +01:00
Ron
7b66c4b7be Update manifest.json 2023-02-02 18:11:13 +01:00
Ron
1aa8cfb78f Use async_forward_entry_setups instead of async_setup_platforms 2023-02-02 18:10:58 +01:00
misa1515
a29be3127e Create sk.json
Created new translation to slovak language
2023-01-20 21:13:04 +01:00
Ron
ef91580157 Updated version 2023-01-10 09:05:17 +01:00
Ron
1c3e5a4385 Merge pull request #61 from arniebarni/main
Use local time zone for alarms
2023-01-10 08:33:02 +01:00
arniebarni
b6aebe1637 use local time zone for alarms 2023-01-09 19:47:11 +01:00
Ron
9cf15616ab Updated version 2023-01-09 13:37:22 +01:00
Ron
911ca663ab Fixed timestamp related sensors 2023-01-09 13:37:05 +01:00
Raimund Huber
fa48f9f4e2 List default for actitivies on sensor, bugfixes 2022-12-08 20:10:58 +01:00
Raimund Huber
4f4d0b8425 Gear service to update default gear per activity on garmin connect 2022-12-06 22:02:51 +01:00
Raimund Huber
8760bc0a78 unreleased python-garminconnect version 2022-12-04 12:35:28 +01:00
Raimund Huber
8efd365163 Fixup exception for non-numeric sensor values e.g. stress_qualifier = UNKNOWN 2022-12-04 12:34:47 +01:00
Raimund Huber
cc6e0af4e5 add basic gear sensors 2022-12-04 12:03:10 +01:00
8 changed files with 404 additions and 66 deletions

View File

@@ -1,15 +0,0 @@
name: Validate with hassfest
on:
push:
pull_request:
schedule:
- cron: "0 0 * * *"
jobs:
validate:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v2"
- uses: home-assistant/actions/hassfest@master

View File

@@ -1,6 +1,9 @@
"""The Garmin Connect integration."""
from datetime import date
from datetime import timedelta
import logging
import asyncio
from collections.abc import Awaitable
from garminconnect import (
Garmin,
@@ -12,10 +15,16 @@ from garminconnect import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.exceptions import ConfigEntryNotReady, IntegrationError
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DATA_COORDINATOR, DEFAULT_UPDATE_INTERVAL, DOMAIN
from .const import (
DATA_COORDINATOR,
DEFAULT_UPDATE_INTERVAL,
DOMAIN,
GEAR,
SERVICE_SETTING,
)
_LOGGER = logging.getLogger(__name__)
@@ -33,11 +42,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {
DATA_COORDINATOR: coordinator,
}
hass.data[DOMAIN][entry.entry_id] = {DATA_COORDINATOR: coordinator}
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
@@ -57,8 +64,13 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
"""Initialize the Garmin Connect hub."""
self.entry = entry
self.hass = hass
self.in_china = False
self._api = Garmin(entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD])
country = self.hass.config.country
if country == "CN":
self.in_china = True
self._api = Garmin(entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD], self.in_china)
super().__init__(
hass, _LOGGER, name=DOMAIN, update_interval=DEFAULT_UPDATE_INTERVAL
@@ -97,9 +109,31 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
body = await self.hass.async_add_executor_job(
self._api.get_body_composition, date.today().isoformat()
)
activities = await self.hass.async_add_executor_job(
self._api.get_activities_by_date, (date.today()-timedelta(days=7)).isoformat(), (date.today()+timedelta(days=1)).isoformat()
)
_LOGGER.debug(body)
alarms = await self.hass.async_add_executor_job(self._api.get_device_alarms)
_LOGGER.debug(alarms)
gear = await self.hass.async_add_executor_job(
self._api.get_gear, summary[GEAR.USERPROFILE_ID]
)
tasks: list[Awaitable] = [
self.hass.async_add_executor_job(
self._api.get_gear_stats, gear_item[GEAR.UUID]
)
for gear_item in gear
]
gear_stats = await asyncio.gather(*tasks)
activity_types = await self.hass.async_add_executor_job(
self._api.get_activity_types
)
gear_defaults = await self.hass.async_add_executor_job(
self._api.get_gear_defaults, summary[GEAR.USERPROFILE_ID]
)
except (
GarminConnectAuthenticationError,
GarminConnectTooManyRequestsError,
@@ -110,8 +144,58 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
raise UpdateFailed(error) from error
return {}
summary['lastActivities'] = activities
return {
**summary,
**body["totalAverage"],
"nextAlarm": alarms,
"gear": gear,
"gear_stats": gear_stats,
"activity_types": activity_types,
"gear_defaults": gear_defaults,
}
async def set_active_gear(self, entity, service_data):
"""Update Garmin Gear settings"""
if not await self.async_login():
raise IntegrationError(
"Failed to login to Garmin Connect, unable to update"
)
setting = service_data.data["setting"]
activity_type_id = next(
filter(
lambda a: a[GEAR.TYPE_KEY] == service_data.data["activity_type"],
self.data["activity_types"],
)
)[GEAR.TYPE_ID]
if setting != SERVICE_SETTING.ONLY_THIS_AS_DEFAULT:
await self.hass.async_add_executor_job(
self._api.set_gear_default,
activity_type_id,
entity.uuid,
setting == SERVICE_SETTING.DEFAULT,
)
else:
old_default_state = await self.hass.async_add_executor_job(
self._api.get_gear_defaults, self.data[GEAR.USERPROFILE_ID]
)
to_deactivate = list(
filter(
lambda o: o[GEAR.ACTIVITY_TYPE_PK] == activity_type_id
and o[GEAR.UUID] != entity.uuid,
old_default_state,
)
)
for active_gear in to_deactivate:
await self.hass.async_add_executor_job(
self._api.set_gear_default,
activity_type_id,
active_gear[GEAR.UUID],
False,
)
await self.hass.async_add_executor_job(
self._api.set_gear_default, activity_type_id, entity.uuid, True
)

View File

@@ -1,5 +1,7 @@
"""Constants for the Garmin Connect integration."""
from datetime import timedelta
from enum import Enum
from typing import NamedTuple
from homeassistant.const import (
LENGTH_METERS,
@@ -88,7 +90,7 @@ GARMIN_ENTITY_LIST = {
"Highly Active Time",
TIME_MINUTES,
"mdi:fire",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
False,
],
@@ -99,7 +101,7 @@ GARMIN_ENTITY_LIST = {
"Awake Duration",
TIME_MINUTES,
"mdi:sleep",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
True,
],
@@ -107,7 +109,7 @@ GARMIN_ENTITY_LIST = {
"Sleep Duration",
TIME_MINUTES,
"mdi:sleep",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
True,
],
@@ -160,13 +162,13 @@ GARMIN_ENTITY_LIST = {
],
"averageStressLevel": ["Avg Stress Level", "lvl", "mdi:flash-alert", None, SensorStateClass.TOTAL, True],
"maxStressLevel": ["Max Stress Level", "lvl", "mdi:flash-alert", None, SensorStateClass.TOTAL, True],
"stressQualifier": ["Stress Qualifier", None, "mdi:flash-alert", None, SensorStateClass.TOTAL, False],
"stressQualifier": ["Stress Qualifier", None, "mdi:flash-alert", None, None, False],
"stressDuration": ["Stress Duration", TIME_MINUTES, "mdi:flash-alert", None, SensorStateClass.TOTAL, False],
"restStressDuration": [
"Rest Stress Duration",
TIME_MINUTES,
"mdi:flash-alert",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
True,
],
@@ -174,7 +176,7 @@ GARMIN_ENTITY_LIST = {
"Activity Stress Duration",
TIME_MINUTES,
"mdi:flash-alert",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
True,
],
@@ -182,7 +184,7 @@ GARMIN_ENTITY_LIST = {
"Uncat. Stress Duration",
TIME_MINUTES,
"mdi:flash-alert",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
True,
],
@@ -190,7 +192,7 @@ GARMIN_ENTITY_LIST = {
"Total Stress Duration",
TIME_MINUTES,
"mdi:flash-alert",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
True,
],
@@ -198,7 +200,7 @@ GARMIN_ENTITY_LIST = {
"Low Stress Duration",
TIME_MINUTES,
"mdi:flash-alert",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
True,
],
@@ -206,7 +208,7 @@ GARMIN_ENTITY_LIST = {
"Medium Stress Duration",
TIME_MINUTES,
"mdi:flash-alert",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
True,
],
@@ -222,7 +224,7 @@ GARMIN_ENTITY_LIST = {
"Stress Percentage",
PERCENTAGE,
"mdi:flash-alert",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
False,
],
@@ -230,7 +232,7 @@ GARMIN_ENTITY_LIST = {
"Rest Stress Percentage",
PERCENTAGE,
"mdi:flash-alert",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
False,
],
@@ -238,7 +240,7 @@ GARMIN_ENTITY_LIST = {
"Activity Stress Percentage",
PERCENTAGE,
"mdi:flash-alert",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
False,
],
@@ -246,7 +248,7 @@ GARMIN_ENTITY_LIST = {
"Uncat. Stress Percentage",
PERCENTAGE,
"mdi:flash-alert",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
False,
],
@@ -254,7 +256,7 @@ GARMIN_ENTITY_LIST = {
"Low Stress Percentage",
PERCENTAGE,
"mdi:flash-alert",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
False,
],
@@ -262,7 +264,7 @@ GARMIN_ENTITY_LIST = {
"Medium Stress Percentage",
PERCENTAGE,
"mdi:flash-alert",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
False,
],
@@ -270,7 +272,7 @@ GARMIN_ENTITY_LIST = {
"High Stress Percentage",
PERCENTAGE,
"mdi:flash-alert",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
False,
],
@@ -278,7 +280,7 @@ GARMIN_ENTITY_LIST = {
"Moderate Intensity",
TIME_MINUTES,
"mdi:flash-alert",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
False,
],
@@ -286,7 +288,7 @@ GARMIN_ENTITY_LIST = {
"Vigorous Intensity",
TIME_MINUTES,
"mdi:run-fast",
None,
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
False,
],
@@ -302,7 +304,7 @@ GARMIN_ENTITY_LIST = {
"Body Battery Charged",
PERCENTAGE,
"mdi:battery-charging-100",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
True,
],
@@ -310,7 +312,7 @@ GARMIN_ENTITY_LIST = {
"Body Battery Drained",
PERCENTAGE,
"mdi:battery-alert-variant-outline",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
True,
],
@@ -318,7 +320,7 @@ GARMIN_ENTITY_LIST = {
"Body Battery Highest",
PERCENTAGE,
"mdi:battery-heart",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
True,
],
@@ -326,7 +328,7 @@ GARMIN_ENTITY_LIST = {
"Body Battery Lowest",
PERCENTAGE,
"mdi:battery-heart-outline",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
True,
],
@@ -334,7 +336,7 @@ GARMIN_ENTITY_LIST = {
"Body Battery Most Recent",
PERCENTAGE,
"mdi:battery-positive",
SensorDeviceClass.BATTERY,
None,
SensorStateClass.TOTAL,
True,
],
@@ -346,7 +348,7 @@ GARMIN_ENTITY_LIST = {
None,
"mdi:diabetes",
SensorDeviceClass.TIMESTAMP,
SensorStateClass.TOTAL,
None,
False,
],
"averageMonitoringEnvironmentAltitude": [
@@ -389,14 +391,39 @@ GARMIN_ENTITY_LIST = {
None,
False,
],
"weight": ["Weight", MASS_KILOGRAMS, "mdi:weight-kilogram", SensorDeviceClass.WEIGHT, SensorStateClass.TOTAL, False],
"weight": ["Weight", MASS_KILOGRAMS, "mdi:weight-kilogram", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"bmi": ["BMI", "bmi", "mdi:food", None, SensorStateClass.TOTAL, False],
"bodyFat": ["Body Fat", PERCENTAGE, "mdi:food", SensorDeviceClass.MOISTURE, SensorStateClass.TOTAL, False],
"bodyWater": ["Body Water", PERCENTAGE, "mdi:water-percent", SensorDeviceClass.MOISTURE, SensorStateClass.TOTAL, False],
"boneMass": ["Bone Mass", MASS_KILOGRAMS, "mdi:bone", SensorDeviceClass.WEIGHT, SensorStateClass.TOTAL, False],
"muscleMass": ["Muscle Mass", MASS_KILOGRAMS, "mdi:dumbbell", SensorDeviceClass.WEIGHT, SensorStateClass.TOTAL, False],
"bodyFat": ["Body Fat", PERCENTAGE, "mdi:food", None, SensorStateClass.TOTAL, False],
"bodyWater": ["Body Water", PERCENTAGE, "mdi:water-percent", None, SensorStateClass.TOTAL, False],
"boneMass": ["Bone Mass", MASS_KILOGRAMS, "mdi:bone", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"muscleMass": ["Muscle Mass", MASS_KILOGRAMS, "mdi:dumbbell", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"physiqueRating": ["Physique Rating", None, "mdi:numeric", None, SensorStateClass.TOTAL, False],
"visceralFat": ["Visceral Fat", PERCENTAGE, "mdi:food", SensorDeviceClass.MOISTURE, SensorStateClass.TOTAL, False],
"visceralFat": ["Visceral Fat", PERCENTAGE, "mdi:food", None, SensorStateClass.TOTAL, False],
"metabolicAge": ["Metabolic Age", TIME_YEARS, "mdi:calendar-heart", None, SensorStateClass.TOTAL, False],
"nextAlarm": ["Next Alarm Time", None, "mdi:alarm", SensorDeviceClass.TIMESTAMP, SensorStateClass.TOTAL, True],
"nextAlarm": ["Next Alarm Time", None, "mdi:alarm", SensorDeviceClass.TIMESTAMP, None, True],
"lastActivities": ["Last Activities", None, "mdi:numeric", SensorStateClass.TOTAL, None, False]
}
GEAR_ICONS = {
"Shoes": "mdi:shoe-sneaker",
"Bike": "mdi:bike",
"Other": "mdi:basketball",
"Golf Clubs": "mdi:golf",
}
class SERVICE_SETTING(NamedTuple):
"""Options for the service settings, see services.yaml"""
ONLY_THIS_AS_DEFAULT = "set this as default, unset others"
DEFAULT = "set as default"
UNSET_DEFAULT = "unset default"
class GEAR(NamedTuple):
UUID = "uuid"
TYPE_KEY = "typeKey"
TYPE_ID = "typeId"
USERPROFILE_ID = "userProfileId"
ACTIVITY_TYPE_PK = "activityTypePk"

View File

@@ -1,11 +1,11 @@
{
"domain": "garmin_connect",
"name": "Garmin Connect",
"documentation": "https://github.com/cyberjunky/home-assistant-garmin_connect",
"issue_tracker": "https://github.com/cyberjunky/home-assistant-garmin_connect/issues",
"requirements": ["garminconnect==0.1.50"],
"codeowners": ["@cyberjunky"],
"config_flow": true,
"documentation": "https://github.com/cyberjunky/home-assistant-garmin_connect",
"issue_tracker": "https://github.com/cyberjunky/home-assistant-garmin_connect/issues",
"iot_class": "cloud_polling",
"version": "0.2.7"
"requirements": ["garminconnect==0.1.55", "tzlocal"],
"version": "0.2.16"
}

View File

@@ -2,27 +2,41 @@
from __future__ import annotations
import logging
import voluptuous as vol
from numbers import Number
import datetime
import pytz
from tzlocal import get_localzone
from homeassistant.components.sensor import (
SensorEntity,
SensorDeviceClass,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_ID,
DEVICE_CLASS_TIMESTAMP,
LENGTH_KILOMETERS,
)
from homeassistant.const import ATTR_ATTRIBUTION, CONF_ID
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_platform
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .alarm_util import calculate_next_active_alarms
from .const import (
DATA_COORDINATOR,
DOMAIN as GARMIN_DOMAIN,
GARMIN_ENTITY_LIST,
GEAR,
GEAR_ICONS,
)
_LOGGER = logging.getLogger(__name__)
@@ -66,8 +80,35 @@ async def async_setup_entry(
enabled_by_default,
)
)
if "gear" in coordinator.data:
for gear_item in coordinator.data["gear"]:
entities.append(
GarminConnectGearSensor(
coordinator,
unique_id,
gear_item[GEAR.UUID],
gear_item["gearTypeName"],
gear_item["displayName"],
None,
True,
)
)
async_add_entities(entities)
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service(
"set_active_gear", ENTITY_SERVICE_SCHEMA, coordinator.set_active_gear
)
ENTITY_SERVICE_SCHEMA = vol.Schema(
{
vol.Required(ATTR_ENTITY_ID): str,
vol.Required("activity_type"): str,
vol.Required("setting"): str,
}
)
class GarminConnectSensor(CoordinatorEntity, SensorEntity):
@@ -104,6 +145,10 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
@property
def native_value(self):
"""Return the state of the sensor."""
if self._type == "lastActivities":
return len(self.coordinator.data[self._type])
if not self.coordinator.data or not self.coordinator.data[self._type]:
return None
@@ -118,8 +163,8 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
)
if active_alarms:
date_time_obj = datetime.datetime.strptime(active_alarms[0], "%Y-%m-%dT%H:%M:%S")
timezone = pytz.timezone('UTC')
timezone_date_time_obj = timezone.localize(date_time_obj)
tz = get_localzone()
timezone_date_time_obj = date_time_obj.replace(tzinfo=tz)
return timezone_date_time_obj
else:
return None
@@ -128,11 +173,11 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
if self._device_class == SensorDeviceClass.TIMESTAMP:
date_time_obj = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
timezone = pytz.timezone('UTC')
timezone_date_time_obj = timezone.localize(date_time_obj)
tz = get_localzone()
timezone_date_time_obj = date_time_obj.replace(tzinfo=tz)
return timezone_date_time_obj
return round(value, 2)
return round(value, 2) if isinstance(value, Number) else value
@property
def extra_state_attributes(self):
@@ -143,6 +188,10 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
attributes = {
"last_synced": self.coordinator.data["lastSyncTimestampGMT"],
}
if self._type == "lastActivities":
attributes["last_Activities"] = self.coordinator.data[self._type]
if self._type == "nextAlarm":
attributes["next_alarms"] = calculate_next_active_alarms(
self.coordinator.data[self._type]
@@ -172,3 +221,134 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
and self.coordinator.data
and self._type in self.coordinator.data
)
class GarminConnectGearSensor(CoordinatorEntity, SensorEntity):
"""Representation of a Garmin Connect Sensor."""
def __init__(
self,
coordinator,
unique_id,
uuid,
sensor_type,
name,
device_class: None,
enabled_default: bool = True,
):
"""Initialize a Garmin Connect sensor."""
super().__init__(coordinator)
self._unique_id = unique_id
self._type = sensor_type
self._uuid = uuid
self._device_class = device_class
self._enabled_default = enabled_default
self._attr_name = name
self._attr_device_class = self._device_class
self._attr_icon = GEAR_ICONS[sensor_type]
self._attr_native_unit_of_measurement = LENGTH_KILOMETERS
self._attr_unique_id = f"{self._unique_id}_{self._uuid}"
self._attr_state_class = SensorStateClass.TOTAL
self._attr_device_class = "garmin_gear"
@property
def uuid(self):
"""Return the entity uuid"""
return self._uuid
@property
def native_value(self):
"""Return the state of the sensor."""
if not self.coordinator.data or not self._stats():
return None
value = self._stats()["totalDistance"]
return round(value / 1000, 2)
@property
def extra_state_attributes(self):
"""Return attributes for sensor."""
gear = self._gear()
stats = self._stats()
gear_defaults = self._gear_defaults()
activity_types = self.coordinator.data["activity_types"]
default_for_activity = self._activity_names_for_gear_defaults(
gear_defaults, activity_types
)
if not self.coordinator.data or not gear or not stats:
return {}
attributes = {
"last_synced": self.coordinator.data["lastSyncTimestampGMT"],
"total_activities": stats["totalActivities"],
"create_date": stats["createDate"],
"update_date": stats["updateDate"],
"date_begin": gear["dateBegin"],
"date_end": gear["dateEnd"],
"gear_make_name": gear["gearMakeName"],
"gear_model_name": gear["gearModelName"],
"gear_status_name": gear["gearStatusName"],
"custom_make_model": gear["customMakeModel"],
"maximum_meters": gear["maximumMeters"],
}
attributes["default_for_activity"] = (
", ".join(default_for_activity) if default_for_activity else "None"
)
return attributes
def _activity_names_for_gear_defaults(self, gear_defaults, activity_types):
return list(
map(
lambda b: b["typeKey"],
filter(
lambda a: a["typeId"]
in map(lambda d: d["activityTypePk"], gear_defaults),
activity_types,
),
)
)
@property
def device_info(self) -> DeviceInfo:
"""Return device information."""
return {
"identifiers": {(GARMIN_DOMAIN, self._unique_id)},
"name": "Garmin Connect",
"manufacturer": "Garmin Connect",
}
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
return self._enabled_default
@property
def available(self) -> bool:
"""Return True if entity is available."""
return super().available and self.coordinator.data and self._gear()
def _stats(self):
"""Get gear statistics from garmin"""
for gear_stats_item in self.coordinator.data["gear_stats"]:
if gear_stats_item[GEAR.UUID] == self._uuid:
return gear_stats_item
def _gear(self):
"""Get gear from garmin"""
for gear_item in self.coordinator.data["gear"]:
if gear_item[GEAR.UUID] == self._uuid:
return gear_item
def _gear_defaults(self):
"""Get gear defaults"""
return list(
filter(
lambda d: d[GEAR.UUID] == self.uuid and d["defaultGear"] is True,
self.coordinator.data["gear_defaults"],
)
)

View File

@@ -0,0 +1,39 @@
set_active_gear:
name: Set active gear for activity
# target:
# entity:
# integration: "garmin_connect"
fields:
activity_type:
required: true
name: activity type
description: garmin activity type
example: running
default: running
selector:
select:
options:
- running
- cycling
- hiking
- other
- walking
- swimming
setting:
required: true
name: setting
description: gear setting to apply
default: set this as default, unset others
selector:
select:
options:
- set this as default, unset others
- set as default
- unset default
entity_id:
description: entity
required: true
selector:
entity:
integration: garmin_connect
device_class: garmin_gear

View File

@@ -6,7 +6,7 @@
"error": {
"cannot_connect": "Falha na liga\u00e7\u00e3o",
"invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida",
"unknown": "Erro inesperado"
"unknown": "Erro desconhecido"
},
"step": {
"user": {
@@ -19,4 +19,4 @@
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Účet je už nakonfigurovaný"
},
"error": {
"cannot_connect": "Nepodarilo sa pripojiť",
"invalid_auth": "Neplatné overenie",
"too_many_requests": "Príliš veľa žiadostí, skúste to neskôr.",
"unknown": "Neočakávaná chyba"
},
"step": {
"user": {
"data": {
"password": "Heslo",
"username": "Užívateľské meno"
},
"description": "Zadajte svoje poverenia.",
"title": "Garmin Connect"
}
}
}
}