Skip to content

Commit 84d03c8

Browse files
committed
Release v1.5.1: Refine trip metrics and reduce API load
1 parent 7ff61bf commit 84d03c8

File tree

16 files changed

+84
-47
lines changed

16 files changed

+84
-47
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to this project are documented in this file.
44

5+
## [1.5.1] - 2026-03-18
6+
7+
### Changed
8+
- **Trip Metrics**: Updated the 7-day average speed sensor to use Geotab's trip `averageSpeed` so it now represents true driving speed instead of averaging peak speeds.
9+
- **API Load**: Reduced trip and fault fetch limits, and moved Go fault diagnostic lookups to on-demand loading so normal polling avoids an unnecessary extra multi-call payload.
10+
11+
### Fixed
12+
- **Developer Documentation**: Added README instructions for running `debug_geotab.py` safely with environment variables.
13+
- **Translations**: Renamed the average speed sensor labels across all bundled translations to match the corrected metric.
14+
515
## [1.5.0] - 2026-03-18
616

717
### Changed

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Entities are logically categorized to ensure a streamlined user interface and ef
4242

4343
### Trip Statistics
4444
* **Aggregated Metrics**: Daily, weekly, and monthly distance tracking.
45-
* **Operational Analysis**: Trip counts and weekly idle time reports.
45+
* **Operational Analysis**: Trip counts, average driving speed, and weekly idle time reports.
4646
* **Last Journey**: Comprehensive data on the most recently completed trip.
4747

4848
---
@@ -81,12 +81,27 @@ Entities are logically categorized to ensure a streamlined user interface and ef
8181

8282
## 🛡️ Technical Integrity & Security
8383

84-
* **API Optimization**: Utilizes an asynchronous architecture to minimize blocking calls and optimize performance during high-volume data retrieval.
84+
* **API Optimization**: Utilizes an asynchronous architecture, cached fault metadata, and a reduced trip window to minimize unnecessary API load during high-volume polling.
8585
* **Resilience**: Features a robust circuit breaker mechanism and automated error recovery to handle API outages or connectivity fluctuations gracefully.
8686
* **Privacy**: Implements data masking protocols for sensitive account information within the public-facing user interface.
8787

8888
---
8989

90+
## 🧪 Local Debug
91+
92+
The repository includes `debug_geotab.py` for manual local checks against the Geotab API.
93+
94+
Before running it, export your credentials as environment variables instead of editing the file:
95+
96+
```bash
97+
export GEOTAB_USERNAME="your@email.com"
98+
export GEOTAB_PASSWORD="your-password"
99+
export GEOTAB_DATABASE="your-database"
100+
python3 debug_geotab.py
101+
```
102+
103+
---
104+
90105
## 📖 Further Documentation
91106

92107
For comprehensive technical specifications, automation templates, and advanced troubleshooting, please refer to the [Documentation folder](docs/).

custom_components/geotab/api.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
_LOGGER = logging.getLogger(__name__)
1717

1818
TRIP_HISTORY_DAYS = 30
19-
TRIP_RESULTS_LIMIT = 500
20-
FAULT_RESULTS_PER_DEVICE = 20
19+
TRIP_RESULTS_LIMIT = 250
20+
FAULT_RESULTS_PER_DEVICE = 10
2121

2222

2323
class GeotabApiClientError(Exception):
@@ -102,24 +102,12 @@ def _blocking_fetch_all(self, include_trips: bool = True) -> tuple[list, list, l
102102
"deviceSearch": {"deviceIds": device_ids},
103103
"state": "Active",
104104
},
105-
"resultsLimit": max(len(device_ids) * FAULT_RESULTS_PER_DEVICE, 100),
105+
"resultsLimit": max(len(device_ids) * FAULT_RESULTS_PER_DEVICE, 20),
106106
},
107107
),
108108
]
109109
call_map = ["status", "faults"]
110110

