WIP: Battery Display Feature Implementation #31

Closed
notfence wants to merge 4 commits from notfence/meshmap-lite:master into master
Contributor

Battery Display Feature Implementation

WARNING! Vibecoded (but tested as much as I can)
Please check code before merging!

изображение_2026-03-10_204018829.png

Overview

This document describes the implementation of a battery display feature for the Meshmap-Lite application. When users click on a node marker on the map, a popup now displays the node's battery voltage and charge level (if available), alongside a battery icon.

Feature Requirements

  1. Display battery voltage (rounded to 2 decimal places) and battery percentage on the node popup
  2. Only show battery information if the node is actively transmitting telemetry data
  3. Hide battery information if telemetry data is stale (older than 1 hour)
  4. Properly handle edge cases:
    • Display 0% as a valid value (not as "no data")
    • Show only voltage or only percentage if one is missing
    • Hide the entire battery section if no data is available

Technical Implementation

1. Backend Changes

internal/repo/repo.go

Change: Added optional Telemetry field to the MapNode struct

type MapNode struct {
    Node      domain.Node                   `json:"node"`
    Position  *domain.NodePosition          `json:"position,omitempty"`
    Telemetry *domain.NodeTelemetrySnapshot `json:"telemetry,omitempty"`
}

Why: Map nodes now include the latest telemetry data, allowing the frontend to display battery information alongside node identity and position data.

internal/persistence/sqlite/store.go

Changes:

  1. Modified GetMapNodes() function:

    • Added LEFT JOIN with node_telemetry_snapshots table
    • Includes telemetry columns in the SELECT statement: power_voltage, power_battery_level, source_channel, observed_at, updated_at
  2. Created scanMapNodeWithTelemetry() function:

    • Parses all node and position data (same as original scanMapNode())
    • Additionally extracts and builds NodeTelemetrySnapshot from telemetry columns
    • Returns a tuple: (domain.Node, *domain.NodePosition, *domain.NodeTelemetrySnapshot, error)
    • Handles NULL telemetry values gracefully - returns nil if no telemetry exists

Why: This ensures that every map node has the latest telemetry data fetched in a single database query, avoiding N+1 query problems.

2. Frontend Changes

web/src/api/types.ts

Change: Added optional telemetry field to the MapNode interface

export interface MapNode {
  node: Node
  position?: NodePosition
  telemetry?: NodeTelemetry
}

Why: TypeScript interface must match the backend API response structure.

web/src/maps/leafletMap.ts

Changes:

  1. Added STALE_TELEMETRY_AGE_MS constant:

    const STALE_TELEMETRY_AGE_MS = 60 * 60 * 1000 // 1 hour
    

    Defines the maximum age of telemetry data (1 hour). Data older than this is considered stale and won't be displayed.

  2. Created formatBatteryInfo() function:

    function formatBatteryInfo(power: { voltage?: number; battery_level?: number }): string
    

    Purpose: Formats battery data for display

    Logic:

    • Checks if voltage or battery level is defined (uses explicit !== undefined && !== null checks)
    • Formats voltage to 2 decimal places: 4.03V
    • Formats battery percentage as rounded integer: 95%
    • Combines values with battery emoji icon (🔋) into a compact string
    • Returns HTML span with class map-popup-battery for styling

    Key Detail: Uses explicit null/undefined checks instead of truthy checks to properly handle 0% as a valid value

    Example outputs:

    • With both values: <span class="map-popup-battery">4.03V 🔋 95%</span>
    • Only voltage: <span class="map-popup-battery">4.03V 🔋</span>
    • Only battery: <span class="map-popup-battery">🔋 0%</span>
    • No data: '' (empty string)
  3. Created isTelemetryStale() function:

    function isTelemetryStale(telemetry?: { observed_at: string }): boolean
    

    Purpose: Checks if telemetry data is too old to display

    Logic:

    • Returns true if telemetry is undefined/null
    • Parses the ISO 8601 timestamp from observed_at
    • Calculates age in milliseconds
    • Returns true if age exceeds STALE_TELEMETRY_AGE_MS (1 hour)

    Why: Ensures stale data doesn't persist on the map after a node stops transmitting

  4. Updated popupHtml() function:

    function popupHtml(
      id: string, 
      title: string, 
      sections: PopupSection[], 
      telemetry?: { power: { voltage?: number; battery_level?: number }; observed_at: string }
    ): string
    

    Changes:

    • Now accepts telemetry parameter
    • Checks both data existence AND freshness before displaying
    • Conditionally calls formatBatteryInfo() only if telemetry is fresh
    • Places battery info in a new .map-popup-details-section container alongside the "Details" link

    Decision Logic:

    const isFresh = !isTelemetryStale(telemetry)
    const hasBatteryInfo = isFresh && (telemetry?.power.voltage !== undefined || telemetry?.power.battery_level !== undefined)
    const batteryInfo = hasBatteryInfo ? formatBatteryInfo(telemetry!.power) : ''
    
  5. Updated marker rendering in render() method:

    • Passes n.telemetry to the popupHtml() function
    • This ensures each marker popup includes battery data if available

