diff --git a/README.md b/README.md index 13e34fe..ed604aa 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ The firmware in `src/main.cpp` implements the boilerplate as a compact Arduino E - Configurable API access control where each route can be `PUBLIC` or require one role. - Public boilerplate APIs: ping, add, and LED brightness. - LED brightness is persisted in non-volatile memory, applied on boot, and loaded into the Admin UI slider. -- DS18B20 temperature sensor readings are exposed through the live Admin UI card and `/api/uptime` compatibility endpoint. +- DS18B20 temperature sensor readings are exposed in Celsius and Fahrenheit through the live Admin UI card and `/api/temperature` endpoint. - User management with the standard roles `Sysadmin`, `UserAdmin`, `WebUIConnect`, and `Debugger`. - Custom role management. System roles are protected and cannot be deleted. - Active/inactive user accounts with role checkboxes in the Admin UI. @@ -204,7 +204,7 @@ The defaults can be changed at compile time: ### DS18B20 temperature sensor -The live Admin UI card reads a DS18B20 one-wire temperature sensor. The default data pin is GPIO3, which is exposed as `D1` on the Seeed Studio XIAO ESP32C3. +The live Admin UI card reads a DS18B20 one-wire temperature sensor and displays both Celsius and Fahrenheit. The default data pin is GPIO3, which is exposed as `D1` on the Seeed Studio XIAO ESP32C3. Connect the 3-wire sensor as follows: @@ -222,6 +222,20 @@ Keep the sensor powered from `3V3`, not `5V`, so the data line stays safe for th #define DS18B20_PIN 3 ``` +Read the current temperature with: + +```http +GET /api/temperature +Authorization: Bearer +``` + +The response includes `temperatureC`, `temperatureF`, `sensorConnected`, and `pin`. The live dashboard subscribes to: + +```http +GET /api/temperature/events +Authorization: Bearer +``` + ### Authentication Login: @@ -326,6 +340,8 @@ Protected APIs and their default roles: | --- | --- | | `POST /api/logout` | `WebUIConnect` | | `GET /api/me` | `WebUIConnect` | +| `GET /api/temperature` | `WebUIConnect` | +| `GET /api/temperature/events` | `WebUIConnect` | | `GET /api/apis` | `Sysadmin` | | `POST /api/apis` | `Sysadmin` | | `GET /api/users` | `UserAdmin` | diff --git a/data/www/admin.html b/data/www/admin.html index 94eb290..e919ccc 100644 --- a/data/www/admin.html +++ b/data/www/admin.html @@ -6,7 +6,7 @@ - + @@ -30,7 +30,7 @@
- +
diff --git a/data/www/components/uptime-card.js b/data/www/components/temperature-card.js similarity index 68% rename from data/www/components/uptime-card.js rename to data/www/components/temperature-card.js index 153e09f..cdfc5af 100644 --- a/data/www/components/uptime-card.js +++ b/data/www/components/temperature-card.js @@ -1,4 +1,4 @@ -class UptimeCard extends HTMLElement { +class TemperatureCard extends HTMLElement { constructor() { super(); this.source = null; @@ -7,12 +7,13 @@ class UptimeCard extends HTMLElement { connectedCallback() { this.innerHTML = `

Live temperature

Server-Sent Events
-
--°C
+
--°C
+
--°F
Disconnected
`; window.addEventListener('app:login', event => this.start(event.detail.token)); @@ -36,13 +37,14 @@ class UptimeCard extends HTMLElement { return; } this.setState('Connecting', false); - this.source = new EventSource('/api/uptime/events?token=' + encodeURIComponent(token)); + this.source = new EventSource('/api/temperature/events?token=' + encodeURIComponent(token)); this.source.addEventListener('open', () => this.setState('Connected', true)); this.source.addEventListener('temperature', event => { try { const json = JSON.parse(event.data); - const connected = !!json.sensorConnected && json.temperatureC !== null; - this.querySelector('#temperature').textContent = connected ? Number(json.temperatureC).toFixed(2) : '--'; + const connected = !!json.sensorConnected && json.temperatureC !== null && json.temperatureF !== null; + this.querySelector('#temperatureC').textContent = connected ? Number(json.temperatureC).toFixed(2) : '--'; + this.querySelector('#temperatureF').textContent = connected ? Number(json.temperatureF).toFixed(2) : '--'; this.setState(connected ? 'Connected' : 'Sensor unavailable', connected); } catch (error) { this.setState('Invalid event', false); @@ -57,4 +59,4 @@ class UptimeCard extends HTMLElement { } } -customElements.define('uptime-card', UptimeCard); +customElements.define('temperature-card', TemperatureCard); diff --git a/include/custom_api.h b/include/custom_api.h index 8dd766e..2c7a941 100644 --- a/include/custom_api.h +++ b/include/custom_api.h @@ -3,5 +3,5 @@ void handlePing(); void handleAdd(); void handleLed(); -void handleUptime(); -void handleUptimeEvents(); +void handleTemperature(); +void handleTemperatureEvents(); diff --git a/src/config/api_definitions_custom.cpp b/src/config/api_definitions_custom.cpp index b93fe57..0d425e8 100644 --- a/src/config/api_definitions_custom.cpp +++ b/src/config/api_definitions_custom.cpp @@ -5,8 +5,8 @@ ApiDef customApiDefs[] = { {"/api/ping", "GET", "", true, handlePing, nullptr}, {"/api/add", "POST", "", true, handleAdd, nullptr}, {"/api/led", "POST", "", true, handleLed, nullptr}, - {"/api/uptime", "GET", "WebUIConnect", false, handleUptime, nullptr}, - {"/api/uptime/events", "GET", "WebUIConnect", false, handleUptimeEvents, nullptr}, + {"/api/temperature", "GET", "WebUIConnect", false, handleTemperature, nullptr}, + {"/api/temperature/events", "GET", "WebUIConnect", false, handleTemperatureEvents, nullptr}, }; const size_t CUSTOM_API_DEF_COUNT = sizeof(customApiDefs) / sizeof(customApiDefs[0]); diff --git a/src/handlers/handlers_custom_api.cpp b/src/handlers/handlers_custom_api.cpp index 292d523..9b42bf1 100644 --- a/src/handlers/handlers_custom_api.cpp +++ b/src/handlers/handlers_custom_api.cpp @@ -71,12 +71,12 @@ void handleLed() { sendJson(200, jsonOk("\"brightness\":" + String(ledBrightness))); } -void handleUptime() { +void handleTemperature() { if (!authorize()) return; sendJson(200, jsonOk(temperatureJsonFields())); } -void handleUptimeEvents() { +void handleTemperatureEvents() { if (!authorize()) return; String payload = "retry: 1000\n"; payload += "event: temperature\n";