Added Devices API wake reason and missing attributes

Ensures the wake reason is allowed to be included in all API requests and responses. This includes allowing the following attributes to be updated via the API:

- `firmware_version`
- `battery_charge`
- `battery_voltage`
- `wifi`
- `width`
- `height`

The above allows BYODs to providde accurate infromation that wasn't originally supported.

Milestone: minor
This commit is contained in:
Brooke Kuhlmann
2026-03-31 14:00:41 -06:00
parent 2eb679bb9c
commit bf5c9bb47a
7 changed files with 96 additions and 47 deletions
+8 -1
View File
@@ -8,14 +8,21 @@ module Terminus
Patch = Dry::Schema.Params do
optional(:model_id).filled :integer
optional(:playlist_id).filled :integer
optional(:friendly_id).filled :string
optional(:label).filled :string
optional(:friendly_id).filled :string
optional(:mac_address).filled Types::MACAddress
optional(:api_key).filled :string
optional(:refresh_rate).filled :integer, gt?: 0
optional(:image_timeout).filled :integer, gteq?: 0
optional(:proxy).filled :bool
optional(:firmware_update).filled :bool
optional(:firmware_version).filled Types::Version
optional(:battery_charge).filled :float, gteq?: 0
optional(:battery_voltage).filled :float, gteq?: 0
optional(:wifi).filled :integer
optional(:width).filled :integer
optional(:height).filled :integer
optional(:wake_reason).filled :string
optional(:sleep_start_at).maybe :string
optional(:sleep_stop_at).maybe :string
end
+7 -2
View File
@@ -12,12 +12,17 @@ module Terminus
optional(:friendly_id).filled :string
required(:mac_address).filled Types::MACAddress
optional(:api_key).filled :string
optional(:battery_charge).filled :integer, gteq?: 0
optional(:battery_voltage).filled :float
optional(:refresh_rate).filled :integer, gt?: 0
optional(:image_timeout).filled :integer, gteq?: 0
optional(:proxy).filled :bool
optional(:firmware_update).filled :bool
optional(:firmware_version).filled Types::Version
optional(:battery_charge).filled :float, gteq?: 0
optional(:battery_voltage).filled :float
optional(:wifi).filled :integer
optional(:width).filled :integer
optional(:height).filled :integer
optional(:wake_reason).filled :string
optional(:sleep_start_at).maybe :string
optional(:sleep_stop_at).maybe :string
+1
View File
@@ -19,6 +19,7 @@ module Terminus
battery_voltage
refresh_rate
image_timeout
wake_reason
width
height
proxy
+16 -3
View File
@@ -44,8 +44,11 @@ The {trmnl_firmware_link} will include the following headers in each HTTP reques
* `HOST`: The host (usually the IP address).
* `ID`: The MAC address of the device.
* `MODEL`: The generic device model (doesn't match {trmnl_api_gem_link} model names but can be used as a fuzzy match).
* `PERCENT_CHARGED`: The remaining charge of the device battery. When present, prefer this value over `BATTERY_VOLTAGE` because this will give you the exact percent of battery charge left.
* `REFRESH_RATE`: The current refresh rate as saved on the device. Example: 100.
* `RSSI`: The WiFi signal strength (usually -100 to 100).
* `SENSORS`: The sensor reasons for any sensors connected to the device.
* `UPDATE_SOURCE`: The reason for why the device was woken from sleep.
* `USER_AGENT`: The device name (usually the ESP32 board name).
* `WIDTH`: The device width. Example: 800.
@@ -259,6 +262,7 @@ curl "https://localhost:2443/api/devices/1" \
"battery_voltage": 4.0,
"refresh_rate": 500,
"image_timeout": 0,
"wake_reason": null,
"width": 800,
"height": 480,
"proxy": false,
@@ -287,6 +291,7 @@ curl "https://localhost:2443/api/devices/1" \
"battery_voltage": 4.0,
"refresh_rate": 500,
"image_timeout": 0,
"wake_reason": null,
"width": 800,
"height": 480,
"proxy": false,
@@ -335,12 +340,17 @@ curl -X "POST" "https://localhost:2443/api/devices" \
"friendly_id": "DEMO11",
"mac_address": "A1:B2:C3:D4:E5:F6",
"api_key": "OScdcN0kFbKjFcid9Kz6Cx",
"battery_charge": 85.0,
"battery_voltage": -70,
"firmware_update": true,
"refresh_rate": 500,
"image_timeout": 0,
"proxy": false,
"firmware_update": true,
"firmware_version": "1.2.3",
"battery_charge": 85.0,
"battery_voltage": -70,
"wifi": -70,
"width": 800,
"height": 480,
"wake_reason": "Awoken from API.",
"sleep_start_at": "18:00:00",
"sleep_stop_at": "06:00:00"
}
@@ -368,6 +378,7 @@ curl -X "POST" "https://localhost:2443/api/devices" \
"battery_voltage": 0.0,
"refresh_rate": 900,
"image_timeout": 0,
"wake_reason": null,
"width": 0,
"height": 0,
"proxy": false,
@@ -421,6 +432,7 @@ You can change a single attribute or multiple attributes at once. All attributes
"battery_voltage": 4.0,
"refresh_rate": 250,
"image_timeout": 0,
"wake_reason": null,
"width": 800,
"height": 480,
"proxy": false,
@@ -466,6 +478,7 @@ curl -X "DELETE" "https://localhost:2443/api/devices/1" \
"battery_voltage": 4.0,
"refresh_rate": 250,
"image_timeout": 0,
"wake_reason": null,
"width": 800,
"height": 480,
"proxy": false,
+10 -3
View File
@@ -14,12 +14,19 @@ RSpec.describe Terminus::Schemas::Devices::Upsert do
friendly_id: "ABC123",
mac_address: "AA:BB:CC:11:22:33",
api_key: "secret",
battery_charge: 85.0,
battery_voltage: 3.5,
refresh_rate: 100,
image_timeout: 0,
proxy: "on",
firmware_update: "on"
firmware_update: "on",
firmware_version: "1.2.3",
battery_charge: 85.0,
battery_voltage: 3.5,
wifi: -75,
width: 800,
height: 480,
wake_reason: "Awoken from test.",
sleep_start_at: "18:00:00",
sleep_end_at: "06:00:00"
}
end
+1
View File
@@ -21,6 +21,7 @@ RSpec.describe Terminus::Serializers::Device do
mac_address: "A1:B2:C3:D4:E5:F6",
api_key: "abc123",
firmware_version: "1.2.3",
wake_reason: nil,
wifi: -40,
battery_charge: 0.0,
battery_voltage: 3.0,
+53 -38
View File
@@ -12,17 +12,24 @@ RSpec.describe "/api/devices", :db do
let :attributes do
{
model_id: model.id,
playlist_id: nil,
playlist_id: playlist.id,
label: "Test",
friendly_id: "ABC123",
label: "Request Test",
mac_address: "A1:B2:C3:D4:E5:F6",
api_key: "abc123",
refresh_rate: 500,
api_key: "secret",
refresh_rate: 100,
image_timeout: 5,
proxy: true,
firmware_update: false,
sleep_start_at: "05:00:00+0000",
sleep_stop_at: "10:00:00+0000"
proxy: "on",
firmware_update: "on",
firmware_version: "1.2.3",
battery_charge: 85.0,
battery_voltage: 3.5,
wifi: -75,
width: 800,
height: 480,
wake_reason: "Awoken from test.",
sleep_start_at: "18:00:00+0000",
sleep_stop_at: "06:00:00+0000"
}
end
@@ -40,19 +47,21 @@ RSpec.describe "/api/devices", :db do
id: device.id,
model_id: model.id,
playlist_id: playlist.id,
friendly_id: "ABC123",
label: "Test",
friendly_id: "ABC123",
mac_address: "A1:B2:C3:D4:E5:F6",
api_key: "abc123",
firmware_version: "1.2.3",
wifi: -44,
battery_voltage: 3.0,
refresh_rate: 900,
image_timeout: 0,
width: 0,
height: 0,
proxy: false,
firmware_update: true,
firmware_version: "1.2.3",
battery_charge: 0.0,
battery_voltage: 3.0,
wifi: -44,
width: 0,
height: 0,
wake_reason: nil,
sleep_start_at: nil,
sleep_stop_at: nil,
created_at: match_rfc_3339,
@@ -92,6 +101,7 @@ RSpec.describe "/api/devices", :db do
battery_voltage: 3.0,
refresh_rate: 900,
image_timeout: 0,
wake_reason: nil,
width: 0,
height: 0,
proxy: false,
@@ -124,21 +134,23 @@ RSpec.describe "/api/devices", :db do
id: kind_of(Integer),
model_id: model.id,
playlist_id: kind_of(Integer),
label: "Test",
friendly_id: match_device_friendly_id,
label: "Request Test",
mac_address: "A1:B2:C3:D4:E5:F6",
api_key: kind_of(String),
firmware_version: nil,
wifi: 0,
battery_voltage: 0.0,
refresh_rate: 500,
refresh_rate: 100,
image_timeout: 5,
width: 0,
height: 0,
proxy: true,
firmware_update: false,
sleep_start_at: "05:00:00",
sleep_stop_at: "10:00:00",
firmware_update: true,
firmware_version: "1.2.3",
battery_voltage: 3.5,
battery_charge: 85.0,
wifi: -75,
width: 800,
height: 480,
wake_reason: "Awoken from test.",
sleep_start_at: "18:00:00",
sleep_stop_at: "06:00:00",
created_at: match_rfc_3339,
updated_at: match_rfc_3339
)
@@ -163,19 +175,21 @@ RSpec.describe "/api/devices", :db do
id: kind_of(Integer),
model_id: model.id,
playlist_id: kind_of(Integer),
friendly_id: match_device_friendly_id,
label: "Test",
friendly_id: match_device_friendly_id,
mac_address: "A1:B2:C3:D4:E5:F6",
api_key: kind_of(String),
firmware_version: nil,
wifi: 0,
battery_voltage: 0.0,
refresh_rate: 900,
image_timeout: 0,
width: 0,
height: 0,
proxy: false,
firmware_update: false,
firmware_version: nil,
battery_voltage: 0.0,
battery_charge: 0.0,
wifi: 0,
width: 0,
height: 0,
wake_reason: nil,
sleep_start_at: nil,
sleep_stop_at: nil,
created_at: match_rfc_3339,
@@ -236,7 +250,7 @@ RSpec.describe "/api/devices", :db do
expect(json_payload.dig(:data, :label)).to eq("Test Patch")
end
it "answers original record when there is nothing to patch" do
it "answers problem details with empty attributes" do
patch routes.path(:api_device_patch, id: device.id),
{device: {}}.to_json,
"HTTP_AUTHORIZATION" => access_token,
@@ -292,20 +306,21 @@ RSpec.describe "/api/devices", :db do
id: device.id,
model_id: model.id,
playlist_id: playlist.id,
friendly_id: "ABC123",
label: "Test",
friendly_id: "ABC123",
mac_address: "A1:B2:C3:D4:E5:F6",
api_key: "abc123",
firmware_version: "1.2.3",
wifi: -44,
battery_charge: 0,
battery_voltage: 3.0,
refresh_rate: 900,
image_timeout: 0,
width: 0,
height: 0,
proxy: false,
firmware_update: true,
firmware_version: "1.2.3",
battery_charge: 0,
battery_voltage: 3.0,
wifi: -44,
width: 0,
height: 0,
wake_reason: nil,
sleep_start_at: nil,
sleep_stop_at: nil,
created_at: match_rfc_3339,