diff --git a/homeassistant/components/tractive/__init__.py b/homeassistant/components/tractive/__init__.py index 1d51ab66585..66a2afccb43 100644 --- a/homeassistant/components/tractive/__init__.py +++ b/homeassistant/components/tractive/__init__.py @@ -27,6 +27,7 @@ from .const import ( ATTR_LED, ATTR_LIVE_TRACKING, ATTR_MINUTES_ACTIVE, + ATTR_TRACKER_STATE, CLIENT, DOMAIN, RECONNECT_INTERVAL, @@ -143,6 +144,8 @@ class TractiveClient: self._hass = hass self._client = client self._user_id = user_id + self._last_hw_time = 0 + self._last_pos_time = 0 self._listen_task: asyncio.Task | None = None @property @@ -181,20 +184,29 @@ class TractiveClient: if server_was_unavailable: _LOGGER.debug("Tractive is back online") server_was_unavailable = False - if event["message"] == "activity_update": self._send_activity_update(event) - else: - if "hardware" in event: - self._send_hardware_update(event) + continue + if ( + "hardware" in event + and self._last_hw_time != event["hardware"]["time"] + ): + self._last_hw_time = event["hardware"]["time"] + self._send_hardware_update(event) - if "position" in event: - self._send_position_update(event) + if ( + "position" in event + and self._last_pos_time != event["position"]["time"] + ): + self._last_pos_time = event["position"]["time"] + self._send_position_update(event) except aiotractive.exceptions.TractiveError: _LOGGER.debug( "Tractive is not available. Internet connection is down? Sleeping %i seconds and retrying", RECONNECT_INTERVAL.total_seconds(), ) + self._last_hw_time = 0 + self._last_pos_time = 0 async_dispatcher_send( self._hass, f"{SERVER_UNAVAILABLE}-{self._user_id}" ) @@ -206,6 +218,7 @@ class TractiveClient: # Sometimes hardware event doesn't contain complete data. payload = { ATTR_BATTERY_LEVEL: event["hardware"]["battery_level"], + ATTR_TRACKER_STATE: event["tracker_state"].lower(), ATTR_BATTERY_CHARGING: event["charging_state"] == "CHARGING", ATTR_LIVE_TRACKING: event.get("live_tracking", {}).get("active"), ATTR_BUZZER: event.get("buzzer_control", {}).get("active"), @@ -229,6 +242,7 @@ class TractiveClient: "latitude": event["position"]["latlong"][0], "longitude": event["position"]["latlong"][1], "accuracy": event["position"]["accuracy"], + "sensor_used": event["position"]["sensor_used"], } self._dispatch_tracker_event( TRACKER_POSITION_UPDATED, event["tracker_id"], payload diff --git a/homeassistant/components/tractive/const.py b/homeassistant/components/tractive/const.py index 6a61024cd51..0d7d62ccae7 100644 --- a/homeassistant/components/tractive/const.py +++ b/homeassistant/components/tractive/const.py @@ -11,6 +11,7 @@ ATTR_BUZZER = "buzzer" ATTR_LED = "led" ATTR_LIVE_TRACKING = "live_tracking" ATTR_MINUTES_ACTIVE = "minutes_active" +ATTR_TRACKER_STATE = "tracker_state" CLIENT = "client" TRACKABLES = "trackables" diff --git a/homeassistant/components/tractive/device_tracker.py b/homeassistant/components/tractive/device_tracker.py index a4109eee71c..218151ae769 100644 --- a/homeassistant/components/tractive/device_tracker.py +++ b/homeassistant/components/tractive/device_tracker.py @@ -3,7 +3,10 @@ from __future__ import annotations from typing import Any -from homeassistant.components.device_tracker import SOURCE_TYPE_GPS +from homeassistant.components.device_tracker import ( + SOURCE_TYPE_BLUETOOTH, + SOURCE_TYPE_GPS, +) from homeassistant.components.device_tracker.config_entry import TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback @@ -47,6 +50,7 @@ class TractiveDeviceTracker(TractiveEntity, TrackerEntity): self._latitude: float = item.pos_report["latlong"][0] self._longitude: float = item.pos_report["latlong"][1] self._accuracy: int = item.pos_report["pos_uncertainty"] + self._source_type: str = item.pos_report["sensor_used"] self._attr_name = f"{self._tracker_id} {item.trackable['details']['name']}" self._attr_unique_id = item.trackable["_id"] @@ -54,6 +58,8 @@ class TractiveDeviceTracker(TractiveEntity, TrackerEntity): @property def source_type(self) -> str: """Return the source type, eg gps or router, of the device.""" + if self._source_type == "PHONE": + return SOURCE_TYPE_BLUETOOTH return SOURCE_TYPE_GPS @property @@ -87,6 +93,7 @@ class TractiveDeviceTracker(TractiveEntity, TrackerEntity): self._latitude = event["latitude"] self._longitude = event["longitude"] self._accuracy = event["accuracy"] + self._source_type = event["sensor_used"] self._attr_available = True self.async_write_ha_state() diff --git a/homeassistant/components/tractive/sensor.py b/homeassistant/components/tractive/sensor.py index 2bbd755da2c..3f6c18fa07f 100644 --- a/homeassistant/components/tractive/sensor.py +++ b/homeassistant/components/tractive/sensor.py @@ -24,6 +24,7 @@ from . import Trackables from .const import ( ATTR_DAILY_GOAL, ATTR_MINUTES_ACTIVE, + ATTR_TRACKER_STATE, CLIENT, DOMAIN, SERVER_UNAVAILABLE, @@ -77,7 +78,9 @@ class TractiveHardwareSensor(TractiveSensor): @callback def handle_hardware_status_update(self, event: dict[str, Any]) -> None: """Handle hardware status update.""" - self._attr_native_value = event[self.entity_description.key] + if (_state := event[self.entity_description.key]) is None: + return + self._attr_native_value = _state self._attr_available = True self.async_write_ha_state() @@ -140,6 +143,14 @@ SENSOR_TYPES: tuple[TractiveSensorEntityDescription, ...] = ( entity_class=TractiveHardwareSensor, entity_category=ENTITY_CATEGORY_DIAGNOSTIC, ), + TractiveSensorEntityDescription( + # Currently, only state operational and not_reporting are used + # More states are available by polling the data + key=ATTR_TRACKER_STATE, + name="Tracker state", + device_class="tractive__tracker_state", + entity_class=TractiveHardwareSensor, + ), TractiveSensorEntityDescription( key=ATTR_MINUTES_ACTIVE, name="Minutes Active", diff --git a/homeassistant/components/tractive/strings.sensor.json b/homeassistant/components/tractive/strings.sensor.json new file mode 100644 index 00000000000..b9c2cd603da --- /dev/null +++ b/homeassistant/components/tractive/strings.sensor.json @@ -0,0 +1,10 @@ +{ + "state": { + "tractive__tracker_state": { + "not_reporting": "Not reporting", + "operational": "Operational", + "system_shutdown_user": "System shutdown user", + "system_startup": "System startup" + } + } +}