From 66c10facfa0432e37c425f62ba57c4ce28c377b1 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sat, 19 Aug 2023 09:48:23 +0000 Subject: [PATCH] Add Tractive `sleep` and `activity` sensors (#98575) * Add sleep and activity sensors * Use device class ENUM * Default value for value_fn --- homeassistant/components/tractive/__init__.py | 4 +++ homeassistant/components/tractive/const.py | 2 ++ homeassistant/components/tractive/sensor.py | 35 ++++++++++++++++++- .../components/tractive/strings.json | 20 +++++++++-- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/tractive/__init__.py b/homeassistant/components/tractive/__init__.py index 043e074270e..c04676768c5 100644 --- a/homeassistant/components/tractive/__init__.py +++ b/homeassistant/components/tractive/__init__.py @@ -23,6 +23,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.dispatcher import async_dispatcher_send from .const import ( + ATTR_ACTIVITY_LABEL, ATTR_BUZZER, ATTR_CALORIES, ATTR_DAILY_GOAL, @@ -32,6 +33,7 @@ from .const import ( ATTR_MINUTES_DAY_SLEEP, ATTR_MINUTES_NIGHT_SLEEP, ATTR_MINUTES_REST, + ATTR_SLEEP_LABEL, ATTR_TRACKER_STATE, CLIENT, CLIENT_ID, @@ -281,10 +283,12 @@ class TractiveClient: def _send_wellness_update(self, event: dict[str, Any]) -> None: payload = { + ATTR_ACTIVITY_LABEL: event["wellness"]["activity_label"], ATTR_CALORIES: event["activity"]["calories"], ATTR_MINUTES_DAY_SLEEP: event["sleep"]["minutes_day_sleep"], ATTR_MINUTES_NIGHT_SLEEP: event["sleep"]["minutes_night_sleep"], ATTR_MINUTES_REST: event["activity"]["minutes_rest"], + ATTR_SLEEP_LABEL: event["wellness"]["sleep_label"], } self._dispatch_tracker_event( TRACKER_WELLNESS_STATUS_UPDATED, event["pet_id"], payload diff --git a/homeassistant/components/tractive/const.py b/homeassistant/components/tractive/const.py index 81936ae5d80..254a8c274f3 100644 --- a/homeassistant/components/tractive/const.py +++ b/homeassistant/components/tractive/const.py @@ -6,6 +6,7 @@ DOMAIN = "tractive" RECONNECT_INTERVAL = timedelta(seconds=10) +ATTR_ACTIVITY_LABEL = "activity_label" ATTR_BUZZER = "buzzer" ATTR_CALORIES = "calories" ATTR_DAILY_GOAL = "daily_goal" @@ -15,6 +16,7 @@ ATTR_MINUTES_ACTIVE = "minutes_active" ATTR_MINUTES_DAY_SLEEP = "minutes_day_sleep" ATTR_MINUTES_NIGHT_SLEEP = "minutes_night_sleep" ATTR_MINUTES_REST = "minutes_rest" +ATTR_SLEEP_LABEL = "sleep_label" ATTR_TRACKER_STATE = "tracker_state" # This client ID was issued by Tractive specifically for Home Assistant. diff --git a/homeassistant/components/tractive/sensor.py b/homeassistant/components/tractive/sensor.py index 6891b74d31b..0d486606802 100644 --- a/homeassistant/components/tractive/sensor.py +++ b/homeassistant/components/tractive/sensor.py @@ -1,6 +1,7 @@ """Support for Tractive sensors.""" from __future__ import annotations +from collections.abc import Callable from dataclasses import dataclass from typing import Any @@ -19,15 +20,18 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import StateType from . import Trackables, TractiveClient from .const import ( + ATTR_ACTIVITY_LABEL, ATTR_CALORIES, ATTR_DAILY_GOAL, ATTR_MINUTES_ACTIVE, ATTR_MINUTES_DAY_SLEEP, ATTR_MINUTES_NIGHT_SLEEP, ATTR_MINUTES_REST, + ATTR_SLEEP_LABEL, ATTR_TRACKER_STATE, CLIENT, DOMAIN, @@ -53,11 +57,14 @@ class TractiveSensorEntityDescription( """Class describing Tractive sensor entities.""" hardware_sensor: bool = False + value_fn: Callable[[StateType], StateType] = lambda state: state class TractiveSensor(TractiveEntity, SensorEntity): """Tractive sensor.""" + entity_description: TractiveSensorEntityDescription + def __init__( self, client: TractiveClient, @@ -82,7 +89,9 @@ class TractiveSensor(TractiveEntity, SensorEntity): @callback def handle_status_update(self, event: dict[str, Any]) -> None: """Handle status update.""" - self._attr_native_value = event[self.entity_description.key] + self._attr_native_value = self.entity_description.value_fn( + event[self.entity_description.key] + ) super().handle_status_update(event) @@ -159,6 +168,30 @@ SENSOR_TYPES: tuple[TractiveSensorEntityDescription, ...] = ( signal_prefix=TRACKER_WELLNESS_STATUS_UPDATED, state_class=SensorStateClass.TOTAL, ), + TractiveSensorEntityDescription( + key=ATTR_SLEEP_LABEL, + translation_key="sleep", + icon="mdi:sleep", + signal_prefix=TRACKER_WELLNESS_STATUS_UPDATED, + value_fn=lambda state: state.lower() if isinstance(state, str) else state, + device_class=SensorDeviceClass.ENUM, + options=[ + "ok", + "good", + ], + ), + TractiveSensorEntityDescription( + key=ATTR_ACTIVITY_LABEL, + translation_key="activity", + icon="mdi:run", + signal_prefix=TRACKER_WELLNESS_STATUS_UPDATED, + value_fn=lambda state: state.lower() if isinstance(state, str) else state, + device_class=SensorDeviceClass.ENUM, + options=[ + "ok", + "good", + ], + ), ) diff --git a/homeassistant/components/tractive/strings.json b/homeassistant/components/tractive/strings.json index 4053d2658f5..e315a8e6013 100644 --- a/homeassistant/components/tractive/strings.json +++ b/homeassistant/components/tractive/strings.json @@ -30,15 +30,22 @@ } }, "sensor": { + "activity": { + "name": "Activity", + "state": { + "ok": "OK", + "good": "Good" + } + }, + "activity_time": { + "name": "Activity time" + }, "calories": { "name": "Calories burned" }, "daily_goal": { "name": "Daily goal" }, - "activity_time": { - "name": "Activity time" - }, "minutes_day_sleep": { "name": "Day sleep" }, @@ -48,6 +55,13 @@ "rest_time": { "name": "Rest time" }, + "sleep": { + "name": "Sleep", + "state": { + "ok": "OK", + "good": "Good" + } + }, "tracker_battery_level": { "name": "Tracker battery" },