web/src/styles.css

Changes:

Replaced the old .map-popup-actions single-action layout with a new flexbox layout:

.map-popup-details-section {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 0.45rem;
  padding-top: 0.45rem;
  border-top: 1px solid var(--surface-border);
  gap: 0.5rem;
}

.map-popup-details-link {
  font-size: 0.9rem;
  font-weight: 600;
  flex: 0 0 auto;
  margin: 0;
}

.map-popup-battery {
  font-size: 0.85rem;
  white-space: nowrap;
  color: var(--pico-muted-color);
  flex: 0 0 auto;
}

Layout: A horizontal flexbox that places the "Details" link on the left and battery info on the right, separated by available space.

Data Flow

User clicks marker on map
    ↓
popupHtml() called with node data + telemetry
    ↓
isTelemetryStale() checks if observed_at < 1 hour old
    ↓
If fresh:
  - formatBatteryInfo() extracts voltage & battery_level
  - Formats as "4.03V 🔋 95%"
  - Renders in .map-popup-battery span on the right
If stale or missing:
  - Battery section shows empty string
  - Only "Details" link appears

Edge Cases Handled

Scenario Behavior
Node never sent telemetry Battery info not shown
Node sent telemetry, then stopped Hidden after 1 hour of silence
Battery level is 0% Shows "🔋 0%" (not hidden)
Only voltage available Shows "4.03V 🔋" (no percentage)
Only battery level available Shows "🔋 95%" (no voltage)
Both 0V and 0% Shows "0.00V 🔋 0%"
Malformed timestamp Treated as stale (hidden)

Testing Checklist

  • Battery icon and values appear in popup when telemetry is present
  • Battery 0% displays correctly (not hidden as falsy value)
  • Battery info disappears after 1 hour of no new telemetry
  • Frontend TypeScript compilation succeeds
  • Backend Go tests pass
  • Map renders correctly with new telemetry data structure

Constants Configuration

The telemetry staleness threshold can be adjusted in web/src/maps/leafletMap.ts:

const STALE_TELEMETRY_AGE_MS = 60 * 60 * 1000 // Currently 1 hour

Change the value for different thresholds:

  • 3 * 60 * 1000 = 3 minutes (testing)
  • 15 * 60 * 1000 = 15 minutes
  • 60 * 60 * 1000 = 1 hour (current)
  • 24 * 60 * 60 * 1000 = 1 day
  • 7 * 24 * 60 * 60 * 1000 = 7 days

Files Modified

Backend

  • internal/repo/repo.go - MapNode struct
  • internal/persistence/sqlite/store.go - GetMapNodes() and scanMapNodeWithTelemetry()

Frontend

  • web/src/api/types.ts - MapNode interface
  • web/src/maps/leafletMap.ts - Constants, formatBatteryInfo(), isTelemetryStale(), popupHtml()
  • web/src/styles.css - Battery display styling