111-
if not self._diagnostics_lookup_cache:
112-
calls.append(
113-
(
114-
"Get",
115-
{
116-
"typeName": "Diagnostic",
117-
"search": {"diagnosticType": "GoFault"},
118-
},
119-
)
120-
)
121-
call_map.append("diagnostics_lookup")
122-
123111
if include_trips:
124112
from_date = (
125113
datetime.now(timezone.utc) - timedelta(days=TRIP_HISTORY_DAYS)
@@ -143,6 +131,19 @@ def _blocking_fetch_all(self, include_trips: bool = True) -> tuple[list, list, l
143131
results = self.client.multi_call(calls)
144132
return devices, results, call_map
145133

134+
def _blocking_load_fault_diagnostics(self) -> dict[str, str]:
135+
"""Load diagnostic names for Geotab Go faults on demand."""
136+
result = self.client.get("Diagnostic", search={"diagnosticType": "GoFault"})
137+
lookup: dict[str, str] = {}
138+
for diagnostic in result:
139+
if not isinstance(diagnostic, dict):
140+
continue
141+
diagnostic_id = diagnostic.get("id")
142+
diagnostic_name = diagnostic.get("name")
143+
if diagnostic_id and diagnostic_name:
144+
lookup[diagnostic_id] = diagnostic_name
145+
return lookup
146+
146147
def _extract_status_diagnostics(self, status_info: dict[str, Any]) -> dict[str, Any]:
147148
"""Extract latest diagnostics already embedded in DeviceStatusInfo."""
148149
diagnostics: dict[str, Any] = {}
@@ -178,6 +179,7 @@ async def async_get_full_device_data(
178179
fault_map: defaultdict[str, list[dict[str, Any]]] = defaultdict(list)
179180
trip_results_dict: dict[str, list[dict[str, Any]]] = {}
180181
diagnostics_lookup = dict(self._diagnostics_lookup_cache)
182+
unknown_fault_diagnostic_ids: set[str] = set()
181183

182184
for index, key in enumerate(call_map):
183185
result = results[index]
@@ -205,15 +207,11 @@ async def async_get_full_device_data(
205207
device = fault.get("device")
206208
if isinstance(device, dict) and device.get("id"):
207209
fault_map[device["id"]].append(fault)
208-
209-
elif key == "diagnostics_lookup" and isinstance(result, list):
210-
for diagnostic in result:
211-
if not isinstance(diagnostic, dict):
212-
continue
213-
diagnostic_id = diagnostic.get("id")
214-
diagnostic_name = diagnostic.get("name")
215-
if diagnostic_id and diagnostic_name:
216-
diagnostics_lookup[diagnostic_id] = diagnostic_name
210+
diagnostic = fault.get("diagnostic")
211+
if isinstance(diagnostic, dict):
212+
diagnostic_id = diagnostic.get("id")
213+
if diagnostic_id and diagnostic_id not in diagnostics_lookup:
214+
unknown_fault_diagnostic_ids.add(diagnostic_id)
217215

218216
elif key.startswith("trip_") and isinstance(result, list):
219217
device_id = key[5:]
@@ -229,6 +227,14 @@ async def async_get_full_device_data(
229227
reverse=True,
230228
)
231229

230+
if unknown_fault_diagnostic_ids:
231+
diagnostics_lookup.update(
232+
await asyncio.wait_for(
233+
loop.run_in_executor(None, self._blocking_load_fault_diagnostics),
234+
timeout=20,
235+
)
236+
)
237+
232238
self._diagnostics_lookup_cache = diagnostics_lookup
233239

234240
total_diagnostics = sum(len(values) for values in diagnostics_map.values())

custom_components/geotab/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
"iot_class": "cloud_polling",
88
"issue_tracker": "https://github.com/Syax89/geotab-hacs-integration/issues",
99
"requirements": ["mygeotab>=0.9.1,<1.0.0"],
10-
"version": "1.5.0"
10+
"version": "1.5.1"
1111
}

custom_components/geotab/strings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"monthly_distance": { "name": "Monthly Distance" },
5353
"daily_trip_count": { "name": "Daily Trip Count" },
5454
"weekly_trip_count": { "name": "Weekly Trip Count" },
55-
"average_trip_speed": { "name": "Average Trip Speed (7d)" },
55+
"average_trip_speed": { "name": "Average Driving Speed (7d)" },
5656
"weekly_idle_time": { "name": "Weekly Idle Time" },
5757
"last_update": { "name": "Last Update" }
5858
},

custom_components/geotab/translations/de.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"monthly_distance": { "name": "Fahrten: Monatliche Strecke" },
5353
"daily_trip_count": { "name": "Fahrten: Anzahl täglicher Fahrten" },
5454
"weekly_trip_count": { "name": "Fahrten: Anzahl wöchentlicher Fahrten" },
55-
"average_trip_speed": { "name": "Fahrten: Durchschnittsgeschwindigkeit (7t)" },
55+
"average_trip_speed": { "name": "Fahrten: Durchschnittliche Fahrgeschwindigkeit (7t)" },
5656
"weekly_idle_time": { "name": "Fahrten: Wöchentliche Leerlaufzeit" },
5757
"last_update": { "name": "Letzte Aktualisierung" }
5858
},

custom_components/geotab/translations/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"monthly_distance": { "name": "Trips: Monthly Distance" },
5353
"daily_trip_count": { "name": "Trips: Daily Trip Count" },
5454
"weekly_trip_count": { "name": "Trips: Weekly Trip Count" },
55-
"average_trip_speed": { "name": "Trips: Avg Speed (7d)" },
55+
"average_trip_speed": { "name": "Trips: Avg Driving Speed (7d)" },
5656
"weekly_idle_time": { "name": "Trips: Weekly Idle Time" },
5757
"last_update": { "name": "Last Update" }
5858
},

