ESPSomfy-RTS/src/outputs/ProtoSocket.h
cjkas 482e495678 feat: socket protocol 2 core on ws://host/ws
The /ws endpoint runs next to the old socket on port 8080.  The
handshake checks the token under security, the device sends hello,
then a sync of the schema (unless the client has it), header, rooms,
shades, groups, repeaters and status, paced by the client's next.
Events go out with the old ones: shadeState or the whole shade on a
config change, groups, rooms, removals, orders, repeaters,
shadeCommand, memory, wifi, ethernet, and the frames and
frequencyScan feeds.

The table of types, fields, errors and feeds is defined once and is
sent as the schema.  The writer, item writers, sync and client frame
parser are pure and natively tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 11:03:07 +02:00

57 lines
2.1 KiB
C++

#ifndef PROTO_SOCKET_H
#define PROTO_SOCKET_H
#include <ESPAsyncWebServer.h>
#include "ProtoSchema.h"
class SomfyShade;
class SomfyGroup;
class SomfyRoom;
// The socket protocol 2 endpoint, ws://host/ws on port 80 (docs/PROTOCOL.md,
// docs/adr/0005-one-websocket-protocol.md). It runs next to the old socket on
// port 8080 until the web UI and Home Assistant have moved over.
//
// async_tcp only reads what clients send and answers with fixed frames that
// hold nothing of the shade model (hello, errors, ok). Everything formatted
// from the model goes out from loop(), in the socket event buffer.
#define PROTO_MAX_CLIENTS 5
// How long a client may take to ask for the next sync frame.
#define PROTO_SYNC_TIMEOUT_MS 30000
class ProtoSocket {
public:
// Adds /ws to the server. From setup(), on loop()'s task.
void begin(AsyncWebServer &server);
// Moves each client's sync on. loop() only.
void loop();
bool hasClients() const;
// Events, loop() only.
void shadeState(SomfyShade &shade);
void shade(SomfyShade &shade);
void shadeRemoved(uint8_t shadeId);
void group(SomfyGroup &group);
void groupState(SomfyGroup &group);
void groupRemoved(uint8_t groupId);
void room(SomfyRoom &room);
void roomRemoved(uint8_t roomId);
// roomOrder, shadeOrder or groupOrder: the ids, in sort order.
void order(proto_type_t type);
void repeaters();
void shadeCommand(SomfyShade &shade, const char *command, const char *source, uint32_t sourceAddress);
void memory();
void wifi();
void ethernet();
// A feed event: write its fields into the writer between the two. Only
// when someone subscribed, so check subscribed() first.
bool subscribed(proto_feed_t feed) const;
ProtoWriter beginFeed(proto_type_t type);
void endFeed(proto_feed_t feed);
static void wsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
private:
TaskHandle_t _owner = nullptr;
bool onLoopTask() const;
JsonFormatter &beginEvent(proto_type_t type);
void endEvent();
};
extern ProtoSocket protoSocket;
#endif