Performance Considerations

  1. Single query performance: Database query joins telemetry in one operation (no N+1 queries)
  2. Stale data check: Timestamp comparison is O(1), negligible performance impact
  3. String formatting: Battery formatting happens only for display, not stored
  4. CSS flexbox: Efficient modern layout with minimal reflows

Future Enhancements

Potential improvements for future iterations:

  1. Configurable staleness threshold: Make STALE_TELEMETRY_AGE_MS configurable via API response
  2. Battery trend indicator: Show if battery is charging (↗) or draining (↘) based on time-series data
  3. Warning colors: Change battery icon color if below 10% threshold
  4. Battery health tracking: Store and display capacity degradation over time
  5. Multiple telemetry sources: Handle battery data from different sensor types
  • Backend telemetry structure: internal/domain/models.go - NodeTelemetrySnapshot
  • API documentation: docs/api.md - /api/v1/map/nodes endpoint
  • Frontend stores: web/src/stores/nodes.ts - State management

Other changes

  • README.md QoL changes
  • go.mod changes: 1.25 -> 1.25.0 (if leave 1.25, you will encounter this error: go: download go1.25 for linux/amd64: toolchain not available)
# Battery Display Feature Implementation **WARNING! Vibecoded (but tested as much as I can) Please check code before merging!** ![изображение_2026-03-10_204018829.png](/attachments/0716bb86-1fb1-4d39-9986-e50e9fda62e5) ## Overview This document describes the implementation of a battery display feature for the Meshmap-Lite application. When users click on a node marker on the map, a popup now displays the node's battery voltage and charge level (if available), alongside a battery icon. ## Feature Requirements 1. Display battery voltage (rounded to 2 decimal places) and battery percentage on the node popup 2. Only show battery information if the node is actively transmitting telemetry data 3. Hide battery information if telemetry data is stale (older than 1 hour) 4. Properly handle edge cases: - Display `0%` as a valid value (not as "no data") - Show only voltage or only percentage if one is missing - Hide the entire battery section if no data is available ## Technical Implementation ### 1. Backend Changes #### `internal/repo/repo.go` **Change**: Added optional `Telemetry` field to the `MapNode` struct ```go type MapNode struct { Node domain.Node `json:"node"` Position *domain.NodePosition `json:"position,omitempty"` Telemetry *domain.NodeTelemetrySnapshot `json:"telemetry,omitempty"` } ``` **Why**: Map nodes now include the latest telemetry data, allowing the frontend to display battery information alongside node identity and position data. #### `internal/persistence/sqlite/store.go` **Changes**: 1. **Modified `GetMapNodes()` function**: - Added LEFT JOIN with `node_telemetry_snapshots` table - Includes telemetry columns in the SELECT statement: `power_voltage`, `power_battery_level`, `source_channel`, `observed_at`, `updated_at` 2. **Created `scanMapNodeWithTelemetry()` function**: - Parses all node and position data (same as original `scanMapNode()`) - Additionally extracts and builds `NodeTelemetrySnapshot` from telemetry columns - Returns a tuple: `(domain.Node, *domain.NodePosition, *domain.NodeTelemetrySnapshot, error)` - Handles NULL telemetry values gracefully - returns `nil` if no telemetry exists **Why**: This ensures that every map node has the latest telemetry data fetched in a single database query, avoiding N+1 query problems. ### 2. Frontend Changes #### `web/src/api/types.ts` **Change**: Added optional `telemetry` field to the `MapNode` interface ```typescript export interface MapNode { node: Node position?: NodePosition telemetry?: NodeTelemetry } ``` **Why**: TypeScript interface must match the backend API response structure. #### `web/src/maps/leafletMap.ts` **Changes**: 1. **Added `STALE_TELEMETRY_AGE_MS` constant**: ```typescript const STALE_TELEMETRY_AGE_MS = 60 * 60 * 1000 // 1 hour ``` Defines the maximum age of telemetry data (1 hour). Data older than this is considered stale and won't be displayed. 2. **Created `formatBatteryInfo()` function**: ```typescript function formatBatteryInfo(power: { voltage?: number; battery_level?: number }): string ``` **Purpose**: Formats battery data for display **Logic**: - Checks if voltage or battery level is defined (uses explicit `!== undefined && !== null` checks) - Formats voltage to 2 decimal places: `4.03V` - Formats battery percentage as rounded integer: `95%` - Combines values with battery emoji icon (🔋) into a compact string - Returns HTML span with class `map-popup-battery` for styling **Key Detail**: Uses explicit null/undefined checks instead of truthy checks to properly handle `0%` as a valid value **Example outputs**: - With both values: `<span class="map-popup-battery">4.03V 🔋 95%</span>` - Only voltage: `<span class="map-popup-battery">4.03V 🔋</span>` - Only battery: `<span class="map-popup-battery">🔋 0%</span>` - No data: `''` (empty string) 3. **Created `isTelemetryStale()` function**: ```typescript function isTelemetryStale(telemetry?: { observed_at: string }): boolean ``` **Purpose**: Checks if telemetry data is too old to display **Logic**: - Returns `true` if telemetry is undefined/null - Parses the ISO 8601 timestamp from `observed_at` - Calculates age in milliseconds - Returns `true` if age exceeds `STALE_TELEMETRY_AGE_MS` (1 hour) **Why**: Ensures stale data doesn't persist on the map after a node stops transmitting 4. **Updated `popupHtml()` function**: ```typescript function popupHtml( id: string, title: string, sections: PopupSection[], telemetry?: { power: { voltage?: number; battery_level?: number }; observed_at: string } ): string ``` **Changes**: - Now accepts `telemetry` parameter - Checks both data existence AND freshness before displaying - Conditionally calls `formatBatteryInfo()` only if telemetry is fresh - Places battery info in a new `.map-popup-details-section` container alongside the "Details" link **Decision Logic**: ```typescript const isFresh = !isTelemetryStale(telemetry) const hasBatteryInfo = isFresh && (telemetry?.power.voltage !== undefined || telemetry?.power.battery_level !== undefined) const batteryInfo = hasBatteryInfo ? formatBatteryInfo(telemetry!.power) : '' ``` 5. **Updated marker rendering in `render()` method**: - Passes `n.telemetry` to the `popupHtml()` function - This ensures each marker popup includes battery data if available #### `web/src/styles.css` **Changes**: Replaced the old `.map-popup-actions` single-action layout with a new flexbox layout: ```css .map-popup-details-section { display: flex; align-items: center; justify-content: space-between; margin-top: 0.45rem; padding-top: 0.45rem; border-top: 1px solid var(--surface-border); gap: 0.5rem; } .map-popup-details-link { font-size: 0.9rem; font-weight: 600; flex: 0 0 auto; margin: 0; } .map-popup-battery { font-size: 0.85rem; white-space: nowrap; color: var(--pico-muted-color); flex: 0 0 auto; } ``` **Layout**: A horizontal flexbox that places the "Details" link on the left and battery info on the right, separated by available space. ## Data Flow ``` User clicks marker on map ↓ popupHtml() called with node data + telemetry ↓ isTelemetryStale() checks if observed_at < 1 hour old ↓ If fresh: - formatBatteryInfo() extracts voltage & battery_level - Formats as "4.03V 🔋 95%" - Renders in .map-popup-battery span on the right If stale or missing: - Battery section shows empty string - Only "Details" link appears ``` ## Edge Cases Handled | Scenario | Behavior | |----------|----------| | Node never sent telemetry | Battery info not shown | | Node sent telemetry, then stopped | Hidden after 1 hour of silence | | Battery level is 0% | Shows "🔋 0%" (not hidden) | | Only voltage available | Shows "4.03V 🔋" (no percentage) | | Only battery level available | Shows "🔋 95%" (no voltage) | | Both 0V and 0% | Shows "0.00V 🔋 0%" | | Malformed timestamp | Treated as stale (hidden) | ## Testing Checklist - [x] Battery icon and values appear in popup when telemetry is present - [x] Battery `0%` displays correctly (not hidden as falsy value) - [x] Battery info disappears after 1 hour of no new telemetry - [x] Frontend TypeScript compilation succeeds - [x] Backend Go tests pass - [x] Map renders correctly with new telemetry data structure ## Constants Configuration The telemetry staleness threshold can be adjusted in `web/src/maps/leafletMap.ts`: ```typescript const STALE_TELEMETRY_AGE_MS = 60 * 60 * 1000 // Currently 1 hour ``` Change the value for different thresholds: - `3 * 60 * 1000` = 3 minutes (testing) - `15 * 60 * 1000` = 15 minutes - `60 * 60 * 1000` = 1 hour (current) - `24 * 60 * 60 * 1000` = 1 day - `7 * 24 * 60 * 60 * 1000` = 7 days ## Files Modified ### Backend - `internal/repo/repo.go` - MapNode struct - `internal/persistence/sqlite/store.go` - GetMapNodes() and scanMapNodeWithTelemetry() ### Frontend - `web/src/api/types.ts` - MapNode interface - `web/src/maps/leafletMap.ts` - Constants, formatBatteryInfo(), isTelemetryStale(), popupHtml() - `web/src/styles.css` - Battery display styling ## Performance Considerations 1. **Single query performance**: Database query joins telemetry in one operation (no N+1 queries) 2. **Stale data check**: Timestamp comparison is O(1), negligible performance impact 3. **String formatting**: Battery formatting happens only for display, not stored 4. **CSS flexbox**: Efficient modern layout with minimal reflows ## Future Enhancements Potential improvements for future iterations: 1. **Configurable staleness threshold**: Make `STALE_TELEMETRY_AGE_MS` configurable via API response 2. **Battery trend indicator**: Show if battery is charging (↗) or draining (↘) based on time-series data 3. **Warning colors**: Change battery icon color if below 10% threshold 4. **Battery health tracking**: Store and display capacity degradation over time 5. **Multiple telemetry sources**: Handle battery data from different sensor types ## Related Documentation - Backend telemetry structure: `internal/domain/models.go` - `NodeTelemetrySnapshot` - API documentation: `docs/api.md` - `/api/v1/map/nodes` endpoint - Frontend stores: `web/src/stores/nodes.ts` - State management ## Other changes - README.md QoL changes - go.mod changes: 1.25 -> 1.25.0 (if leave 1.25, you will encounter this error: go: download go1.25 for linux/amd64: toolchain not available)
notfence changed title from master to Battery Display Feature Implementation 2026-03-10 20:43:04 +03:00
Owner

