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