Alex2ESP/readme.md
David 2ed52c8721 Reconnect with back-off on every disconnect reason and log it
The session is opened again whatever AsyncMqttClient gives as the reason, after 1 s doubling
to 60 s (AlexaReconnectBackoff, reset when a session opens), and only while Wi-Fi is up.
1.1.0 retried a lost TCP connection every 5 s and nothing else, silently: a refused password
or a broker that was restarting left the board offline until a reset.
Every disconnect prints its reason in words and the wait before the next attempt. The first
connect waits for Wi-Fi; a connect without an answer after 30 s counts as failed, because the
MQTT client reports nothing when no TCP connection was made. Keep-alive 30 s.
basicLight: RAM 34,452 -> 34,476 B, flash 337,461 -> 338,281 B; host tests 41 -> 45.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 18:43:00 +00:00

27 KiB
Raw Blame History

Alex2ESP

Alex2ESP is a lightweight Arduino/PlatformIO library for integrating ESP8266 microcontrollers with Amazon Alexa smart home APIs through the Alex2MQTT skill. The library simplifies the process of creating Alexa-compatible devices using MQTT.

Supported target: ESP8266 (developed and tested on a Wemos D1 mini). ESP32: untested - the code carries include guards for it, but it has never been compiled or run there.

For more details on how to configure Alex2ESP, visit Alex2MQTT Documentation.


Features

  • Supports "All" Alexa smart home capabilities.
  • Provides action mapping for advanced directive customization including Open,Close,Raise,and Lower commands.
  • Allows custom JSON injection in both discovery and state reporting to allow for unsupported devices.
  • Event-driven architecture for handling Alexa directives.

Alternatives

one of the most popular library for controlling esp like devices using alexa is FauxmoESP however FauxmoESP works by emulating a light bulb so it is limited in what commands it accepts.


Quick Start

Installation

The library lives on Forgejo at https://git.stormysdream.club/platformio/Alex2ESP (public). It is not in the PlatformIO registry or the Arduino Library Manager: install it from git.

PlatformIO

[env:d1_mini]
platform = espressif8266
board = d1_mini
framework = arduino
monitor_speed = 74880
lib_deps =
    https://git.stormysdream.club/platformio/Alex2ESP.git
    marvinroger/AsyncMqttClient@^0.9.0
    bblanchon/ArduinoJson@^7

To pin a release instead of main, append the tag: https://git.stormysdream.club/platformio/Alex2ESP.git#v1.1.0. AsyncMqttClient pulls in ESPAsyncTCP by itself; if PlatformIO notes that more than one ESPAsyncTCP package matches, add esp32async/ESPAsyncTCP@^2.0.0 to lib_deps to pick one explicitly. The examples print at 74880 baud, the ESP8266 boot-ROM rate, so the boot messages stay readable in the same monitor.

Arduino IDE

Download the repository as a ZIP (https://git.stormysdream.club/platformio/Alex2ESP/archive/main.zip) and add it with Sketch -> Include Library -> Add .ZIP Library. Install AsyncMqttClient, ArduinoJson (7.x) and ESP Async TCP (by ESP32Async) from the Library Manager. Install the ESP8266 board package and select your board (for example LOLIN(WEMOS) D1 R2 & mini).

Then include the library in your project:

#include <Alex2ESP.h>

Complete sketches are in examples/: basicLight.cpp, lightWithBrightness.cpp, lightWithColorTemp.cpp, tempSensor.cpp and blindControl.cpp. Every example joins Wi-Fi with WiFi.begin(WIFI_SSID, WIFI_PASSWORD); fill in the SSID, the password and your Alex2MQTT credentials before flashing.


Creating a Basic Device

Example: Light Control

Here’s how to create a simple device that controls a light:

First create an instance of the alexa client and initilize it with your MQTT Credentials

Alex2ESP alexClient;

Credentials can be found at https://alex2mqtt.stormysdream.club/ after logging in with amazon. begin() takes the MQTT username, the MQTT password and the root topic, in that order (the same order as alex2node's new Alex2MQTT(username, password, rootTopic)).

  alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);