В целом более-менее ок, но нужно сделать ряд исправлений и убедиться в том, что фича реализована полноценно (см. тред про WebSocket).

В целом более-менее ок, но нужно сделать ряд исправлений и убедиться в том, что фича реализована полноценно (см. тред про WebSocket).
skobkin left a comment

Нужно убрать лишнее, сделать мелкие правки по неявному неймингу и убедиться в полноценной реализации обновления состояния.

Нужно убрать лишнее, сделать мелкие правки по неявному неймингу и убедиться в полноценной реализации обновления состояния.
README.md Outdated
@ -24,3 +24,2 @@
1. `go run ./cmd/server --config ./config.example.yaml`
2. Open `http://localhost:8080`
1. `git clone https://git.skobk.in/skobkin/meshmap-lite.git`
Owner

Не нужно менять документацию в PR не касающемся документации если документация не касается фичи, которая меняется.

Не нужно менять документацию в PR не касающемся документации если документация не касается фичи, которая меняется.
notfence marked this conversation as resolved
go.mod Outdated
@ -1,6 +1,6 @@
module meshmap-lite
go 1.25
go 1.25.0
Owner

Не нужно коммитить такие изменения. От них сборка фичи не зависит.

Не нужно коммитить такие изменения. От них сборка фичи не зависит.
notfence marked this conversation as resolved
@ -702,6 +704,94 @@ func scanMapNode(rows *sql.Rows) (domain.Node, *domain.NodePosition, error) {
return n, pos, nil
}
func scanMapNodeWithTelemetry(rows *sql.Rows) (domain.Node, *domain.NodePosition, *domain.NodeTelemetrySnapshot, error) {
Owner

Этот метод стоит расположить рядом с оригинальным методом scanMapNode.

Этот метод стоит расположить рядом с оригинальным методом `scanMapNode`.
Author
Contributor

вроде бы и так рядом? func scanMapNodeWithTelemetry идет сразу после func scanMapNode

вроде бы и так рядом? func scanMapNodeWithTelemetry идет сразу после func scanMapNode
skobkin marked this conversation as resolved
@ -705,0 +769,4 @@
}
}
var telemetry *domain.NodeTelemetrySnapshot
Owner

