AlexaDevice: capability array, linked list, a directive handler with the device in hand

onDirective(handler) registers void handler(AlexaDirective&): the directive comes with its device, the
capability it is for, namespace, name, instance, correlationToken and payload, and response() and
stateReport() build the answer, so one function serves several devices. registerEvent() is kept; the
five examples compile unchanged and announce the same discovery objects.
Devices are a linked list on the bridge and a device holds ALEX2ESP_MAX_CAPABILITIES (8) capability
pointers in place of two std::deque; one capability more is refused with an error.
A device without a handler, and a handler that sends nothing for a capability the device lacks, are
answered with the ErrorResponse INVALID_DIRECTIVE instead of a timeout. A directive whose endpointId is
not a device of the sketch is ignored at DEBUG.
basicLight on a D1 mini: static RAM 30,672 -> 30,600 B, flash 332,541 -> 333,437 B, 0 warnings in the five
examples; 88 host tests (11 new, test/test_dispatch).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
David 2026-09-28 19:45:57 +00:00
parent 7afc4346e8
commit d3e7ee7f2a
10 changed files with 767 additions and 67 deletions

View file

@ -8,7 +8,8 @@
* endpoints through the MQTT broker at alex2mqtt.stormysdream.club.
*
* <root>/discover in answered with one discovery object per device on <root>/discover_r
* <root>/<endpointId>/alexaDirective in the directive, handed to the device's ReportState / Event handler
* <root>/<endpointId>/alexaDirective in the directive, handed to the handler of the device (onDirective(), or
* the ReportState / Event handlers of 1.x)
* <root>/<endpointId>/alexaResponce out the report the handler built with buildStatusMessage()
*/
@ -25,7 +26,6 @@
#include "AlexaLog.h"
#include "AlexaTransport.h"
#include "AlexaUtils.h"
#include <deque>
enum class Alex2ESPState
{
@ -58,7 +58,7 @@ public:
AsyncMqttClientDisconnectReason getDisconnectReason() const;
// Returns the device with this endpointId, creating it on first use. The pointer stays valid for the lifetime
// of the client: devices live in a std::deque, which never relocates its elements when another one is added.
// of the client. nullptr when the heap has no room for another device; the reason is printed.
// Call it after begin(): a device takes the root topic of its reports when it is created.
AlexaDevice *getDevice(const String &name, const String &endpointId);
@ -86,7 +86,7 @@ private:
String discoverTopicSend; // The topic we send discovery messages
String directiveFilter; // The subscription that delivers the directives of every endpoint
std::deque<AlexaDevice> devices; // Collection of devices (deque: pointers handed out by getDevice stay valid)
AlexaDeviceList devices; // On the heap, one by one: pointers handed out by getDevice stay valid
Alex2ESPState state;
AsyncMqttClientDisconnectReason disconnectReason;
@ -104,7 +104,7 @@ private:
bool discoveryRequested; // Set by the MQTT callback, taken by loop()
bool discoveryActive; // An answer is going out
bool discoveryDeferred; // The answer had to pause: the MQTT client or the heap was full
size_t discoveryNext; // Next device to announce
AlexaDevice *discoveryNext; // Next device to announce
size_t discoveryAnnounced; // Devices announced in this answer
unsigned long discoveryStarted; // millis() when the Discover arrived
unsigned long discoveryLastAttempt; // millis() of the publish that was refused
@ -127,7 +127,6 @@ private:
void publishDiscovery();
void processDirective();
AlexaDevice *findDevice(const char *endpointId, size_t length);
AlexaSendResult trySend(const char *topic, JsonDocument &doc, size_t *length);
void logRefusal(const char *topic, AlexaSendResult result, size_t length);
const char *logName(const char *topic) const; // The topic as the log may print it: without the root topic