Once initilized you can begin to add virtual devices, in this example we add a power controller to toggle a light


  AlexaDevice* device1 = alexClient.getDevice("Test Lamp", "ESP-01");
  device1->setDisplayCategory(DisplayCategory::LIGHT);
  device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);

  device1->registerEvent("ReportState", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
    // Build and send status report
    device1->buildStatusMessage(directive["header"]["correlationToken"])
      .AddHealthProp(EndpointHealth::OK)
      .AddPowerControllerProp(outputState)
      .send();
  });

  // Register event listener for "Event" directive to handle state changes
  device1->registerEvent("Event", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
    if (type == AlexaInterfaceType::POWER_CONTROLLER) {
      if (directive["header"]["name"] == "TurnOn") {
        outputState = PowerController::ON;
        Serial.println("Turning ON");
      } else if (directive["header"]["name"] == "TurnOff") {
        outputState = PowerController::OFF;
        Serial.println("Turning OFF");
      }

      // Send the updated state after change
      device1->buildStatusMessage(directive["header"]["correlationToken"], true)
        .AddHealthProp(EndpointHealth::OK)
        .AddPowerControllerProp(outputState)
        .send();
    }
  });

The pointers returned by getDevice() and addCapability() stay valid for the lifetime of the client, so keeping them in globals and adding more devices or capabilities later is fine.


Example: Toggle Controller for Blinds

Alex2ESP also supports action mapping so we can map keywords such as "open" and "close" to a toggle controller For a comprehensive list of Alexa actions and mappings, visit the Alexa Developer Documentation

AlexaDevice* device = alexClient.getDevice("Bedroom Blinds", "ESP-01");
device->setDisplayCategory(DisplayCategory::INTERIOR_BLIND);

AlexaInterface* toggleController = device->addCapability(AlexaInterfaceType::TOGGLE_CONTROLLER);
toggleController->setInstance("ESP-01.Toggle");
toggleController->addFriendlyName("Bedroom Blinds", "en-US");

ActionMapping closeMapping({AlexaActions::Close}, "TurnOn");
ActionMapping openMapping({AlexaActions::Open}, "TurnOff");
toggleController->addActionMapping(closeMapping);
toggleController->addActionMapping(openMapping);

An ActionMapping takes an optional third argument, the directive payload as JSON text (for example "{\"rangeValue\": 0}" for a RangeController). It is emitted as an object in the discovery response and omitted when empty.


How it talks to Alex2MQTT

Everything goes over MQTT (port 1883 of alex2mqtt.stormysdream.club); the library makes no HTTP requests.

  • Session. begin() starts SNTP (pool.ntp.org, time.nist.gov) and returns; loop() opens the MQTT session once Wi-Fi is up and the clock is set, or after 5 s of Wi-Fi without an answer, and subscribes to <root>/discover and <root>/+/alexaDirective. getState() is CONNECTED when the broker has acknowledged both subscriptions; [Alex2ESP] error: the broker refused the subscription to <topic> means that the root topic is not the one of the account. A session that ends or cannot be opened prints [Alex2ESP] error: disconnected: <reason>; next attempt in N s and is opened again after 1 s, then 2 s, 4 s and so on up to once a minute, for as long as Wi-Fi is up; the wait starts at 1 s again once a session is open. the broker refused the username or the password is the reason to look for when a board never shows up in Alexa. A sketch that sets the clock itself (its own configTime() with a time zone, an RTC) calls alexClient.setTimeSource(false) before begin(). The board has to reach an NTP server: DNS for the two names and outbound UDP port 123. Without the time of day the session still opens, the reports carry a timeOfSample in 1970, and the library prints [Alex2ESP] error: the clock is not set, ... when it connects and then at most once a minute while reports are sent.
  • Discovery. On <root>/discover the library answers with one discovery object per device on <root>/discover_r. The backend accepts one endpoint object per message and collects everything that arrives within 1 s for Alexa's discovery answer (up to 5 s for its proactive AddOrUpdate push), so all devices are published back to back from the next loop(). Each object sits on the heap (about 1 KB) until the MQTT client has sent it; when the client cannot take another one (free heap under 4 KB), the library prints [Alex2ESP] discovery deferred at <endpointId> and sends the rest from loop() as the queue drains, for up to 5 s after the request. [Alex2ESP] error: discovery gave up: N device(s) not announced means those devices missed this answer - on the backend's proactive discovery that can remove them from Alexa until the next one.
  • Directives. The directive arrives as JSON on <root>/<endpointId>/alexaDirective. A directive larger than one TCP segment arrives in fragments, which are put together in one heap block that exists only until loop() has parsed it. loop() then fires ReportState or Event (and DirectiveReceived, if registered) with the directive: directive["header"], directive["endpoint"], directive["payload"]. Every call of loop() handles one directive, in the order of arrival. Up to eight directives wait for it: Alexa sends a group command ("turn off the kitchen") as one directive per endpoint, and they arrive faster than a busy sketch calls loop(). A directive that arrives twice (a broker that mirrors its topics delivers every message twice) is handled once; the repeat is recognised while it arrives and takes no place among the waiting ones.
  • Reports. send() publishes the report on <root>/<endpointId>/alexaResponce at once. The backend waits 7 s for it, so answer from the event handler. Every property carries the board's UTC time as timeOfSample.
  • Limits. A directive may be 2047 bytes (ALEX2ESP_MAX_DIRECTIVE), a report or the discovery object of one device 3071 (ALEX2ESP_MAX_MESSAGE). The second limit is the first plus 1024 unless it is set: an answer repeats the correlationToken of its directive, which is most of a large directive, and adds 140 to 170 bytes per property, so the largest directive can be answered with six properties. Eight directives (ALEX2ESP_MAX_QUEUED_DIRECTIVES) of 8188 bytes together (ALEX2ESP_MAX_QUEUED_BYTES, four times the largest directive) may wait for loop(); one that finds no place is dropped with [Alex2ESP] error: directive of N bytes on <topic> dropped: .... -D<name>=<value> in build_flags changes a limit. send() returns false when the report was not sent: no session with the broker, the MQTT client or the heap cannot take it, or it is too large. Nothing is ever sent truncated, and nothing is dropped without a line on Serial.
  • Serial output. Every line of the library starts with [Alex2ESP], a problem with [Alex2ESP] error:. alexClient.setLogLevel(AlexaLogLevel::ERROR) leaves only the problems, AlexaLogLevel::NONE nothing; the default, AlexaLogLevel::INFO, adds the session, discovery and one line per directive ([Alex2ESP] ESP-01 <- Alexa.PowerController.TurnOn). AlexaLogLevel::DEBUG (sizes and free heap per message) has to be compiled in with -DALEX2ESP_LOG_MAX=3; -DALEX2ESP_LOG_MAX=0 compiles every line out. Credentials and correlation tokens are never printed. A sketch that defines a macro named DEBUG, ERROR or INFO cannot write the level of that name; it passes the number instead, for example alexClient.setLogLevel(static_cast<AlexaLogLevel>(3)) for DEBUG.

