AlexaStatusMessage: DeferredResponse, ChangeReport, ErrorResponse, scene and doorbell events, UUID-shaped messageId
A message has a kind, and the kind decides its topic: answers on <root>/<id>/alexaResponce, the answer after a DeferredResponse on <root>/<id>/deferredResponse (sendAsync()), a ChangeReport on <root>/changeReport with the changed properties apart from the others (context()), DoorbellPress on <root>/event. AlexaDirective gains deferred(), error(type, message), sceneStarted() and sceneStopped(); thermostat error types go out in the namespace of the thermostat. The helpers are add*Prop, the Add*Prop names stay; a temperature keeps its scale (69 FAHRENHEIT, was 20.56 CELSIUS). messageId is a version 4 UUID from the hardware random source, where rand() seeded per second gave two messages one id. basicLight: static RAM 30,540 B (-76), flash 334,981 B (+1,280); 112 host tests (bridge logic 52, messages 19, new). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
42af413a6e
commit
3215457f91
12 changed files with 1093 additions and 90 deletions
82
readme.md
82
readme.md
|
|
@ -199,7 +199,30 @@ Everything goes over MQTT (port 1883 of `alex2mqtt.stormysdream.club`); the libr
|
|||
- **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 <root>/discover` 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 after a session that lasted a minute: a session that the broker closes right after it has accepted it, as it does to one of two boards with the same client id, is opened again after the longer wait. `the broker refused the username or the password` is the reason to look for when a board never shows up in Alexa, `the broker did not answer within 30 s` stands for an attempt that the library gave up. When the link goes down the library prints `[Alex2ESP] error: Wi-Fi is down, waiting for it`, once per loss; the MQTT client reports the lost connection when its keep-alive runs out, and the session is opened again when Wi-Fi is back. 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 calls the handler of the device: the one of `onDirective()`, or else `ReportState` or `Event` of `registerEvent()` (and before it `DirectiveReceived`, if registered) with the directive: `directive["header"]`, `directive["endpoint"]`, `directive["payload"]`. A device without a handler answers with the ErrorResponse `INVALID_DIRECTIVE`, and so does a device whose handler sent nothing for a directive that names a capability the device does not have; both print an error. A handler that sends nothing for a capability of its device leaves the directive unanswered, which prints `[Alex2ESP] error: <endpointId>: the handler sent no answer to ...`. 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`.
|
||||
- **Reports.** `send()` publishes a message at once, on the topic of its kind. Every property carries the board's UTC time as `timeOfSample`, every message a `messageId` of its own, a UUID made of the random source of the hardware.
|
||||
|
||||
| Kind | Built with | Topic |
|
||||
|---|---|---|
|
||||
| Response | `d.response()` | `<root>/<endpointId>/alexaResponce` |
|
||||
| StateReport | `d.stateReport()` | `<root>/<endpointId>/alexaResponce` |
|
||||
| ErrorResponse | `d.error(AlexaErrorType::VALUE_OUT_OF_RANGE, "0 to 100")` | `<root>/<endpointId>/alexaResponce` |
|
||||
| DeferredResponse | `d.deferred(7)` | `<root>/<endpointId>/alexaResponce` |
|
||||
| the answer after it | `device->response(token)` ... `sendAsync()` | `<root>/<endpointId>/deferredResponse` |
|
||||
| ActivationStarted, DeactivationStarted | `d.sceneStarted()`, `d.sceneStopped()` | `<root>/<endpointId>/alexaResponce` |
|
||||
| ChangeReport | `device->changeReport(AlexaCause::PHYSICAL_INTERACTION)` | `<root>/changeReport` |
|
||||
| DoorbellPress | `device->doorbellPress()` | `<root>/event` |
|
||||
|
||||
The backend waits 7 s for the answer to a directive, so answer from the handler. A device that takes longer (Alexa accepts this for locks and Wake-on-LAN) sends `d.deferred(seconds)`, keeps a copy of `d.correlationToken` and sends the answer when it is done: `device->response(token).addHealthProp(EndpointHealth::OK).sendAsync()`. An `ErrorResponse` takes what its type asks for in `payload()`, for example `payload()["validRange"]["maximumValue"] = 100`; the types of a thermostat (`REQUESTED_SETPOINTS_TOO_CLOSE`, ...) are sent in the namespace `Alexa.ThermostatController`. A `ChangeReport` tells Alexa that properties of a capability with `setProactivelyReported(true)` changed: the properties added before `context()` are those that changed, the ones after it the others.
|
||||
|
||||
```cpp
|
||||
device->changeReport(AlexaCause::PHYSICAL_INTERACTION)
|
||||
.addPowerControllerProp(PowerController::ON)
|
||||
.context()
|
||||
.addHealthProp(EndpointHealth::OK)
|
||||
.send();
|
||||
```
|
||||
|
||||
A `ChangeReport` without a property that changed is not sent and prints an error.
|
||||
- **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. The username, the password, the root topic and correlation tokens are never printed, at any level, so a log can be posted as it is: a topic appears without its root (`ESP-01/alexaResponce`), a directive under its endpoint id. 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`.
|
||||
|
||||
|
|
@ -209,32 +232,32 @@ Boards that run 1.1.0 or older keep working: the backend still publishes the tok
|
|||
|
||||
## Interface Types
|
||||
|
||||
The library describes these interfaces. `addCapability()` takes the row, `AlexaInterfaces::PowerController`, or the type as 1.x sketches write it, `AlexaInterfaceType::POWER_CONTROLLER`. A sketch links the rows it names and no others.
|
||||
The library describes these interfaces. `addCapability()` takes the row, `AlexaInterfaces::PowerController`, or the type as 1.x sketches write it, `AlexaInterfaceType::POWER_CONTROLLER`. A sketch links the rows it names and no others. The report helpers of 1.x, `AddHealthProp` and the others with a capital letter, stay as names of the ones in the table.
|
||||
|
||||
| Row in `AlexaInterfaces` | `AlexaInterfaceType::` | Version | Properties | Report helper |
|
||||
|---|---|---|---|---|
|
||||
| `EndpointHealth` | `ENDPOINT_HEALTH` | 3.1 | connectivity | `AddHealthProp` |
|
||||
| `PowerController` | `POWER_CONTROLLER` | 3 | powerState | `AddPowerControllerProp` |
|
||||
| `BrightnessController` | `BRIGHTNESS_CONTROLLER` | 3 | brightness | `AddBrightnessControllerProp` |
|
||||
| `ColorTemperatureController` | `COLOR_TEMPERATURE_CONTROLLER` | 3 | colorTemperatureInKelvin | `AddColorTemperatureControllerProp` |
|
||||
| `ToggleController` | `TOGGLE_CONTROLLER` | 3 | toggleState | `AddToggleControllerProp` |
|
||||
| `TemperatureSensor` | `TEMPERATURE_SENSOR` | 3 | temperature | `AddTemperatureSensorProp` |
|
||||
| `ColorController` | `COLOR_CONTROLLER` | 3 | color | `AddContextProp` |
|
||||
| `PowerLevelController` | `POWER_LEVEL_CONTROLLER` | 3 | powerLevel | `AddContextProp` |
|
||||
| `PercentageController` | `PERCENTAGE_CONTROLLER` | 3 | percentage | `AddContextProp` |
|
||||
| `RangeController` | `RANGE_CONTROLLER` | 3 | rangeValue | `AddContextProp` |
|
||||
| `ModeController` | `MODE_CONTROLLER` | 3 | mode | `AddContextProp` |
|
||||
| `ThermostatController` | `THERMOSTAT_CONTROLLER` | 3.2 | targetSetpoint, lowerSetpoint, upperSetpoint, thermostatMode | `AddContextProp` |
|
||||
| `HumiditySensor` | `HUMIDITY_SENSOR` | 3 | relativeHumidity | `AddContextProp` |
|
||||
| `LockController` | `LOCK_CONTROLLER` | 3 | lockState | `AddContextProp` |
|
||||
| `ContactSensor` | `CONTACT_SENSOR` | 3 | detectionState | `AddContextProp` |
|
||||
| `MotionSensor` | `MOTION_SENSOR` | 3 | detectionState | `AddContextProp` |
|
||||
| `TimeHoldController` | `TIME_HOLD_CONTROLLER` | 3 | holdStartTime, holdEndTime | `AddContextProp` |
|
||||
| `Speaker` | `SPEAKER` | 3 | volume, muted | `AddContextProp` |
|
||||
| `PlaybackStateReporter` | `PLAYBACK_STATE_REPORTER` | 3 | playbackState | `AddContextProp` |
|
||||
| `InputController` | `INPUT_CONTROLLER` | 3 | input | `AddContextProp` |
|
||||
| `ChannelController` | `CHANNEL_CONTROLLER` | 3 | channel | `AddContextProp` |
|
||||
| `InventoryLevelSensor` | `INVENTORY_LEVEL_SENSOR` | 3 | level | `AddContextProp` |
|
||||
| `EndpointHealth` | `ENDPOINT_HEALTH` | 3.1 | connectivity | `addHealthProp` |
|
||||
| `PowerController` | `POWER_CONTROLLER` | 3 | powerState | `addPowerControllerProp` |
|
||||
| `BrightnessController` | `BRIGHTNESS_CONTROLLER` | 3 | brightness | `addBrightnessControllerProp` |
|
||||
| `ColorTemperatureController` | `COLOR_TEMPERATURE_CONTROLLER` | 3 | colorTemperatureInKelvin | `addColorTemperatureControllerProp` |
|
||||
| `ToggleController` | `TOGGLE_CONTROLLER` | 3 | toggleState | `addToggleControllerProp` |
|
||||
| `TemperatureSensor` | `TEMPERATURE_SENSOR` | 3 | temperature | `addTemperatureSensorProp` |
|
||||
| `ColorController` | `COLOR_CONTROLLER` | 3 | color | `addContextProp` |
|
||||
| `PowerLevelController` | `POWER_LEVEL_CONTROLLER` | 3 | powerLevel | `addContextProp` |
|
||||
| `PercentageController` | `PERCENTAGE_CONTROLLER` | 3 | percentage | `addContextProp` |
|
||||
| `RangeController` | `RANGE_CONTROLLER` | 3 | rangeValue | `addContextProp` |
|
||||
| `ModeController` | `MODE_CONTROLLER` | 3 | mode | `addContextProp` |
|
||||
| `ThermostatController` | `THERMOSTAT_CONTROLLER` | 3.2 | targetSetpoint, lowerSetpoint, upperSetpoint, thermostatMode | `addContextProp` |
|
||||
| `HumiditySensor` | `HUMIDITY_SENSOR` | 3 | relativeHumidity | `addContextProp` |
|
||||
| `LockController` | `LOCK_CONTROLLER` | 3 | lockState | `addContextProp` |
|
||||
| `ContactSensor` | `CONTACT_SENSOR` | 3 | detectionState | `addContextProp` |
|
||||
| `MotionSensor` | `MOTION_SENSOR` | 3 | detectionState | `addContextProp` |
|
||||
| `TimeHoldController` | `TIME_HOLD_CONTROLLER` | 3 | holdStartTime, holdEndTime | `addContextProp` |
|
||||
| `Speaker` | `SPEAKER` | 3 | volume, muted | `addContextProp` |
|
||||
| `PlaybackStateReporter` | `PLAYBACK_STATE_REPORTER` | 3 | playbackState | `addContextProp` |
|
||||
| `InputController` | `INPUT_CONTROLLER` | 3 | input | `addContextProp` |
|
||||
| `ChannelController` | `CHANNEL_CONTROLLER` | 3 | channel | `addContextProp` |
|
||||
| `InventoryLevelSensor` | `INVENTORY_LEVEL_SENSOR` | 3 | level | `addContextProp` |
|
||||
| `PlaybackController` | `PLAYBACK_CONTROLLER` | 3 | `"properties": {}` | |
|
||||
| `WakeOnLANController` | `WAKE_ON_LAN_CONTROLLER` | 3 | `"properties": {}` | |
|
||||
| `SceneController` | `SCENE_CONTROLLER` | 3 | no `properties` object | |
|
||||
|
|
@ -294,7 +317,12 @@ Behaviour changes:
|
|||
- The handler of `Event` gets the type of the capability of its device that the directive names, and `AlexaInterfaceType::UNKNOWN` for a namespace the device has no capability for. 1.1.0 looked the namespace up among all interfaces.
|
||||
- Removed: `AlexaInterfaceType::AUTHORIZATION_CONTROLLER` and `AUTOMOTIVE_VEHICLE_DATA`, which Alexa has withdrawn. `AlexaInterfaceUtils` (`toString`, `fromString`, `getVersion`, `getProps`): a row has the namespace, the version and the properties, `alexaInterfaceRow(type)` gives the row of a type. The constructor of `AlexaInterface` takes a row. `DisplayCategory` and `AlexaInterfaceType` are one byte wide.
|
||||
- New: `AlexaCapability`, which `addCapability()` returns. `addCapability(AlexaInterfaces::RangeController, "Blind.Lift")` takes the instance of an interface that a device may have several of, and a device can have several capabilities of one such interface. `addFriendlyAsset()`, `setConfiguration()`, `addStateMapping()` and `setNonControllable()` are new; every setter returns the capability. `matches(namespace, instance)` tells whether a directive is for the capability. `AlexaInterface` and `AlexaActions` stay as names of `AlexaCapability` and `AlexaAction`, so a 1.x sketch compiles as it is.
|
||||
- New: `AlexaDevice::onDirective(handler)`. The handler, `void handler(AlexaDirective& d)`, gets every directive of its device with the device (`d.device`), the capability it is for (`d.capability`, `d.type`), its `ns`, `name`, `instance`, `correlationToken` and `payload`, and builds the answer with `d.response()` or `d.stateReport()`; one function can serve several devices. `registerEvent()` stays for the handlers of 1.x, which a device calls when it has no handler of `onDirective()`. `AlexaDevice::findCapability(namespace, instance)`, `AlexaStatusMessage::asErrorResponse(type, message)`.
|
||||
- New: `AlexaDevice::onDirective(handler)`. The handler, `void handler(AlexaDirective& d)`, gets every directive of its device with the device (`d.device`), the capability it is for (`d.capability`, `d.type`), its `ns`, `name`, `instance`, `correlationToken` and `payload`, and builds the answer with `d.response()`, `d.stateReport()`, `d.error(type, message)`, `d.deferred(seconds)` or, for a scene, `d.sceneStarted()` and `d.sceneStopped()`; one function can serve several devices. `registerEvent()` stays for the handlers of 1.x, which a device calls when it has no handler of `onDirective()`. `AlexaDevice::findCapability(namespace, instance)`.
|
||||
- New kinds of messages beside `Response` and `StateReport`: `ErrorResponse` (`AlexaErrorType` has the types of Alexa, `asErrorResponse(type, message)` turns an answer into one), `DeferredResponse` and the answer that follows it (`sendAsync()`, on `<root>/<endpointId>/deferredResponse`), `ChangeReport` (`AlexaDevice::changeReport(cause)`, on `<root>/changeReport`), the events of a scene, and `DoorbellPress` (`AlexaDevice::doorbellPress()`, or `event(row, name)` for another interface, on `<root>/event`). `AlexaDevice::response(token)` and `stateReport(token)` build an answer outside of the handler. `payload()` gives the payload of a message, `getKind()` its kind.
|
||||
- The report helpers begin with a small letter: `addHealthProp`, `addPowerControllerProp`, `addBrightnessControllerProp`, `addColorTemperatureControllerProp`, `addToggleControllerProp(instance, state)`, `addTemperatureSensorProp(value, scale)`, `addContextProp`. The names of 1.x stay, with their order of arguments.
|
||||
- A temperature is reported in the scale it is given in: `AddTemperatureSensorProp(TemperatureSensorScale::FAHRENHEIT, 69)` reports 69 `FAHRENHEIT`, where 1.1.0 reported 20.56 `CELSIUS`. `TemperatureSensorScale::KELVIN` is new.
|
||||
- `messageId` is a UUID of version 4 from the random source of the hardware. 1.1.0 sent 37 characters from `rand()`, seeded with the time in seconds: two messages of one second had the same id.
|
||||
- A `ChangeReport` or an event that a handler sends is not taken for the answer to its directive.
|
||||
- A directive that nothing answers is answered by the library with the ErrorResponse `INVALID_DIRECTIVE`: when the device has no handler for it, and when it names a capability that the device does not have and the handler sent nothing. 1.1.0 left both to the timeout, after which Alexa says that the device does not respond. A handler that sends nothing for a capability of its device prints an error; nothing is sent for it.
|
||||
- Devices are a linked list, and a device holds its capabilities in an array of 8 pointers (`ALEX2ESP_MAX_CAPABILITIES`); both were a `std::deque`. A ninth capability is refused with an error and `addCapability()` returns `nullptr`. `getDevice()` returns `nullptr` with an error when the heap has no room for the device. An `AlexaDevice` cannot be copied. A directive whose topic names a device of the board and whose `endpointId` does not is ignored with a line at `DEBUG` (it was an error).
|
||||
- A capability takes 116 bytes of heap and holds 3 friendly names, 4 action mappings and 2 state mappings (`ALEX2ESP_MAX_FRIENDLY_NAMES`, `ALEX2ESP_MAX_ACTION_MAPPINGS`, `ALEX2ESP_MAX_STATE_MAPPINGS`); one more is refused with an error. The instance and the text of a friendly name are copied, as before. The locale and the directive name and payload of an `ActionMapping` are kept as pointers, where 1.1.0 copied them: pass literals. An `ActionMapping` announces its actions in the order of `AlexaAction`.
|
||||
|
|
@ -302,9 +330,9 @@ Behaviour changes:
|
|||
- `PlaybackController` and `WakeOnLANController` are announced with `"properties": {}`, as their pages show them.
|
||||
- Removed: `AlexaInterface::getJSON()` (`toJson()` adds the capability to the capabilities of its endpoint) and `getProps()` (the row has the properties), `ActionMapping::getJSON()` and the `String` and `std::vector` members of `ActionMapping`, the class `FriendlyName`.
|
||||
|
||||
Memory: `examples/basicLight.cpp` for a D1 mini takes 30,616 bytes of static RAM (1.1.0: 52,768) and 333,701 bytes of flash (1.1.0: 350,885), as PlatformIO reports them (espressif8266 4.2.1, Arduino core 3.1.2). 18,260 bytes of the static RAM were the five 2 KB queue slots, three more 2 KB buffers and the two HTTP clients; 3,720 were the names, versions and properties of all interfaces and the names of the display categories, which are in flash now. SNTP and the time stamp are 1.8 KB of the flash figure. The instance, names, configuration and mappings of a capability are 2.0 KB of it, the directive handler and the ErrorResponse 0.9 KB.
|
||||
Memory: `examples/basicLight.cpp` for a D1 mini takes 30,540 bytes of static RAM (1.1.0: 52,768) and 334,981 bytes of flash (1.1.0: 350,885), as PlatformIO reports them (espressif8266 4.2.1, Arduino core 3.1.2). 18,260 bytes of the static RAM were the five 2 KB queue slots, three more 2 KB buffers and the two HTTP clients; 3,720 were the names, versions and properties of all interfaces and the names of the display categories, which are in flash now. SNTP and the time stamp are 1.8 KB of the flash figure. The instance, names, configuration and mappings of a capability are 2.0 KB of it, the directive handler and the ErrorResponse 0.9 KB, the kinds of messages and the UUID 1.3 KB. A kind that a sketch does not send is not linked.
|
||||
|
||||
Tests: `pio test -e native` in the repository runs 91 host tests: 50 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), 30 of discovery (the discovery object of every example against what 1.1.0 announced, the 28 rows against the interface pages, a type without a row, the discovery objects of a range, a mode and a toggle controller, a scene and a doorbell, what a capability refuses) and 11 of dispatch (the device and the capability a directive reaches, one handler for two devices, the handlers of 1.x, a capability more than a device holds, the answers to a directive for a capability the device lacks and to one without a handler). No board is needed.
|
||||
Tests: `pio test -e native` in the repository runs 112 host tests: 52 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, the message id), 30 of discovery (the discovery object of every example against what 1.1.0 announced, the 28 rows against the interface pages, a type without a row, the discovery objects of a range, a mode and a toggle controller, a scene and a doorbell, what a capability refuses), 11 of dispatch (the device and the capability a directive reaches, one handler for two devices, the handlers of 1.x, a capability more than a device holds, the answers to a directive for a capability the device lacks and to one without a handler) and 19 of the messages (every kind against the JSON and the topic it is published with, the names of 1.x, the scale of a temperature, what `send()` and `sendAsync()` refuse). 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.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue