docs: reformat FEATURES.md — summary table with dates, numerical order

- All 10 FEATs now in strict numerical order under single Feature ideas section
- Added summary table (ID, Status, Created, Title, Phase) for quick scanning
- Created dates sourced from git history (ef3a951 through bc29a01)
- Removed orphan Ideas that landed section (FEAT-06/07 merged into main flow)
This commit is contained in:
Mathieu Moran
2026-08-12 02:34:36 -04:00
parent 7a6c1750c9
commit 555af3694e
2 changed files with 42 additions and 22 deletions
+32 -20
View File
@@ -16,6 +16,23 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
--- ---
## Summary
| ID | Status | Created | Title | Phase |
|----|--------|---------|-------|-------|
| FEAT-01 | `IDEA` | 2026-08-10 | Parallel/concurrent chunked dashcam uploads | Phase 27 |
| FEAT-02 | `IDEA` | 2026-08-10 | QR / claim-code device pairing flow | Phase 27 |
| FEAT-03 | `SHIPPED` | 2026-08-10 | Manual start odometer fallback | Phase 27 |
| FEAT-04 | `IDEA` | 2026-08-11 | Replace SIM868 2G with 4G LTE modem | Phase 27 |
| FEAT-05 | `SHIPPED` | 2026-08-11 | DTC history synced to remote server | Phase 27 |
| FEAT-06 | `SHIPPED` | 2026-08-11 | Trip ID column in history table | Phase 26 |
| FEAT-07 | `SHIPPED` | 2026-08-11 | Phone-hotspot auto-connect docs | Phase 22 |
| FEAT-08 | `SHIPPED` | 2026-08-11 | Predictable IP on phone hotspot | Phase 22 |
| FEAT-09 | `WONTFIX` | 2026-08-11 | mDNS (yaris.local) on phone hotspot | — |
| FEAT-10 | `IDEA` | 2026-08-12 | Home Assistant integration | unassigned |
---
## Feature ideas ## Feature ideas
### FEAT-01 — Parallel/concurrent chunked dashcam uploads ### FEAT-01 — Parallel/concurrent chunked dashcam uploads
@@ -92,6 +109,21 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
Upload skips when OBD disconnected (avoids clearing server DTCs on Pi reboot). Upload skips when OBD disconnected (avoids clearing server DTCs on Pi reboot).
Snapshot is hash-compared to avoid redundant uploads. Snapshot is hash-compared to avoid redundant uploads.
### FEAT-06 — Trip ID column in history table
- **Status**: `SHIPPED` 2026-08-11
- **Area**: `static/js/history.js`, `templates/history.html`, `server/templates/history.html`
- **Phase**: Phase 26
- **What**: Narrow `#ID` column on server history (hidden on Pi via `data-show-id`).
Checkbox column and batch-delete restored on server only.
### FEAT-07 — Phone-hotspot auto-connect documentation
- **Status**: `SHIPPED` 2026-08-11 (already implemented in Phase 22, documented here)
- **Area**: `deploy/yaris_uplink.py`, Settings page, netwatch timer
- **What**: The Pi auto-connects to a saved phone hotspot on wlan1 within ~60s of it
appearing in range, and falls back to home-wifi when it disappears. The wlan0 AP
stays up regardless. Test flow: Settings → save hotspot name + password → "Wait for
hotspot" → enable phone hotspot → verify mode changes to "Phone hotspot".
### FEAT-08 — Predictable IP on phone hotspot (DHCP client-id) ### FEAT-08 — Predictable IP on phone hotspot (DHCP client-id)
- **Status**: `SHIPPED` 2026-08-11 (Phase 22) - **Status**: `SHIPPED` 2026-08-11 (Phase 22)
- **Area**: `deploy/yaris_uplink.py`, phone-hotspot NM profile - **Area**: `deploy/yaris_uplink.py`, phone-hotspot NM profile
@@ -151,24 +183,4 @@ dedicated PLAN.md phase (or fold it into the relevant existing work) and mark it
- Keep the integration opt-in and lightweight — don't add MQTT dependencies to the core - Keep the integration opt-in and lightweight — don't add MQTT dependencies to the core
app.py startup unless the user explicitly configures it. app.py startup unless the user explicitly configures it.
---
## Ideas that landed
(No entries yet — shipped features land here. Keep the original idea text intact and add the
shipped phase/date.)
### FEAT-07 — Phone-hotspot auto-connect documentation
- **Status**: `SHIPPED` 2026-08-11 (already implemented in Phase 22, documented here)
- **Area**: `deploy/yaris_uplink.py`, Settings page, netwatch timer
- **What**: The Pi auto-connects to a saved phone hotspot on wlan1 within ~60s of it
appearing in range, and falls back to home-wifi when it disappears. The wlan0 AP
stays up regardless. Test flow: Settings → save hotspot name + password → "Wait for
hotspot" → enable phone hotspot → verify mode changes to "Phone hotspot".
### FEAT-06 — Trip ID column in history table
- **Status**: `SHIPPED` 2026-08-11
- **Area**: `static/js/history.js`, `templates/history.html`, `server/templates/history.html`
- **Phase**: Phase 26
- **What**: Narrow `#ID` column on server history (hidden on Pi via `data-show-id`).
Checkbox column and batch-delete restored on server only.
@@ -24,13 +24,18 @@ import okhttp3.Request
import okio.buffer import okio.buffer
import okio.sink import okio.sink
import java.io.File import java.io.File
import java.util.concurrent.TimeUnit
class DownloadWorker( class DownloadWorker(
context: Context, context: Context,
params: WorkerParameters, params: WorkerParameters,
) : CoroutineWorker(context, params) { ) : CoroutineWorker(context, params) {
private val okHttp = OkHttpClient.Builder().build() private val okHttp = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.MINUTES) // slow Pi WiFi can take 5min+ per file
.writeTimeout(30, TimeUnit.SECONDS)
.build()
private val notificationManager = NotificationManagerCompat.from(context) private val notificationManager = NotificationManagerCompat.from(context)
companion object { companion object {
@@ -69,13 +74,16 @@ class DownloadWorker(
dir.mkdirs() dir.mkdirs()
val localFile = File(dir, filename) val localFile = File(dir, filename)
// Skip if already downloaded // Skip if already downloaded with correct size
if (localFile.exists() && localFile.length() == totalSize && totalSize > 0) { if (localFile.exists() && localFile.length() == totalSize && totalSize > 0) {
return@withContext Result.success( return@withContext Result.success(
workDataOf("filename" to filename, "bytes" to totalSize) workDataOf("filename" to filename, "bytes" to totalSize)
) )
} }
// Delete partial from previous failed attempt
localFile.delete()
setForeground(createForegroundInfo(filename)) setForeground(createForegroundInfo(filename))
try { try {