custom_components/geotab/translations/es.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"monthly_distance": { "name": "Viajes: Distancia mensual" },
5353
"daily_trip_count": { "name": "Viajes: Contador de viajes diario" },
5454
"weekly_trip_count": { "name": "Viajes: Contador de viajes semanal" },
55-
"average_trip_speed": { "name": "Viajes: Velocidad media (7d)" },
55+
"average_trip_speed": { "name": "Viajes: Velocidad media de marcha (7d)" },
5656
"weekly_idle_time": { "name": "Viajes: Tiempo al ralentí semanal" },
5757
"last_update": { "name": "Última actualización" }
5858
},

custom_components/geotab/translations/fr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"monthly_distance": { "name": "Trajets : Distance mensuelle" },
5353
"daily_trip_count": { "name": "Trajets : Nombre de trajets quotidiens" },
5454
"weekly_trip_count": { "name": "Trajets : Nombre de trajets hebdomadaires" },
55-
"average_trip_speed": { "name": "Trajets : Vitesse moyenne (7j)" },
55+
"average_trip_speed": { "name": "Trajets : Vitesse moyenne de conduite (7j)" },
5656
"weekly_idle_time": { "name": "Trajets : Temps au ralenti hebdomadaire" },
5757
"last_update": { "name": "Dernière mise à jour" }
5858
},

custom_components/geotab/translations/it.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"monthly_distance": { "name": "Viaggi: Distanza Mensile" },
5353
"daily_trip_count": { "name": "Viaggi: Numero Viaggi Giornalieri" },
5454
"weekly_trip_count": { "name": "Viaggi: Numero Viaggi Settimanali" },
55-
"average_trip_speed": { "name": "Viaggi: Velocità Media (7g)" },
55+
"average_trip_speed": { "name": "Viaggi: Velocità Media di Marcia (7g)" },
5656
"weekly_idle_time": { "name": "Viaggi: Tempo al Minimo Settimanale" },
5757
"last_update": { "name": "Ultimo Aggiornamento" }
5858
},

0 commit comments

Comments
 (0)