Boards that run 1.1.0 or older keep working: the backend still publishes the token on <root>/<endpointId>/alexaDirective_e and serves the HTTP routes they use.


Interface Types

Alexa Interface Type Status
AlexaInterfaceType::ENDPOINT_HEALTH Fully Supported
AlexaInterfaceType::POWER_CONTROLLER Fully Supported
AlexaInterfaceType::BRIGHTNESS_CONTROLLER Fully Supported
AlexaInterfaceType::TOGGLE_CONTROLLER Fully Supported
AlexaInterfaceType::TEMPERATURE_SENSOR Fully Supported
AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER Fully Supported
AlexaInterfaceType::AUTOMATION_MANAGEMENT Supported*
AlexaInterfaceType::CHANNEL_CONTROLLER Supported*
AlexaInterfaceType::COLOR_CONTROLLER Supported*
AlexaInterfaceType::CONTACT_SENSOR Supported*
AlexaInterfaceType::APPLICATION_STATE_REPORTER Supported*
AlexaInterfaceType::AUDIO_PLAY_QUEUE Supported*
AlexaInterfaceType::AUTHORIZATION_CONTROLLER Supported*
AlexaInterfaceType::AUTOMOTIVE_VEHICLE_DATA Supported*
AlexaInterfaceType::CAMERA_LIVE_VIEW_CONTROLLER Supported*
AlexaInterfaceType::CAMERA_STREAM_CONTROLLER Supported*
AlexaInterfaceType::COMMISSIONABLE Supported*
AlexaInterfaceType::CONSENT_MANAGEMENT_CONSENT_REQUIRED_REPORTER Supported*
AlexaInterfaceType::COOKING Supported*
AlexaInterfaceType::DATA_CONTROLLER Supported*
AlexaInterfaceType::DEVICE_USAGE_ESTIMATION Supported*
AlexaInterfaceType::DEVICE_USAGE_METER Supported*
AlexaInterfaceType::DOORBELL_EVENT_SOURCE Supported*
AlexaInterfaceType::EQUALIZER_CONTROLLER Supported*
AlexaInterfaceType::INPUT_CONTROLLER Supported*
AlexaInterfaceType::INVENTORY_LEVEL_SENSOR Supported*
AlexaInterfaceType::INVENTORY_LEVEL_USAGE_SENSOR Supported*
AlexaInterfaceType::INVENTORY_USAGE_SENSOR Supported*
AlexaInterfaceType::KEYPAD_CONTROLLER Supported*
AlexaInterfaceType::LAUNCHER Supported*
AlexaInterfaceType::LOCK_CONTROLLER Supported*
AlexaInterfaceType::MEDIA_PLAYBACK Supported*
AlexaInterfaceType::MEDIA_SEARCH Supported*
AlexaInterfaceType::MODE_CONTROLLER Supported*
AlexaInterfaceType::MOTION_SENSOR Supported*
AlexaInterfaceType::PERCENTAGE_CONTROLLER Supported*
AlexaInterfaceType::PLAYBACK_CONTROLLER Supported*
AlexaInterfaceType::PLAYBACK_STATE_REPORTER Supported*
AlexaInterfaceType::PROACTIVE_NOTIFICATION_SOURCE Supported*
AlexaInterfaceType::RANGE_CONTROLLER Supported*
AlexaInterfaceType::RECORD_CONTROLLER Supported*
AlexaInterfaceType::REMOTE_VIDEO_PLAYER Supported*
AlexaInterfaceType::RTC_SESSION_CONTROLLER Supported*
AlexaInterfaceType::SCENE_CONTROLLER Supported*
AlexaInterfaceType::SECURITY_PANEL_CONTROLLER Supported*
AlexaInterfaceType::SEEK_CONTROLLER Supported*
AlexaInterfaceType::SIMPLE_EVENT_SOURCE Supported*
AlexaInterfaceType::SMART_VISION_OBJECT_DETECTION_SENSOR Supported*
AlexaInterfaceType::SMART_VISION_SNAPSHOT_PROVIDER Supported*
AlexaInterfaceType::SPEAKER Supported*
AlexaInterfaceType::STEP_SPEAKER Supported*
AlexaInterfaceType::THERMOSTAT_CONTROLLER Supported*
AlexaInterfaceType::THERMOSTAT_CONTROLLER_CONFIGURATION Supported*
AlexaInterfaceType::THERMOSTAT_CONTROLLER_HVAC_COMPONENTS Supported*
AlexaInterfaceType::THERMOSTAT_CONTROLLER_SCHEDULE Supported*
AlexaInterfaceType::TIME_HOLD_CONTROLLER Supported*
AlexaInterfaceType::UI_CONTROLLER Supported*
AlexaInterfaceType::USER_PREFERENCE Supported*
AlexaInterfaceType::VIDEO_RECORDER Supported*
AlexaInterfaceType::WAKE_ON_LAN_CONTROLLER Supported*

