Add wellness sensors to Tractive integration (#96719)

* Add sleep sensors

* Add minutes rest sensor

* Add calories sensor

* Add state_class to entity descriptions
This commit is contained in:
Maciej Bieniek 2023-07-17 07:02:42 +00:00 committed by GitHub
parent 085eebc903
commit 79bcca2853
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 102 additions and 4 deletions

View File

@ -24,10 +24,14 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
from .const import ( from .const import (
ATTR_BUZZER, ATTR_BUZZER,
ATTR_CALORIES,
ATTR_DAILY_GOAL, ATTR_DAILY_GOAL,
ATTR_LED, ATTR_LED,
ATTR_LIVE_TRACKING, ATTR_LIVE_TRACKING,
ATTR_MINUTES_ACTIVE, ATTR_MINUTES_ACTIVE,
ATTR_MINUTES_DAY_SLEEP,
ATTR_MINUTES_NIGHT_SLEEP,
ATTR_MINUTES_REST,
ATTR_TRACKER_STATE, ATTR_TRACKER_STATE,
CLIENT, CLIENT,
CLIENT_ID, CLIENT_ID,
@ -38,6 +42,7 @@ from .const import (
TRACKER_ACTIVITY_STATUS_UPDATED, TRACKER_ACTIVITY_STATUS_UPDATED,
TRACKER_HARDWARE_STATUS_UPDATED, TRACKER_HARDWARE_STATUS_UPDATED,
TRACKER_POSITION_UPDATED, TRACKER_POSITION_UPDATED,
TRACKER_WELLNESS_STATUS_UPDATED,
) )
PLATFORMS = [ PLATFORMS = [
@ -202,6 +207,9 @@ class TractiveClient:
if event["message"] == "activity_update": if event["message"] == "activity_update":
self._send_activity_update(event) self._send_activity_update(event)
continue continue
if event["message"] == "wellness_overview":
self._send_wellness_update(event)
continue
if ( if (
"hardware" in event "hardware" in event
and self._last_hw_time != event["hardware"]["time"] and self._last_hw_time != event["hardware"]["time"]
@ -264,6 +272,17 @@ class TractiveClient:
TRACKER_ACTIVITY_STATUS_UPDATED, event["pet_id"], payload TRACKER_ACTIVITY_STATUS_UPDATED, event["pet_id"], payload
) )
def _send_wellness_update(self, event: dict[str, Any]) -> None:
payload = {
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"],
}
self._dispatch_tracker_event(
TRACKER_WELLNESS_STATUS_UPDATED, event["pet_id"], payload
)
def _send_position_update(self, event: dict[str, Any]) -> None: def _send_position_update(self, event: dict[str, Any]) -> None:
payload = { payload = {
"latitude": event["position"]["latlong"][0], "latitude": event["position"]["latlong"][0],

View File

@ -6,11 +6,15 @@ DOMAIN = "tractive"
RECONNECT_INTERVAL = timedelta(seconds=10) RECONNECT_INTERVAL = timedelta(seconds=10)
ATTR_DAILY_GOAL = "daily_goal"
ATTR_BUZZER = "buzzer" ATTR_BUZZER = "buzzer"
ATTR_CALORIES = "calories"
ATTR_DAILY_GOAL = "daily_goal"
ATTR_LED = "led" ATTR_LED = "led"
ATTR_LIVE_TRACKING = "live_tracking" ATTR_LIVE_TRACKING = "live_tracking"
ATTR_MINUTES_ACTIVE = "minutes_active" 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_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.
@ -23,5 +27,6 @@ TRACKABLES = "trackables"
TRACKER_HARDWARE_STATUS_UPDATED = f"{DOMAIN}_tracker_hardware_status_updated" TRACKER_HARDWARE_STATUS_UPDATED = f"{DOMAIN}_tracker_hardware_status_updated"
TRACKER_POSITION_UPDATED = f"{DOMAIN}_tracker_position_updated" TRACKER_POSITION_UPDATED = f"{DOMAIN}_tracker_position_updated"
TRACKER_ACTIVITY_STATUS_UPDATED = f"{DOMAIN}_tracker_activity_updated" TRACKER_ACTIVITY_STATUS_UPDATED = f"{DOMAIN}_tracker_activity_updated"
TRACKER_WELLNESS_STATUS_UPDATED = f"{DOMAIN}_tracker_wellness_updated"
SERVER_UNAVAILABLE = f"{DOMAIN}_server_unavailable" SERVER_UNAVAILABLE = f"{DOMAIN}_server_unavailable"

View File

@ -8,6 +8,7 @@ from homeassistant.components.sensor import (
SensorDeviceClass, SensorDeviceClass,
SensorEntity, SensorEntity,
SensorEntityDescription, SensorEntityDescription,
SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
@ -22,8 +23,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import Trackables from . import Trackables
from .const import ( from .const import (
ATTR_CALORIES,
ATTR_DAILY_GOAL, ATTR_DAILY_GOAL,
ATTR_MINUTES_ACTIVE, ATTR_MINUTES_ACTIVE,
ATTR_MINUTES_DAY_SLEEP,
ATTR_MINUTES_NIGHT_SLEEP,
ATTR_MINUTES_REST,
ATTR_TRACKER_STATE, ATTR_TRACKER_STATE,
CLIENT, CLIENT,
DOMAIN, DOMAIN,
@ -31,6 +36,7 @@ from .const import (
TRACKABLES, TRACKABLES,
TRACKER_ACTIVITY_STATUS_UPDATED, TRACKER_ACTIVITY_STATUS_UPDATED,
TRACKER_HARDWARE_STATUS_UPDATED, TRACKER_HARDWARE_STATUS_UPDATED,
TRACKER_WELLNESS_STATUS_UPDATED,
) )
from .entity import TractiveEntity from .entity import TractiveEntity
@ -107,8 +113,8 @@ class TractiveActivitySensor(TractiveSensor):
"""Tractive active sensor.""" """Tractive active sensor."""
@callback @callback
def handle_activity_status_update(self, event: dict[str, Any]) -> None: def handle_status_update(self, event: dict[str, Any]) -> None:
"""Handle activity status update.""" """Handle status update."""
self._attr_native_value = event[self.entity_description.key] self._attr_native_value = event[self.entity_description.key]
self._attr_available = True self._attr_available = True
self.async_write_ha_state() self.async_write_ha_state()
@ -120,7 +126,30 @@ class TractiveActivitySensor(TractiveSensor):
async_dispatcher_connect( async_dispatcher_connect(
self.hass, self.hass,
f"{TRACKER_ACTIVITY_STATUS_UPDATED}-{self._trackable['_id']}", f"{TRACKER_ACTIVITY_STATUS_UPDATED}-{self._trackable['_id']}",
self.handle_activity_status_update, self.handle_status_update,
)
)
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SERVER_UNAVAILABLE}-{self._user_id}",
self.handle_server_unavailable,
)
)
class TractiveWellnessSensor(TractiveActivitySensor):
"""Tractive wellness sensor."""
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{TRACKER_WELLNESS_STATUS_UPDATED}-{self._trackable['_id']}",
self.handle_status_update,
) )
) )
@ -155,6 +184,23 @@ SENSOR_TYPES: tuple[TractiveSensorEntityDescription, ...] = (
icon="mdi:clock-time-eight-outline", icon="mdi:clock-time-eight-outline",
native_unit_of_measurement=UnitOfTime.MINUTES, native_unit_of_measurement=UnitOfTime.MINUTES,
entity_class=TractiveActivitySensor, entity_class=TractiveActivitySensor,
state_class=SensorStateClass.TOTAL,
),
TractiveSensorEntityDescription(
key=ATTR_MINUTES_REST,
translation_key="minutes_rest",
icon="mdi:clock-time-eight-outline",
native_unit_of_measurement=UnitOfTime.MINUTES,
entity_class=TractiveWellnessSensor,
state_class=SensorStateClass.TOTAL,
),
TractiveSensorEntityDescription(
key=ATTR_CALORIES,
translation_key="calories",
icon="mdi:fire",
native_unit_of_measurement="kcal",
entity_class=TractiveWellnessSensor,
state_class=SensorStateClass.TOTAL,
), ),
TractiveSensorEntityDescription( TractiveSensorEntityDescription(
key=ATTR_DAILY_GOAL, key=ATTR_DAILY_GOAL,
@ -163,6 +209,22 @@ SENSOR_TYPES: tuple[TractiveSensorEntityDescription, ...] = (
native_unit_of_measurement=UnitOfTime.MINUTES, native_unit_of_measurement=UnitOfTime.MINUTES,
entity_class=TractiveActivitySensor, entity_class=TractiveActivitySensor,
), ),
TractiveSensorEntityDescription(
key=ATTR_MINUTES_DAY_SLEEP,
translation_key="minutes_day_sleep",
icon="mdi:sleep",
native_unit_of_measurement=UnitOfTime.MINUTES,
entity_class=TractiveWellnessSensor,
state_class=SensorStateClass.TOTAL,
),
TractiveSensorEntityDescription(
key=ATTR_MINUTES_NIGHT_SLEEP,
translation_key="minutes_night_sleep",
icon="mdi:sleep",
native_unit_of_measurement=UnitOfTime.MINUTES,
entity_class=TractiveWellnessSensor,
state_class=SensorStateClass.TOTAL,
),
) )

View File

@ -30,12 +30,24 @@
} }
}, },
"sensor": { "sensor": {
"calories": {
"name": "Calories burned"
},
"daily_goal": { "daily_goal": {
"name": "Daily goal" "name": "Daily goal"
}, },
"minutes_active": { "minutes_active": {
"name": "Minutes active" "name": "Minutes active"
}, },
"minutes_day_sleep": {
"name": "Day sleep"
},
"minutes_night_sleep": {
"name": "Night sleep"
},
"minutes_rest": {
"name": "Minutes rest"
},
"tracker_battery_level": { "tracker_battery_level": {
"name": "Tracker battery" "name": "Tracker battery"
}, },