Add examples (brightness, color temp, temp sensor, blind control) + AlexaStatusMessage; update src/keywords/readme; drop library.json/properties
This commit is contained in:
parent
9bf63a4146
commit
1313f47856
19 changed files with 2780 additions and 2849 deletions
|
|
@ -1,93 +1,93 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char* WIFI_SSID = "";
|
||||
|
||||
const char* ALEXA_USERNAME = "";
|
||||
const char* ALEXA_PASSWORD = "";
|
||||
const char* ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice* device1;
|
||||
|
||||
// Initial state for the light (PowerController)
|
||||
PowerController outputState = PowerController::OFF;
|
||||
|
||||
void setup() {
|
||||
// Initialize the LED as output
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice("Test Lamp", "ESP-01");
|
||||
|
||||
// Set the device's display category to LIGHT
|
||||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
// Add a power controller capability to the device
|
||||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
|
||||
// Update the state of the LED based on the power controller state
|
||||
digitalWrite(LED_BUILTIN, outputState != PowerController::ON);
|
||||
}
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char* WIFI_SSID = "";
|
||||
|
||||
const char* ALEXA_USERNAME = "";
|
||||
const char* ALEXA_PASSWORD = "";
|
||||
const char* ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice* device1;
|
||||
|
||||
// Initial state for the light (PowerController)
|
||||
PowerController outputState = PowerController::OFF;
|
||||
|
||||
void setup() {
|
||||
// Initialize the LED as output
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice("Test Lamp", "ESP-01");
|
||||
|
||||
// Set the device's display category to LIGHT
|
||||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
// Add a power controller capability to the device
|
||||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
|
||||
// Update the state of the LED based on the power controller state
|
||||
digitalWrite(LED_BUILTIN, outputState != PowerController::ON);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,127 +1,127 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char *WIFI_SSID = "";
|
||||
|
||||
const char *ALEXA_USERNAME = "";
|
||||
const char *ALEXA_PASSWORD = "";
|
||||
const char *ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice *device1;
|
||||
|
||||
// Initial state for the blinds (PowerController)
|
||||
PowerController outputState = PowerController::OFF;
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Initialize the LED as output
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
String deviceName="Bedroom Blinds";
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice(deviceName, "ESP-01");
|
||||
|
||||
// Set the device's display category to LIGHT
|
||||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
//a power controller will allow us to "turn on/off" the blinds (not really usefull but is possable)
|
||||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||||
|
||||
// Add a toggle controller capability to the device
|
||||
AlexaInterface* toggleController=device1->addCapability(AlexaInterfaceType::TOGGLE_CONTROLLER);
|
||||
|
||||
//toggle controllers are unique in that they require some additinal attrabutes by default we first need to set an instance name
|
||||
toggleController->setInstance("ESP-01.Toggle");
|
||||
|
||||
//we also need to set a name for the interface, this is a sub device listed under the main device name
|
||||
//to allow open/close commands as well as on/off commands we can use the same name as device name
|
||||
toggleController->addFriendlyName(deviceName.c_str(),"en-US");
|
||||
|
||||
// Example of adding action mappings to map open/close to on/off
|
||||
ActionMapping closeMapping({AlexaActions::Close}, "TurnOn");
|
||||
|
||||
ActionMapping openMapping({AlexaActions::Open}, "TurnOff");
|
||||
// the full supported list of alexa action mappings can be found on
|
||||
// https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery-objects.html#action-mapping
|
||||
//note, only 'generic controller interffaces support action mappings for a full list visit
|
||||
//https://developer.amazon.com/en-US/docs/alexa/device-apis/generic-controllers.html
|
||||
|
||||
|
||||
toggleController->addActionMapping(closeMapping);
|
||||
toggleController->addActionMapping(openMapping);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
device1->registerEvent("ReportState", [](const JsonDocument &directive, const AlexaInterfaceType &type)
|
||||
{
|
||||
|
||||
Serial.println("Report state requested.");
|
||||
|
||||
// Build and send status report
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddToggleControllerProp(outputState,"ESP-01.Toggle")
|
||||
.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::TOGGLE_CONTROLLER||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)
|
||||
.AddToggleControllerProp(outputState,"ESP-01.Toggle")
|
||||
.AddPowerControllerProp(outputState)
|
||||
.send();
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
|
||||
// Update the state of the LED based on the power controller state
|
||||
digitalWrite(LED_BUILTIN, outputState != PowerController::ON);
|
||||
}
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char *WIFI_SSID = "";
|
||||
|
||||
const char *ALEXA_USERNAME = "";
|
||||
const char *ALEXA_PASSWORD = "";
|
||||
const char *ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice *device1;
|
||||
|
||||
// Initial state for the blinds (PowerController)
|
||||
PowerController outputState = PowerController::OFF;
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Initialize the LED as output
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
String deviceName="Bedroom Blinds";
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice(deviceName, "ESP-01");
|
||||
|
||||
// Set the device's display category to LIGHT
|
||||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
//a power controller will allow us to "turn on/off" the blinds (not really usefull but is possable)
|
||||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||||
|
||||
// Add a toggle controller capability to the device
|
||||
AlexaInterface* toggleController=device1->addCapability(AlexaInterfaceType::TOGGLE_CONTROLLER);
|
||||
|
||||
//toggle controllers are unique in that they require some additinal attrabutes by default we first need to set an instance name
|
||||
toggleController->setInstance("ESP-01.Toggle");
|
||||
|
||||
//we also need to set a name for the interface, this is a sub device listed under the main device name
|
||||
//to allow open/close commands as well as on/off commands we can use the same name as device name
|
||||
toggleController->addFriendlyName(deviceName.c_str(),"en-US");
|
||||
|
||||
// Example of adding action mappings to map open/close to on/off
|
||||
ActionMapping closeMapping({AlexaActions::Close}, "TurnOn");
|
||||
|
||||
ActionMapping openMapping({AlexaActions::Open}, "TurnOff");
|
||||
// the full supported list of alexa action mappings can be found on
|
||||
// https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery-objects.html#action-mapping
|
||||
//note, only 'generic controller interffaces support action mappings for a full list visit
|
||||
//https://developer.amazon.com/en-US/docs/alexa/device-apis/generic-controllers.html
|
||||
|
||||
|
||||
toggleController->addActionMapping(closeMapping);
|
||||
toggleController->addActionMapping(openMapping);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
device1->registerEvent("ReportState", [](const JsonDocument &directive, const AlexaInterfaceType &type)
|
||||
{
|
||||
|
||||
Serial.println("Report state requested.");
|
||||
|
||||
// Build and send status report
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddToggleControllerProp(outputState,"ESP-01.Toggle")
|
||||
.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::TOGGLE_CONTROLLER||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)
|
||||
.AddToggleControllerProp(outputState,"ESP-01.Toggle")
|
||||
.AddPowerControllerProp(outputState)
|
||||
.send();
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
|
||||
// Update the state of the LED based on the power controller state
|
||||
digitalWrite(LED_BUILTIN, outputState != PowerController::ON);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,110 +1,110 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char *WIFI_SSID = "";
|
||||
|
||||
const char *ALEXA_USERNAME = "";
|
||||
const char *ALEXA_PASSWORD = "";
|
||||
const char *ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice* device1;
|
||||
|
||||
// Initial state for the light (PowerController)
|
||||
PowerController outputState = PowerController::OFF;
|
||||
int brightness=100;
|
||||
void setup() {
|
||||
// Initialize the LED as output
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice("Test Lamp", "ESP-01");
|
||||
|
||||
// Set the device's display category to LIGHT
|
||||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
// Add a power controller capability to the device
|
||||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||||
|
||||
// Add a brightness controller to the device to allow dimming
|
||||
device1->addCapability(AlexaInterfaceType::BRIGHTNESS_CONTROLLER);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
device1->registerEvent("ReportState", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
|
||||
// Build and send status report
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddPowerControllerProp(outputState)
|
||||
.AddBrightnessControllerProp(brightness)
|
||||
.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");
|
||||
}
|
||||
}else if (type == AlexaInterfaceType::BRIGHTNESS_CONTROLLER) {
|
||||
if(directive["header"]["name"]=="SetBrightness"){
|
||||
brightness=directive["payload"]["brightness"];
|
||||
Serial.println("setting brightness to: "+brightness);
|
||||
outputState = PowerController::ON;
|
||||
}
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the updated state after change
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"], true)
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddPowerControllerProp(outputState)
|
||||
.AddBrightnessControllerProp(brightness)
|
||||
.send();
|
||||
});
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
|
||||
// Update the state of the LED based on the power controller state and brightness
|
||||
if(outputState == PowerController::ON){
|
||||
analogWrite(LED_BUILTIN,map(brightness,0,100,255,0));
|
||||
}else{
|
||||
digitalWrite(LED_BUILTIN, true);
|
||||
}
|
||||
}
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char *WIFI_SSID = "";
|
||||
|
||||
const char *ALEXA_USERNAME = "";
|
||||
const char *ALEXA_PASSWORD = "";
|
||||
const char *ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice* device1;
|
||||
|
||||
// Initial state for the light (PowerController)
|
||||
PowerController outputState = PowerController::OFF;
|
||||
int brightness=100;
|
||||
void setup() {
|
||||
// Initialize the LED as output
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice("Test Lamp", "ESP-01");
|
||||
|
||||
// Set the device's display category to LIGHT
|
||||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
// Add a power controller capability to the device
|
||||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||||
|
||||
// Add a brightness controller to the device to allow dimming
|
||||
device1->addCapability(AlexaInterfaceType::BRIGHTNESS_CONTROLLER);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
device1->registerEvent("ReportState", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
|
||||
// Build and send status report
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddPowerControllerProp(outputState)
|
||||
.AddBrightnessControllerProp(brightness)
|
||||
.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");
|
||||
}
|
||||
}else if (type == AlexaInterfaceType::BRIGHTNESS_CONTROLLER) {
|
||||
if(directive["header"]["name"]=="SetBrightness"){
|
||||
brightness=directive["payload"]["brightness"];
|
||||
Serial.println("setting brightness to: "+brightness);
|
||||
outputState = PowerController::ON;
|
||||
}
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the updated state after change
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"], true)
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddPowerControllerProp(outputState)
|
||||
.AddBrightnessControllerProp(brightness)
|
||||
.send();
|
||||
});
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
|
||||
// Update the state of the LED based on the power controller state and brightness
|
||||
if(outputState == PowerController::ON){
|
||||
analogWrite(LED_BUILTIN,map(brightness,0,100,255,0));
|
||||
}else{
|
||||
digitalWrite(LED_BUILTIN, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,122 +1,122 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char *WIFI_SSID = "";
|
||||
|
||||
const char *ALEXA_USERNAME = "";
|
||||
const char *ALEXA_PASSWORD = "";
|
||||
const char *ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice* device1;
|
||||
|
||||
// Initial state for the light (PowerController)
|
||||
PowerController outputState = PowerController::OFF;
|
||||
int brightness=100;
|
||||
int colorTemp=0;
|
||||
void setup() {
|
||||
// Initialize the LED as output
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice("Test Lamp ESP", "ESP-01");
|
||||
|
||||
// Set the device's display category to LIGHT
|
||||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
// Add a power controller capability to the device
|
||||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||||
|
||||
// Add a brightness controller to the device to allow dimming
|
||||
device1->addCapability(AlexaInterfaceType::BRIGHTNESS_CONTROLLER);
|
||||
|
||||
// Add a color temperature controller
|
||||
device1->addCapability(AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
device1->registerEvent("ReportState", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
|
||||
// Build and send status report
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddPowerControllerProp(outputState)
|
||||
.AddBrightnessControllerProp(brightness)
|
||||
.AddColorTemperatureControllerProp(colorTemp)
|
||||
.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");
|
||||
}
|
||||
}else if (type == AlexaInterfaceType::BRIGHTNESS_CONTROLLER) {
|
||||
if(directive["header"]["name"]=="SetBrightness"){
|
||||
brightness=directive["payload"]["brightness"];
|
||||
Serial.println("setting brightness to: "+brightness);
|
||||
outputState = PowerController::ON;
|
||||
}
|
||||
}else if (type == AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER) {
|
||||
if(directive["header"]["name"]=="SetColorTemperature"){
|
||||
colorTemp=directive["payload"]["colorTemperatureInKelvin"];
|
||||
Serial.println("setting color temperature to: "+colorTemp);
|
||||
outputState = PowerController::ON;
|
||||
}
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the updated state after change
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"], true)
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddPowerControllerProp(outputState)
|
||||
.AddBrightnessControllerProp(brightness)
|
||||
.AddColorTemperatureControllerProp(colorTemp)
|
||||
.send();
|
||||
});
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
|
||||
// Update the state of the LED based on the power controller state and brightness
|
||||
if(outputState == PowerController::ON){
|
||||
analogWrite(LED_BUILTIN,map(brightness,0,100,255,0));
|
||||
}else{
|
||||
digitalWrite(LED_BUILTIN, true);
|
||||
}
|
||||
}
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char *WIFI_SSID = "";
|
||||
|
||||
const char *ALEXA_USERNAME = "";
|
||||
const char *ALEXA_PASSWORD = "";
|
||||
const char *ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice* device1;
|
||||
|
||||
// Initial state for the light (PowerController)
|
||||
PowerController outputState = PowerController::OFF;
|
||||
int brightness=100;
|
||||
int colorTemp=0;
|
||||
void setup() {
|
||||
// Initialize the LED as output
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice("Test Lamp ESP", "ESP-01");
|
||||
|
||||
// Set the device's display category to LIGHT
|
||||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
// Add a power controller capability to the device
|
||||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||||
|
||||
// Add a brightness controller to the device to allow dimming
|
||||
device1->addCapability(AlexaInterfaceType::BRIGHTNESS_CONTROLLER);
|
||||
|
||||
// Add a color temperature controller
|
||||
device1->addCapability(AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
device1->registerEvent("ReportState", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
|
||||
// Build and send status report
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddPowerControllerProp(outputState)
|
||||
.AddBrightnessControllerProp(brightness)
|
||||
.AddColorTemperatureControllerProp(colorTemp)
|
||||
.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");
|
||||
}
|
||||
}else if (type == AlexaInterfaceType::BRIGHTNESS_CONTROLLER) {
|
||||
if(directive["header"]["name"]=="SetBrightness"){
|
||||
brightness=directive["payload"]["brightness"];
|
||||
Serial.println("setting brightness to: "+brightness);
|
||||
outputState = PowerController::ON;
|
||||
}
|
||||
}else if (type == AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER) {
|
||||
if(directive["header"]["name"]=="SetColorTemperature"){
|
||||
colorTemp=directive["payload"]["colorTemperatureInKelvin"];
|
||||
Serial.println("setting color temperature to: "+colorTemp);
|
||||
outputState = PowerController::ON;
|
||||
}
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the updated state after change
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"], true)
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddPowerControllerProp(outputState)
|
||||
.AddBrightnessControllerProp(brightness)
|
||||
.AddColorTemperatureControllerProp(colorTemp)
|
||||
.send();
|
||||
});
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
|
||||
// Update the state of the LED based on the power controller state and brightness
|
||||
if(outputState == PowerController::ON){
|
||||
analogWrite(LED_BUILTIN,map(brightness,0,100,255,0));
|
||||
}else{
|
||||
digitalWrite(LED_BUILTIN, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +1,67 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char *WIFI_SSID = "";
|
||||
|
||||
const char *ALEXA_USERNAME = "";
|
||||
const char *ALEXA_PASSWORD = "";
|
||||
const char *ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice* device1;
|
||||
|
||||
void setup() {
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice("ESP Temp", "ESP-01");
|
||||
|
||||
// Set the device's display category to TEMPERATURE_SENSOR
|
||||
device1->setDisplayCategory(DisplayCategory::TEMPERATURE_SENSOR);
|
||||
|
||||
// Add a temperature sensor capability to the device
|
||||
device1->addCapability(AlexaInterfaceType::TEMPERATURE_SENSOR);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
device1->registerEvent("ReportState", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
|
||||
// Build and send status report
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddTemperatureSensorProp(TemperatureSensorScale::FAHRENHEIT,69)
|
||||
.send();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
}
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
// Define constants for WiFi and Alexa Client configuration
|
||||
const char *WIFI_SSID = "";
|
||||
|
||||
const char *ALEXA_USERNAME = "";
|
||||
const char *ALEXA_PASSWORD = "";
|
||||
const char *ALEXA_ROOT_TOPIC = "";
|
||||
|
||||
// Create the Alexa client object
|
||||
Alex2ESP alexClient;
|
||||
AlexaDevice* device1;
|
||||
|
||||
void setup() {
|
||||
|
||||
// Initialize serial communication for debugging
|
||||
Serial.begin(74880);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID);
|
||||
|
||||
Serial.print("[WIFI] Connecting to WiFi");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.printf("[WIFI] Connected to SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
// Initialize the Alexa client
|
||||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||||
|
||||
// Register a new device with a unique ID and name
|
||||
device1 = alexClient.getDevice("ESP Temp", "ESP-01");
|
||||
|
||||
// Set the device's display category to TEMPERATURE_SENSOR
|
||||
device1->setDisplayCategory(DisplayCategory::TEMPERATURE_SENSOR);
|
||||
|
||||
// Add a temperature sensor capability to the device
|
||||
device1->addCapability(AlexaInterfaceType::TEMPERATURE_SENSOR);
|
||||
|
||||
// Register event listener for "ReportState" directive
|
||||
device1->registerEvent("ReportState", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
|
||||
// Build and send status report
|
||||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||||
.AddHealthProp(EndpointHealth::OK)
|
||||
.AddTemperatureSensorProp(TemperatureSensorScale::FAHRENHEIT,69)
|
||||
.send();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process Alexa client communication
|
||||
alexClient.loop();
|
||||
}
|
||||
|
|
|
|||
14
keywords.txt
14
keywords.txt
|
|
@ -1,7 +1,7 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map For Espalexa
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
#######################################
|
||||
# Syntax Coloring Map For Espalexa
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
|
|
|||
59
library.json
59
library.json
|
|
@ -1,59 +0,0 @@
|
|||
{
|
||||
"name": "Alex2ESP",
|
||||
"version": "1.0.0",
|
||||
"description": "A companion library for the Alex2MQTT Alexa Skill. Connects ESP-based devices to alex2mqtt.stormysdream.club for seamless integration with MQTT.",
|
||||
"keywords": [
|
||||
"alexa",
|
||||
"mqtt",
|
||||
"esp",
|
||||
"voice assistant",
|
||||
"home automation"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/chaos511/Alex2ESP"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "David",
|
||||
"email": "Alex2ESP@stormysdream.club",
|
||||
"maintainer": true
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"platforms": [
|
||||
"espressif8266",
|
||||
"espressif32"
|
||||
],
|
||||
"examples": [
|
||||
"examples/*"
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"owner": "marvinroger",
|
||||
"name": "AsyncMqttClient",
|
||||
"version": "^0.9.0"
|
||||
},
|
||||
{
|
||||
"owner":"me-no-dev",
|
||||
"name": "ESPAsyncTCP",
|
||||
"version": ">=1.2.2",
|
||||
"platforms": "espressif8266"
|
||||
},
|
||||
{
|
||||
"owner":"me-no-dev",
|
||||
"name": "AsyncTCP",
|
||||
"version": ">=1.1.1",
|
||||
"platforms": "espressif32"
|
||||
},
|
||||
{
|
||||
"owner": "bblanchon",
|
||||
"name": "ArduinoJson",
|
||||
"version": "^7.2.1"
|
||||
}
|
||||
],
|
||||
"headers": "Alex2ESP"
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
name=Alex2ESP
|
||||
version=1.0.0
|
||||
author=David Alex2ESP@stormysdream.club
|
||||
maintainer=David Alex2ESP@stormysdream.club
|
||||
sentence=A companion library for the Alex2MQTT Alexa Skill.
|
||||
paragraph=Connects ESP-based devices to alex2mqtt.stormysdream.club, enabling seamless integration with MQTT and Alexa Voice Service.
|
||||
category=Communication
|
||||
url=https://github.com/chaos511/Alex2ESP
|
||||
architectures=esp8266,esp32
|
||||
includes=Alex2ESP.h
|
||||
398
readme.md
398
readme.md
|
|
@ -1,199 +1,199 @@
|
|||
# Alex2ESP
|
||||
|
||||
Alex2ESP is a lightweight Arduino/PlatformIO library for integrating ESP8266 and ESP32 microcontrollers with Amazon Alexa smart home APIs. The library simplifies the process of creating Alexa-compatible devices using MQTT.
|
||||
|
||||
For more details on how to configure Alex2ESP, visit [Alex2MQTT Documentation](https://alex2mqtt.stormysdream.club/).
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 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](https://github.com/vintlabs/fauxmoESP)
|
||||
however FauxmoESP works by emulating a light bulb so it is limited in what commands it accepts.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
### Installation
|
||||
Download the library as zip or via PlatformIO and include it in your project:
|
||||
```cpp
|
||||
#include <Alex2ESP.h>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
```cpp
|
||||
Alex2ESP alexClient;
|
||||
```
|
||||
|
||||
Credentials can be found at https://alex2mqtt.stormysdream.club/ after logging in with amazon.
|
||||
```cpp
|
||||
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
|
||||
```cpp
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
```
|
||||
---
|
||||
### 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](https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery-objects.html#action-mapping)
|
||||
|
||||
```cpp
|
||||
AlexaDevice* device = alexClient.getDevice("Bedroom Blinds", "ESP-01");
|
||||
device->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
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);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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 ussage
|
||||
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:
|
||||
|
||||
```cpp
|
||||
JsonDocument doc;
|
||||
|
||||
doc["namespace"] = "Alexa.PowerController";
|
||||
doc["name"] = "powerState";
|
||||
doc["value"] = "ON";
|
||||
doc["timeOfSample"] = "{REPLACE_WITH_DATETIME}"; // Alex2MQTT server wil do the replace
|
||||
doc["uncertaintyInMilliseconds"] = 0;
|
||||
AddContextProp(doc.as<JsonObject>())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
Feel free to submit pull requests or issues for feature requests and bug fixes.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
This project is licensed under the MIT License. See the LICENSE file for details.
|
||||
|
||||
# Alex2ESP
|
||||
|
||||
Alex2ESP is a lightweight Arduino/PlatformIO library for integrating ESP8266 and ESP32 microcontrollers with Amazon Alexa smart home APIs. The library simplifies the process of creating Alexa-compatible devices using MQTT.
|
||||
|
||||
For more details on how to configure Alex2ESP, visit [Alex2MQTT Documentation](https://alex2mqtt.stormysdream.club/).
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 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](https://github.com/vintlabs/fauxmoESP)
|
||||
however FauxmoESP works by emulating a light bulb so it is limited in what commands it accepts.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
### Installation
|
||||
Download the library as zip or via PlatformIO and include it in your project:
|
||||
```cpp
|
||||
#include <Alex2ESP.h>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
```cpp
|
||||
Alex2ESP alexClient;
|
||||
```
|
||||
|
||||
Credentials can be found at https://alex2mqtt.stormysdream.club/ after logging in with amazon.
|
||||
```cpp
|
||||
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
|
||||
```cpp
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
```
|
||||
---
|
||||
### 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](https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery-objects.html#action-mapping)
|
||||
|
||||
```cpp
|
||||
AlexaDevice* device = alexClient.getDevice("Bedroom Blinds", "ESP-01");
|
||||
device->setDisplayCategory(DisplayCategory::LIGHT);
|
||||
|
||||
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);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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 ussage
|
||||
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:
|
||||
|
||||
```cpp
|
||||
JsonDocument doc;
|
||||
|
||||
doc["namespace"] = "Alexa.PowerController";
|
||||
doc["name"] = "powerState";
|
||||
doc["value"] = "ON";
|
||||
doc["timeOfSample"] = "{REPLACE_WITH_DATETIME}"; // Alex2MQTT server wil do the replace
|
||||
doc["uncertaintyInMilliseconds"] = 0;
|
||||
AddContextProp(doc.as<JsonObject>())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
Feel free to submit pull requests or issues for feature requests and bug fixes.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
This project is licensed under the MIT License. See the LICENSE file for details.
|
||||
|
||||
|
|
|
|||
776
src/Alex2ESP.cpp
776
src/Alex2ESP.cpp
|
|
@ -1,389 +1,389 @@
|
|||
/*
|
||||
* @title Alex2ESP Library
|
||||
* @version 1.0.0
|
||||
* @author David
|
||||
* @license MIT
|
||||
* @contributors chaos511
|
||||
*
|
||||
* @description The Alex2ESP library is a companion to the Alex2MQTT Alexa Skill,
|
||||
* providing seamless integration between ESP-based devices and the Alex2MQTT server.
|
||||
* This library connects to alex2mqtt.stormysdream.club, where the skill is hosted,
|
||||
* allowing your devices to communicate effortlessly with the Alexa Voice Service
|
||||
* using MQTT as the backbone.
|
||||
*/
|
||||
#include "Alex2ESP.h"
|
||||
|
||||
Alex2ESP::Alex2ESP()
|
||||
: rootTopic(), mqttUsername(nullptr), mqttPassword(nullptr), _state(Alex2ESPState::UNINITIALIZED), _disconnectReason(AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) {}
|
||||
|
||||
void Alex2ESP::begin(const char *rootTopic, const char *username, const char *password)
|
||||
{
|
||||
|
||||
_state = Alex2ESPState::INITIALIZED;
|
||||
_disconnectReason = AsyncMqttClientDisconnectReason::TCP_DISCONNECTED;
|
||||
this->rootTopic = rootTopic;
|
||||
this->mqttUsername = username;
|
||||
this->mqttPassword = password;
|
||||
|
||||
discoverTopic = (String(this->rootTopic) + String("/discover"));
|
||||
discoverTopicSend = (String(this->rootTopic) + String("/discover_r"));
|
||||
|
||||
TopicESP = (String(this->rootTopic) + String("/+/alexaDirective_e"));
|
||||
|
||||
// Log the root topic and credentials
|
||||
AlexaUtils::log("Setting root topic: ");
|
||||
AlexaUtils::log(rootTopic);
|
||||
AlexaUtils::log(" with creds ");
|
||||
AlexaUtils::log(username);
|
||||
AlexaUtils::log(":");
|
||||
AlexaUtils::logln(password);
|
||||
AlexaUtils::printMemoryInfo();
|
||||
|
||||
// Set up MQTT client callbacks
|
||||
mqttClient.onConnect([this](bool sessionPresent)
|
||||
{ this->onMqttConnect(sessionPresent); });
|
||||
mqttClient.onDisconnect([this](AsyncMqttClientDisconnectReason reason)
|
||||
{ this->onMqttDisconnect(reason); });
|
||||
mqttClient.onSubscribe([this](uint16_t packetId, uint8_t qos)
|
||||
{ this->onSubscribe(packetId, qos); });
|
||||
mqttClient.onMessage([this](char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
|
||||
{ this->onMessage(topic, payload, properties, len, index, total); });
|
||||
|
||||
// Configure MQTT client
|
||||
mqttClient.setServer(mqttServer, mqttPort);
|
||||
mqttClient.setCredentials(username, password);
|
||||
|
||||
// TODO: Throw error if dns fails or has no internet access?
|
||||
_state = Alex2ESPState::CONNECTING;
|
||||
mqttClient.connect();
|
||||
}
|
||||
|
||||
void Alex2ESP::onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
_state = Alex2ESPState::SUBSCRIBING;
|
||||
|
||||
AlexaUtils::log("Connected to MQTT server, Now subscribing to: ");
|
||||
AlexaUtils::log(discoverTopic.c_str());
|
||||
AlexaUtils::log(" ");
|
||||
AlexaUtils::logln(TopicESP.c_str());
|
||||
|
||||
mqttClient.subscribe(discoverTopic.c_str(), 1);
|
||||
mqttClient.subscribe(TopicESP.c_str(), 1);
|
||||
}
|
||||
|
||||
void Alex2ESP::onSubscribe(uint16_t packetId, uint8_t qos)
|
||||
{
|
||||
_state = Alex2ESPState::CONNECTED;
|
||||
|
||||
AlexaUtils::log("Subscribed with packetId: ");
|
||||
AlexaUtils::log(packetId);
|
||||
AlexaUtils::log(" and QoS: ");
|
||||
AlexaUtils::logln(qos);
|
||||
}
|
||||
|
||||
// Internal: Handle disconnection
|
||||
// TODO: auto reconnect?
|
||||
void Alex2ESP::onMqttDisconnect(AsyncMqttClientDisconnectReason reason)
|
||||
{
|
||||
_state = Alex2ESPState::DISCONNECTED;
|
||||
_disconnectReason = reason;
|
||||
}
|
||||
|
||||
void Alex2ESP::onMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t length, size_t index, size_t total)
|
||||
{
|
||||
AlexaUtils::logln("OnMessage Start");
|
||||
AlexaUtils::printMemoryInfo();
|
||||
|
||||
clearToSend = false;
|
||||
|
||||
if (strcmp(topic, discoverTopic.c_str()) == 0)
|
||||
{
|
||||
|
||||
// boolean successful = messagePackExtractor(topic, payload, length, index, total);
|
||||
|
||||
// if (successful && inputDoc["name"] == "Discover")
|
||||
// {
|
||||
for (const AlexaDevice &device : devices)
|
||||
{
|
||||
AlexaUtils::logln("Getting device json");
|
||||
JsonDocument jsonData = device.getDeviceJSON();
|
||||
String jsonString;
|
||||
serializeJson(jsonData, jsonString);
|
||||
jsonData.clear();
|
||||
AlexaUtils::enqueue(jsonString.c_str(), discoverTopicSend.c_str());
|
||||
}
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto &device : devices)
|
||||
{
|
||||
String directiveTopic = String(rootTopic) + "/" + device.getEndpointId() + "/alexaDirective_e";
|
||||
|
||||
if (strcmp(topic, directiveTopic.c_str()) == 0)
|
||||
{
|
||||
AlexaUtils::logln(topic);
|
||||
payload[length] = '\0';
|
||||
AlexaUtils::logln(payload);
|
||||
AlexaUtils::enqueueReceive(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
AlexaUtils::logln("OnMessage End");
|
||||
AlexaUtils::printMemoryInfo();
|
||||
clearToSend = true;
|
||||
}
|
||||
|
||||
// boolean Alex2ESP::messagePackExtractor(char *topic, char *payload, size_t length, size_t index, size_t total)
|
||||
// {
|
||||
// payload[length] = '\0'; // Truncate the payload
|
||||
|
||||
// uint8_t messageId = payload[0]; // Message ID (byte 0)
|
||||
// uint8_t fragmentId = payload[1]; // Fragment ID (byte 1)
|
||||
// uint8_t totalFragments = payload[2]; // Total Fragments (byte 2)
|
||||
|
||||
// if (totalFragments > MAX_FRAGMENT_COUNT)
|
||||
// {
|
||||
// return false; //Message too long, skip.
|
||||
// }
|
||||
|
||||
// if (fragmentId == 0)
|
||||
// {
|
||||
// clearCache();
|
||||
// cache.totalParts = totalFragments;
|
||||
// cache.messageId = String(messageId);
|
||||
// }
|
||||
|
||||
// if (cache.messageId != String(messageId))
|
||||
// {
|
||||
// clearCache();
|
||||
// cache.messageId = String(messageId);
|
||||
// cache.totalParts = totalFragments;
|
||||
// }
|
||||
|
||||
// if (fragmentId < cache.totalParts)
|
||||
// {
|
||||
// strncpy(cache.message + cache.fragmentOffset, payload + 3, length - 3); // Copy the fragment data
|
||||
// cache.fragmentOffset=cache.fragmentOffset+(length-3); //
|
||||
// cache.receivedParts++;
|
||||
// Serial.printf("Received fragment %d of %d, length %d receivedParts: %d \n", fragmentId + 1, totalFragments, length, cache.receivedParts);
|
||||
|
||||
// if (isMessageComplete())
|
||||
// {
|
||||
// AlexaUtils::logln("Full message received.");
|
||||
// AlexaUtils::logln(cache.message);
|
||||
|
||||
// DeserializationError error = deserializeJson(inputDoc, cache.message);
|
||||
// if (error)
|
||||
// {
|
||||
// AlexaUtils::log("Failed to parse message: ");
|
||||
// AlexaUtils::logln(String(error.f_str()));
|
||||
// clearCache();
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// AlexaUtils::logln("JSON message parsed successfully!");
|
||||
// clearCache(true);
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
AlexaDevice *Alex2ESP::getDevice(const String &name, const String &endpointId)
|
||||
{
|
||||
// Check if a device with the given endpointId already exists
|
||||
for (auto &device : devices)
|
||||
{
|
||||
if (device.getEndpointId() == endpointId)
|
||||
{
|
||||
return &device; // Return the existing device
|
||||
}
|
||||
}
|
||||
|
||||
// If the device doesn't exist, create a new one
|
||||
devices.emplace_back(name, rootTopic, endpointId);
|
||||
|
||||
Serial.print("Created new device: ");
|
||||
Serial.print(name);
|
||||
Serial.print(" with endpointId: ");
|
||||
Serial.println(endpointId);
|
||||
|
||||
// Return a pointer to the newly created device
|
||||
return &devices.back();
|
||||
}
|
||||
|
||||
// void Alex2ESP::clearCache(boolean skipDocClear)
|
||||
// {
|
||||
// if (!skipDocClear)
|
||||
// {
|
||||
// inputDoc.clear();
|
||||
// }
|
||||
// cache.receivedParts = 0; // Reset the count of received parts
|
||||
// cache.messageId = ""; // Clear the message ID
|
||||
// cache.totalParts = 0; // Reset the total parts count
|
||||
// cache.fragmentOffset = 0;
|
||||
// memset(cache.message, 0, sizeof(cache.message)); // Clear the message buffer
|
||||
// }
|
||||
|
||||
// bool Alex2ESP::isMessageComplete()
|
||||
// {
|
||||
// return cache.receivedParts == cache.totalParts; // Check if we have received all parts
|
||||
// }
|
||||
|
||||
Alex2ESPState Alex2ESP::getState() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
AsyncMqttClientDisconnectReason Alex2ESP::getDisconnectReason() const
|
||||
{
|
||||
return _disconnectReason;
|
||||
}
|
||||
|
||||
void Alex2ESP::handleMqttReconnection()
|
||||
{
|
||||
if (!mqttClient.connected() && _disconnectReason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED)
|
||||
{
|
||||
if (millis() - lastReconnectTime > 5000)
|
||||
{
|
||||
lastReconnectTime = millis();
|
||||
reconnectAttempt++;
|
||||
mqttClient.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
void Alex2ESP::processHttpPost()
|
||||
{
|
||||
if (!AlexaUtils::isQueueEmpty())
|
||||
{
|
||||
AlexaUtils::logln("processHttpPost");
|
||||
|
||||
const char *payload = AlexaUtils::dequeueVals(false);
|
||||
|
||||
httpPOST.begin(wifiPOST, "http://alex2mqtt.stormysdream.club/Alex2ESP");
|
||||
httpPOST.setAuthorization(mqttUsername, mqttPassword);
|
||||
httpPOST.addHeader("Content-Type", "text/plain");
|
||||
|
||||
AlexaUtils::log("Sending Data: ");
|
||||
AlexaUtils::logln(payload);
|
||||
|
||||
int httpResponseCode = httpPOST.POST(payload);
|
||||
|
||||
if (httpResponseCode != 200)
|
||||
{
|
||||
|
||||
if (retryCountPOST >= MAX_RETRY_COUNT)
|
||||
{
|
||||
AlexaUtils::dequeueVals(true);
|
||||
}
|
||||
|
||||
AlexaUtils::log("POST Error code: ");
|
||||
AlexaUtils::logln(httpResponseCode);
|
||||
retryCountPOST++;
|
||||
if (httpResponseCode > 0)
|
||||
{
|
||||
String response = httpPOST.getString();
|
||||
Serial.println("Response:");
|
||||
Serial.println(response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
retryCountPOST = 0;
|
||||
AlexaUtils::dequeueVals(true);
|
||||
}
|
||||
httpPOST.end();
|
||||
}
|
||||
}
|
||||
void Alex2ESP::processHttpGet()
|
||||
{
|
||||
if (!AlexaUtils::isReceiveQueueEmpty())
|
||||
{
|
||||
AlexaUtils::logln("processHttpGet");
|
||||
|
||||
String uuid;
|
||||
AlexaUtils::dequeueReceive(uuid, false);
|
||||
AlexaUtils::log("Sending get request for uuid: ");
|
||||
// AlexaUtils::logln(uuid);
|
||||
|
||||
httpGET.begin(wifiGET, "http://alex2mqtt.stormysdream.club/Alex2ESP/" + uuid);
|
||||
httpGET.setAuthorization(mqttUsername, mqttPassword);
|
||||
int httpResponseCode = httpGET.GET();
|
||||
|
||||
Serial.print("HTTP Response code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
if (httpResponseCode != 200)
|
||||
{
|
||||
Serial.print("Error code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
if (retryCountGET >= MAX_RETRY_COUNT)
|
||||
{
|
||||
AlexaUtils::dequeueReceive(uuid, true);
|
||||
}
|
||||
|
||||
retryCountGET++;
|
||||
}
|
||||
else
|
||||
{
|
||||
retryCountGET = 0;
|
||||
AlexaUtils::dequeueReceive(uuid, true);
|
||||
String payloadStr = httpGET.getString();
|
||||
size_t length = payloadStr.length();
|
||||
|
||||
if (length < AlexaUtils::MAX_PAYLOAD_LENGTH - 1)
|
||||
{
|
||||
payloadStr.toCharArray(AlexaUtils::receivePayload, length + 1);
|
||||
payloadStr = "";
|
||||
AlexaUtils::receivePayload[length + 1] = '\0';
|
||||
// AlexaUtils::logln(AlexaUtils::receivePayload);
|
||||
|
||||
DeserializationError error = deserializeJson(inputDoc, AlexaUtils::receivePayload);
|
||||
if (error)
|
||||
{
|
||||
AlexaUtils::log("Failed to parse message: ");
|
||||
AlexaUtils::logln(String(error.f_str()));
|
||||
inputDoc.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
AlexaUtils::logln("JSON message parsed successfully!");
|
||||
|
||||
for (auto &device : devices)
|
||||
{
|
||||
if (strcmp(device.getEndpointId().c_str(), inputDoc["directive"]["endpoint"]["endpointId"]) == 0)
|
||||
{
|
||||
serializeJson(inputDoc, Serial);
|
||||
Serial.println();
|
||||
device.triggerEvent("DirectiveReceived", inputDoc["directive"], AlexaInterfaceType::UNKNOWN);
|
||||
|
||||
if (inputDoc["directive"]["header"]["name"] == "ReportState")
|
||||
{
|
||||
device.triggerEvent("ReportState", inputDoc["directive"], AlexaInterfaceType::UNKNOWN);
|
||||
}
|
||||
else
|
||||
{
|
||||
device.triggerEvent("Event", inputDoc["directive"], AlexaInterfaceUtils::fromString(inputDoc["directive"]["header"]["namespace"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
httpGET.end();
|
||||
}
|
||||
}
|
||||
|
||||
void Alex2ESP::loop()
|
||||
{
|
||||
|
||||
handleMqttReconnection();
|
||||
if(clearToSend){
|
||||
processHttpPost();
|
||||
processHttpGet();
|
||||
}
|
||||
|
||||
return;
|
||||
/*
|
||||
* @title Alex2ESP Library
|
||||
* @version 1.0.0
|
||||
* @author David
|
||||
* @license MIT
|
||||
* @contributors chaos511
|
||||
*
|
||||
* @description The Alex2ESP library is a companion to the Alex2MQTT Alexa Skill,
|
||||
* providing seamless integration between ESP-based devices and the Alex2MQTT server.
|
||||
* This library connects to alex2mqtt.stormysdream.club, where the skill is hosted,
|
||||
* allowing your devices to communicate effortlessly with the Alexa Voice Service
|
||||
* using MQTT as the backbone.
|
||||
*/
|
||||
#include "Alex2ESP.h"
|
||||
|
||||
Alex2ESP::Alex2ESP()
|
||||
: rootTopic(), mqttUsername(nullptr), mqttPassword(nullptr), _state(Alex2ESPState::UNINITIALIZED), _disconnectReason(AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) {}
|
||||
|
||||
void Alex2ESP::begin(const char *rootTopic, const char *username, const char *password)
|
||||
{
|
||||
|
||||
_state = Alex2ESPState::INITIALIZED;
|
||||
_disconnectReason = AsyncMqttClientDisconnectReason::TCP_DISCONNECTED;
|
||||
this->rootTopic = rootTopic;
|
||||
this->mqttUsername = username;
|
||||
this->mqttPassword = password;
|
||||
|
||||
discoverTopic = (String(this->rootTopic) + String("/discover"));
|
||||
discoverTopicSend = (String(this->rootTopic) + String("/discover_r"));
|
||||
|
||||
TopicESP = (String(this->rootTopic) + String("/+/alexaDirective_e"));
|
||||
|
||||
// Log the root topic and credentials
|
||||
AlexaUtils::log("Setting root topic: ");
|
||||
AlexaUtils::log(rootTopic);
|
||||
AlexaUtils::log(" with creds ");
|
||||
AlexaUtils::log(username);
|
||||
AlexaUtils::log(":");
|
||||
AlexaUtils::logln(password);
|
||||
AlexaUtils::printMemoryInfo();
|
||||
|
||||
// Set up MQTT client callbacks
|
||||
mqttClient.onConnect([this](bool sessionPresent)
|
||||
{ this->onMqttConnect(sessionPresent); });
|
||||
mqttClient.onDisconnect([this](AsyncMqttClientDisconnectReason reason)
|
||||
{ this->onMqttDisconnect(reason); });
|
||||
mqttClient.onSubscribe([this](uint16_t packetId, uint8_t qos)
|
||||
{ this->onSubscribe(packetId, qos); });
|
||||
mqttClient.onMessage([this](char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
|
||||
{ this->onMessage(topic, payload, properties, len, index, total); });
|
||||
|
||||
// Configure MQTT client
|
||||
mqttClient.setServer(mqttServer, mqttPort);
|
||||
mqttClient.setCredentials(username, password);
|
||||
|
||||
// TODO: Throw error if dns fails or has no internet access?
|
||||
_state = Alex2ESPState::CONNECTING;
|
||||
mqttClient.connect();
|
||||
}
|
||||
|
||||
void Alex2ESP::onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
_state = Alex2ESPState::SUBSCRIBING;
|
||||
|
||||
AlexaUtils::log("Connected to MQTT server, Now subscribing to: ");
|
||||
AlexaUtils::log(discoverTopic.c_str());
|
||||
AlexaUtils::log(" ");
|
||||
AlexaUtils::logln(TopicESP.c_str());
|
||||
|
||||
mqttClient.subscribe(discoverTopic.c_str(), 1);
|
||||
mqttClient.subscribe(TopicESP.c_str(), 1);
|
||||
}
|
||||
|
||||
void Alex2ESP::onSubscribe(uint16_t packetId, uint8_t qos)
|
||||
{
|
||||
_state = Alex2ESPState::CONNECTED;
|
||||
|
||||
AlexaUtils::log("Subscribed with packetId: ");
|
||||
AlexaUtils::log(packetId);
|
||||
AlexaUtils::log(" and QoS: ");
|
||||
AlexaUtils::logln(qos);
|
||||
}
|
||||
|
||||
// Internal: Handle disconnection
|
||||
// TODO: auto reconnect?
|
||||
void Alex2ESP::onMqttDisconnect(AsyncMqttClientDisconnectReason reason)
|
||||
{
|
||||
_state = Alex2ESPState::DISCONNECTED;
|
||||
_disconnectReason = reason;
|
||||
}
|
||||
|
||||
void Alex2ESP::onMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t length, size_t index, size_t total)
|
||||
{
|
||||
AlexaUtils::logln("OnMessage Start");
|
||||
AlexaUtils::printMemoryInfo();
|
||||
|
||||
clearToSend = false;
|
||||
|
||||
if (strcmp(topic, discoverTopic.c_str()) == 0)
|
||||
{
|
||||
|
||||
// boolean successful = messagePackExtractor(topic, payload, length, index, total);
|
||||
|
||||
// if (successful && inputDoc["name"] == "Discover")
|
||||
// {
|
||||
for (const AlexaDevice &device : devices)
|
||||
{
|
||||
AlexaUtils::logln("Getting device json");
|
||||
JsonDocument jsonData = device.getDeviceJSON();
|
||||
String jsonString;
|
||||
serializeJson(jsonData, jsonString);
|
||||
jsonData.clear();
|
||||
AlexaUtils::enqueue(jsonString.c_str(), discoverTopicSend.c_str());
|
||||
}
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto &device : devices)
|
||||
{
|
||||
String directiveTopic = String(rootTopic) + "/" + device.getEndpointId() + "/alexaDirective_e";
|
||||
|
||||
if (strcmp(topic, directiveTopic.c_str()) == 0)
|
||||
{
|
||||
AlexaUtils::logln(topic);
|
||||
payload[length] = '\0';
|
||||
AlexaUtils::logln(payload);
|
||||
AlexaUtils::enqueueReceive(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
AlexaUtils::logln("OnMessage End");
|
||||
AlexaUtils::printMemoryInfo();
|
||||
clearToSend = true;
|
||||
}
|
||||
|
||||
// boolean Alex2ESP::messagePackExtractor(char *topic, char *payload, size_t length, size_t index, size_t total)
|
||||
// {
|
||||
// payload[length] = '\0'; // Truncate the payload
|
||||
|
||||
// uint8_t messageId = payload[0]; // Message ID (byte 0)
|
||||
// uint8_t fragmentId = payload[1]; // Fragment ID (byte 1)
|
||||
// uint8_t totalFragments = payload[2]; // Total Fragments (byte 2)
|
||||
|
||||
// if (totalFragments > MAX_FRAGMENT_COUNT)
|
||||
// {
|
||||
// return false; //Message too long, skip.
|
||||
// }
|
||||
|
||||
// if (fragmentId == 0)
|
||||
// {
|
||||
// clearCache();
|
||||
// cache.totalParts = totalFragments;
|
||||
// cache.messageId = String(messageId);
|
||||
// }
|
||||
|
||||
// if (cache.messageId != String(messageId))
|
||||
// {
|
||||
// clearCache();
|
||||
// cache.messageId = String(messageId);
|
||||
// cache.totalParts = totalFragments;
|
||||
// }
|
||||
|
||||
// if (fragmentId < cache.totalParts)
|
||||
// {
|
||||
// strncpy(cache.message + cache.fragmentOffset, payload + 3, length - 3); // Copy the fragment data
|
||||
// cache.fragmentOffset=cache.fragmentOffset+(length-3); //
|
||||
// cache.receivedParts++;
|
||||
// Serial.printf("Received fragment %d of %d, length %d receivedParts: %d \n", fragmentId + 1, totalFragments, length, cache.receivedParts);
|
||||
|
||||
// if (isMessageComplete())
|
||||
// {
|
||||
// AlexaUtils::logln("Full message received.");
|
||||
// AlexaUtils::logln(cache.message);
|
||||
|
||||
// DeserializationError error = deserializeJson(inputDoc, cache.message);
|
||||
// if (error)
|
||||
// {
|
||||
// AlexaUtils::log("Failed to parse message: ");
|
||||
// AlexaUtils::logln(String(error.f_str()));
|
||||
// clearCache();
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// AlexaUtils::logln("JSON message parsed successfully!");
|
||||
// clearCache(true);
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
AlexaDevice *Alex2ESP::getDevice(const String &name, const String &endpointId)
|
||||
{
|
||||
// Check if a device with the given endpointId already exists
|
||||
for (auto &device : devices)
|
||||
{
|
||||
if (device.getEndpointId() == endpointId)
|
||||
{
|
||||
return &device; // Return the existing device
|
||||
}
|
||||
}
|
||||
|
||||
// If the device doesn't exist, create a new one
|
||||
devices.emplace_back(name, rootTopic, endpointId);
|
||||
|
||||
Serial.print("Created new device: ");
|
||||
Serial.print(name);
|
||||
Serial.print(" with endpointId: ");
|
||||
Serial.println(endpointId);
|
||||
|
||||
// Return a pointer to the newly created device
|
||||
return &devices.back();
|
||||
}
|
||||
|
||||
// void Alex2ESP::clearCache(boolean skipDocClear)
|
||||
// {
|
||||
// if (!skipDocClear)
|
||||
// {
|
||||
// inputDoc.clear();
|
||||
// }
|
||||
// cache.receivedParts = 0; // Reset the count of received parts
|
||||
// cache.messageId = ""; // Clear the message ID
|
||||
// cache.totalParts = 0; // Reset the total parts count
|
||||
// cache.fragmentOffset = 0;
|
||||
// memset(cache.message, 0, sizeof(cache.message)); // Clear the message buffer
|
||||
// }
|
||||
|
||||
// bool Alex2ESP::isMessageComplete()
|
||||
// {
|
||||
// return cache.receivedParts == cache.totalParts; // Check if we have received all parts
|
||||
// }
|
||||
|
||||
Alex2ESPState Alex2ESP::getState() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
AsyncMqttClientDisconnectReason Alex2ESP::getDisconnectReason() const
|
||||
{
|
||||
return _disconnectReason;
|
||||
}
|
||||
|
||||
void Alex2ESP::handleMqttReconnection()
|
||||
{
|
||||
if (!mqttClient.connected() && _disconnectReason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED)
|
||||
{
|
||||
if (millis() - lastReconnectTime > 5000)
|
||||
{
|
||||
lastReconnectTime = millis();
|
||||
reconnectAttempt++;
|
||||
mqttClient.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
void Alex2ESP::processHttpPost()
|
||||
{
|
||||
if (!AlexaUtils::isQueueEmpty())
|
||||
{
|
||||
AlexaUtils::logln("processHttpPost");
|
||||
|
||||
const char *payload = AlexaUtils::dequeueVals(false);
|
||||
|
||||
httpPOST.begin(wifiPOST, "http://alex2mqtt.stormysdream.club/Alex2ESP");
|
||||
httpPOST.setAuthorization(mqttUsername, mqttPassword);
|
||||
httpPOST.addHeader("Content-Type", "text/plain");
|
||||
|
||||
AlexaUtils::log("Sending Data: ");
|
||||
AlexaUtils::logln(payload);
|
||||
|
||||
int httpResponseCode = httpPOST.POST(payload);
|
||||
|
||||
if (httpResponseCode != 200)
|
||||
{
|
||||
|
||||
if (retryCountPOST >= MAX_RETRY_COUNT)
|
||||
{
|
||||
AlexaUtils::dequeueVals(true);
|
||||
}
|
||||
|
||||
AlexaUtils::log("POST Error code: ");
|
||||
AlexaUtils::logln(httpResponseCode);
|
||||
retryCountPOST++;
|
||||
if (httpResponseCode > 0)
|
||||
{
|
||||
String response = httpPOST.getString();
|
||||
Serial.println("Response:");
|
||||
Serial.println(response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
retryCountPOST = 0;
|
||||
AlexaUtils::dequeueVals(true);
|
||||
}
|
||||
httpPOST.end();
|
||||
}
|
||||
}
|
||||
void Alex2ESP::processHttpGet()
|
||||
{
|
||||
if (!AlexaUtils::isReceiveQueueEmpty())
|
||||
{
|
||||
AlexaUtils::logln("processHttpGet");
|
||||
|
||||
String uuid;
|
||||
AlexaUtils::dequeueReceive(uuid, false);
|
||||
AlexaUtils::log("Sending get request for uuid: ");
|
||||
// AlexaUtils::logln(uuid);
|
||||
|
||||
httpGET.begin(wifiGET, "http://alex2mqtt.stormysdream.club/Alex2ESP/" + uuid);
|
||||
httpGET.setAuthorization(mqttUsername, mqttPassword);
|
||||
int httpResponseCode = httpGET.GET();
|
||||
|
||||
Serial.print("HTTP Response code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
if (httpResponseCode != 200)
|
||||
{
|
||||
Serial.print("Error code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
if (retryCountGET >= MAX_RETRY_COUNT)
|
||||
{
|
||||
AlexaUtils::dequeueReceive(uuid, true);
|
||||
}
|
||||
|
||||
retryCountGET++;
|
||||
}
|
||||
else
|
||||
{
|
||||
retryCountGET = 0;
|
||||
AlexaUtils::dequeueReceive(uuid, true);
|
||||
String payloadStr = httpGET.getString();
|
||||
size_t length = payloadStr.length();
|
||||
|
||||
if (length < AlexaUtils::MAX_PAYLOAD_LENGTH - 1)
|
||||
{
|
||||
payloadStr.toCharArray(AlexaUtils::receivePayload, length + 1);
|
||||
payloadStr = "";
|
||||
AlexaUtils::receivePayload[length + 1] = '\0';
|
||||
// AlexaUtils::logln(AlexaUtils::receivePayload);
|
||||
|
||||
DeserializationError error = deserializeJson(inputDoc, AlexaUtils::receivePayload);
|
||||
if (error)
|
||||
{
|
||||
AlexaUtils::log("Failed to parse message: ");
|
||||
AlexaUtils::logln(String(error.f_str()));
|
||||
inputDoc.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
AlexaUtils::logln("JSON message parsed successfully!");
|
||||
|
||||
for (auto &device : devices)
|
||||
{
|
||||
if (strcmp(device.getEndpointId().c_str(), inputDoc["directive"]["endpoint"]["endpointId"]) == 0)
|
||||
{
|
||||
serializeJson(inputDoc, Serial);
|
||||
Serial.println();
|
||||
device.triggerEvent("DirectiveReceived", inputDoc["directive"], AlexaInterfaceType::UNKNOWN);
|
||||
|
||||
if (inputDoc["directive"]["header"]["name"] == "ReportState")
|
||||
{
|
||||
device.triggerEvent("ReportState", inputDoc["directive"], AlexaInterfaceType::UNKNOWN);
|
||||
}
|
||||
else
|
||||
{
|
||||
device.triggerEvent("Event", inputDoc["directive"], AlexaInterfaceUtils::fromString(inputDoc["directive"]["header"]["namespace"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
httpGET.end();
|
||||
}
|
||||
}
|
||||
|
||||
void Alex2ESP::loop()
|
||||
{
|
||||
|
||||
handleMqttReconnection();
|
||||
if(clearToSend){
|
||||
processHttpPost();
|
||||
processHttpGet();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
208
src/Alex2ESP.h
208
src/Alex2ESP.h
|
|
@ -1,104 +1,104 @@
|
|||
/*
|
||||
* @title Alex2ESP Library
|
||||
* @version 1.0.0
|
||||
* @author David
|
||||
* @license MIT
|
||||
* @contributors chaos511
|
||||
*
|
||||
* @description The Alex2ESP library is a companion to the Alex2MQTT Alexa Skill,
|
||||
* providing seamless integration between ESP-based devices and the Alex2MQTT server.
|
||||
* This library connects to alex2mqtt.stormysdream.club, where the skill is hosted,
|
||||
* allowing your devices to communicate effortlessly with the Alexa Voice Service
|
||||
* using MQTT as the backbone.
|
||||
*/
|
||||
|
||||
#ifndef ALEX2ESP_H
|
||||
#define ALEX2ESP_H
|
||||
class AlexaDevice;
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "AlexaDevice.h"
|
||||
#include <AlexaInterface.h>
|
||||
#include <vector>
|
||||
#include <AlexaUtils.h>
|
||||
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
|
||||
enum class Alex2ESPState
|
||||
{
|
||||
UNINITIALIZED,
|
||||
INITIALIZED,
|
||||
CONNECTING,
|
||||
SUBSCRIBING,
|
||||
CONNECTED,
|
||||
DISCONNECTED
|
||||
};
|
||||
|
||||
class Alex2ESP
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
Alex2ESP();
|
||||
|
||||
// Begin function for initialization
|
||||
void begin(const char *rootTopic, const char *username, const char *password);
|
||||
Alex2ESPState getState() const;
|
||||
void loop();
|
||||
|
||||
AsyncMqttClientDisconnectReason getDisconnectReason() const;
|
||||
|
||||
AlexaDevice *getDevice(const String &name, const String &endpointId);
|
||||
void messageSplitAndSend(String messagepackString, String topic);
|
||||
|
||||
private:
|
||||
static const int MAX_RETRY_COUNT = 2; // Define maximum retry count
|
||||
int retryCountPOST=0;
|
||||
int retryCountGET=0;
|
||||
|
||||
boolean clearToSend=false;
|
||||
AsyncMqttClient mqttClient; // MQTT client instance
|
||||
String rootTopic; // Root topic for communication
|
||||
String discoverTopic; // The topic we listen on for discovery messages
|
||||
String discoverTopicSend; // The topic we send discovery messages
|
||||
|
||||
String TopicESP; // The topic we listen on for esp messages
|
||||
|
||||
const char *mqttUsername; // Username for authentication
|
||||
const char *mqttPassword; // Password for authentication
|
||||
|
||||
unsigned long lastReconnectTime;
|
||||
int reconnectAttempt;
|
||||
|
||||
HTTPClient httpGET;
|
||||
HTTPClient httpPOST;
|
||||
|
||||
WiFiClient wifiGET;
|
||||
WiFiClient wifiPOST;
|
||||
JsonDocument inputDoc;
|
||||
|
||||
std::vector<AlexaDevice> devices; // Collection of devices
|
||||
|
||||
Alex2ESPState _state;
|
||||
AsyncMqttClientDisconnectReason _disconnectReason;
|
||||
|
||||
// MQTT connection details
|
||||
const char *mqttServer = "alex2mqtt.stormysdream.club";
|
||||
uint16_t mqttPort = 1883;
|
||||
|
||||
// Internal event handlers
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason);
|
||||
void onSubscribe(uint16_t packetId, uint8_t qos);
|
||||
void onMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t length, size_t index, size_t total);
|
||||
|
||||
//loop processing function
|
||||
void handleMqttReconnection();
|
||||
void processHttpPost();
|
||||
void processHttpGet();
|
||||
|
||||
};
|
||||
|
||||
#endif // ALEX2ESP_H
|
||||
/*
|
||||
* @title Alex2ESP Library
|
||||
* @version 1.0.0
|
||||
* @author David
|
||||
* @license MIT
|
||||
* @contributors chaos511
|
||||
*
|
||||
* @description The Alex2ESP library is a companion to the Alex2MQTT Alexa Skill,
|
||||
* providing seamless integration between ESP-based devices and the Alex2MQTT server.
|
||||
* This library connects to alex2mqtt.stormysdream.club, where the skill is hosted,
|
||||
* allowing your devices to communicate effortlessly with the Alexa Voice Service
|
||||
* using MQTT as the backbone.
|
||||
*/
|
||||
|
||||
#ifndef ALEX2ESP_H
|
||||
#define ALEX2ESP_H
|
||||
class AlexaDevice;
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "AlexaDevice.h"
|
||||
#include <AlexaInterface.h>
|
||||
#include <vector>
|
||||
#include <AlexaUtils.h>
|
||||
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
|
||||
enum class Alex2ESPState
|
||||
{
|
||||
UNINITIALIZED,
|
||||
INITIALIZED,
|
||||
CONNECTING,
|
||||
SUBSCRIBING,
|
||||
CONNECTED,
|
||||
DISCONNECTED
|
||||
};
|
||||
|
||||
class Alex2ESP
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
Alex2ESP();
|
||||
|
||||
// Begin function for initialization
|
||||
void begin(const char *rootTopic, const char *username, const char *password);
|
||||
Alex2ESPState getState() const;
|
||||
void loop();
|
||||
|
||||
AsyncMqttClientDisconnectReason getDisconnectReason() const;
|
||||
|
||||
AlexaDevice *getDevice(const String &name, const String &endpointId);
|
||||
void messageSplitAndSend(String messagepackString, String topic);
|
||||
|
||||
private:
|
||||
static const int MAX_RETRY_COUNT = 2; // Define maximum retry count
|
||||
int retryCountPOST=0;
|
||||
int retryCountGET=0;
|
||||
|
||||
boolean clearToSend=false;
|
||||
AsyncMqttClient mqttClient; // MQTT client instance
|
||||
String rootTopic; // Root topic for communication
|
||||
String discoverTopic; // The topic we listen on for discovery messages
|
||||
String discoverTopicSend; // The topic we send discovery messages
|
||||
|
||||
String TopicESP; // The topic we listen on for esp messages
|
||||
|
||||
const char *mqttUsername; // Username for authentication
|
||||
const char *mqttPassword; // Password for authentication
|
||||
|
||||
unsigned long lastReconnectTime;
|
||||
int reconnectAttempt;
|
||||
|
||||
HTTPClient httpGET;
|
||||
HTTPClient httpPOST;
|
||||
|
||||
WiFiClient wifiGET;
|
||||
WiFiClient wifiPOST;
|
||||
JsonDocument inputDoc;
|
||||
|
||||
std::vector<AlexaDevice> devices; // Collection of devices
|
||||
|
||||
Alex2ESPState _state;
|
||||
AsyncMqttClientDisconnectReason _disconnectReason;
|
||||
|
||||
// MQTT connection details
|
||||
const char *mqttServer = "alex2mqtt.stormysdream.club";
|
||||
uint16_t mqttPort = 1883;
|
||||
|
||||
// Internal event handlers
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason);
|
||||
void onSubscribe(uint16_t packetId, uint8_t qos);
|
||||
void onMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t length, size_t index, size_t total);
|
||||
|
||||
//loop processing function
|
||||
void handleMqttReconnection();
|
||||
void processHttpPost();
|
||||
void processHttpGet();
|
||||
|
||||
};
|
||||
|
||||
#endif // ALEX2ESP_H
|
||||
|
|
|
|||
|
|
@ -1,164 +1,164 @@
|
|||
#include "AlexaDevice.h"
|
||||
|
||||
AlexaDevice::AlexaDevice(const String& name, const String& rootTopic, const String& endpointId)
|
||||
: name(name), endpointId(endpointId), rootTopic(rootTopic) {
|
||||
// Initialize event arrays to nullptr
|
||||
for (int i = 0; i < MAX_EVENTS; ++i) {
|
||||
eventNames[i] = nullptr; // Set all event names to nullptr
|
||||
eventCallbacks[i] = nullptr; // Set all callbacks to nullptr
|
||||
}
|
||||
}
|
||||
|
||||
// Setters and Getters
|
||||
void AlexaDevice::setName(const String& name) {
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
String AlexaDevice::getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
String AlexaDevice::getEndpointId() const {
|
||||
return endpointId;
|
||||
}
|
||||
|
||||
DisplayCategory AlexaDevice::getDisplayCategory() const {
|
||||
return displayCategory;
|
||||
}
|
||||
|
||||
void AlexaDevice::setDisplayCategory(DisplayCategory category) {
|
||||
displayCategory = category;
|
||||
}
|
||||
|
||||
String AlexaDevice::getDescription() const {
|
||||
return description;
|
||||
}
|
||||
|
||||
void AlexaDevice::setDescription(const String& description) {
|
||||
this->description = description;
|
||||
}
|
||||
|
||||
String AlexaDevice::getManufacturerName() const {
|
||||
return manufacturerName;
|
||||
}
|
||||
|
||||
void AlexaDevice::setManufacturerName(const String& manufacturerName) {
|
||||
this->manufacturerName = manufacturerName;
|
||||
}
|
||||
|
||||
String AlexaDevice::getManufacturer() const {
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
void AlexaDevice::setManufacturer(const String& manufacturer) {
|
||||
this->manufacturer = manufacturer;
|
||||
}
|
||||
|
||||
String AlexaDevice::getModel() const {
|
||||
return model;
|
||||
}
|
||||
|
||||
void AlexaDevice::setModel(const String& model) {
|
||||
this->model = model;
|
||||
}
|
||||
|
||||
String AlexaDevice::getSoftwareVersion() const {
|
||||
return softwareVersion;
|
||||
}
|
||||
|
||||
|
||||
AlexaInterface* AlexaDevice::addCapability(AlexaInterfaceType type) {
|
||||
// Check if a interface with the given type already exists
|
||||
for (auto& iface : capabilities) {
|
||||
if (iface.getType() == type) {
|
||||
return &iface; // Return the existing interface
|
||||
}
|
||||
}
|
||||
|
||||
// If the interface doesn't exist, create a new one
|
||||
capabilities.emplace_back(type);
|
||||
|
||||
Serial.print("Created new interface with type: ");
|
||||
Serial.println(capabilities.back().getTypeString());
|
||||
|
||||
// Return a pointer to the newly created device
|
||||
return &capabilities.back();
|
||||
}
|
||||
|
||||
|
||||
|
||||
JsonDocument AlexaDevice::getDeviceJSON() const {
|
||||
|
||||
JsonDocument json;
|
||||
|
||||
// Main device attributes
|
||||
json["endpointId"] = endpointId;
|
||||
json["friendlyName"] = name;
|
||||
json["description"] = description;
|
||||
json["manufacturerName"] = manufacturerName;
|
||||
|
||||
|
||||
|
||||
JsonArray displayCategories = json["displayCategories"].to<JsonArray>();
|
||||
displayCategories.add(DisplayCategoryUtils::toString(displayCategory));
|
||||
|
||||
JsonObject additionalAttributes = json["additionalAttributes"].to<JsonObject>();
|
||||
additionalAttributes["manufacturer"] = manufacturer;
|
||||
additionalAttributes["model"] = model;
|
||||
additionalAttributes["serialNumber"] = "ESP2Alex";
|
||||
additionalAttributes["firmwareVersion"] = "1.0.0";
|
||||
additionalAttributes["softwareVersion"] = softwareVersion;
|
||||
additionalAttributes["customIdentifier"] = "ESP2Alex";
|
||||
|
||||
JsonArray capabilitiesArray = json["capabilities"].to<JsonArray>();
|
||||
|
||||
for (AlexaInterface capability : capabilities) {
|
||||
capabilitiesArray.add(capability.getJSON().as<JsonObject>());
|
||||
}
|
||||
|
||||
// capabilitiesArray.add(capability.getJSON().as<JsonObject>());
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// void AlexaDevice::registerEvent(const String& eventName, std::function<void(const JsonDocument&,const AlexaInterfaceType&)> callback) {
|
||||
// eventCallbacks[eventName] = callback;
|
||||
// }
|
||||
|
||||
// void AlexaDevice::triggerEvent(const String& eventName, const JsonDocument& directive,const AlexaInterfaceType& type) const {
|
||||
// auto it = eventCallbacks.find(eventName);
|
||||
// if (it != eventCallbacks.end()) {
|
||||
// // Call the registered callback function
|
||||
// it->second(directive,type);
|
||||
// } else {
|
||||
// Serial.print("No event registered for: ");
|
||||
// Serial.println(eventName);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// Register an event callback function
|
||||
void AlexaDevice::registerEvent(const char* eventName, void (*callback)(const JsonDocument&, const AlexaInterfaceType&)) {
|
||||
for (int i = 0; i < MAX_EVENTS; ++i) {
|
||||
// Find an empty slot for the new event
|
||||
if (eventNames[i] == nullptr) {
|
||||
eventNames[i] = eventName; // Store the event name
|
||||
eventCallbacks[i] = callback; // Store the callback function
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger the event and invoke the corresponding callback
|
||||
void AlexaDevice::triggerEvent(const char* eventName, const JsonDocument& directive, const AlexaInterfaceType& type) const {
|
||||
for (int i = 0; i < MAX_EVENTS; ++i) {
|
||||
if (eventNames[i] != nullptr && strcmp(eventNames[i], eventName) == 0) {
|
||||
// Found the event name, call the corresponding callback function
|
||||
eventCallbacks[i](directive, type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If no event was found, print an error message
|
||||
Serial.print("No event registered for: ");
|
||||
Serial.println(eventName);
|
||||
#include "AlexaDevice.h"
|
||||
|
||||
AlexaDevice::AlexaDevice(const String& name, const String& rootTopic, const String& endpointId)
|
||||
: name(name), endpointId(endpointId), rootTopic(rootTopic) {
|
||||
// Initialize event arrays to nullptr
|
||||
for (int i = 0; i < MAX_EVENTS; ++i) {
|
||||
eventNames[i] = nullptr; // Set all event names to nullptr
|
||||
eventCallbacks[i] = nullptr; // Set all callbacks to nullptr
|
||||
}
|
||||
}
|
||||
|
||||
// Setters and Getters
|
||||
void AlexaDevice::setName(const String& name) {
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
String AlexaDevice::getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
String AlexaDevice::getEndpointId() const {
|
||||
return endpointId;
|
||||
}
|
||||
|
||||
DisplayCategory AlexaDevice::getDisplayCategory() const {
|
||||
return displayCategory;
|
||||
}
|
||||
|
||||
void AlexaDevice::setDisplayCategory(DisplayCategory category) {
|
||||
displayCategory = category;
|
||||
}
|
||||
|
||||
String AlexaDevice::getDescription() const {
|
||||
return description;
|
||||
}
|
||||
|
||||
void AlexaDevice::setDescription(const String& description) {
|
||||
this->description = description;
|
||||
}
|
||||
|
||||
String AlexaDevice::getManufacturerName() const {
|
||||
return manufacturerName;
|
||||
}
|
||||
|
||||
void AlexaDevice::setManufacturerName(const String& manufacturerName) {
|
||||
this->manufacturerName = manufacturerName;
|
||||
}
|
||||
|
||||
String AlexaDevice::getManufacturer() const {
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
void AlexaDevice::setManufacturer(const String& manufacturer) {
|
||||
this->manufacturer = manufacturer;
|
||||
}
|
||||
|
||||
String AlexaDevice::getModel() const {
|
||||
return model;
|
||||
}
|
||||
|
||||
void AlexaDevice::setModel(const String& model) {
|
||||
this->model = model;
|
||||
}
|
||||
|
||||
String AlexaDevice::getSoftwareVersion() const {
|
||||
return softwareVersion;
|
||||
}
|
||||
|
||||
|
||||
AlexaInterface* AlexaDevice::addCapability(AlexaInterfaceType type) {
|
||||
// Check if a interface with the given type already exists
|
||||
for (auto& iface : capabilities) {
|
||||
if (iface.getType() == type) {
|
||||
return &iface; // Return the existing interface
|
||||
}
|
||||
}
|
||||
|
||||
// If the interface doesn't exist, create a new one
|
||||
capabilities.emplace_back(type);
|
||||
|
||||
Serial.print("Created new interface with type: ");
|
||||
Serial.println(capabilities.back().getTypeString());
|
||||
|
||||
// Return a pointer to the newly created device
|
||||
return &capabilities.back();
|
||||
}
|
||||
|
||||
|
||||
|
||||
JsonDocument AlexaDevice::getDeviceJSON() const {
|
||||
|
||||
JsonDocument json;
|
||||
|
||||
// Main device attributes
|
||||
json["endpointId"] = endpointId;
|
||||
json["friendlyName"] = name;
|
||||
json["description"] = description;
|
||||
json["manufacturerName"] = manufacturerName;
|
||||
|
||||
|
||||
|
||||
JsonArray displayCategories = json["displayCategories"].to<JsonArray>();
|
||||
displayCategories.add(DisplayCategoryUtils::toString(displayCategory));
|
||||
|
||||
JsonObject additionalAttributes = json["additionalAttributes"].to<JsonObject>();
|
||||
additionalAttributes["manufacturer"] = manufacturer;
|
||||
additionalAttributes["model"] = model;
|
||||
additionalAttributes["serialNumber"] = "ESP2Alex";
|
||||
additionalAttributes["firmwareVersion"] = "1.0.0";
|
||||
additionalAttributes["softwareVersion"] = softwareVersion;
|
||||
additionalAttributes["customIdentifier"] = "ESP2Alex";
|
||||
|
||||
JsonArray capabilitiesArray = json["capabilities"].to<JsonArray>();
|
||||
|
||||
for (AlexaInterface capability : capabilities) {
|
||||
capabilitiesArray.add(capability.getJSON().as<JsonObject>());
|
||||
}
|
||||
|
||||
// capabilitiesArray.add(capability.getJSON().as<JsonObject>());
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// void AlexaDevice::registerEvent(const String& eventName, std::function<void(const JsonDocument&,const AlexaInterfaceType&)> callback) {
|
||||
// eventCallbacks[eventName] = callback;
|
||||
// }
|
||||
|
||||
// void AlexaDevice::triggerEvent(const String& eventName, const JsonDocument& directive,const AlexaInterfaceType& type) const {
|
||||
// auto it = eventCallbacks.find(eventName);
|
||||
// if (it != eventCallbacks.end()) {
|
||||
// // Call the registered callback function
|
||||
// it->second(directive,type);
|
||||
// } else {
|
||||
// Serial.print("No event registered for: ");
|
||||
// Serial.println(eventName);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// Register an event callback function
|
||||
void AlexaDevice::registerEvent(const char* eventName, void (*callback)(const JsonDocument&, const AlexaInterfaceType&)) {
|
||||
for (int i = 0; i < MAX_EVENTS; ++i) {
|
||||
// Find an empty slot for the new event
|
||||
if (eventNames[i] == nullptr) {
|
||||
eventNames[i] = eventName; // Store the event name
|
||||
eventCallbacks[i] = callback; // Store the callback function
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger the event and invoke the corresponding callback
|
||||
void AlexaDevice::triggerEvent(const char* eventName, const JsonDocument& directive, const AlexaInterfaceType& type) const {
|
||||
for (int i = 0; i < MAX_EVENTS; ++i) {
|
||||
if (eventNames[i] != nullptr && strcmp(eventNames[i], eventName) == 0) {
|
||||
// Found the event name, call the corresponding callback function
|
||||
eventCallbacks[i](directive, type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If no event was found, print an error message
|
||||
Serial.print("No event registered for: ");
|
||||
Serial.println(eventName);
|
||||
}
|
||||
|
|
@ -1,204 +1,204 @@
|
|||
#ifndef ALEXADEVICE_H
|
||||
#define ALEXADEVICE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "AlexaInterface.h"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <AlexaStatusMessage.h>
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
|
||||
#define MAX_EVENTS 10
|
||||
|
||||
enum class DisplayCategory {
|
||||
ACTIVITY_TRIGGER,
|
||||
AIR_CONDITIONER,
|
||||
AIR_FRESHENER,
|
||||
AIR_PURIFIER,
|
||||
AIR_QUALITY_MONITOR,
|
||||
ALEXA_VOICE_ENABLED,
|
||||
AUTO_ACCESSORY,
|
||||
BLUETOOTH_SPEAKER,
|
||||
CAMERA,
|
||||
CHRISTMAS_TREE,
|
||||
COFFEE_MAKER,
|
||||
COMPUTER,
|
||||
CONTACT_SENSOR,
|
||||
DISHWASHER,
|
||||
DOOR,
|
||||
DOORBELL,
|
||||
DRYER,
|
||||
EXTERIOR_BLIND,
|
||||
FAN,
|
||||
GAME_CONSOLE,
|
||||
GARAGE_DOOR,
|
||||
HEADPHONES,
|
||||
HUB,
|
||||
INTERIOR_BLIND,
|
||||
LAPTOP,
|
||||
LIGHT,
|
||||
MICROWAVE,
|
||||
MOBILE_PHONE,
|
||||
MOTION_SENSOR,
|
||||
MUSIC_SYSTEM,
|
||||
NETWORK_HARDWARE,
|
||||
OTHER,
|
||||
OVEN,
|
||||
PHONE,
|
||||
PRINTER,
|
||||
REMOTE,
|
||||
ROUTER,
|
||||
SCENE_TRIGGER,
|
||||
SCREEN,
|
||||
SECURITY_PANEL,
|
||||
SECURITY_SYSTEM,
|
||||
SLOW_COOKER,
|
||||
SMARTLOCK,
|
||||
SMARTPLUG,
|
||||
SPEAKER,
|
||||
STREAMING_DEVICE,
|
||||
SWITCH,
|
||||
TABLET,
|
||||
TEMPERATURE_SENSOR,
|
||||
THERMOSTAT,
|
||||
TV,
|
||||
VACUUM_CLEANER,
|
||||
VEHICLE,
|
||||
WASHER,
|
||||
WATER_HEATER,
|
||||
WEARABLE
|
||||
};
|
||||
|
||||
class DisplayCategoryUtils {
|
||||
public:
|
||||
static String toString(DisplayCategory category) {
|
||||
static const std::unordered_map<DisplayCategory, String> categoryMap = {
|
||||
{DisplayCategory::ACTIVITY_TRIGGER, "ACTIVITY_TRIGGER"},
|
||||
{DisplayCategory::AIR_CONDITIONER, "AIR_CONDITIONER"},
|
||||
{DisplayCategory::AIR_FRESHENER, "AIR_FRESHENER"},
|
||||
{DisplayCategory::AIR_PURIFIER, "AIR_PURIFIER"},
|
||||
{DisplayCategory::AIR_QUALITY_MONITOR, "AIR_QUALITY_MONITOR"},
|
||||
{DisplayCategory::ALEXA_VOICE_ENABLED, "ALEXA_VOICE_ENABLED"},
|
||||
{DisplayCategory::AUTO_ACCESSORY, "AUTO_ACCESSORY"},
|
||||
{DisplayCategory::BLUETOOTH_SPEAKER, "BLUETOOTH_SPEAKER"},
|
||||
{DisplayCategory::CAMERA, "CAMERA"},
|
||||
{DisplayCategory::CHRISTMAS_TREE, "CHRISTMAS_TREE"},
|
||||
{DisplayCategory::COFFEE_MAKER, "COFFEE_MAKER"},
|
||||
{DisplayCategory::COMPUTER, "COMPUTER"},
|
||||
{DisplayCategory::CONTACT_SENSOR, "CONTACT_SENSOR"},
|
||||
{DisplayCategory::DISHWASHER, "DISHWASHER"},
|
||||
{DisplayCategory::DOOR, "DOOR"},
|
||||
{DisplayCategory::DOORBELL, "DOORBELL"},
|
||||
{DisplayCategory::DRYER, "DRYER"},
|
||||
{DisplayCategory::EXTERIOR_BLIND, "EXTERIOR_BLIND"},
|
||||
{DisplayCategory::FAN, "FAN"},
|
||||
{DisplayCategory::GAME_CONSOLE, "GAME_CONSOLE"},
|
||||
{DisplayCategory::GARAGE_DOOR, "GARAGE_DOOR"},
|
||||
{DisplayCategory::HEADPHONES, "HEADPHONES"},
|
||||
{DisplayCategory::HUB, "HUB"},
|
||||
{DisplayCategory::INTERIOR_BLIND, "INTERIOR_BLIND"},
|
||||
{DisplayCategory::LAPTOP, "LAPTOP"},
|
||||
{DisplayCategory::LIGHT, "LIGHT"},
|
||||
{DisplayCategory::MICROWAVE, "MICROWAVE"},
|
||||
{DisplayCategory::MOBILE_PHONE, "MOBILE_PHONE"},
|
||||
{DisplayCategory::MOTION_SENSOR, "MOTION_SENSOR"},
|
||||
{DisplayCategory::MUSIC_SYSTEM, "MUSIC_SYSTEM"},
|
||||
{DisplayCategory::NETWORK_HARDWARE, "NETWORK_HARDWARE"},
|
||||
{DisplayCategory::OTHER, "OTHER"},
|
||||
{DisplayCategory::OVEN, "OVEN"},
|
||||
{DisplayCategory::PHONE, "PHONE"},
|
||||
{DisplayCategory::PRINTER, "PRINTER"},
|
||||
{DisplayCategory::REMOTE, "REMOTE"},
|
||||
{DisplayCategory::ROUTER, "ROUTER"},
|
||||
{DisplayCategory::SCENE_TRIGGER, "SCENE_TRIGGER"},
|
||||
{DisplayCategory::SCREEN, "SCREEN"},
|
||||
{DisplayCategory::SECURITY_PANEL, "SECURITY_PANEL"},
|
||||
{DisplayCategory::SECURITY_SYSTEM, "SECURITY_SYSTEM"},
|
||||
{DisplayCategory::SLOW_COOKER, "SLOW_COOKER"},
|
||||
{DisplayCategory::SMARTLOCK, "SMARTLOCK"},
|
||||
{DisplayCategory::SMARTPLUG, "SMARTPLUG"},
|
||||
{DisplayCategory::SPEAKER, "SPEAKER"},
|
||||
{DisplayCategory::STREAMING_DEVICE, "STREAMING_DEVICE"},
|
||||
{DisplayCategory::SWITCH, "SWITCH"},
|
||||
{DisplayCategory::TABLET, "TABLET"},
|
||||
{DisplayCategory::TEMPERATURE_SENSOR, "TEMPERATURE_SENSOR"},
|
||||
{DisplayCategory::THERMOSTAT, "THERMOSTAT"},
|
||||
{DisplayCategory::TV, "TV"},
|
||||
{DisplayCategory::VACUUM_CLEANER, "VACUUM_CLEANER"},
|
||||
{DisplayCategory::VEHICLE, "VEHICLE"},
|
||||
{DisplayCategory::WASHER, "WASHER"},
|
||||
{DisplayCategory::WATER_HEATER, "WATER_HEATER"},
|
||||
{DisplayCategory::WEARABLE, "WEARABLE"}
|
||||
};
|
||||
|
||||
auto it = categoryMap.find(category);
|
||||
if (it != categoryMap.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class AlexaDevice {
|
||||
public:
|
||||
AlexaDevice(const String& name, const String& rootTopic, const String& endpointId);
|
||||
|
||||
void setName(const String& name);
|
||||
String getName() const;
|
||||
|
||||
String getEndpointId() const;
|
||||
|
||||
DisplayCategory getDisplayCategory() const;
|
||||
void setDisplayCategory(DisplayCategory category);
|
||||
|
||||
String getDescription() const;
|
||||
void setDescription(const String& description);
|
||||
|
||||
String getManufacturerName() const;
|
||||
void setManufacturerName(const String& manufacturerName);
|
||||
|
||||
String getManufacturer() const;
|
||||
void setManufacturer(const String& manufacturer);
|
||||
|
||||
String getModel() const;
|
||||
void setModel(const String& model);
|
||||
|
||||
String getSoftwareVersion() const;
|
||||
|
||||
JsonDocument getDeviceJSON() const;
|
||||
|
||||
AlexaInterface* addCapability(AlexaInterfaceType type);
|
||||
|
||||
void registerEvent(const char* eventName, void (*callback)(const JsonDocument&, const AlexaInterfaceType&));
|
||||
|
||||
void triggerEvent(const char* eventName, const JsonDocument& directive, const AlexaInterfaceType& type) const;
|
||||
|
||||
AlexaStatusMessage buildStatusMessage(const String& correlationToken,const bool isResponse=false) {
|
||||
return AlexaStatusMessage(correlationToken,rootTopic,endpointId,isResponse);
|
||||
}
|
||||
|
||||
private:
|
||||
String name;
|
||||
String endpointId;
|
||||
String rootTopic;
|
||||
const char* eventNames[MAX_EVENTS];
|
||||
void (*eventCallbacks[MAX_EVENTS])(const JsonDocument&, const AlexaInterfaceType&);
|
||||
|
||||
DisplayCategory displayCategory = DisplayCategory::OTHER;
|
||||
String description = "Alexa2MQTT Default Device";
|
||||
String manufacturerName = "Alexa2MQTT";
|
||||
String manufacturer = "Alexa2MQTT";
|
||||
String model = "Alexa2MQTT";
|
||||
const String softwareVersion = "1.0.0";
|
||||
|
||||
std::vector<AlexaInterface> capabilities;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
#ifndef ALEXADEVICE_H
|
||||
#define ALEXADEVICE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "AlexaInterface.h"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <AlexaStatusMessage.h>
|
||||
#include <Alex2ESP.h>
|
||||
|
||||
|
||||
#define MAX_EVENTS 10
|
||||
|
||||
enum class DisplayCategory {
|
||||
ACTIVITY_TRIGGER,
|
||||
AIR_CONDITIONER,
|
||||
AIR_FRESHENER,
|
||||
AIR_PURIFIER,
|
||||
AIR_QUALITY_MONITOR,
|
||||
ALEXA_VOICE_ENABLED,
|
||||
AUTO_ACCESSORY,
|
||||
BLUETOOTH_SPEAKER,
|
||||
CAMERA,
|
||||
CHRISTMAS_TREE,
|
||||
COFFEE_MAKER,
|
||||
COMPUTER,
|
||||
CONTACT_SENSOR,
|
||||
DISHWASHER,
|
||||
DOOR,
|
||||
DOORBELL,
|
||||
DRYER,
|
||||
EXTERIOR_BLIND,
|
||||
FAN,
|
||||
GAME_CONSOLE,
|
||||
GARAGE_DOOR,
|
||||
HEADPHONES,
|
||||
HUB,
|
||||
INTERIOR_BLIND,
|
||||
LAPTOP,
|
||||
LIGHT,
|
||||
MICROWAVE,
|
||||
MOBILE_PHONE,
|
||||
MOTION_SENSOR,
|
||||
MUSIC_SYSTEM,
|
||||
NETWORK_HARDWARE,
|
||||
OTHER,
|
||||
OVEN,
|
||||
PHONE,
|
||||
PRINTER,
|
||||
REMOTE,
|
||||
ROUTER,
|
||||
SCENE_TRIGGER,
|
||||
SCREEN,
|
||||
SECURITY_PANEL,
|
||||
SECURITY_SYSTEM,
|
||||
SLOW_COOKER,
|
||||
SMARTLOCK,
|
||||
SMARTPLUG,
|
||||
SPEAKER,
|
||||
STREAMING_DEVICE,
|
||||
SWITCH,
|
||||
TABLET,
|
||||
TEMPERATURE_SENSOR,
|
||||
THERMOSTAT,
|
||||
TV,
|
||||
VACUUM_CLEANER,
|
||||
VEHICLE,
|
||||
WASHER,
|
||||
WATER_HEATER,
|
||||
WEARABLE
|
||||
};
|
||||
|
||||
class DisplayCategoryUtils {
|
||||
public:
|
||||
static String toString(DisplayCategory category) {
|
||||
static const std::unordered_map<DisplayCategory, String> categoryMap = {
|
||||
{DisplayCategory::ACTIVITY_TRIGGER, "ACTIVITY_TRIGGER"},
|
||||
{DisplayCategory::AIR_CONDITIONER, "AIR_CONDITIONER"},
|
||||
{DisplayCategory::AIR_FRESHENER, "AIR_FRESHENER"},
|
||||
{DisplayCategory::AIR_PURIFIER, "AIR_PURIFIER"},
|
||||
{DisplayCategory::AIR_QUALITY_MONITOR, "AIR_QUALITY_MONITOR"},
|
||||
{DisplayCategory::ALEXA_VOICE_ENABLED, "ALEXA_VOICE_ENABLED"},
|
||||
{DisplayCategory::AUTO_ACCESSORY, "AUTO_ACCESSORY"},
|
||||
{DisplayCategory::BLUETOOTH_SPEAKER, "BLUETOOTH_SPEAKER"},
|
||||
{DisplayCategory::CAMERA, "CAMERA"},
|
||||
{DisplayCategory::CHRISTMAS_TREE, "CHRISTMAS_TREE"},
|
||||
{DisplayCategory::COFFEE_MAKER, "COFFEE_MAKER"},
|
||||
{DisplayCategory::COMPUTER, "COMPUTER"},
|
||||
{DisplayCategory::CONTACT_SENSOR, "CONTACT_SENSOR"},
|
||||
{DisplayCategory::DISHWASHER, "DISHWASHER"},
|
||||
{DisplayCategory::DOOR, "DOOR"},
|
||||
{DisplayCategory::DOORBELL, "DOORBELL"},
|
||||
{DisplayCategory::DRYER, "DRYER"},
|
||||
{DisplayCategory::EXTERIOR_BLIND, "EXTERIOR_BLIND"},
|
||||
{DisplayCategory::FAN, "FAN"},
|
||||
{DisplayCategory::GAME_CONSOLE, "GAME_CONSOLE"},
|
||||
{DisplayCategory::GARAGE_DOOR, "GARAGE_DOOR"},
|
||||
{DisplayCategory::HEADPHONES, "HEADPHONES"},
|
||||
{DisplayCategory::HUB, "HUB"},
|
||||
{DisplayCategory::INTERIOR_BLIND, "INTERIOR_BLIND"},
|
||||
{DisplayCategory::LAPTOP, "LAPTOP"},
|
||||
{DisplayCategory::LIGHT, "LIGHT"},
|
||||
{DisplayCategory::MICROWAVE, "MICROWAVE"},
|
||||
{DisplayCategory::MOBILE_PHONE, "MOBILE_PHONE"},
|
||||
{DisplayCategory::MOTION_SENSOR, "MOTION_SENSOR"},
|
||||
{DisplayCategory::MUSIC_SYSTEM, "MUSIC_SYSTEM"},
|
||||
{DisplayCategory::NETWORK_HARDWARE, "NETWORK_HARDWARE"},
|
||||
{DisplayCategory::OTHER, "OTHER"},
|
||||
{DisplayCategory::OVEN, "OVEN"},
|
||||
{DisplayCategory::PHONE, "PHONE"},
|
||||
{DisplayCategory::PRINTER, "PRINTER"},
|
||||
{DisplayCategory::REMOTE, "REMOTE"},
|
||||
{DisplayCategory::ROUTER, "ROUTER"},
|
||||
{DisplayCategory::SCENE_TRIGGER, "SCENE_TRIGGER"},
|
||||
{DisplayCategory::SCREEN, "SCREEN"},
|
||||
{DisplayCategory::SECURITY_PANEL, "SECURITY_PANEL"},
|
||||
{DisplayCategory::SECURITY_SYSTEM, "SECURITY_SYSTEM"},
|
||||
{DisplayCategory::SLOW_COOKER, "SLOW_COOKER"},
|
||||
{DisplayCategory::SMARTLOCK, "SMARTLOCK"},
|
||||
{DisplayCategory::SMARTPLUG, "SMARTPLUG"},
|
||||
{DisplayCategory::SPEAKER, "SPEAKER"},
|
||||
{DisplayCategory::STREAMING_DEVICE, "STREAMING_DEVICE"},
|
||||
{DisplayCategory::SWITCH, "SWITCH"},
|
||||
{DisplayCategory::TABLET, "TABLET"},
|
||||
{DisplayCategory::TEMPERATURE_SENSOR, "TEMPERATURE_SENSOR"},
|
||||
{DisplayCategory::THERMOSTAT, "THERMOSTAT"},
|
||||
{DisplayCategory::TV, "TV"},
|
||||
{DisplayCategory::VACUUM_CLEANER, "VACUUM_CLEANER"},
|
||||
{DisplayCategory::VEHICLE, "VEHICLE"},
|
||||
{DisplayCategory::WASHER, "WASHER"},
|
||||
{DisplayCategory::WATER_HEATER, "WATER_HEATER"},
|
||||
{DisplayCategory::WEARABLE, "WEARABLE"}
|
||||
};
|
||||
|
||||
auto it = categoryMap.find(category);
|
||||
if (it != categoryMap.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class AlexaDevice {
|
||||
public:
|
||||
AlexaDevice(const String& name, const String& rootTopic, const String& endpointId);
|
||||
|
||||
void setName(const String& name);
|
||||
String getName() const;
|
||||
|
||||
String getEndpointId() const;
|
||||
|
||||
DisplayCategory getDisplayCategory() const;
|
||||
void setDisplayCategory(DisplayCategory category);
|
||||
|
||||
String getDescription() const;
|
||||
void setDescription(const String& description);
|
||||
|
||||
String getManufacturerName() const;
|
||||
void setManufacturerName(const String& manufacturerName);
|
||||
|
||||
String getManufacturer() const;
|
||||
void setManufacturer(const String& manufacturer);
|
||||
|
||||
String getModel() const;
|
||||
void setModel(const String& model);
|
||||
|
||||
String getSoftwareVersion() const;
|
||||
|
||||
JsonDocument getDeviceJSON() const;
|
||||
|
||||
AlexaInterface* addCapability(AlexaInterfaceType type);
|
||||
|
||||
void registerEvent(const char* eventName, void (*callback)(const JsonDocument&, const AlexaInterfaceType&));
|
||||
|
||||
void triggerEvent(const char* eventName, const JsonDocument& directive, const AlexaInterfaceType& type) const;
|
||||
|
||||
AlexaStatusMessage buildStatusMessage(const String& correlationToken,const bool isResponse=false) {
|
||||
return AlexaStatusMessage(correlationToken,rootTopic,endpointId,isResponse);
|
||||
}
|
||||
|
||||
private:
|
||||
String name;
|
||||
String endpointId;
|
||||
String rootTopic;
|
||||
const char* eventNames[MAX_EVENTS];
|
||||
void (*eventCallbacks[MAX_EVENTS])(const JsonDocument&, const AlexaInterfaceType&);
|
||||
|
||||
DisplayCategory displayCategory = DisplayCategory::OTHER;
|
||||
String description = "Alexa2MQTT Default Device";
|
||||
String manufacturerName = "Alexa2MQTT";
|
||||
String manufacturer = "Alexa2MQTT";
|
||||
String model = "Alexa2MQTT";
|
||||
const String softwareVersion = "1.0.0";
|
||||
|
||||
std::vector<AlexaInterface> capabilities;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
1446
src/AlexaInterface.h
1446
src/AlexaInterface.h
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +1,3 @@
|
|||
#include "AlexaStatusMessage.h"
|
||||
|
||||
char AlexaStatusMessage::outputString[MAX_STATUS_REPORT_SIZE];
|
||||
#include "AlexaStatusMessage.h"
|
||||
|
||||
char AlexaStatusMessage::outputString[MAX_STATUS_REPORT_SIZE];
|
||||
|
|
|
|||
|
|
@ -1,167 +1,167 @@
|
|||
#include <ArduinoJson.h>
|
||||
#include <Alex2ESP.h>
|
||||
#include <AlexaUtils.h>
|
||||
|
||||
#ifndef ALEXA_STATUS_MESSAGE_H
|
||||
#define ALEXA_STATUS_MESSAGE_H
|
||||
|
||||
#define MAX_STATUS_REPORT_SIZE 2048
|
||||
|
||||
enum class EndpointHealth
|
||||
{
|
||||
OK,
|
||||
UNREACHABLE
|
||||
};
|
||||
|
||||
enum class PowerController
|
||||
{
|
||||
ON,
|
||||
OFF
|
||||
};
|
||||
enum class TemperatureSensorScale
|
||||
{
|
||||
CELSIUS,
|
||||
FAHRENHEIT
|
||||
};
|
||||
|
||||
class AlexaStatusMessage
|
||||
{
|
||||
public:
|
||||
AlexaStatusMessage(const String &correlationToken, const String &rootTopic, const String &endpointId, const bool isResponse)
|
||||
{
|
||||
this->endpointId = endpointId;
|
||||
this->rootTopic = rootTopic;
|
||||
|
||||
JsonObject event = doc["event"].to<JsonObject>();
|
||||
|
||||
JsonObject event_header = event["header"].to<JsonObject>();
|
||||
event_header["namespace"] = "Alexa";
|
||||
if (isResponse)
|
||||
{
|
||||
event_header["name"] = "Response";
|
||||
}
|
||||
else
|
||||
{
|
||||
event_header["name"] = "StateReport";
|
||||
}
|
||||
event_header["payloadVersion"] = "3";
|
||||
event_header["messageId"] = generateMessageId();
|
||||
event_header["correlationToken"] = correlationToken;
|
||||
|
||||
JsonObject event_endpoint = event["endpoint"].to<JsonObject>();
|
||||
event_endpoint["endpointId"] = endpointId;
|
||||
contextProperties = doc["context"]["properties"].to<JsonArray>();
|
||||
}
|
||||
|
||||
void setEndpointId(const String &endpointId)
|
||||
{
|
||||
// event["endpoint"]["endpointId"] = endpointId;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddHealthProp(EndpointHealth endpointHealth, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
JsonDocument healthValue;
|
||||
healthValue["value"] = (endpointHealth == EndpointHealth::OK) ? "OK" : "UNREACHABLE";
|
||||
return AddProperty(AlexaInterfaceType::ENDPOINT_HEALTH, "connectivity", healthValue.as<JsonObject>(), uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddPowerControllerProp(PowerController powerController, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
String value = (powerController == PowerController::ON) ? "ON" : "OFF";
|
||||
return AddProperty(AlexaInterfaceType::POWER_CONTROLLER, "powerState", value, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddTemperatureSensorProp(TemperatureSensorScale tempSensor,float value, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
JsonDocument tempValue;
|
||||
tempValue["scale"] = "CELSIUS";
|
||||
tempValue["value"] = value;
|
||||
if((tempSensor == TemperatureSensorScale::FAHRENHEIT)){
|
||||
tempValue["value"] = (value - 32) * 5.0 / 9.0;
|
||||
}
|
||||
return AddProperty(AlexaInterfaceType::TEMPERATURE_SENSOR, "temperature", tempValue.as<JsonObject>(), uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddBrightnessControllerProp(unsigned int brightness, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
return AddProperty(AlexaInterfaceType::BRIGHTNESS_CONTROLLER, "brightness", brightness, uncertaintyInMs);
|
||||
}
|
||||
AlexaStatusMessage &AddColorTemperatureControllerProp(unsigned int colorTemperature, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
return AddProperty(AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER, "colorTemperatureInKelvin", colorTemperature, uncertaintyInMs);
|
||||
}
|
||||
AlexaStatusMessage &AddToggleControllerProp(PowerController powerController,String instanceName, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
String value = (powerController == PowerController::ON) ? "ON" : "OFF";
|
||||
return AddProperty(AlexaInterfaceType::TOGGLE_CONTROLLER, "toggleState", value, uncertaintyInMs,instanceName);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddContextProp(const JsonObject &property)
|
||||
{
|
||||
contextProperties.add(property);
|
||||
return *this; // Return a reference to the current object
|
||||
}
|
||||
|
||||
void send()
|
||||
{
|
||||
doc.shrinkToFit();
|
||||
serializeJson(doc, outputString);
|
||||
doc.clear();
|
||||
String topic = rootTopic + "/" + endpointId + "/alexaResponce";
|
||||
AlexaUtils::enqueue(outputString, topic.c_str());
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
String rootTopic;
|
||||
String endpointId;
|
||||
JsonDocument doc;
|
||||
JsonArray contextProperties;
|
||||
static char outputString[MAX_STATUS_REPORT_SIZE];
|
||||
|
||||
// TODO: make real uuid4 gen function
|
||||
String generateMessageId()
|
||||
{
|
||||
char buffer[38];
|
||||
const char charset[] = "0123456789abcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // Allowed characters
|
||||
|
||||
// Seed the random number generator (optional)
|
||||
srand(static_cast<unsigned int>(time(nullptr)));
|
||||
|
||||
// Generate 37 random characters
|
||||
for (int i = 0; i < 37; ++i)
|
||||
{
|
||||
buffer[i] = charset[rand() % (sizeof(charset) - 1)]; // Pick a random character
|
||||
}
|
||||
|
||||
buffer[37] = '\0'; // Null-terminate the string
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
AlexaStatusMessage &AddProperty(AlexaInterfaceType type, const String &propertyName, const T &value, unsigned int uncertaintyInMs = 0,const String &instanceName="")
|
||||
{
|
||||
JsonDocument prop;
|
||||
prop["namespace"] = AlexaInterfaceUtils::toString(type);
|
||||
prop["name"] = propertyName;
|
||||
|
||||
if (!instanceName.isEmpty()) {
|
||||
prop["instance"] = instanceName;
|
||||
}
|
||||
if constexpr (std::is_same_v<T, JsonObject>)
|
||||
{
|
||||
prop["value"] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
prop["value"] = value;
|
||||
}
|
||||
|
||||
prop["timeOfSample"] = "{REPLACE_WITH_DATETIME}";
|
||||
prop["uncertaintyInMilliseconds"] = uncertaintyInMs;
|
||||
|
||||
contextProperties.add(prop.as<JsonObject>());
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
#include <ArduinoJson.h>
|
||||
#include <Alex2ESP.h>
|
||||
#include <AlexaUtils.h>
|
||||
|
||||
#ifndef ALEXA_STATUS_MESSAGE_H
|
||||
#define ALEXA_STATUS_MESSAGE_H
|
||||
|
||||
#define MAX_STATUS_REPORT_SIZE 2048
|
||||
|
||||
enum class EndpointHealth
|
||||
{
|
||||
OK,
|
||||
UNREACHABLE
|
||||
};
|
||||
|
||||
enum class PowerController
|
||||
{
|
||||
ON,
|
||||
OFF
|
||||
};
|
||||
enum class TemperatureSensorScale
|
||||
{
|
||||
CELSIUS,
|
||||
FAHRENHEIT
|
||||
};
|
||||
|
||||
class AlexaStatusMessage
|
||||
{
|
||||
public:
|
||||
AlexaStatusMessage(const String &correlationToken, const String &rootTopic, const String &endpointId, const bool isResponse)
|
||||
{
|
||||
this->endpointId = endpointId;
|
||||
this->rootTopic = rootTopic;
|
||||
|
||||
JsonObject event = doc["event"].to<JsonObject>();
|
||||
|
||||
JsonObject event_header = event["header"].to<JsonObject>();
|
||||
event_header["namespace"] = "Alexa";
|
||||
if (isResponse)
|
||||
{
|
||||
event_header["name"] = "Response";
|
||||
}
|
||||
else
|
||||
{
|
||||
event_header["name"] = "StateReport";
|
||||
}
|
||||
event_header["payloadVersion"] = "3";
|
||||
event_header["messageId"] = generateMessageId();
|
||||
event_header["correlationToken"] = correlationToken;
|
||||
|
||||
JsonObject event_endpoint = event["endpoint"].to<JsonObject>();
|
||||
event_endpoint["endpointId"] = endpointId;
|
||||
contextProperties = doc["context"]["properties"].to<JsonArray>();
|
||||
}
|
||||
|
||||
void setEndpointId(const String &endpointId)
|
||||
{
|
||||
// event["endpoint"]["endpointId"] = endpointId;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddHealthProp(EndpointHealth endpointHealth, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
JsonDocument healthValue;
|
||||
healthValue["value"] = (endpointHealth == EndpointHealth::OK) ? "OK" : "UNREACHABLE";
|
||||
return AddProperty(AlexaInterfaceType::ENDPOINT_HEALTH, "connectivity", healthValue.as<JsonObject>(), uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddPowerControllerProp(PowerController powerController, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
String value = (powerController == PowerController::ON) ? "ON" : "OFF";
|
||||
return AddProperty(AlexaInterfaceType::POWER_CONTROLLER, "powerState", value, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddTemperatureSensorProp(TemperatureSensorScale tempSensor,float value, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
JsonDocument tempValue;
|
||||
tempValue["scale"] = "CELSIUS";
|
||||
tempValue["value"] = value;
|
||||
if((tempSensor == TemperatureSensorScale::FAHRENHEIT)){
|
||||
tempValue["value"] = (value - 32) * 5.0 / 9.0;
|
||||
}
|
||||
return AddProperty(AlexaInterfaceType::TEMPERATURE_SENSOR, "temperature", tempValue.as<JsonObject>(), uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddBrightnessControllerProp(unsigned int brightness, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
return AddProperty(AlexaInterfaceType::BRIGHTNESS_CONTROLLER, "brightness", brightness, uncertaintyInMs);
|
||||
}
|
||||
AlexaStatusMessage &AddColorTemperatureControllerProp(unsigned int colorTemperature, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
return AddProperty(AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER, "colorTemperatureInKelvin", colorTemperature, uncertaintyInMs);
|
||||
}
|
||||
AlexaStatusMessage &AddToggleControllerProp(PowerController powerController,String instanceName, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
String value = (powerController == PowerController::ON) ? "ON" : "OFF";
|
||||
return AddProperty(AlexaInterfaceType::TOGGLE_CONTROLLER, "toggleState", value, uncertaintyInMs,instanceName);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AddContextProp(const JsonObject &property)
|
||||
{
|
||||
contextProperties.add(property);
|
||||
return *this; // Return a reference to the current object
|
||||
}
|
||||
|
||||
void send()
|
||||
{
|
||||
doc.shrinkToFit();
|
||||
serializeJson(doc, outputString);
|
||||
doc.clear();
|
||||
String topic = rootTopic + "/" + endpointId + "/alexaResponce";
|
||||
AlexaUtils::enqueue(outputString, topic.c_str());
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
String rootTopic;
|
||||
String endpointId;
|
||||
JsonDocument doc;
|
||||
JsonArray contextProperties;
|
||||
static char outputString[MAX_STATUS_REPORT_SIZE];
|
||||
|
||||
// TODO: make real uuid4 gen function
|
||||
String generateMessageId()
|
||||
{
|
||||
char buffer[38];
|
||||
const char charset[] = "0123456789abcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // Allowed characters
|
||||
|
||||
// Seed the random number generator (optional)
|
||||
srand(static_cast<unsigned int>(time(nullptr)));
|
||||
|
||||
// Generate 37 random characters
|
||||
for (int i = 0; i < 37; ++i)
|
||||
{
|
||||
buffer[i] = charset[rand() % (sizeof(charset) - 1)]; // Pick a random character
|
||||
}
|
||||
|
||||
buffer[37] = '\0'; // Null-terminate the string
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
AlexaStatusMessage &AddProperty(AlexaInterfaceType type, const String &propertyName, const T &value, unsigned int uncertaintyInMs = 0,const String &instanceName="")
|
||||
{
|
||||
JsonDocument prop;
|
||||
prop["namespace"] = AlexaInterfaceUtils::toString(type);
|
||||
prop["name"] = propertyName;
|
||||
|
||||
if (!instanceName.isEmpty()) {
|
||||
prop["instance"] = instanceName;
|
||||
}
|
||||
if constexpr (std::is_same_v<T, JsonObject>)
|
||||
{
|
||||
prop["value"] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
prop["value"] = value;
|
||||
}
|
||||
|
||||
prop["timeOfSample"] = "{REPLACE_WITH_DATETIME}";
|
||||
prop["uncertaintyInMilliseconds"] = uncertaintyInMs;
|
||||
|
||||
contextProperties.add(prop.as<JsonObject>());
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,220 +1,220 @@
|
|||
#include "AlexaUtils.h"
|
||||
|
||||
// #define Alex2ESP_DEBUG
|
||||
|
||||
uint8_t AlexaUtils::nextMessageId = 0;
|
||||
|
||||
char AlexaUtils::receiveQueue[MAX_QUEUE_LENGTH][16]={};
|
||||
char AlexaUtils::receivePayload[MAX_PAYLOAD_LENGTH];
|
||||
char AlexaUtils::topicQueue[MAX_QUEUE_LENGTH][MAX_TOPIC_LENGTH] = {};
|
||||
char AlexaUtils::packetQueue[MAX_QUEUE_LENGTH][MAX_PACKET_LENGTH] = {};
|
||||
char AlexaUtils::combinedData[MAX_TOPIC_LENGTH + MAX_PACKET_LENGTH + 32];
|
||||
|
||||
int AlexaUtils::queueStart = 0;
|
||||
int AlexaUtils::queueEnd = 0;
|
||||
int AlexaUtils::queueCount = 0;
|
||||
|
||||
|
||||
int AlexaUtils::queueStartReceive = 0;
|
||||
int AlexaUtils::queueEndReceive = 0;
|
||||
int AlexaUtils::queueCountReceive = 0;
|
||||
|
||||
// Enqueue a packet into the receive queue
|
||||
bool AlexaUtils::enqueueReceive(const char* packet) {
|
||||
if (isReceiveQueueFull()) {
|
||||
return false; // Queue is full
|
||||
}
|
||||
AlexaUtils::log("Enqueing: ");
|
||||
AlexaUtils::logln(packet);
|
||||
|
||||
// Copy the packet into the receive queue
|
||||
strncpy(receiveQueue[queueEndReceive], packet, sizeof(receiveQueue[0]) - 1);
|
||||
receiveQueue[queueEndReceive][sizeof(receiveQueue[0]) - 1] = '\0'; // Ensure null-termination
|
||||
|
||||
// Update the queueEnd and queueCount
|
||||
queueEndReceive = (queueEndReceive + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCountReceive++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dequeue a packet from the receive queue
|
||||
bool AlexaUtils::dequeueReceive(String& packet,bool remove) {
|
||||
if (isReceiveQueueEmpty()) {
|
||||
return false; // Queue is empty
|
||||
}
|
||||
|
||||
// Read the front of the receive queue
|
||||
packet = String(receiveQueue[queueStartReceive]);
|
||||
|
||||
if(remove){
|
||||
// Clear the dequeued slot (optional but good for debugging)
|
||||
memset(receiveQueue[queueStartReceive], 0, sizeof(receiveQueue[0]));
|
||||
|
||||
// Update the queueStart and queueCount
|
||||
queueStartReceive = (queueStartReceive + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCountReceive--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the receive queue is empty
|
||||
bool AlexaUtils::isReceiveQueueEmpty() {
|
||||
return queueCountReceive == 0;
|
||||
}
|
||||
|
||||
// Check if the receive queue is full
|
||||
bool AlexaUtils::isReceiveQueueFull() {
|
||||
return queueCountReceive == MAX_QUEUE_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
// Enqueue a topic and packet into the queue
|
||||
bool AlexaUtils::enqueue(const char* packet,const char* topic) {
|
||||
if (isQueueFull()) {
|
||||
return false; // Queue is full
|
||||
}
|
||||
|
||||
// Copy the topic and packet into the respective arrays
|
||||
strncpy(topicQueue[queueEnd], topic, MAX_TOPIC_LENGTH - 1);
|
||||
topicQueue[queueEnd][MAX_TOPIC_LENGTH - 1] = '\0'; // Ensure null-termination
|
||||
|
||||
strncpy(packetQueue[queueEnd], packet, MAX_PACKET_LENGTH - 1);
|
||||
packetQueue[queueEnd][MAX_PACKET_LENGTH - 1] = '\0'; // Ensure null-termination
|
||||
|
||||
// Update the queueEnd and queueCount
|
||||
queueEnd = (queueEnd + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCount++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dequeue a topic and packet from the queue
|
||||
bool AlexaUtils::dequeue(String& topic, String& packet) {
|
||||
if (isQueueEmpty()) {
|
||||
return false; // Queue is empty
|
||||
}
|
||||
|
||||
// Read the front of the queue
|
||||
topic = String(topicQueue[queueStart]);
|
||||
packet = String(packetQueue[queueStart]);
|
||||
|
||||
// Clear the dequeued slot (optional but good for debugging)
|
||||
memset(topicQueue[queueStart], 0, MAX_TOPIC_LENGTH);
|
||||
memset(packetQueue[queueStart], 0, MAX_PACKET_LENGTH);
|
||||
|
||||
// Update the queueStart and queueCount
|
||||
queueStart = (queueStart + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCount--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const char* AlexaUtils::dequeueVals(bool remove) {
|
||||
if (isQueueEmpty()) {
|
||||
return nullptr; // Queue is empty
|
||||
}
|
||||
|
||||
// Reset combinedData before use to ensure no leftover data from previous calls
|
||||
memset(combinedData, 0, sizeof(combinedData));
|
||||
|
||||
// Get lengths of the topic and packet
|
||||
int topicLen = strlen(topicQueue[queueStart]);
|
||||
int packetLen = strlen(packetQueue[queueStart]);
|
||||
|
||||
// Calculate available space in the combinedData buffer
|
||||
size_t availableSpace = sizeof(combinedData) - 1; // Reserve space for the null terminator
|
||||
|
||||
// Check if the combined data can fit in the buffer
|
||||
if ((topicLen + packetLen + strlen(MESSAGE_PAYLOAD_SPLIT)) > availableSpace) {
|
||||
// Return null if the combined data is too large for the buffer
|
||||
AlexaUtils::logln("Error: Combined data exceeds buffer size.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Concatenate the topic, separator, and packet into combinedData
|
||||
snprintf(combinedData, sizeof(combinedData), "%s%s%s", topicQueue[queueStart], MESSAGE_PAYLOAD_SPLIT, packetQueue[queueStart]);
|
||||
|
||||
if (remove) {
|
||||
// Clear the dequeued slot
|
||||
memset(topicQueue[queueStart], 0, MAX_TOPIC_LENGTH);
|
||||
memset(packetQueue[queueStart], 0, MAX_PACKET_LENGTH);
|
||||
|
||||
// Update the queueStart and queueCount
|
||||
queueStart = (queueStart + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCount--;
|
||||
}
|
||||
|
||||
// Return the pointer to the combined data
|
||||
return combinedData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Check if the queue is empty
|
||||
bool AlexaUtils::isQueueEmpty() {
|
||||
return queueCount == 0;
|
||||
}
|
||||
|
||||
// Check if the queue is full
|
||||
bool AlexaUtils::isQueueFull() {
|
||||
return queueCount == MAX_QUEUE_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AlexaUtils::log(const char *string) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.print(string);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::logln(const char *string) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(string);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::log(int num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.print(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::logln(int num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::log(uint16_t num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.print(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::logln(uint16_t num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::log(uint8_t num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.print(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::logln(uint8_t num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void AlexaUtils::logln(String string) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(string);
|
||||
#endif
|
||||
}
|
||||
#include "AlexaUtils.h"
|
||||
|
||||
// #define Alex2ESP_DEBUG
|
||||
|
||||
uint8_t AlexaUtils::nextMessageId = 0;
|
||||
|
||||
char AlexaUtils::receiveQueue[MAX_QUEUE_LENGTH][16]={};
|
||||
char AlexaUtils::receivePayload[MAX_PAYLOAD_LENGTH];
|
||||
char AlexaUtils::topicQueue[MAX_QUEUE_LENGTH][MAX_TOPIC_LENGTH] = {};
|
||||
char AlexaUtils::packetQueue[MAX_QUEUE_LENGTH][MAX_PACKET_LENGTH] = {};
|
||||
char AlexaUtils::combinedData[MAX_TOPIC_LENGTH + MAX_PACKET_LENGTH + 32];
|
||||
|
||||
int AlexaUtils::queueStart = 0;
|
||||
int AlexaUtils::queueEnd = 0;
|
||||
int AlexaUtils::queueCount = 0;
|
||||
|
||||
|
||||
int AlexaUtils::queueStartReceive = 0;
|
||||
int AlexaUtils::queueEndReceive = 0;
|
||||
int AlexaUtils::queueCountReceive = 0;
|
||||
|
||||
// Enqueue a packet into the receive queue
|
||||
bool AlexaUtils::enqueueReceive(const char* packet) {
|
||||
if (isReceiveQueueFull()) {
|
||||
return false; // Queue is full
|
||||
}
|
||||
AlexaUtils::log("Enqueing: ");
|
||||
AlexaUtils::logln(packet);
|
||||
|
||||
// Copy the packet into the receive queue
|
||||
strncpy(receiveQueue[queueEndReceive], packet, sizeof(receiveQueue[0]) - 1);
|
||||
receiveQueue[queueEndReceive][sizeof(receiveQueue[0]) - 1] = '\0'; // Ensure null-termination
|
||||
|
||||
// Update the queueEnd and queueCount
|
||||
queueEndReceive = (queueEndReceive + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCountReceive++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dequeue a packet from the receive queue
|
||||
bool AlexaUtils::dequeueReceive(String& packet,bool remove) {
|
||||
if (isReceiveQueueEmpty()) {
|
||||
return false; // Queue is empty
|
||||
}
|
||||
|
||||
// Read the front of the receive queue
|
||||
packet = String(receiveQueue[queueStartReceive]);
|
||||
|
||||
if(remove){
|
||||
// Clear the dequeued slot (optional but good for debugging)
|
||||
memset(receiveQueue[queueStartReceive], 0, sizeof(receiveQueue[0]));
|
||||
|
||||
// Update the queueStart and queueCount
|
||||
queueStartReceive = (queueStartReceive + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCountReceive--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the receive queue is empty
|
||||
bool AlexaUtils::isReceiveQueueEmpty() {
|
||||
return queueCountReceive == 0;
|
||||
}
|
||||
|
||||
// Check if the receive queue is full
|
||||
bool AlexaUtils::isReceiveQueueFull() {
|
||||
return queueCountReceive == MAX_QUEUE_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
// Enqueue a topic and packet into the queue
|
||||
bool AlexaUtils::enqueue(const char* packet,const char* topic) {
|
||||
if (isQueueFull()) {
|
||||
return false; // Queue is full
|
||||
}
|
||||
|
||||
// Copy the topic and packet into the respective arrays
|
||||
strncpy(topicQueue[queueEnd], topic, MAX_TOPIC_LENGTH - 1);
|
||||
topicQueue[queueEnd][MAX_TOPIC_LENGTH - 1] = '\0'; // Ensure null-termination
|
||||
|
||||
strncpy(packetQueue[queueEnd], packet, MAX_PACKET_LENGTH - 1);
|
||||
packetQueue[queueEnd][MAX_PACKET_LENGTH - 1] = '\0'; // Ensure null-termination
|
||||
|
||||
// Update the queueEnd and queueCount
|
||||
queueEnd = (queueEnd + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCount++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dequeue a topic and packet from the queue
|
||||
bool AlexaUtils::dequeue(String& topic, String& packet) {
|
||||
if (isQueueEmpty()) {
|
||||
return false; // Queue is empty
|
||||
}
|
||||
|
||||
// Read the front of the queue
|
||||
topic = String(topicQueue[queueStart]);
|
||||
packet = String(packetQueue[queueStart]);
|
||||
|
||||
// Clear the dequeued slot (optional but good for debugging)
|
||||
memset(topicQueue[queueStart], 0, MAX_TOPIC_LENGTH);
|
||||
memset(packetQueue[queueStart], 0, MAX_PACKET_LENGTH);
|
||||
|
||||
// Update the queueStart and queueCount
|
||||
queueStart = (queueStart + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCount--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const char* AlexaUtils::dequeueVals(bool remove) {
|
||||
if (isQueueEmpty()) {
|
||||
return nullptr; // Queue is empty
|
||||
}
|
||||
|
||||
// Reset combinedData before use to ensure no leftover data from previous calls
|
||||
memset(combinedData, 0, sizeof(combinedData));
|
||||
|
||||
// Get lengths of the topic and packet
|
||||
int topicLen = strlen(topicQueue[queueStart]);
|
||||
int packetLen = strlen(packetQueue[queueStart]);
|
||||
|
||||
// Calculate available space in the combinedData buffer
|
||||
size_t availableSpace = sizeof(combinedData) - 1; // Reserve space for the null terminator
|
||||
|
||||
// Check if the combined data can fit in the buffer
|
||||
if ((topicLen + packetLen + strlen(MESSAGE_PAYLOAD_SPLIT)) > availableSpace) {
|
||||
// Return null if the combined data is too large for the buffer
|
||||
AlexaUtils::logln("Error: Combined data exceeds buffer size.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Concatenate the topic, separator, and packet into combinedData
|
||||
snprintf(combinedData, sizeof(combinedData), "%s%s%s", topicQueue[queueStart], MESSAGE_PAYLOAD_SPLIT, packetQueue[queueStart]);
|
||||
|
||||
if (remove) {
|
||||
// Clear the dequeued slot
|
||||
memset(topicQueue[queueStart], 0, MAX_TOPIC_LENGTH);
|
||||
memset(packetQueue[queueStart], 0, MAX_PACKET_LENGTH);
|
||||
|
||||
// Update the queueStart and queueCount
|
||||
queueStart = (queueStart + 1) % MAX_QUEUE_LENGTH;
|
||||
queueCount--;
|
||||
}
|
||||
|
||||
// Return the pointer to the combined data
|
||||
return combinedData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Check if the queue is empty
|
||||
bool AlexaUtils::isQueueEmpty() {
|
||||
return queueCount == 0;
|
||||
}
|
||||
|
||||
// Check if the queue is full
|
||||
bool AlexaUtils::isQueueFull() {
|
||||
return queueCount == MAX_QUEUE_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AlexaUtils::log(const char *string) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.print(string);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::logln(const char *string) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(string);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::log(int num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.print(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::logln(int num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::log(uint16_t num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.print(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::logln(uint16_t num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::log(uint8_t num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.print(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AlexaUtils::logln(uint8_t num) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(num);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void AlexaUtils::logln(String string) {
|
||||
#ifdef Alex2ESP_DEBUG
|
||||
Serial.println(string);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
166
src/AlexaUtils.h
166
src/AlexaUtils.h
|
|
@ -1,83 +1,83 @@
|
|||
#ifndef ALEXA_UTILS_H
|
||||
#define ALEXA_UTILS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <Arduino.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
#include <cstring> // Include for strcpy and memset
|
||||
|
||||
class AlexaUtils
|
||||
{
|
||||
public:
|
||||
static const int MAX_PAYLOAD_LENGTH = 2048; // Maximum length for receive packets
|
||||
static char receivePayload[MAX_PAYLOAD_LENGTH];
|
||||
|
||||
static bool enqueueReceive(const char* packet);
|
||||
static bool dequeueReceive(String& packet,bool remove);
|
||||
static bool isReceiveQueueEmpty();
|
||||
static bool isReceiveQueueFull();
|
||||
|
||||
static bool enqueue(const char* topic, const char* packet);
|
||||
static bool dequeue(String& topic, String& packet);
|
||||
static bool isQueueEmpty();
|
||||
static bool isQueueFull();
|
||||
static const char* dequeueVals(bool remove);
|
||||
|
||||
static void log(const char *string);
|
||||
static void logln(const char *string);
|
||||
|
||||
static void log(int num);
|
||||
static void logln(int num);
|
||||
|
||||
static void log(uint16_t num);
|
||||
static void logln(uint16_t num);
|
||||
|
||||
static void log(uint8_t num);
|
||||
static void logln(uint8_t num);
|
||||
|
||||
static void logln(String string);
|
||||
|
||||
// Incrementing message ID
|
||||
static uint8_t nextMessageId;
|
||||
|
||||
// Static function to set the MQTT client reference
|
||||
static void printMemoryInfo()
|
||||
{
|
||||
size_t freeHeap = ESP.getFreeHeap();
|
||||
size_t freeStack = ESP.getFreeContStack(); // Requires ESP8266 core 3.0.0+
|
||||
size_t sketchSize = ESP.getSketchSize();
|
||||
size_t freeSketchSpace = ESP.getFreeSketchSpace();
|
||||
|
||||
// Print in custom format
|
||||
Serial.printf("freeHeap: %u, freeStack: %u, freeROM: %u, usedROM: %u\n",
|
||||
freeHeap,
|
||||
freeStack,
|
||||
freeSketchSpace,
|
||||
sketchSize);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static const int MAX_QUEUE_LENGTH = 5; // Define maximum queue length
|
||||
static const int MAX_TOPIC_LENGTH = 128; // Maximum length for topics
|
||||
static const int MAX_PACKET_LENGTH = 2048; // Maximum length for sending packets
|
||||
static constexpr char MESSAGE_PAYLOAD_SPLIT[32] = "{MESSAGE_PAYLOAD_SPLIT}";
|
||||
|
||||
|
||||
// Static arrays for topics and packets
|
||||
static char topicQueue[MAX_QUEUE_LENGTH][MAX_TOPIC_LENGTH];
|
||||
static char packetQueue[MAX_QUEUE_LENGTH][MAX_PACKET_LENGTH];
|
||||
static char combinedData[MAX_TOPIC_LENGTH + MAX_PACKET_LENGTH + 32];
|
||||
|
||||
static char receiveQueue[MAX_QUEUE_LENGTH][16];
|
||||
static int queueStart; // Index of the front of the queue
|
||||
static int queueEnd; // Index of the back of the queue
|
||||
static int queueCount; // Number of items in the queue
|
||||
|
||||
static int queueStartReceive;
|
||||
static int queueEndReceive;
|
||||
static int queueCountReceive;
|
||||
};
|
||||
|
||||
#endif // ALEXA_UTILS_H
|
||||
#ifndef ALEXA_UTILS_H
|
||||
#define ALEXA_UTILS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <Arduino.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
#include <cstring> // Include for strcpy and memset
|
||||
|
||||
class AlexaUtils
|
||||
{
|
||||
public:
|
||||
static const int MAX_PAYLOAD_LENGTH = 2048; // Maximum length for receive packets
|
||||
static char receivePayload[MAX_PAYLOAD_LENGTH];
|
||||
|
||||
static bool enqueueReceive(const char* packet);
|
||||
static bool dequeueReceive(String& packet,bool remove);
|
||||
static bool isReceiveQueueEmpty();
|
||||
static bool isReceiveQueueFull();
|
||||
|
||||
static bool enqueue(const char* topic, const char* packet);
|
||||
static bool dequeue(String& topic, String& packet);
|
||||
static bool isQueueEmpty();
|
||||
static bool isQueueFull();
|
||||
static const char* dequeueVals(bool remove);
|
||||
|
||||
static void log(const char *string);
|
||||
static void logln(const char *string);
|
||||
|
||||
static void log(int num);
|
||||
static void logln(int num);
|
||||
|
||||
static void log(uint16_t num);
|
||||
static void logln(uint16_t num);
|
||||
|
||||
static void log(uint8_t num);
|
||||
static void logln(uint8_t num);
|
||||
|
||||
static void logln(String string);
|
||||
|
||||
// Incrementing message ID
|
||||
static uint8_t nextMessageId;
|
||||
|
||||
// Static function to set the MQTT client reference
|
||||
static void printMemoryInfo()
|
||||
{
|
||||
size_t freeHeap = ESP.getFreeHeap();
|
||||
size_t freeStack = ESP.getFreeContStack(); // Requires ESP8266 core 3.0.0+
|
||||
size_t sketchSize = ESP.getSketchSize();
|
||||
size_t freeSketchSpace = ESP.getFreeSketchSpace();
|
||||
|
||||
// Print in custom format
|
||||
Serial.printf("freeHeap: %u, freeStack: %u, freeROM: %u, usedROM: %u\n",
|
||||
freeHeap,
|
||||
freeStack,
|
||||
freeSketchSpace,
|
||||
sketchSize);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static const int MAX_QUEUE_LENGTH = 5; // Define maximum queue length
|
||||
static const int MAX_TOPIC_LENGTH = 128; // Maximum length for topics
|
||||
static const int MAX_PACKET_LENGTH = 2048; // Maximum length for sending packets
|
||||
static constexpr char MESSAGE_PAYLOAD_SPLIT[32] = "{MESSAGE_PAYLOAD_SPLIT}";
|
||||
|
||||
|
||||
// Static arrays for topics and packets
|
||||
static char topicQueue[MAX_QUEUE_LENGTH][MAX_TOPIC_LENGTH];
|
||||
static char packetQueue[MAX_QUEUE_LENGTH][MAX_PACKET_LENGTH];
|
||||
static char combinedData[MAX_TOPIC_LENGTH + MAX_PACKET_LENGTH + 32];
|
||||
|
||||
static char receiveQueue[MAX_QUEUE_LENGTH][16];
|
||||
static int queueStart; // Index of the front of the queue
|
||||
static int queueEnd; // Index of the back of the queue
|
||||
static int queueCount; // Number of items in the queue
|
||||
|
||||
static int queueStartReceive;
|
||||
static int queueEndReceive;
|
||||
static int queueCountReceive;
|
||||
};
|
||||
|
||||
#endif // ALEXA_UTILS_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue