Alex2ESP/examples/Scene/Scene.ino
David c3d02f462e Announce a doorbell with proactivelyReported and a scene with supportsDeactivation
An interface without properties says beside its name what Alexa has to know
of it; the library left both out. With a board and Alexa on 2026-09-28 the
discovery of a doorbell was accepted (202), the endpoint was not listed, and
its DoorbellPress was answered with 500 INTERNAL_SERVICE_EXCEPTION.

DoorbellEventSource now always carries "proactivelyReported": true.
SceneController carries "supportsDeactivation", false until the sketch calls
setSupportsDeactivation(true); on another interface the call is refused and
logged. The Scene example says that it can be undone.

148 host tests. Light: static RAM 30,616 B, flash 332,705 B (+140 B).

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

104 lines
2.9 KiB
C++

// Scene: something Alexa starts by name, "Alexa, turn on Movie Night" (Alexa.SceneController).
//
// A scene has no state to report. Activate is answered with ActivationStarted in place of a Response, Deactivate
// with DeactivationStarted. The scene of this sketch switches two outputs: the ceiling lamp off and the lamp
// behind the screen on.
//
// setSupportsDeactivation(true) tells Alexa that the scene can be undone: "Alexa, turn off Movie Night" then
// arrives as Deactivate.
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Alex2ESP.h>
// Wi-Fi and the credentials of your Alex2MQTT account
const char *WIFI_SSID = "";
const char *WIFI_PASSWORD = "";
const char *ALEXA_USERNAME = "";
const char *ALEXA_PASSWORD = "";
const char *ALEXA_ROOT_TOPIC = "";
const uint8_t CEILING_PIN = 5; // D1 on a Wemos D1 mini, high = on
const uint8_t SCREEN_PIN = 4; // D2
Alex2ESP alexa;
// Joins the Wi-Fi network and returns after 30 s at the latest. Without a connection the sketch carries on: the
// ESP8266 keeps trying, and alexa.loop() opens the MQTT session once Wi-Fi is up.
void connectWiFi()
{
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.printf("\n[WIFI] Connecting to \"%s\"\n", WIFI_SSID);
unsigned long started = millis();
while (WiFi.status() != WL_CONNECTED && millis() - started < 30000)
{
delay(100);
}
if (WiFi.status() == WL_CONNECTED)
{
Serial.printf("[WIFI] Connected, IP address %s\n", WiFi.localIP().toString().c_str());
}
else
{
Serial.printf("[WIFI] No connection after 30 s (status %d): check WIFI_SSID and WIFI_PASSWORD. Still trying.\n",
WiFi.status());
}
}
void setScene(bool active)
{
digitalWrite(CEILING_PIN, active ? LOW : HIGH);
digitalWrite(SCREEN_PIN, active ? HIGH : LOW);
}
void onDirective(AlexaDirective &directive)
{
if (directive.isReportState())
{
directive.stateReport().addHealthProp(EndpointHealth::OK).send();
return;
}
if (directive.is("Activate"))
{
setScene(true);
directive.sceneStarted().send();
}
else if (directive.is("Deactivate"))
{
setScene(false);
directive.sceneStopped().send();
}
else
{
directive.error(AlexaErrorType::INVALID_DIRECTIVE, "A scene is activated and deactivated").send();
}
}
void setup()
{
Serial.begin(74880);
pinMode(CEILING_PIN, OUTPUT);
pinMode(SCREEN_PIN, OUTPUT);
setScene(false);
connectWiFi();
// MQTT user name, MQTT password, root topic
alexa.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
// The name Alexa shows, and the id of the endpoint: every device of an account has its own
AlexaDevice *scene = alexa.getDevice("Movie Night", "esp-scene");
scene->setDisplayCategory(DisplayCategory::SCENE_TRIGGER);
scene->addCapability(AlexaInterfaces::SceneController)->setSupportsDeactivation(true);
scene->addCapability(AlexaInterfaces::EndpointHealth);
scene->onDirective(onDirective);
}
void loop()
{
alexa.loop();
}