Тут стоило бы посмотреть как сейчас упаковывается телеметрия и, возможно, использовать общий код для этого чтобы не создавать разночтений.

Тут стоило бы посмотреть как сейчас упаковывается телеметрия и, _возможно_, использовать общий код для этого чтобы не создавать разночтений.
@ -54,7 +54,6 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
Owner

Этих изменений в PR не должно быть.

Этих изменений в PR не должно быть.
notfence marked this conversation as resolved
@ -129,3 +130,3 @@
row('FW', displayValue(n.node.firmware_version))
]))
]))
]), n.telemetry)
Owner

Коммент не к этой конкретно сточке, а чтобы открыть тред, который должен быть закрыт перед мерджем.

Не увидел ни в коде ни в описании PR в эджкейсах или тестировании ничего о live-апдейтах из вебсокетов.

  • Мы получаем из вебсокетов события с телеметрией на фронтенд?
  • Мы их обрабатываем?
    • Хранилище состояния нод на фронтенде обновляется из этих данных?
  • Попап на маркере будет обновлён после поступления такого события?
Коммент не к этой конкретно сточке, а чтобы открыть тред, который должен быть закрыт перед мерджем. Не увидел ни в коде ни в описании PR в эджкейсах или тестировании ничего о live-апдейтах из вебсокетов. - [ ] Мы получаем из вебсокетов события с телеметрией на фронтенд? - [ ] Мы их обрабатываем? - [ ] Хранилище состояния нод на фронтенде обновляется из этих данных? - [ ] Попап на маркере будет обновлён после поступления такого события?
Author
Contributor

