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>
104 lines
2.9 KiB
C++
104 lines
2.9 KiB
C++
// Doorbell: a button that tells Alexa when it is pressed (Alexa.DoorbellEventSource).
|
|
//
|
|
// The button connects the pin to ground; the internal pull-up makes the pin high while it is released. A level has
|
|
// to hold for 50 ms before it counts. Every press publishes a DoorbellPress event on <root>/event. A press while
|
|
// there is no session with the broker is lost: the library prints why it was not sent.
|
|
//
|
|
// This event has not been tried with Alexa yet.
|
|
#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 BUTTON_PIN = 14; // D5 on a Wemos D1 mini
|
|
const unsigned long DEBOUNCE_MS = 50;
|
|
|
|
Alex2ESP alexa;
|
|
AlexaDevice *doorbell;
|
|
|
|
bool pressed = false;
|
|
bool lastLevel = false;
|
|
unsigned long lastEdge = 0;
|
|
|
|
// 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 onDirective(AlexaDirective &directive)
|
|
{
|
|
if (directive.isReportState())
|
|
{
|
|
directive.stateReport().addHealthProp(EndpointHealth::OK).send();
|
|
return;
|
|
}
|
|
|
|
directive.error(AlexaErrorType::INVALID_DIRECTIVE, "A doorbell takes no directives").send();
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
Serial.begin(74880);
|
|
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
|
|
|
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
|
|
doorbell = alexa.getDevice("Front Door Bell", "esp-doorbell");
|
|
doorbell->setDisplayCategory(DisplayCategory::DOORBELL);
|
|
doorbell->addCapability(AlexaInterfaces::DoorbellEventSource);
|
|
doorbell->addCapability(AlexaInterfaces::EndpointHealth);
|
|
doorbell->onDirective(onDirective);
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
alexa.loop();
|
|
|
|
bool level = digitalRead(BUTTON_PIN) == LOW;
|
|
if (level != lastLevel)
|
|
{
|
|
lastLevel = level;
|
|
lastEdge = millis();
|
|
}
|
|
|
|
if (level != pressed && millis() - lastEdge >= DEBOUNCE_MS)
|
|
{
|
|
pressed = level;
|
|
if (pressed)
|
|
{
|
|
Serial.println("[DOORBELL] pressed");
|
|
doorbell->doorbellPress().send();
|
|
}
|
|
}
|
|
}
|