(*Partial support or limited implementation advanced configuration is required)


Advanced usage

For devices with limited or partial support, Alex2ESP provides functions that allow you to attach custom JSON objects to the status report. For example, to report the status of a PowerController type, you can use the AddPowerControllerProp function. However, if a specific "add props" function does not exist for your use case, you can utilize the AddContextProp function to pass a custom JSON object.

For instance, to manually report the state of a PowerController, you can use the following code:

    JsonDocument doc;

    doc["namespace"] = "Alexa.PowerController";
    doc["name"] = "powerState";
    doc["value"] = "ON";
    doc["uncertaintyInMilliseconds"] = 0;
    AddContextProp(doc.as<JsonObject>())

AddContextProp sets timeOfSample to the current time when the object has none. (Until 1.1.0 a sketch wrote "{REPLACE_WITH_DATETIME}" there for the server to replace; the library now replaces that itself.)


Changelog

1.2.0

Behaviour changes:

  • Directives arrive over MQTT. The library subscribes to <root>/+/alexaDirective, where Alex2MQTT has always published the whole directive, instead of fetching it over HTTP with the token from <root>/<endpointId>/alexaDirective_e. The HTTP detour dates from 2024, when a directive larger than one TCP segment reached the MQTT callback in pieces; the pieces are now put together by their offset and the total length, in one heap block that lives until loop() has parsed the directive. loop() no longer stalls for two HTTP round trips per directive.
  • Reports leave over MQTT. send() publishes on <root>/<endpointId>/alexaResponce at once, where 1.1.0 queued the report for an HTTP POST from a later loop(). It returns false when there is no session with the broker, when the MQTT client or the heap cannot take the report, or when the report is over 3071 bytes (ALEX2ESP_MAX_MESSAGE, by default 1024 more than the largest directive, so that every directive that is accepted can be answered); each case prints its reason. The 5-slot send queue is gone.
  • timeOfSample is the board's own time in UTC, for example 2026-09-28T13:05:09Z. 1.1.0 sent the placeholder {REPLACE_WITH_DATETIME}, which only the backend's HTTP route replaced. begin() starts SNTP (pool.ntp.org, time.nist.gov) and no longer connects itself: loop() opens the MQTT session once the clock is set, or after 5 s without an answer, so the session comes up a few seconds later than before. alexClient.setTimeSource(false) before begin() leaves the clock to the sketch. AddContextProp() fills timeOfSample in when the property has none or carries the old placeholder. The board needs an answer from an NTP server (DNS, outbound UDP port 123), which 1.1.0 did not: without one the reports carry a time in 1970, and the library says so when it connects and then at most once a minute while reports are sent.
  • A directive is parsed and handed to the sketch from loop(), one per call, in the order of arrival. Up to eight directives of 8188 bytes together wait for it on the heap (ALEX2ESP_MAX_QUEUED_DIRECTIVES, ALEX2ESP_MAX_QUEUED_BYTES; 1.1.0 queued five tokens), because Alexa sends a group command as one directive per endpoint. A directive that finds no place is dropped, as is one over 2047 bytes (ALEX2ESP_MAX_DIRECTIVE); both print an error.
  • A directive that arrives twice is handled once: the Alex2MQTT broker currently delivers every message twice through a mirror. The repeat is recognised while it arrives, by a hash of its bytes, and takes no place among the waiting directives; a directive that comes again with other bytes is recognised by its messageId. The last 16 directives are remembered; the repeat prints repeated directive ... ignored.
  • The subscription delivers the directives of every endpoint of the account. Those for endpoints of another board are recognised by their topic and neither buffered nor parsed.
  • Discovery is answered from loop(), not inside the MQTT callback. A discovery object over 3071 bytes (ALEX2ESP_MAX_MESSAGE) is refused with an error; the other devices are still announced.
  • getState() stays INITIALIZED until the first connect, and becomes CONNECTED when the broker has acknowledged both subscriptions (1.1.0: the first of them). A subscription that the broker refuses prints an error that names the topic and what to check, the root topic passed to begin(); the state stays SUBSCRIBING. When the MQTT client does not take a subscription, the session is closed and the next one subscribes again.
  • Serial output goes through one log with levels. alexClient.setLogLevel() takes AlexaLogLevel::NONE, ERROR, INFO (the default) or DEBUG; -DALEX2ESP_LOG_MAX=<0..3> in build_flags sets the highest level that is compiled in (default 2, INFO). This replaces the Alex2ESP_DEBUG define inside AlexaUtils.cpp. 1.1.0 printed the memory figures for every MQTT message and the whole directive, correlation token included, for every directive; both are gone. A sketch or a build flag that defines DEBUG, ERROR or INFO as a macro (#define DEBUG 1, -DDEBUG) still compiles: the library sets those macros aside where it declares its levels and passes a level by its number everywhere else.
  • The session is opened again whatever ended it, and the reason is printed in words: disconnected: the broker refused the username or the password, check the two passed to begin(); next attempt in 1 s. 1.1.0 reconnected every 5 s after a lost TCP connection only and printed nothing; a board with a wrong password, or one that met the broker while it was restarting, stayed offline until it was reset. The wait is 1 s after a session ended and doubles with every attempt that fails, up to 60 s. Nothing is tried while Wi-Fi is down, and the first connect waits for Wi-Fi as well (waiting for Wi-Fi: ... is printed once). A connect that has no answer after 30 s counts as failed. The MQTT keep-alive is 30 s (1.1.0: the 15 s of the MQTT client).
  • getDevice() before begin() prints an error: the device would have no root topic. A second begin() is ignored with an error.
  • A report is sent through the client that created its device. An AlexaDevice or an AlexaStatusMessage that a sketch constructs itself has no client: send() returns false and prints report for <endpointId> not sent: its device was not created by getDevice(), where 1.1.0 queued the report. Use getDevice() and buildStatusMessage(). Both constructors take the client as an optional last argument, an AlexaTransport*.
  • New: Alex2ESP::setLogLevel(), Alex2ESP::setTimeSource(), AlexaDevice::hasEndpointId(), AlexaLog, AlexaSendResult, and AlexaTransport, the interface a device sends and stamps its reports through. Alex2ESP implements it with publish(topic, document), which sends a JsonDocument on the session of the library under the same checks as a report and returns an AlexaSendResult, and timestamp(buffer, size), which writes the current time as timeOfSample has it.
  • Removed: the queues and buffers of AlexaUtils (enqueue, dequeue, dequeueVals, enqueueReceive, dequeueReceive, isQueueEmpty, isQueueFull, isReceiveQueueEmpty, isReceiveQueueFull, receivePayload, nextMessageId) and its log/logln, which printed nothing unless the library was edited; AlexaUtils::printMemoryInfo() stays. MAX_STATUS_REPORT_SIZE (the limit is ALEX2ESP_MAX_MESSAGE). The library no longer includes ESP8266HTTPClient.

Memory: examples/basicLight.cpp for a D1 mini takes 34,476 bytes of static RAM (1.1.0: 52,768) and 338,281 bytes of flash (1.1.0: 350,885), as PlatformIO reports them (espressif8266 4.2.1, Arduino core 3.1.2). The static RAM was the five 2 KB queue slots, three more 2 KB buffers and the two HTTP clients. SNTP and the time stamp are 1.8 KB of the flash figure.

Tests: pio test -e native in the repository runs 45 host tests of the receive and publish logic (reassembly of fragments, the directives that wait for loop(), repeated directives, the size limits, a heap without room, the wait between reconnects, topics, time stamps). No board is needed.

Packaging: library.json, library.properties and the softwareVersion and firmwareVersion that a device reports in discovery say 1.2.0; the library has the number in one place, ALEX2ESP_VERSION in src/AlexaVersion.h. The platformio.ini of the repository is for the host tests; a sketch does not need it.

Boards that run 1.1.0 are not affected: the backend keeps the token topic and the HTTP routes.

1.1.0

Behaviour changes:

  • begin() now takes (username, password, rootTopic), the order every example and this readme always used (and the order alex2node uses). 1.0.0 declared (rootTopic, username, password), so a sketch written from the examples could not authenticate with the broker.
  • Discovery answers go straight to MQTT (<root>/discover_r), one object per device, all devices at once; if the MQTT client cannot take another object (free heap under 4 KB) the rest is sent from loop() as the client's queue drains, for up to 5 s. 1.0.0 pushed them one per loop() through a 5-slot HTTP queue and silently dropped the sixth device onwards, which the backend then removed from Alexa.
  • AlexaStatusMessage::send() returns bool: an oversized report (over 2047 bytes) or a full queue is reported on Serial and dropped instead of being sent truncated. AlexaUtils::enqueue() refuses packets that would not fit instead of truncating them.
  • Discovery JSON: semantics is emitted only when a capability has action mappings; an ActionMapping directive payload is emitted as a JSON object (parsed from the text you pass) or omitted when empty, instead of the string "{}".
  • Response/StateReport events include the required "payload": {}.
  • Devices and capabilities are stored in std::deque; pointers from getDevice()/addCapability() no longer dangle once another device or capability is added.
  • The MQTT payload is copied using its length (no write past the client's buffer); fragmented directive ids are ignored (a Discover is answered on its first fragment, its payload is unused).
  • begin() no longer prints the MQTT credentials to Serial in debug builds.
  • DirectiveReceived no longer prints "No event registered" when the sketch has not registered it.
  • Removed: Alex2ESP::messageSplitAndSend (declared, never defined) and AlexaStatusMessage::setEndpointId (did nothing).
  • Reported softwareVersion/firmwareVersion in the discovery attributes are now 1.1.0.

Examples: WiFi.begin(WIFI_SSID, WIFI_PASSWORD) (1.0.0 could only join open networks), LED_BUILTIN fallback, fixed brightness/colour-temperature Serial output, blinds use DisplayCategory::INTERIOR_BLIND.

Packaging: library.json and library.properties restored with the Forgejo URL and the dependencies, LICENSE (MIT), a real keywords.txt, LF line endings (.gitattributes), ArduinoJson 7 deprecated calls replaced (warning-free build). ESP8266 is the supported target; ESP32 is untested.


Contributing

Feel free to submit pull requests or issues for feature requests and bug fixes. pio test -e native in the repository root runs the host tests (no board needed); they have to pass.


License

This project is licensed under the MIT License. See the LICENSE file for details.