Add firmware build timestamp support for sensors; update detection workflows and UI accordingly.

This commit is contained in:
Kai Börnert
2026-04-27 16:46:24 +02:00
parent c04109a76c
commit e0b8acd55c
11 changed files with 204 additions and 33 deletions

View File

@@ -199,9 +199,22 @@ export interface BatteryState {
state_of_health: string
}
export interface DetectionPlant {
/// Request: which sensors to send IDENTIFY_CMD to.
export interface SensorRequest {
sensor_a: boolean,
sensor_b: boolean
sensor_b: boolean,
}
export interface DetectionRequest {
plant: SensorRequest[]
}
/// Response: detection result per plant.
/// sensor_a / sensor_b: firmware build timestamp in minutes since Unix epoch,
/// or null if the sensor did not respond.
export interface DetectionPlant {
sensor_a: number | null,
sensor_b: number | null,
}
export interface Detection {

View File

@@ -29,7 +29,7 @@ import {
SetTime, SSIDList, TankInfo,
TestPump,
VersionInfo,
SaveInfo, SolarState, PumpTestResult, Detection, CanPower
SaveInfo, SolarState, PumpTestResult, Detection, DetectionRequest, CanPower
} from "./api";
import {SolarView} from "./solarview";
import {toast} from "./toast";
@@ -339,7 +339,7 @@ export class Controller {
)
}
async detectSensors(detection: Detection, silent: boolean = false) {
async detectSensors(detection: DetectionRequest, silent: boolean = false) {
let counter = 0
let limit = 5
if (!silent) {
@@ -577,7 +577,7 @@ export class Controller {
this.hardwareView = new HardwareConfigView(this)
this.detectBtn = document.getElementById("detect_sensors") as HTMLButtonElement
this.detectBtn.onclick = () => {
const detection: Detection = {
const detection: DetectionRequest = {
plant: Array.from({length: PLANT_COUNT}, () => ({
sensor_a: true,
sensor_b: true,
@@ -615,7 +615,7 @@ export class Controller {
try {
await this.measure_moisture(true);
const detection: Detection = {
const detection: DetectionRequest = {
plant: Array.from({length: PLANT_COUNT}, () => ({
sensor_a: true,
sensor_b: true,

View File

@@ -133,10 +133,18 @@
<span class="plantsensorkey">Sensor A:</span>
<span class="plantsensorvalue" id="plant_${plantId}_moisture_a">not measured</span>
</div>
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<span class="plantsensorkey">Sensor A FW:</span>
<span class="plantsensorvalue" id="plant_${plantId}_sensor_a_fw_build">unknown</span>
</div>
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<div class="plantsensorkey">Sensor B:</div>
<span class="plantsensorvalue" id="plant_${plantId}_moisture_b">not measured</span>
</div>
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<span class="plantsensorkey">Sensor B FW:</span>
<span class="plantsensorvalue" id="plant_${plantId}_sensor_b_fw_build">unknown</span>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantsensorkey">Max Current</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_test_current_max">not_tested</span>

View File

@@ -1,7 +1,14 @@
import {DetectionPlant, Detection, PlantConfig, PumpTestResult} from "./api";
import {Detection, DetectionPlant, DetectionRequest, PlantConfig, PumpTestResult} from "./api";
export const PLANT_COUNT = 8;
/** Format a firmware build timestamp (minutes since Unix epoch) as a human-readable date/time. */
function formatBuildMinutes(buildMinutes: number | null): string {
if (buildMinutes === null) return "not detected";
if (buildMinutes === 0) return "detected (no timestamp)";
const ms = buildMinutes * 60 * 1000;
return new Date(ms).toISOString().replace("T", " ").slice(0, 16) + " UTC";
}
import {Controller} from "./main";
@@ -79,6 +86,8 @@ export class PlantView {
private readonly mode: HTMLSelectElement;
private readonly moistureA: HTMLElement;
private readonly moistureB: HTMLElement;
private readonly sensorAFwBuild: HTMLElement;
private readonly sensorBFwBuild: HTMLElement;
private readonly maxConsecutivePumpCount: HTMLInputElement;
private readonly minPumpCurrentMa: HTMLInputElement;
private readonly maxPumpCurrentMa: HTMLInputElement;
@@ -109,6 +118,8 @@ export class PlantView {
this.moistureA = document.getElementById("plant_" + plantId + "_moisture_a")! as HTMLElement;
this.moistureB = document.getElementById("plant_" + plantId + "_moisture_b")! as HTMLElement;
this.sensorAFwBuild = document.getElementById("plant_" + plantId + "_sensor_a_fw_build")! as HTMLElement;
this.sensorBFwBuild = document.getElementById("plant_" + plantId + "_sensor_b_fw_build")! as HTMLElement;
this.pump_test_current_max = document.getElementById("plant_" + plantId + "_pump_test_current_max")! as HTMLElement;
this.pump_test_current_min = document.getElementById("plant_" + plantId + "_pump_test_current_min")! as HTMLElement;
@@ -124,7 +135,7 @@ export class PlantView {
this.testSensorAButton = document.getElementById("plant_" + plantId + "_test_sensor_a")! as HTMLButtonElement;
this.testSensorAButton.onclick = () => {
const detection: Detection = {
const detection: DetectionRequest = {
plant: Array.from({length: PLANT_COUNT}, (_v, idx) => ({
sensor_a: idx === plantId,
sensor_b: false,
@@ -135,7 +146,7 @@ export class PlantView {
this.testSensorBButton = document.getElementById("plant_" + plantId + "_test_sensor_b")! as HTMLButtonElement;
this.testSensorBButton.onclick = () => {
const detection: Detection = {
const detection: DetectionRequest = {
plant: Array.from({length: PLANT_COUNT}, (_v, idx) => ({
sensor_a: false,
sensor_b: idx === plantId,
@@ -360,19 +371,23 @@ export class PlantView {
}
setDetectionResult(plantResult: DetectionPlant) {
console.log("setDetectionResult plantResult: " + plantResult.sensor_a + " " + plantResult.sensor_b)
const sensorADetected = plantResult.sensor_a !== null;
const sensorBDetected = plantResult.sensor_b !== null;
console.log("setDetectionResult plantResult: a=" + plantResult.sensor_a + " b=" + plantResult.sensor_b);
var changed = false;
if (this.sensorAInstalled.checked != plantResult.sensor_a) {
if (this.sensorAInstalled.checked != sensorADetected) {
changed = true;
this.sensorAInstalled.checked = plantResult.sensor_a;
this.sensorAInstalled.checked = sensorADetected;
}
if (this.sensorBInstalled.checked != plantResult.sensor_b) {
if (this.sensorBInstalled.checked != sensorBDetected) {
changed = true;
this.sensorBInstalled.checked = plantResult.sensor_b;
this.sensorBInstalled.checked = sensorBDetected;
}
if (changed) {
this.controller.configChanged();
}
this.sensorAFwBuild.innerText = formatBuildMinutes(plantResult.sensor_a);
this.sensorBFwBuild.innerText = formatBuildMinutes(plantResult.sensor_b);
}
}