AlexaCapability: instance, asset and text names, configuration, action and state mappings, nonControllable

AlexaCapability replaces the header-only AlexaInterface as what a device holds: 116 bytes with fixed places for
3 names, 4 action mappings and 2 state mappings, no std::vector or std::string. AlexaInterface and AlexaActions
stay as 1.x names; the five examples compile unchanged and announce the same bytes.
addCapability(row, instance) adds several capabilities of one generic controller. An instanced interface without
an instance or a name, an instance or semantics on an interface that takes none, and a full store are refused
with an ERROR line that says what to change.
PlaybackController and WakeOnLANController announce "properties": {} (AIF_EMPTY_PROPERTIES).
basicLight on a D1 mini: static RAM 30,788 -> 30,672 B, flash 330,509 -> 332,541 B; host tests 61 -> 77.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
David 2026-09-28 19:33:57 +00:00
parent 8b11cc2394
commit 7afc4346e8
12 changed files with 1084 additions and 253 deletions

View file

@ -58,17 +58,20 @@ public:
JsonDocument getDeviceJSON() const;
// Returns the capability that describes this interface, creating it on first use. The pointer stays valid for
// the lifetime of the device: capabilities live in a std::deque, which never relocates its elements when
// another one is added.
AlexaInterface* addCapability(const AlexaInterfaceDesc& row);
// Returns the capability of this device for the interface, creating it on first use. An interface that a
// device may have several of (RangeController, ModeController, ToggleController) takes the instance that tells
// them apart, "Blind.Lift"; without one, and with an instance for any other interface, nothing is added, the
// reason is printed and nullptr returned. The pointer stays valid for the lifetime of the device:
// capabilities live in a std::deque, which never relocates its elements when another one is added.
AlexaCapability* addCapability(const AlexaInterfaceDesc& row, const char* instance = nullptr);
// The same by the type of the interface, as 1.x sketches write it. A type without a row in AlexaInterfaces
// cannot be described: nothing is added, the reason is printed and nullptr returned. Inlined, so that a type
// written out in the sketch links its row only (alexaInterfaceRow).
__attribute__((always_inline)) AlexaInterface* addCapability(AlexaInterfaceType type) {
// The same by the type of the interface, as 1.x sketches write it: they call setInstance() on what they get,
// so the instance is not asked for here but when the device is announced. A type without a row in
// AlexaInterfaces cannot be described: nothing is added, the reason is printed and nullptr returned. Inlined,
// so that a type written out in the sketch links its row only (alexaInterfaceRow).
__attribute__((always_inline)) AlexaCapability* addCapability(AlexaInterfaceType type) {
const AlexaInterfaceDesc* row = alexaInterfaceRow(type);
return row != nullptr ? addCapability(*row) : refuseCapability(type);
return row != nullptr ? capabilityOf(*row, nullptr) : refuseCapability(type);
}
// The type of the capability of this device with the namespace of a directive ("Alexa.PowerController"),
@ -86,7 +89,9 @@ public:
}
private:
AlexaInterface* refuseCapability(AlexaInterfaceType type);
// The capability with this row and, when one is given, this instance; added when the device has none
AlexaCapability* capabilityOf(const AlexaInterfaceDesc& row, const char* instance);
AlexaCapability* refuseCapability(AlexaInterfaceType type);
String name;
String endpointId;
@ -102,7 +107,7 @@ private:
String model = "Alexa2MQTT";
const String softwareVersion = ALEX2ESP_VERSION;
std::deque<AlexaInterface> capabilities; // deque: pointers handed out by addCapability stay valid
std::deque<AlexaCapability> capabilities; // deque: pointers handed out by addCapability stay valid
};