Ответы на вопросы о live-обновлениях батареи
1️⃣ Мы получаем из вебсокетов события с телеметрией на фронтенд?
ДА, события отправляются.

Бэкенд отправляет событие:

Событие типа "node.telemetry" содержит полный NodeTelemetrySnapshot с voltage, battery_level и observed_at.

2️⃣ Мы их обрабатываем?
НЕТ

Посмотри на ws.ts (строка 47-90):

Событие "node.telemetry" просто игнорируется!

3️⃣ Хранилище состояния нод на фронтенде обновляется из этих данных?
НЕТ.

В nodes.ts есть методы:

upsertMapNode(item: MapNode) — обновляет весь MapNode с телеметрией
upsertNode(node: Node) — обновляет только Node
upsertPosition(position: NodePosition) — обновляет только Position
НЕТ метода upsertTelemetry()
События батареи вообще не попадают в стор.

4️⃣ Попап на маркере будет обновлён после поступления такого события?
НЕТ, попап не обновится.

Попап перерендерится только если:

Пользователь повторно откроет его
Произойдет перезагрузка всего списка нод через REST API
🔴 Что нужно сделать перед мерджем
Нужно добавить обработчик события "node.telemetry" в ws.ts:

Плюс нужна guard-функция:

Это обязательно перед мерджем, иначе батарея не будет обновляться в реальном времени!
//
от себя скажу, процент и вольтаж обновляются только при перезагрузке страницы. В принципе нам и не нужно в реальном времени обновлять эти данные

Ответы на вопросы о live-обновлениях батареи 1️⃣ Мы получаем из вебсокетов события с телеметрией на фронтенд? ✅ ДА, события отправляются. Бэкенд отправляет событие: Событие типа "node.telemetry" содержит полный NodeTelemetrySnapshot с voltage, battery_level и observed_at. 2️⃣ Мы их обрабатываем? ❌ НЕТ Посмотри на ws.ts (строка 47-90): Событие "node.telemetry" просто игнорируется! 3️⃣ Хранилище состояния нод на фронтенде обновляется из этих данных? ❌ НЕТ. В nodes.ts есть методы: upsertMapNode(item: MapNode) — обновляет весь MapNode с телеметрией upsertNode(node: Node) — обновляет только Node upsertPosition(position: NodePosition) — обновляет только Position НЕТ метода upsertTelemetry() События батареи вообще не попадают в стор. 4️⃣ Попап на маркере будет обновлён после поступления такого события? ❌ НЕТ, попап не обновится. Попап перерендерится только если: Пользователь повторно откроет его Произойдет перезагрузка всего списка нод через REST API 🔴 Что нужно сделать перед мерджем Нужно добавить обработчик события "node.telemetry" в ws.ts: Плюс нужна guard-функция: Это обязательно перед мерджем, иначе батарея не будет обновляться в реальном времени! // от себя скажу, процент и вольтаж обновляются только при перезагрузке страницы. В принципе нам и не нужно в реальном времени обновлять эти данные
Owner

от себя скажу, процент и вольтаж обновляются только при перезагрузке страницы. В принципе нам и не нужно в реальном времени обновлять эти данные

Это идёт вразрез с текущей концепцией карты. Она по возможности должна без обновления показывать актуальную информацию если это не вызывает каких-то совсем неприемлемых доработок.

> от себя скажу, процент и вольтаж обновляются только при перезагрузке страницы. В принципе нам и не нужно в реальном времени обновлять эти данные Это идёт вразрез с текущей концепцией карты. Она по возможности должна без обновления показывать актуальную информацию если это не вызывает каких-то совсем неприемлемых доработок.
@ -488,0 +494,4 @@
return ''
}
const voltage = hasVoltage ? power.voltage!.toFixed(2) + 'V' : ''
Owner
-const voltage
+const batteryVoltage
```diff -const voltage +const batteryVoltage ```
notfence marked this conversation as resolved
@ -488,0 +496,4 @@
const voltage = hasVoltage ? power.voltage!.toFixed(2) + 'V' : ''
// Handle 0% as a valid value - use explicit null/undefined checks instead of truthy check
const battery = hasBatteryLevel ? Math.round(power.battery_level!) + '%' : ''
Owner
-const battery
+const batteryLevel
```diff -const battery +const batteryLevel ```
notfence marked this conversation as resolved
@ -488,0 +508,4 @@
return parts.length > 0 ? `<span class="map-popup-battery">${parts.join(' ')}</span>` : ''
}
function isTelemetryStale(telemetry?: { observed_at: string }): boolean {
Owner

По-хорошему это должно управляться с бэкенда из конфигурации.
Но я заведу на это #32 сам.

По-хорошему это должно управляться с бэкенда из конфигурации. Но я заведу на это #32 сам.
skobkin marked this conversation as resolved
skobkin self-assigned this 2026-03-10 22:10:34 +03:00
skobkin changed title from Battery Display Feature Implementation to WIP: Battery Display Feature Implementation 2026-03-10 22:14:49 +03:00
Owner

Closing in favor of #33

Closing in favor of #33
skobkin closed this pull request 2026-03-10 23:15:10 +03:00

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
skobkin/meshmap-lite!31
No description provided.