Redis API Reference
All Librescoot services communicate through Redis on the MDB. Below are the state hashes, command queues, and pub/sub channels.
The datastore runs on the MDB at
192.168.7.1:6379. The DBC connects over USB Ethernet. From a connected machine:
redis-cli -h 192.168.7.1 # or via SSH tunnel: ssh -L 6379:localhost:6379 root@<mdb-ip>
Since 1.2 the datastore is Valkey rather than Redis. It speaks the same protocol on the same port, and
redis-cli stays available as a compat symlink, so every command on this page works unchanged. Client --redis-* flags across the services are unchanged too.
Go services use the redis-ipc library which wraps go-redis with helpers for hash publishing, queue processing, and pub/sub. Read its README for the patterns used across all services.
State Hashes
State is stored in Redis hashes. Services update fields with HSET and publish change notifications on channels of the same name.
vehicle
redis-cli -h 192.168.7.1 HGETALL vehicle
| Field | Values | Description |
|---|---|---|
state | stand-by / parked / hop-on / hop-on-learning / ready-to-drive / waiting-seatbox / shutting-down / updating / waiting-hibernation / waiting-hibernation-advanced / waiting-hibernation-seatbox / waiting-hibernation-confirm | Vehicle operating state |
handlebar:position | on-place / off-place | Handlebar position sensor |
handlebar:lock-sensor | locked / unlocked | Handlebar lock state |
seatbox:lock | open / closed | Seatbox lock state |
seatbox:button | on / off | Seat open button |
kickstand | up / down | Side stand position |
brake:left / brake:right | on / off | Brake lever states |
blinker:switch | left / right / both / off | Blinker switch position |
blinker:state | on / off | Blinker currently active |
main-power | on / off | Main power state |
horn:button | on / off | Horn button |
battery:0 / battery:1
redis-cli -h 192.168.7.1 HGETALL battery:0
| Field | Type | Description |
|---|---|---|
present | true / false | Battery presence |
state | string | Battery state |
charge | integer (%) | Charge level |
voltage | integer (mV) | Battery voltage |
current | integer (mA) | Battery current |
temperature:0–temperature:3 | integer (°C) | Temperature sensors |
state-of-health | integer (%) | Battery health |
cycle-count | integer | Charge cycles |
serial-number | string | Battery serial |
fw-version | string | BMS firmware version |
engine-ecu
redis-cli -h 192.168.7.1 HGETALL engine-ecu
| Field | Type | Description |
|---|---|---|
speed | integer (km/h) | Vehicle speed |
rpm | integer | Motor RPM |
odometer | integer (m) | Total distance |
motor:voltage | integer (mV) | Motor voltage |
motor:current | integer (mA) | Motor current |
temperature | integer (°C) | ECU temperature |
throttle | on / off | Throttle active |
kers | on / off | Regenerative braking active |
kers-reason-off | string | Why KERS is off (none / cold / hot) |
gear | integer | Gear position |
fw-version | hex string | ECU firmware version |
internet
| Field | Description |
|---|---|
status | Connection status (connected / disconnected) |
access-tech | Access technology (e.g. LTE) |
signal-quality | Signal strength 0–100 |
ip-address | Current IP address |
modem-state | Modem power state |
gps
| Field | Description |
|---|---|
state | off / searching / fix-established / error |
latitude / longitude | Current position (6 decimal places) |
altitude | Altitude in meters |
speed | GPS speed |
course | Heading in degrees |
timestamp | GPS timestamp (ISO format) |
alarm
| Field | Values |
|---|---|
status | disabled / disarmed / delay-armed / armed / level-1-triggered / level-2-triggered / seatbox-access. motion-service watches this and reactively re-derives the BMX055 chip profile. |
motion
Written by motion-service (BMX055 owner). Heading fields refresh from each magnetometer sample (5 Hz).
| Field | Description |
|---|---|
current-profile | idle / armed-awake / armed-hibernation / level1 / waiting — chip configuration in registers |
heading | 0–359 degrees, integer, vehicle NED frame |
heading-deg | Tilt-compensated, EMA-smoothed |
heading-accuracy | Heuristic 1-σ degrees |
heading-tilt | Vehicle tilt angle from level |
initialized / streaming / polling-rate-hz | Service status |
last-interrupt-timestamp | Unix-ms of last motion-engine fire |
wake-cause | Set once on motion-service startup if INT_STATUS had a latched bit (wake-from-hibernation indicator). Read + deleted by alarm-service. |
Pub/sub channels published by motion-service:
motion:sensors— 10 Hz JSON IMU stream{accel, gyro, mag}motion:heading— 5 Hz JSONHeadingReadingwith raw + EMA-smoothed angles, accuracy, tiltmotion:interrupt— JSON{type, timestamp, engine}motion edges;type:"wake-hibernation"for the post-resume sentinelmotion:ready— fired once on startupmotion:rpc— request channel for synchronous methods (prepare-hibernation,get-calibration,clear-latch,soft-reset)
settings
Persistent configuration managed by settings-service. All fields are readable/writable via lsc settings get/set. Notable keys:
| Key | Description |
|---|---|
alarm.enabled | true/false, alarm system on/off |
alarm.honk | true/false, horn during alarm |
alarm.duration | Alarm duration in seconds |
updates.mdb.channel | OTA channel: testing / nightly / stable |
updates.mdb.method | full or delta |
dashboard.valhalla-url | Routing engine endpoint |
dashboard.map.type | online / offline |
scooter.auto-standby-seconds | Auto-lock timeout (0 = disabled) |
pm.hibernation-timer | Delay-based hibernation timeout in seconds (0 = disabled; minimum 300 s — values 1–299 are clamped up). Default 259200 (3 days). Timer runs whenever the vehicle is in any idle state and resets on activity. |
pm.default-state | Idle-time target power state: run (stay awake) or suspend (auto-sleep after the pre-suspend delay) |
pm.scheduled-hibernate-enabled | true/false — arms the cron-based hibernation schedule. Requires a valid pm.scheduled-hibernate-cron, a non-zero pm.scheduled-hibernate-duration, and a time-synced wall clock before any fires are dispatched. |
pm.scheduled-hibernate-cron | 5-field cron expression (minute hour day-of-month month day-of-week) defining when scheduled hibernation fires. Empty disables. Fires are rate-limited to one every 15 min — pathological expressions like * * * * * or */5 * * * * can’t put the scooter into a hibernate loop. Example: 0 22 * * * (every day at 22:00). |
pm.scheduled-hibernate-duration | Wake-by duration applied at fire time as a Go duration string (e.g. 8h, 30m). The system wakes at fire-time + duration (wall-clock target). If the vehicle isn’t in stand-by at fire time, the request is deferred until the next stand-by transition; the wake-by target is preserved. |
pm.wake-timer-max-seconds | Safety cap on the wake-timer duration sent to the nRF52 for any hibernate-for request (ad-hoc or scheduled). Requests above this are clamped. Minimum 60, maximum/default 604800 (1 week). |
pm.wake-timer-ack-timeout | How long pm-service waits for the nRF52 to confirm a wake-timer was armed before aborting the hibernation. Go duration string, minimum 1 s, default 10s. |
ota
| Field | Description |
|---|---|
status:mdb / status:dbc | idle / downloading / preparing / installing / pending-reboot / error |
download-progress:mdb | Download progress 0–100 |
update-version:mdb | Target version string |
error-message:mdb | Error description if status is error |
sms
Latest-value convenience state for SMS, written by modem-service. Per-message history lives on the streams described under SMS streams.
| Field | Description |
|---|---|
state | idle / sending / error |
last-received-from | Sender of the most recent inbound message |
last-received-text | Body of the most recent inbound message |
last-received-at | RFC 3339 timestamp of the most recent inbound message |
last-sent-to | Recipient of the last successfully sent message |
last-sent-at | RFC 3339 timestamp of the last successful send |
unread-count | Inbound messages recorded since modem-service started. Nothing marks them read, so treat it as a counter, not an inbox. |
version:mdb / version:dbc
Written by version-service from /etc/os-release. Contains fields like version_id, build_id, id.
Command Queues
Services accept commands via Redis lists. Send with LPUSH; services consume with BRPOP.
| Queue | Commands |
|---|---|
scooter:state | lock, unlock, lock-hibernate |
scooter:seatbox | open |
scooter:power | run, suspend, hibernate, hibernate-manual, reboot |
scooter:blinker | left, right, both, off |
scooter:horn | on, off |
scooter:alarm | enable, disable, start:<seconds>, stop |
scooter:modem | enable, disable |
scooter:update:mdb / scooter:update:dbc | check-now |
scooter:bluetooth | advertising-start-with-whitelisting, advertising-restart-no-whitelisting, advertising-stop, delete-all-bonds |
scooter:sms | JSON send request (see below), not a plain string like the other queues |
Example:
redis-cli -h 192.168.7.1 LPUSH scooter:state unlock redis-cli -h 192.168.7.1 LPUSH scooter:alarm start:30
Sending an SMS
scooter:sms takes a JSON object. id is optional and comes back as request-id on the sms:sent stream, so a sender can match outcomes to its own requests.
redis-cli -h 192.168.7.1 LPUSH scooter:sms \
'{"id":"my-token","to":"+491701234567","text":"hello from the scooter"}'
Pub/Sub
When a service updates a hash field, it publishes the changed field name to a channel with the same name as the hash. Consumers subscribe and re-read only the changed field.
# Subscribe to vehicle state changes redis-cli -h 192.168.7.1 SUBSCRIBE vehicle # Subscribe to battery updates redis-cli -h 192.168.7.1 SUBSCRIBE battery:0 battery:1
The published message payload is the field name that changed (e.g. "state", "charge"). Recipients call HGET <hash> <field> to get the new value.
This pattern is implemented in the redis-ipc library.
SMS streams
Individual messages don't fit the hash pattern (each one is an event, not a state), so modem-service appends them to two streams and announces each entry as JSON on a channel of the same name.
| Stream / channel | Carries |
|---|---|
sms:received | One entry per inbound message: id, from, text, timestamp |
sms:sent | One entry per terminal send outcome: id, request-id, to, text, outcome (sent or error), error, timestamp |
# Replay what's still buffered redis-cli -h 192.168.7.1 XRANGE sms:received - + # Or subscribe for JSON pushes as they happen redis-cli -h 192.168.7.1 SUBSCRIBE sms:received sms:sent
Both streams are capped at 100 entries. The datastore isn't persisted across reboots, so this bounds memory rather than retaining history: everything is gone after a restart either way. The XADD is the delivery commit, so a message that made it onto sms:received won't be delivered twice.
Delivery over LTE relies on an SGs association at the network side, which can expire while the modem sits idle on data only. modem-service can keep it alive with periodic self-calls, but that is off by default; start it with
-sms-keepalive if inbound messages stop arriving after long idle periods.