This is the common library for devicectrl, providing core data structures for devices and the network API along with implementations of some protocols.
devicectrl is a local-first smart home and IoT stack focusing on simplicity, reliability, and speed. I designed it for my own use, it lacks any features that I do not find useful. This project is not complete and will evolve as I add new capabilities.
- Devices are individual IoT components that control hardware like a switch or a light. They are responsible for receiving attribute update commands from the server and applying them to the hardware they control. Most devices run on an esp32c6.
- Clients are user-interfacing machines that send attribute update requests to the server. This could be anything from a web interface to a headless keyboard controller.
- The server is the central communication hub for all devices and clients, aggregating update requests and notifications. The server is also responsible for handling scenes and running automations.
See Dependends for implementations of these components.
This crate contains the specification and message enums source for the following protocols:
A simple JSON-serialized protocol based on TCP means for devices where implementing TLS is impractical.
Once the TCP connection has been established, both sides must send a randomly generated u32 nonce. Nonces are connection-specific and must be regenerated for each connection attempt. Nonces must use big-endian byte ordering.
Client -> Server:
[ u32 nonce ]
Server -> Client:
[ u32 nonce ]
After nonces are exchanged, the device will identify itself by sending the ServerBoundSimpleMessage::Identify(DeviceId) message. The identify message is sent so that the server can select the correct verifying key for the device, ensuring the integrity of future messages sent.
Data lengths must use big-endian byte ordering.
Client -> Server:
[ u32 len | data (len bytes) ]
Now that the server can verify the device's messages, future messages will be send with an incrementing nonce and signature. The sent must be derived from the nonce received from the other side, incremented before each message. Example: if the server sends initial nonce 22 to the client, the client's next message will contain nonce 23, 24, 25, .... Nonces must wrap around to 0 after hitting the u32 limit.
Client -> Server / Server -> Client
[ u32 nonce | u32 len | data (len bytes) | 64 byte signature ]
The signature should be computed with ECDSA curve P-256 on all of the preceding data, including nonce and data length.
See DeviceBoundSimpleMessage and ServerBoundSimpleMessage for valid messages.
A TLS-based device protocol.
WIP
See DeviceBoundKryptonMessage and ServerBoundKryptonMessage for valid messages.
The following three protocols send and receive the same messages, provided by the socket protocol enums.
See ClientBoundSocketMessage and ServerBoundSocketMessage for valid messages.
A standard tcp stream, protected with TLS 1.3 using client authentication to provide authentication, requiring the client to also provide a certificate trusted by the server.
The TCP server and clients must serialize messages with JSON, delineating messages with a newline (\n).
Note: I plan to switch the message framing from newline-delineated to length-delineated eventually.
Nearly identical to the TCP server but using websocket semantics.
A standard tcp stream, protected with TLS 1.3 using client authentication to provide authentication, requiring the client to also provide a certificate trusted by the server.
The WebSocket server and clients must serialize messages with JSON, but since the WebSocket protocol provides framing, each JSON message is sent as a single WebSocket message without appending newlines.
Messages are sent as POST requests to / with the message contents serialized with JSON and placed in the body.
Like the other socket-based servers, the security of this protocol is underpinned by TLS 1.3 using client authentication to provide authentication, requiring the client to also provide a certificate trusted by the server
Cental server for processing update requests from clients by sending update commands to devices and relaying state update notifications.
Client for sending update requests generated from input events from physical devices like keyboards.
Device implementation to communicate with FanLamp Pro V2 ceiling fans.
Device implementation for a simple switch running on an esp32c6.
Device implementation for a switch managing ACPI power control running on an esp32c6.
Device implementation for a simple dimmable light running on an esp32c6.