Add Tractive sleep and activity sensors (#98575)

* Add sleep and activity sensors

* Use device class ENUM

* Default value for value_fn
This commit is contained in:
Maciej Bieniek 2023-08-19 09:48:23 +00:00 committed by GitHub
parent 8a6bde1191
commit 66c10facfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 4 deletions

View File

@ -23,6 +23,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from .const import ( from .const import (
ATTR_ACTIVITY_LABEL,
ATTR_BUZZER, ATTR_BUZZER,
ATTR_CALORIES, ATTR_CALORIES,
ATTR_DAILY_GOAL, ATTR_DAILY_GOAL,
@ -32,6 +33,7 @@ from .const import (
ATTR_MINUTES_DAY_SLEEP, ATTR_MINUTES_DAY_SLEEP,
ATTR_MINUTES_NIGHT_SLEEP, ATTR_MINUTES_NIGHT_SLEEP,
ATTR_MINUTES_REST, ATTR_MINUTES_REST,
ATTR_SLEEP_LABEL,
ATTR_TRACKER_STATE, ATTR_TRACKER_STATE,
CLIENT, CLIENT,
CLIENT_ID, CLIENT_ID,
@ -281,10 +283,12 @@ class TractiveClient:
def _send_wellness_update(self, event: dict[str, Any]) -> None: def _send_wellness_update(self, event: dict[str, Any]) -> None:
payload = { payload = {
ATTR_ACTIVITY_LABEL: event["wellness"]["activity_label"],
ATTR_CALORIES: event["activity"]["calories"], ATTR_CALORIES: event["activity"]["calories"],
ATTR_MINUTES_DAY_SLEEP: event["sleep"]["minutes_day_sleep"], ATTR_MINUTES_DAY_SLEEP: event["sleep"]["minutes_day_sleep"],
ATTR_MINUTES_NIGHT_SLEEP: event["sleep"]["minutes_night_sleep"], ATTR_MINUTES_NIGHT_SLEEP: event["sleep"]["minutes_night_sleep"],
ATTR_MINUTES_REST: event["activity"]["minutes_rest"], ATTR_MINUTES_REST: event["activity"]["minutes_rest"],
ATTR_SLEEP_LABEL: event["wellness"]["sleep_label"],
} }
self._dispatch_tracker_event( self._dispatch_tracker_event(
TRACKER_WELLNESS_STATUS_UPDATED, event["pet_id"], payload TRACKER_WELLNESS_STATUS_UPDATED, event["pet_id"], payload

View File

@ -6,6 +6,7 @@ DOMAIN = "tractive"
RECONNECT_INTERVAL = timedelta(seconds=10) RECONNECT_INTERVAL = timedelta(seconds=10)
ATTR_ACTIVITY_LABEL = "activity_label"
ATTR_BUZZER = "buzzer" ATTR_BUZZER = "buzzer"
ATTR_CALORIES = "calories" ATTR_CALORIES = "calories"
ATTR_DAILY_GOAL = "daily_goal" ATTR_DAILY_GOAL = "daily_goal"
@ -15,6 +16,7 @@ ATTR_MINUTES_ACTIVE = "minutes_active"
ATTR_MINUTES_DAY_SLEEP = "minutes_day_sleep" ATTR_MINUTES_DAY_SLEEP = "minutes_day_sleep"
ATTR_MINUTES_NIGHT_SLEEP = "minutes_night_sleep" ATTR_MINUTES_NIGHT_SLEEP = "minutes_night_sleep"
ATTR_MINUTES_REST = "minutes_rest" ATTR_MINUTES_REST = "minutes_rest"
ATTR_SLEEP_LABEL = "sleep_label"
ATTR_TRACKER_STATE = "tracker_state" ATTR_TRACKER_STATE = "tracker_state"
# This client ID was issued by Tractive specifically for Home Assistant. # This client ID was issued by Tractive specifically for Home Assistant.

View File

@ -1,6 +1,7 @@
"""Support for Tractive sensors.""" """Support for Tractive sensors."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
@ -19,15 +20,18 @@ from homeassistant.const import (
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from . import Trackables, TractiveClient from . import Trackables, TractiveClient
from .const import ( from .const import (
ATTR_ACTIVITY_LABEL,
ATTR_CALORIES, ATTR_CALORIES,
ATTR_DAILY_GOAL, ATTR_DAILY_GOAL,
ATTR_MINUTES_ACTIVE, ATTR_MINUTES_ACTIVE,
ATTR_MINUTES_DAY_SLEEP, ATTR_MINUTES_DAY_SLEEP,
ATTR_MINUTES_NIGHT_SLEEP, ATTR_MINUTES_NIGHT_SLEEP,
ATTR_MINUTES_REST, ATTR_MINUTES_REST,
ATTR_SLEEP_LABEL,
ATTR_TRACKER_STATE, ATTR_TRACKER_STATE,
CLIENT, CLIENT,
DOMAIN, DOMAIN,
@ -53,11 +57,14 @@ class TractiveSensorEntityDescription(
"""Class describing Tractive sensor entities.""" """Class describing Tractive sensor entities."""
hardware_sensor: bool = False hardware_sensor: bool = False
value_fn: Callable[[StateType], StateType] = lambda state: state
class TractiveSensor(TractiveEntity, SensorEntity): class TractiveSensor(TractiveEntity, SensorEntity):
"""Tractive sensor.""" """Tractive sensor."""
entity_description: TractiveSensorEntityDescription
def __init__( def __init__(
self, self,
client: TractiveClient, client: TractiveClient,
@ -82,7 +89,9 @@ class TractiveSensor(TractiveEntity, SensorEntity):
@callback @callback
def handle_status_update(self, event: dict[str, Any]) -> None: def handle_status_update(self, event: dict[str, Any]) -> None:
"""Handle status update.""" """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) super().handle_status_update(event)
@ -159,6 +168,30 @@ SENSOR_TYPES: tuple[TractiveSensorEntityDescription, ...] = (
signal_prefix=TRACKER_WELLNESS_STATUS_UPDATED, signal_prefix=TRACKER_WELLNESS_STATUS_UPDATED,
state_class=SensorStateClass.TOTAL, 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",
],
),
) )

View File

@ -30,15 +30,22 @@
} }
}, },
"sensor": { "sensor": {
"activity": {
"name": "Activity",
"state": {
"ok": "OK",
"good": "Good"
}
},
"activity_time": {
"name": "Activity time"
},
"calories": { "calories": {
"name": "Calories burned" "name": "Calories burned"
}, },
"daily_goal": { "daily_goal": {
"name": "Daily goal" "name": "Daily goal"
}, },
"activity_time": {
"name": "Activity time"
},
"minutes_day_sleep": { "minutes_day_sleep": {
"name": "Day sleep" "name": "Day sleep"
}, },
@ -48,6 +55,13 @@
"rest_time": { "rest_time": {
"name": "Rest time" "name": "Rest time"
}, },
"sleep": {
"name": "Sleep",
"state": {
"ok": "OK",
"good": "Good"
}
},
"tracker_battery_level": { "tracker_battery_level": {
"name": "Tracker battery" "name": "Tracker battery"
}, },