mirror of
https://github.com/HASwitchPlate/openHASP.git
synced 2025-07-27 13:16:45 +00:00
Dispatcher rewrite
This commit is contained in:
parent
3d5d77928a
commit
8662935676
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#define HASP_VERSION_MAJOR 0
|
#define HASP_VERSION_MAJOR 0
|
||||||
#define HASP_VERSION_MINOR 2
|
#define HASP_VERSION_MINOR 2
|
||||||
#define HASP_VERSION_REVISION 1027
|
#define HASP_VERSION_REVISION 1108
|
||||||
|
|
||||||
#define HASP_USE_APP 1
|
#define HASP_USE_APP 1
|
||||||
|
|
||||||
|
@ -122,6 +122,7 @@ lv_obj_t * get_page(uint8_t pageid)
|
|||||||
if(pageid >= sizeof pages / sizeof *pages) return NULL;
|
if(pageid >= sizeof pages / sizeof *pages) return NULL;
|
||||||
return pages[pageid];
|
return pages[pageid];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_page_id(lv_obj_t * obj, uint8_t * pageid)
|
bool get_page_id(lv_obj_t * obj, uint8_t * pageid)
|
||||||
{
|
{
|
||||||
lv_obj_t * page = lv_obj_get_screen(obj);
|
lv_obj_t * page = lv_obj_get_screen(obj);
|
||||||
@ -671,7 +672,7 @@ String haspGetVersion()
|
|||||||
void haspClearPage(uint16_t pageid)
|
void haspClearPage(uint16_t pageid)
|
||||||
{
|
{
|
||||||
lv_obj_t * page = get_page(pageid);
|
lv_obj_t * page = get_page(pageid);
|
||||||
if(!page) {
|
if(!page || pageid > 255) {
|
||||||
Log.warning(F("HASP: Page ID %u not defined"), pageid);
|
Log.warning(F("HASP: Page ID %u not defined"), pageid);
|
||||||
} else if(page == lv_layer_sys() /*|| page == lv_layer_top()*/) {
|
} else if(page == lv_layer_sys() /*|| page == lv_layer_top()*/) {
|
||||||
Log.warning(F("HASP: Cannot clear system layer"));
|
Log.warning(F("HASP: Cannot clear system layer"));
|
||||||
@ -946,7 +947,7 @@ void haspLoadPage(const char * pages)
|
|||||||
Log.notice(F("HASP: Loading file %s"), pages);
|
Log.notice(F("HASP: Loading file %s"), pages);
|
||||||
|
|
||||||
File file = SPIFFS.open(pages, "r");
|
File file = SPIFFS.open(pages, "r");
|
||||||
dispatchJsonl(file);
|
dispatchParseJsonl(file);
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
Log.notice(F("HASP: File %s loaded"), pages);
|
Log.notice(F("HASP: File %s loaded"), pages);
|
||||||
|
@ -392,18 +392,18 @@ void debugLoop()
|
|||||||
Serial.print(ch);
|
Serial.print(ch);
|
||||||
if(ch == 13 || ch == 10) {
|
if(ch == 13 || ch == 10) {
|
||||||
serialInputBuffer[serialInputIndex] = 0;
|
serialInputBuffer[serialInputIndex] = 0;
|
||||||
if(serialInputIndex > 0) dispatchCommand(serialInputBuffer);
|
if(serialInputIndex > 0) dispatchTextLine(serialInputBuffer);
|
||||||
serialInputIndex = 0;
|
serialInputIndex = 0;
|
||||||
} else {
|
} else {
|
||||||
if(serialInputIndex < sizeof(serialInputBuffer) - 1) {
|
if(serialInputIndex < sizeof(serialInputBuffer) - 1) {
|
||||||
serialInputBuffer[serialInputIndex++] = ch;
|
serialInputBuffer[serialInputIndex++] = ch;
|
||||||
}
|
}
|
||||||
serialInputBuffer[serialInputIndex] = 0;
|
// if(strcmp(serialInputBuffer, "jsonl=") == 0) {
|
||||||
if(strcmp(serialInputBuffer, "jsonl=") == 0) {
|
// dispatchJsonl(Serial);
|
||||||
dispatchJsonl(Serial);
|
// serialInputIndex = 0;
|
||||||
serialInputIndex = 0;
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
serialInputBuffer[serialInputIndex] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,7 +448,7 @@ void debugLoop()
|
|||||||
void debugEverySecond()
|
void debugEverySecond()
|
||||||
{
|
{
|
||||||
if(debugTelePeriod > 0 && (millis() - debugLastMillis) >= debugTelePeriod * 1000) {
|
if(debugTelePeriod > 0 && (millis() - debugLastMillis) >= debugTelePeriod * 1000) {
|
||||||
dispatchStatusUpdate();
|
dispatch_output_statusupdate();
|
||||||
debugLastMillis = millis();
|
debugLastMillis = millis();
|
||||||
}
|
}
|
||||||
// printLocalTime();
|
// printLocalTime();
|
||||||
|
@ -13,43 +13,47 @@
|
|||||||
|
|
||||||
#include "hasp_conf.h"
|
#include "hasp_conf.h"
|
||||||
|
|
||||||
inline void dispatchPrintln(String header, String & data)
|
inline bool isON(const char * payload)
|
||||||
{
|
|
||||||
Log.notice(F("%s: %s"), header.c_str(), data.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isON(const char * payload)
|
|
||||||
{
|
{
|
||||||
return strcasecmp_P(payload, PSTR("ON")) == 0;
|
return strcasecmp_P(payload, PSTR("ON")) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getOnOff(bool state)
|
|
||||||
{
|
|
||||||
String result((char *)0);
|
|
||||||
result.reserve(128);
|
|
||||||
result = state ? F("ON") : F("OFF");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispatchSetup()
|
void dispatchSetup()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void dispatchLoop()
|
void dispatchLoop()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void dispatchStatusUpdate()
|
// Send status update message to the client
|
||||||
|
void dispatch_output_statusupdate()
|
||||||
{
|
{
|
||||||
#if HASP_USE_MQTT>0
|
#if HASP_USE_MQTT > 0
|
||||||
mqtt_send_statusupdate();
|
mqtt_send_statusupdate();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchOutput(int output, bool state)
|
// Format filesystem and erase EEPROM
|
||||||
|
bool dispatch_factory_reset()
|
||||||
|
{
|
||||||
|
bool formated, erased = true;
|
||||||
|
|
||||||
|
#if HASP_USE_SPIFFS > 0
|
||||||
|
formated = SPIFFS.format();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HASP_USE_EEPROM > 0
|
||||||
|
erased = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return formated && erased;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispatchGpioOutput(int output, bool state)
|
||||||
{
|
{
|
||||||
int pin = 0;
|
int pin = 0;
|
||||||
|
|
||||||
if(pin >= 0) {
|
if(pin >= 0) {
|
||||||
Log.notice(F("PIN OUTPUT STATE %d"),state);
|
Log.notice(F("PIN OUTPUT STATE %d"), state);
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
ledcWrite(99, state ? 1023 : 0); // ledChannel and value
|
ledcWrite(99, state ? 1023 : 0); // ledChannel and value
|
||||||
@ -62,16 +66,63 @@ void dispatchOutput(int output, bool state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchOutput(String strTopic, const char * payload)
|
void dispatchGpioOutput(String strTopic, const char * payload)
|
||||||
{
|
{
|
||||||
String strTemp((char *)0);
|
String strTemp((char *)0);
|
||||||
strTemp.reserve(128);
|
strTemp.reserve(128);
|
||||||
strTemp = strTopic.substring(7, strTopic.length());
|
strTemp = strTopic.substring(7, strTopic.length());
|
||||||
dispatchOutput(strTemp.toInt(), isON(payload));
|
dispatchGpioOutput(strTemp.toInt(), isON(payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchButtonAttribute(String & strTopic, const char * payload)
|
void dispatchParseJson(char * payload)
|
||||||
|
{ // Parse an incoming JSON array into individual commands
|
||||||
|
/* if(strPayload.endsWith(",]")) {
|
||||||
|
// Trailing null array elements are an artifact of older Home Assistant automations
|
||||||
|
// and need to be removed before parsing by ArduinoJSON 6+
|
||||||
|
strPayload.remove(strPayload.length() - 2, 2);
|
||||||
|
strPayload.concat("]");
|
||||||
|
}*/
|
||||||
|
size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 256;
|
||||||
|
DynamicJsonDocument haspCommands(maxsize);
|
||||||
|
DeserializationError jsonError = deserializeJson(haspCommands, payload);
|
||||||
|
// haspCommands.shrinkToFit();
|
||||||
|
|
||||||
|
if(jsonError) { // Couldn't parse incoming JSON command
|
||||||
|
Log.warning(F("JSON: Failed to parse incoming JSON command with error: %s"), jsonError.c_str());
|
||||||
|
} else {
|
||||||
|
|
||||||
|
JsonArray arr = haspCommands.as<JsonArray>();
|
||||||
|
for(JsonVariant command : arr) {
|
||||||
|
dispatchTextLine(command.as<String>().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispatchParseJsonl(Stream & stream)
|
||||||
{
|
{
|
||||||
|
DynamicJsonDocument jsonl(3 * 128u);
|
||||||
|
uint8_t savedPage = haspGetPage();
|
||||||
|
|
||||||
|
// Log.notice(F("DISPATCH: jsonl"));
|
||||||
|
|
||||||
|
while(deserializeJson(jsonl, stream) == DeserializationError::Ok) {
|
||||||
|
// serializeJson(jsonl, Serial);
|
||||||
|
// Serial.println();
|
||||||
|
haspNewObject(jsonl.as<JsonObject>(), savedPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispatchParseJsonl(char * payload)
|
||||||
|
{
|
||||||
|
CharStream stream(payload);
|
||||||
|
dispatchParseJsonl(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// p[x].b[y]=value
|
||||||
|
inline void dispatch_process_button_attribute(String strTopic, const char * payload)
|
||||||
|
{
|
||||||
|
// Log.trace(F("BTN ATTR: %s = %s"), strTopic.c_str(), payload);
|
||||||
|
|
||||||
String strPageId((char *)0);
|
String strPageId((char *)0);
|
||||||
String strTemp((char *)0);
|
String strTemp((char *)0);
|
||||||
|
|
||||||
@ -96,199 +147,246 @@ void dispatchButtonAttribute(String & strTopic, const char * payload)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// objectattribute=value
|
// objectattribute=value
|
||||||
void dispatchAttribute(String strTopic, const char * payload)
|
void dispatchCommand(const char * topic, const char * payload)
|
||||||
{
|
{
|
||||||
if(strTopic.startsWith("p[")) {
|
if(!strcmp_P(topic, PSTR("json"))) { // '[...]/device/command/json' -m '["dim=5", "page 1"]' =
|
||||||
dispatchButtonAttribute(strTopic, payload);
|
// nextionSendCmd("dim=50"), nextionSendCmd("page 1")
|
||||||
|
dispatchParseJson((char *)payload);
|
||||||
|
|
||||||
} else if(strTopic == F("page")) {
|
} else if(!strcmp_P(topic, PSTR("jsonl"))) {
|
||||||
|
dispatchParseJsonl((char *)payload);
|
||||||
|
|
||||||
|
} else if(!strcmp_P(topic, PSTR("page"))) {
|
||||||
dispatchPage(payload);
|
dispatchPage(payload);
|
||||||
|
|
||||||
} else if(strTopic == F("dim") || strTopic == F("brightness")) {
|
} else if(!strcmp_P(topic, PSTR("dim")) || !strcmp_P(topic, PSTR("brightness"))) {
|
||||||
dispatchDim(payload);
|
dispatchDim(payload);
|
||||||
|
|
||||||
} else if(strTopic == F("light")) {
|
} else if(!strcmp_P(topic, PSTR("light"))) {
|
||||||
dispatchBacklight(payload);
|
dispatchBacklight(payload);
|
||||||
|
|
||||||
} else if(strTopic == F("reboot") || strTopic == F("restart")) {
|
} else if(!strcmp_P(topic, PSTR("reboot")) || !strcmp_P(topic, PSTR("restart"))) {
|
||||||
dispatchReboot(true);
|
dispatchReboot(true);
|
||||||
|
|
||||||
} else if(strTopic == F("clearpage")) {
|
} else if(!strcmp_P(topic, PSTR("factoryreset"))) {
|
||||||
|
dispatch_factory_reset();
|
||||||
|
delay(250);
|
||||||
|
dispatchReboot(false); // don't save config
|
||||||
|
|
||||||
|
} else if(!strcmp_P(topic, PSTR("clearpage"))) {
|
||||||
dispatchClearPage(payload);
|
dispatchClearPage(payload);
|
||||||
|
|
||||||
} else if(strTopic == F("update")) {
|
} else if(!strcmp_P(topic, PSTR("update"))) {
|
||||||
dispatchWebUpdate(payload);
|
dispatchWebUpdate(payload);
|
||||||
|
|
||||||
} else if(strTopic == F("setupap")) {
|
} else if(!strcmp_P(topic, PSTR("setupap"))) {
|
||||||
oobeFakeSetup();
|
oobeFakeSetup();
|
||||||
|
|
||||||
} else if(strTopic.length() == 7 && strTopic.startsWith(F("output"))) {
|
} else if(strlen(topic) == 7 && topic == strstr_P(topic, PSTR("output"))) {
|
||||||
dispatchOutput(strTopic, payload);
|
dispatchGpioOutput(topic, payload);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispatchPage(String strPageid)
|
} else if(strcasecmp_P(topic, PSTR("calibrate")) == 0) {
|
||||||
{
|
guiCalibrate();
|
||||||
dispatchPrintln(F("PAGE"), strPageid);
|
|
||||||
|
} else if(strcasecmp_P(topic, PSTR("wakeup")) == 0) {
|
||||||
|
haspWakeUp();
|
||||||
|
|
||||||
|
} else if(strcasecmp_P(topic, PSTR("screenshot")) == 0) {
|
||||||
|
guiTakeScreenshot("/screenhot.bmp");
|
||||||
|
|
||||||
|
} else if(strcasecmp_P(topic, PSTR("")) == 0 || strcasecmp_P(topic, PSTR("statusupdate")) == 0) {
|
||||||
|
dispatch_output_statusupdate();
|
||||||
|
|
||||||
|
} else if(topic == strstr_P(topic, PSTR("p["))) {
|
||||||
|
dispatch_process_button_attribute(topic, payload);
|
||||||
|
|
||||||
|
} else if(!strcmp_P(topic, F_CONFIG_SSID) || !strcmp_P(topic, F_CONFIG_PASS)) {
|
||||||
|
DynamicJsonDocument settings(45);
|
||||||
|
settings[topic] = payload;
|
||||||
|
wifiSetConfig(settings.as<JsonObject>());
|
||||||
|
|
||||||
|
} else if(!strcmp_P(topic, PSTR("mqtthost")) || !strcmp_P(topic, PSTR("mqttport")) ||
|
||||||
|
!strcmp_P(topic, PSTR("mqttuser")) || !strcmp_P(topic, PSTR("mqttpass"))) {
|
||||||
|
char item[5];
|
||||||
|
memset(item, 0, sizeof(item));
|
||||||
|
strncpy(item, topic + 4, 4);
|
||||||
|
DynamicJsonDocument settings(45);
|
||||||
|
settings[item] = payload;
|
||||||
|
mqttSetConfig(settings.as<JsonObject>());
|
||||||
|
|
||||||
if(strPageid.length() == 0) {
|
|
||||||
} else {
|
} else {
|
||||||
if(strPageid.toInt() <= 250) haspSetPage(strPageid.toInt());
|
if(strlen(payload) == 0) {
|
||||||
|
// dispatchTextLine(topic); // Could cause an infinite loop!
|
||||||
|
}
|
||||||
|
Log.warning(F(LOG_CMND_CTR "Command not found %s => %s"), topic, payload);
|
||||||
}
|
}
|
||||||
String strPage((char *)0);
|
}
|
||||||
strPage.reserve(128);
|
|
||||||
strPage = haspGetPage();
|
// Get or Set a page
|
||||||
|
void dispatchPage(const char * page)
|
||||||
|
{
|
||||||
|
// dispatchPrintln(F("PAGE"), strPageid);
|
||||||
|
|
||||||
|
if(strlen(page) > 0 && atoi(page) <= 250) {
|
||||||
|
haspSetPage(atoi(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log result
|
||||||
|
char buffer[4];
|
||||||
|
itoa(haspGetPage(), buffer, DEC);
|
||||||
|
|
||||||
#if HASP_USE_MQTT > 0
|
#if HASP_USE_MQTT > 0
|
||||||
mqtt_send_state(F("page"), strPage.c_str());
|
mqtt_send_state(F("page"), buffer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HASP_USE_TASMOTA_SLAVE > 0
|
#if HASP_USE_TASMOTA_SLAVE > 0
|
||||||
slave_send_state(F("page"), strPage.c_str());
|
slave_send_state(F("page"), buffer);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchClearPage(String strPageid)
|
// Clears a page id or the current page if empty
|
||||||
|
void dispatchClearPage(const char * page)
|
||||||
{
|
{
|
||||||
dispatchPrintln(F("CLEAR"), strPageid);
|
if(strlen(page) == 0) {
|
||||||
|
|
||||||
if(strPageid.length() == 0) {
|
|
||||||
haspClearPage(haspGetPage());
|
haspClearPage(haspGetPage());
|
||||||
} else {
|
} else {
|
||||||
haspClearPage(strPageid.toInt());
|
haspClearPage(atoi(page));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchDim(String strDimLevel)
|
void dispatchDim(const char * level)
|
||||||
{
|
{
|
||||||
// Set the current state
|
// Set the current state
|
||||||
if(strDimLevel.length() != 0) guiSetDim(strDimLevel.toInt());
|
if(strlen(level) != 0) guiSetDim(atoi(level));
|
||||||
dispatchPrintln(F("DIM"), strDimLevel);
|
// dispatchPrintln(F("DIM"), strDimLevel);
|
||||||
char buffer[8];
|
char buffer[4];
|
||||||
|
|
||||||
#if defined(HASP_USE_MQTT) || defined(HASP_USE_TASMOTA_SLAVE)
|
#if defined(HASP_USE_MQTT) || defined(HASP_USE_TASMOTA_SLAVE)
|
||||||
itoa(guiGetDim(), buffer, DEC);
|
itoa(guiGetDim(), buffer, DEC);
|
||||||
|
|
||||||
#if HASP_USE_MQTT > 0
|
#if HASP_USE_MQTT > 0
|
||||||
mqtt_send_state(F("dim"), buffer);
|
mqtt_send_state(F("dim"), buffer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HASP_USE_TASMOTA_SLAVE > 0
|
#if HASP_USE_TASMOTA_SLAVE > 0
|
||||||
slave_send_state(F("dim"), buffer);
|
slave_send_state(F("dim"), buffer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchBacklight(String strPayload)
|
void dispatchBacklight(const char * payload)
|
||||||
{
|
{
|
||||||
strPayload.toUpperCase();
|
// strPayload.toUpperCase();
|
||||||
dispatchPrintln(F("LIGHT"), strPayload);
|
// dispatchPrintln(F("LIGHT"), strPayload);
|
||||||
|
|
||||||
// Set the current state
|
// Set the current state
|
||||||
if(strPayload.length() != 0) guiSetBacklight(isON(strPayload.c_str()));
|
if(strlen(payload) != 0) guiSetBacklight(isON(payload));
|
||||||
|
|
||||||
// Return the current state
|
// Return the current state
|
||||||
strPayload = getOnOff(guiGetBacklight());
|
|
||||||
#if HASP_USE_MQTT > 0
|
#if HASP_USE_MQTT > 0
|
||||||
mqtt_send_state(F("light"), strPayload.c_str());
|
mqtt_send_state(F("light"), guiGetBacklight() ? PSTR("ON") : PSTR("OFF"));
|
||||||
#endif
|
|
||||||
#if HASP_USE_TASMOTA_SLAVE > 0
|
|
||||||
slave_send_state(F("light"), strPayload.c_str());
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if HASP_USE_TASMOTA_SLAVE > 0
|
||||||
|
slave_send_state(F("light"), guiGetBacklight() ? PSTR("ON") : PSTR("OFF"));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchCommand(String cmnd)
|
// Strip command/config prefix from the topic and process the payload
|
||||||
|
void dispatchTopicPayload(const char * topic, const char * payload)
|
||||||
{
|
{
|
||||||
dispatchPrintln(F("CMND"), cmnd);
|
// Log.trace(F("TOPIC: short topic: %s"), topic);
|
||||||
|
|
||||||
|
if(!strcmp_P(topic, PSTR("command"))) {
|
||||||
|
dispatchTextLine((char *)payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(topic == strstr_P(topic, PSTR("command/"))) { // startsWith command/
|
||||||
|
topic += 8u;
|
||||||
|
// Log.trace(F("MQTT IN: command subtopic: %s"), topic);
|
||||||
|
|
||||||
|
// '[...]/device/command/p[1].b[4].txt' -m '"Lights On"' ==
|
||||||
|
// nextionSetAttr("p[1].b[4].txt", "\"Lights On\"")
|
||||||
|
dispatchCommand(topic, (char *)payload);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(topic == strstr_P(topic, PSTR("config/"))) { // startsWith command/
|
||||||
|
topic += 7u;
|
||||||
|
dispatchConfig(topic, (char *)payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchCommand(topic, (char *)payload); // dispatch as is
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse one line of text and execute the command(s)
|
||||||
|
void dispatchTextLine(const char * cmnd)
|
||||||
|
{
|
||||||
|
// dispatchPrintln(F("CMND"), cmnd);
|
||||||
|
|
||||||
|
if(cmnd == strstr_P(cmnd, PSTR("page "))) { // startsWith command/
|
||||||
|
dispatchPage(cmnd + 5);
|
||||||
|
|
||||||
if(cmnd.startsWith(F("page "))) {
|
|
||||||
cmnd = cmnd.substring(5, cmnd.length());
|
|
||||||
String strTopic((char *)0);
|
|
||||||
strTopic.reserve(128);
|
|
||||||
strTopic = F("page");
|
|
||||||
dispatchAttribute(strTopic, cmnd.c_str());
|
|
||||||
} else if(cmnd == F("calibrate")) {
|
|
||||||
guiCalibrate();
|
|
||||||
} else if(cmnd == F("wakeup")) {
|
|
||||||
haspWakeUp();
|
|
||||||
} else if(cmnd == F("screenshot")) {
|
|
||||||
// guiTakeScreenshot("/screenhot.bmp");
|
|
||||||
} else if(cmnd == F("") || cmnd == F("statusupdate")) {
|
|
||||||
dispatchStatusUpdate();
|
|
||||||
} else {
|
} else {
|
||||||
|
size_t pos1 = std::string(cmnd).find("=");
|
||||||
|
size_t pos2 = std::string(cmnd).find(" ");
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
if(pos1 != std::string::npos) {
|
||||||
|
if(pos2 != std::string::npos) {
|
||||||
|
pos = (pos1 < pos2 ? pos1 : pos2);
|
||||||
|
} else {
|
||||||
|
pos = pos1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(pos2 != std::string::npos) {
|
||||||
|
pos = pos2;
|
||||||
|
} else {
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int pos = cmnd.indexOf("=");
|
|
||||||
if(pos > 0) {
|
if(pos > 0) {
|
||||||
String strTopic((char *)0);
|
String strTopic((char *)0);
|
||||||
String strPayload((char *)0);
|
// String strPayload((char *)0);
|
||||||
|
|
||||||
strTopic.reserve(128);
|
strTopic.reserve(pos + 1);
|
||||||
strPayload.reserve(128);
|
// strPayload.reserve(128);
|
||||||
|
|
||||||
strTopic = cmnd.substring(0, pos);
|
strTopic = String(cmnd).substring(0, pos);
|
||||||
strPayload = cmnd.substring(pos + 1, cmnd.length());
|
// strPayload = cmnd.substring(pos + 1, cmnd.length());
|
||||||
|
// cmnd[pos] = 0; // change '=' character into '\0'
|
||||||
|
|
||||||
dispatchAttribute(strTopic, strPayload.c_str());
|
dispatchTopicPayload(strTopic.c_str(),
|
||||||
|
cmnd + pos + 1); // topic is before '=', payload is after '=' position
|
||||||
} else {
|
} else {
|
||||||
dispatchAttribute(cmnd, "");
|
dispatchTopicPayload(cmnd, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchJson(char * payload)
|
// send idle state to the client
|
||||||
{ // Parse an incoming JSON array into individual commands
|
void dispatch_output_idle_state(const char * state)
|
||||||
/* if(strPayload.endsWith(",]")) {
|
|
||||||
// Trailing null array elements are an artifact of older Home Assistant automations
|
|
||||||
// and need to be removed before parsing by ArduinoJSON 6+
|
|
||||||
strPayload.remove(strPayload.length() - 2, 2);
|
|
||||||
strPayload.concat("]");
|
|
||||||
}*/
|
|
||||||
size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 256;
|
|
||||||
DynamicJsonDocument haspCommands(maxsize);
|
|
||||||
DeserializationError jsonError = deserializeJson(haspCommands, payload);
|
|
||||||
// haspCommands.shrinkToFit();
|
|
||||||
|
|
||||||
if(jsonError) { // Couldn't parse incoming JSON command
|
|
||||||
Log.warning(F("JSON: Failed to parse incoming JSON command with error: %s"), jsonError.c_str());
|
|
||||||
} else {
|
|
||||||
|
|
||||||
JsonArray arr = haspCommands.as<JsonArray>();
|
|
||||||
for(JsonVariant command : arr) {
|
|
||||||
dispatchCommand(command.as<String>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispatchJsonl(Stream & stream)
|
|
||||||
{
|
|
||||||
DynamicJsonDocument jsonl(3 * 128u);
|
|
||||||
uint8_t savedPage = haspGetPage();
|
|
||||||
|
|
||||||
Log.notice(F("DISPATCH: jsonl"));
|
|
||||||
|
|
||||||
while(deserializeJson(jsonl, stream) == DeserializationError::Ok) {
|
|
||||||
// serializeJson(jsonl, Serial);
|
|
||||||
// Serial.println();
|
|
||||||
haspNewObject(jsonl.as<JsonObject>(), savedPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispatchJsonl(char * payload)
|
|
||||||
{
|
|
||||||
CharStream stream(payload);
|
|
||||||
dispatchJsonl(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispatchIdle(const char * state)
|
|
||||||
{
|
{
|
||||||
#if !defined(HASP_USE_MQTT) && !defined(HASP_USE_TASMOTA_SLAVE)
|
#if !defined(HASP_USE_MQTT) && !defined(HASP_USE_TASMOTA_SLAVE)
|
||||||
Log.notice(F("OUT: idle = %s"), state);
|
Log.notice(F("OUT: idle = %s"), state);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#if HASP_USE_MQTT > 0
|
#if HASP_USE_MQTT > 0
|
||||||
mqtt_send_state(F("idle"), state);
|
mqtt_send_state(F("idle"), state);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HASP_USE_TASMOTA_SLAVE > 0
|
#if HASP_USE_TASMOTA_SLAVE > 0
|
||||||
slave_send_state(F("idle"), state);
|
slave_send_state(F("idle"), state);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restart the device
|
||||||
void dispatchReboot(bool saveConfig)
|
void dispatchReboot(bool saveConfig)
|
||||||
{
|
{
|
||||||
if(saveConfig) configWriteConfig();
|
if(saveConfig) configWriteConfig();
|
||||||
@ -313,7 +411,7 @@ void dispatch_button(uint8_t id, const char * event)
|
|||||||
#if HASP_USE_MQTT > 0
|
#if HASP_USE_MQTT > 0
|
||||||
mqtt_send_input(id, event);
|
mqtt_send_input(id, event);
|
||||||
#endif
|
#endif
|
||||||
#if HASP_USE_TASMOTA_SLAVE>0
|
#if HASP_USE_TASMOTA_SLAVE > 0
|
||||||
slave_send_input(id, event);
|
slave_send_input(id, event);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@ -327,6 +425,7 @@ void dispatchWebUpdate(const char * espOtaUrl)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send return output back to the client
|
||||||
void IRAM_ATTR dispatch_obj_attribute_str(uint8_t pageid, uint8_t btnid, const char * attribute, const char * data)
|
void IRAM_ATTR dispatch_obj_attribute_str(uint8_t pageid, uint8_t btnid, const char * attribute, const char * data)
|
||||||
{
|
{
|
||||||
#if !defined(HASP_USE_MQTT) && !defined(HASP_USE_TASMOTA_SLAVE)
|
#if !defined(HASP_USE_MQTT) && !defined(HASP_USE_TASMOTA_SLAVE)
|
||||||
@ -341,6 +440,7 @@ void IRAM_ATTR dispatch_obj_attribute_str(uint8_t pageid, uint8_t btnid, const c
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get or Set a part of the config.json file
|
||||||
void dispatchConfig(const char * topic, const char * payload)
|
void dispatchConfig(const char * topic, const char * payload)
|
||||||
{
|
{
|
||||||
DynamicJsonDocument doc(128 * 2);
|
DynamicJsonDocument doc(128 * 2);
|
||||||
@ -426,7 +526,7 @@ void dispatchConfig(const char * topic, const char * payload)
|
|||||||
settings.remove(F("pass")); // hide password in output
|
settings.remove(F("pass")); // hide password in output
|
||||||
size_t size = serializeJson(doc, buffer, sizeof(buffer));
|
size_t size = serializeJson(doc, buffer, sizeof(buffer));
|
||||||
#if !defined(HASP_USE_MQTT) && !defined(HASP_USE_TASMOTA_SLAVE)
|
#if !defined(HASP_USE_MQTT) && !defined(HASP_USE_TASMOTA_SLAVE)
|
||||||
Log.notice(F("OUT: config %s = %s"),topic,buffer);
|
Log.notice(F("OUT: config %s = %s"), topic, buffer);
|
||||||
#else
|
#else
|
||||||
#if HASP_USE_MQTT > 0
|
#if HASP_USE_MQTT > 0
|
||||||
mqtt_send_state(F("config"), buffer);
|
mqtt_send_state(F("config"), buffer);
|
||||||
|
@ -3,26 +3,29 @@
|
|||||||
|
|
||||||
#include "ArduinoJson.h"
|
#include "ArduinoJson.h"
|
||||||
|
|
||||||
|
#define LOG_CMND_CTR "CMND: "
|
||||||
|
|
||||||
void dispatchSetup(void);
|
void dispatchSetup(void);
|
||||||
void dispatchLoop(void);
|
void dispatchLoop(void);
|
||||||
|
|
||||||
void dispatchAttribute(String strTopic, const char * strPayload);
|
// void dispatchCommand(const char * topic, const char * payload); // intenal
|
||||||
void dispatchCommand(String cmnd);
|
|
||||||
void dispatchConfig(const char * topic, const char * payload);
|
void dispatchConfig(const char * topic, const char * payload);
|
||||||
|
void dispatchTopicPayload(const char * topic, const char * payload);
|
||||||
|
void dispatchTextLine(const char * cmnd);
|
||||||
|
|
||||||
void dispatchJson(char * strPayload);
|
// void dispatchParseJson(char * strPayload);
|
||||||
void dispatchJsonl(char * strPayload);
|
// void dispatchParseJsonl(char * strPayload);
|
||||||
void dispatchJsonl(Stream & stream);
|
void dispatchParseJsonl(Stream & stream);
|
||||||
|
|
||||||
void dispatchPage(String strPageid);
|
void dispatchPage(const char * page);
|
||||||
void dispatchClearPage(String strPageid);
|
void dispatchClearPage(const char * page);
|
||||||
void dispatchDim(String strDimLevel);
|
void dispatchDim(const char * level);
|
||||||
void dispatchBacklight(String strPayload);
|
void dispatchBacklight(const char * payload);
|
||||||
|
|
||||||
void dispatchWebUpdate(const char * espOtaUrl);
|
void dispatchWebUpdate(const char * espOtaUrl);
|
||||||
void dispatchIdle(const char * state);
|
void dispatch_output_idle_state(const char * state);
|
||||||
void dispatchReboot(bool saveConfig);
|
void dispatchReboot(bool saveConfig);
|
||||||
void dispatchStatusUpdate(void);
|
void dispatch_output_statusupdate(void);
|
||||||
|
|
||||||
void dispatch_button(uint8_t id, const char * event);
|
void dispatch_button(uint8_t id, const char * event);
|
||||||
|
|
||||||
|
@ -90,19 +90,19 @@ static bool guiCheckSleep()
|
|||||||
uint32_t idle = lv_disp_get_inactive_time(NULL);
|
uint32_t idle = lv_disp_get_inactive_time(NULL);
|
||||||
if(idle >= (guiSleepTime1 + guiSleepTime2) * 1000U) {
|
if(idle >= (guiSleepTime1 + guiSleepTime2) * 1000U) {
|
||||||
if(guiSleeping != 2) {
|
if(guiSleeping != 2) {
|
||||||
dispatchIdle(("LONG")); // Literal string
|
dispatch_output_idle_state(("LONG")); // Literal string
|
||||||
guiSleeping = 2;
|
guiSleeping = 2;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if(idle >= guiSleepTime1 * 1000U) {
|
} else if(idle >= guiSleepTime1 * 1000U) {
|
||||||
if(guiSleeping != 1) {
|
if(guiSleeping != 1) {
|
||||||
dispatchIdle(("SHORT")); // Literal string
|
dispatch_output_idle_state(("SHORT")); // Literal string
|
||||||
guiSleeping = 1;
|
guiSleeping = 1;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(guiSleeping != 0) {
|
if(guiSleeping != 0) {
|
||||||
dispatchIdle(("OFF")); // Literal string
|
dispatch_output_idle_state(("OFF")); // Literal string
|
||||||
guiSleeping = 0;
|
guiSleeping = 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -1071,7 +1071,7 @@ void webHandleGuiConfig()
|
|||||||
}
|
}
|
||||||
webSendFooter();
|
webSendFooter();
|
||||||
|
|
||||||
if(webServer.hasArg(F("action"))) dispatchCommand(webServer.arg(F("action")));
|
if(webServer.hasArg(F("action"))) dispatchTextLine(webServer.arg(F("action")).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -1471,7 +1471,7 @@ void httpHandleResetConfig()
|
|||||||
#if HASP_USE_SPIFFS > 0
|
#if HASP_USE_SPIFFS > 0
|
||||||
bool formatted = SPIFFS.format();
|
bool formatted = SPIFFS.format();
|
||||||
if(formatted) {
|
if(formatted) {
|
||||||
httpMessage += F("<b>Resetting all saved settings and restarting device into WiFi AP mode</b>");
|
httpMessage += F("<b>Resetting all saved settings and restarting device</b>");
|
||||||
} else {
|
} else {
|
||||||
httpMessage += F("<b>Failed to format the internal flash partition</b>");
|
httpMessage += F("<b>Failed to format the internal flash partition</b>");
|
||||||
resetConfirmed = false;
|
resetConfirmed = false;
|
||||||
|
@ -239,7 +239,10 @@ void mqtt_send_statusupdate()
|
|||||||
// Receive incoming messages
|
// Receive incoming messages
|
||||||
static void mqtt_message_cb(char * topic_p, byte * payload, unsigned int length)
|
static void mqtt_message_cb(char * topic_p, byte * payload, unsigned int length)
|
||||||
{ // Handle incoming commands from MQTT
|
{ // Handle incoming commands from MQTT
|
||||||
if(length >= MQTT_MAX_PACKET_SIZE) return;
|
if(length >= MQTT_MAX_PACKET_SIZE) {
|
||||||
|
Log.error(F("MQTT RCV: Payload too long (%d bytes)"), length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
payload[length] = '\0';
|
payload[length] = '\0';
|
||||||
|
|
||||||
// String strTopic((char *)0);
|
// String strTopic((char *)0);
|
||||||
@ -271,36 +274,6 @@ static void mqtt_message_cb(char * topic_p, byte * payload, unsigned int length)
|
|||||||
Log.error(F("MQTT: Message received with invalid topic"));
|
Log.error(F("MQTT: Message received with invalid topic"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Log.trace(F("MQTT IN: short topic: %s"), topic);
|
|
||||||
|
|
||||||
if(!strcmp_P(topic, PSTR("command"))) {
|
|
||||||
dispatchCommand((char *)payload);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(topic == strstr_P(topic, PSTR("command/"))) { // startsWith command/
|
|
||||||
topic += 8u;
|
|
||||||
// Log.trace(F("MQTT IN: command subtopic: %s"), topic);
|
|
||||||
|
|
||||||
if(!strcmp_P(topic, PSTR("json"))) { // '[...]/device/command/json' -m '["dim=5", "page 1"]' =
|
|
||||||
// nextionSendCmd("dim=50"), nextionSendCmd("page 1")
|
|
||||||
dispatchJson((char *)payload); // Send to nextionParseJson()
|
|
||||||
} else if(!strcmp_P(topic, PSTR("jsonl"))) {
|
|
||||||
dispatchJsonl((char *)payload);
|
|
||||||
} else if(length == 0) {
|
|
||||||
dispatchCommand(topic);
|
|
||||||
} else { // '[...]/device/command/p[1].b[4].txt' -m '"Lights On"' ==
|
|
||||||
// nextionSetAttr("p[1].b[4].txt", "\"Lights On\"")
|
|
||||||
dispatchAttribute(topic, (char *)payload);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(topic == strstr_P(topic, PSTR("config/"))) { // startsWith command/
|
|
||||||
topic += 7u;
|
|
||||||
dispatchConfig(topic, (char *)payload);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// catch a dangling LWT from a previous connection if it appears
|
// catch a dangling LWT from a previous connection if it appears
|
||||||
if(!strcmp_P(topic, PSTR("status")) && !strcasecmp_P((char *)payload, PSTR("OFF"))) {
|
if(!strcmp_P(topic, PSTR("status")) && !strcasecmp_P((char *)payload, PSTR("OFF"))) {
|
||||||
@ -308,7 +281,9 @@ static void mqtt_message_cb(char * topic_p, byte * payload, unsigned int length)
|
|||||||
snprintf_P(topicBuffer, sizeof(topicBuffer), PSTR("%sstatus"), mqttNodeTopic);
|
snprintf_P(topicBuffer, sizeof(topicBuffer), PSTR("%sstatus"), mqttNodeTopic);
|
||||||
mqttClient.publish(topicBuffer, "ON", true);
|
mqttClient.publish(topicBuffer, "ON", true);
|
||||||
Log.notice(F("MQTT: binary_sensor state: [status] : ON"));
|
Log.notice(F("MQTT: binary_sensor state: [status] : ON"));
|
||||||
return;
|
// return;
|
||||||
|
} else {
|
||||||
|
dispatchTopicPayload(topic, (char *)payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -458,7 +433,9 @@ void mqttReconnect()
|
|||||||
mqttReconnectCount = 0;
|
mqttReconnectCount = 0;
|
||||||
|
|
||||||
haspReconnect();
|
haspReconnect();
|
||||||
dispatchPage(String(haspGetPage()));
|
char page[4] = "999";
|
||||||
|
itoa(haspGetPage(), page, DEC);
|
||||||
|
dispatchPage(page);
|
||||||
mqtt_send_statusupdate();
|
mqtt_send_statusupdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ void TASMO_DATA_RECEIVE(char *data)
|
|||||||
Log.verbose(F("TAS: dataType [%s]"), dataType);
|
Log.verbose(F("TAS: dataType [%s]"), dataType);
|
||||||
|
|
||||||
if (!strcmp(dataType, "p[")){ //
|
if (!strcmp(dataType, "p[")){ //
|
||||||
dispatchCommand(data);
|
dispatchTextLine(data);
|
||||||
} else if (!strcmp(dataType, "[\"")) {
|
} else if (!strcmp(dataType, "[\"")) {
|
||||||
dispatchJson(data);
|
dispatchJson(data);
|
||||||
} else {
|
} else {
|
||||||
@ -123,7 +123,7 @@ void TASMO_DATA_RECEIVE(char *data)
|
|||||||
} else if (!strcmp(slvCmd, "clearpage")) {
|
} else if (!strcmp(slvCmd, "clearpage")) {
|
||||||
dispatchClearPage(slvVal);
|
dispatchClearPage(slvVal);
|
||||||
} else {
|
} else {
|
||||||
dispatchCommand(data);
|
dispatchTextLine(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ static void telnetProcessLine()
|
|||||||
if(strcasecmp_P(telnetInputBuffer, PSTR("exit")) == 0) {
|
if(strcasecmp_P(telnetInputBuffer, PSTR("exit")) == 0) {
|
||||||
telnetClientDisconnect();
|
telnetClientDisconnect();
|
||||||
} else {
|
} else {
|
||||||
dispatchCommand(telnetInputBuffer);
|
dispatchTextLine(telnetInputBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
[env:d1mini32-lolintft24]
|
[env:d1mini32-lolintft24]
|
||||||
platform = espressif32@^2.0.0
|
platform = espressif32@^2.0.0
|
||||||
board = wemos_d1_mini32
|
board = wemos_d1_mini32
|
||||||
|
monitor_filters = esp32_exception_decoder
|
||||||
monitor_port = COM6 ; To change the port, use platform_override.ini
|
monitor_port = COM6 ; To change the port, use platform_override.ini
|
||||||
upload_port = COM6 ; To change the port, use platform_override.ini
|
upload_port = COM6 ; To change the port, use platform_override.ini
|
||||||
;upload_protocol = espota ; You can switch to ArduinoOTA after flashing over serial once
|
;upload_protocol = espota ; You can switch to ArduinoOTA after flashing over serial once
|
||||||
|
Loading…
x
Reference in New Issue
Block a user