From 3255382132810c865a9c5f9a82f473116e3f8071 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 27 Sep 2023 20:39:26 +0200 Subject: [PATCH] Add JSON override for ESP-NOW remote. --- wled00/const.h | 1 + wled00/remote.cpp | 148 +++++++++++++++++++++++++++++++++------------- 2 files changed, 109 insertions(+), 40 deletions(-) diff --git a/wled00/const.h b/wled00/const.h index 7135f8de..5930445a 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -348,6 +348,7 @@ #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached #define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist #define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist +#define ERR_FS_RMLOAD 14 // It was attempted to load an remote JSON cmd, but the "remote.json" file does not exist #define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured #define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented) #define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) diff --git a/wled00/remote.cpp b/wled00/remote.cpp index 936391a9..bce34f9d 100644 --- a/wled00/remote.cpp +++ b/wled00/remote.cpp @@ -18,17 +18,17 @@ // product spec. That remote is used as the baseline for behavior and availability // since it's broadly commercially available and works out of the box as a drop-in typedef struct WizMoteMessageStructure { - uint8_t program; // 0x91 for ON button, 0x81 for all others - uint8_t seq[4]; // Incremetal sequence number 32 bit unsigned integer LSB first - uint8_t byte5 = 32; // Unknown - uint8_t button; // Identifies which button is being pressed - uint8_t byte8 = 1; // Unknown, but always 0x01 - uint8_t byte9 = 100; // Unnkown, but always 0x64 + uint8_t program; // 0x91 for ON button, 0x81 for all others + uint8_t seq[4]; // Incremetal sequence number 32 bit unsigned integer LSB first + uint8_t byte5; // Unknown (seen 0x20) + uint8_t button; // Identifies which button is being pressed + uint8_t byte8; // Unknown, but always 0x01 + uint8_t byte9; // Unnkown, but always 0x64 - uint8_t byte10; // Unknown, maybe checksum - uint8_t byte11; // Unknown, maybe checksum - uint8_t byte12; // Unknown, maybe checksum - uint8_t byte13; // Unknown, maybe checksum + uint8_t byte10; // Unknown, maybe checksum + uint8_t byte11; // Unknown, maybe checksum + uint8_t byte12; // Unknown, maybe checksum + uint8_t byte13; // Unknown, maybe checksum } message_structure_t; static uint32_t last_seq = UINT32_MAX; @@ -38,29 +38,30 @@ static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED; static const byte brightnessSteps[] = { 6, 9, 14, 22, 33, 50, 75, 113, 170, 255 }; -static const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t); +static const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(byte); -static bool nightModeActive() { +inline bool nightModeActive() { return brightnessBeforeNightMode != NIGHT_MODE_DEACTIVATED; } static void activateNightMode() { + if (nightModeActive()) return; brightnessBeforeNightMode = bri; bri = NIGHT_MODE_BRIGHTNESS; + stateUpdated(CALL_MODE_BUTTON); } static bool resetNightMode() { - if (!nightModeActive()) { - return false; - } + if (!nightModeActive()) return false; bri = brightnessBeforeNightMode; brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED; + stateUpdated(CALL_MODE_BUTTON); return true; } // increment `bri` to the next `brightnessSteps` value static void brightnessUp() { - if (nightModeActive()) { return; } + if (nightModeActive()) return; // dumb incremental search is efficient enough for so few items for (uint8_t index = 0; index < numBrightnessSteps; ++index) { if (brightnessSteps[index] > bri) { @@ -68,11 +69,12 @@ static void brightnessUp() { break; } } + stateUpdated(CALL_MODE_BUTTON); } // decrement `bri` to the next `brightnessSteps` value static void brightnessDown() { - if (nightModeActive()) { return; } + if (nightModeActive()) return; // dumb incremental search is efficient enough for so few items for (int index = numBrightnessSteps - 1; index >= 0; --index) { if (brightnessSteps[index] < bri) { @@ -80,30 +82,94 @@ static void brightnessDown() { break; } } + stateUpdated(CALL_MODE_BUTTON); } static void setOn() { - if (resetNightMode()) { - stateUpdated(CALL_MODE_BUTTON); - } + resetNightMode(); if (!bri) { - toggleOnOff(); + toggleOnOff(); + stateUpdated(CALL_MODE_BUTTON); } } static void setOff() { - if (resetNightMode()) { - stateUpdated(CALL_MODE_BUTTON); - } + resetNightMode(); if (bri) { - toggleOnOff(); + toggleOnOff(); + stateUpdated(CALL_MODE_BUTTON); } } inline void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) { + resetNightMode(); applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID); } - + +// this function follows the same principle as decodeIRJson() +static bool remoteJson(int button) +{ + char objKey[10]; + bool parsed = false; + + if (!requestJSONBufferLock(22)) return false; + + sprintf_P(objKey, PSTR("\"%d\":"), button); + + // attempt to read command from remote.json + readObjectFromFile("/remote.json", objKey, &doc); + JsonObject fdo = doc.as(); + if (fdo.isNull()) { + // the received button does not exist + if (!WLED_FS.exists("/remote.json")) errorFlag = ERR_FS_RMLOAD; //warn if file itself doesn't exist + releaseJSONBufferLock(); + return parsed; + } + + String cmdStr = fdo["cmd"].as(); + JsonObject jsonCmdObj = fdo["cmd"]; //object + + if (jsonCmdObj.isNull()) // we could also use: fdo["cmd"].is() + { + if (cmdStr.startsWith("!")) { + // call limited set of C functions + if (cmdStr.startsWith(F("!incBri"))) { + brightnessUp(); + parsed = true; + } else if (cmdStr.startsWith(F("!decBri"))) { + brightnessDown(); + parsed = true; + } else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback + uint8_t p1 = fdo["PL"] | 1; + uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1); + uint8_t p3 = fdo["FP"] | 0; + presetWithFallback(p1, p2, p3); + parsed = true; + } + } else { + // HTTP API command + String apireq = "win"; apireq += '&'; // reduce flash string usage + //if (cmdStr.indexOf("~") || fdo["rpt"]) lastValidCode = code; // repeatable action + if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix + if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) { + char tmp[10]; + sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId()); + cmdStr += tmp; + } + fdo.clear(); // clear JSON buffer (it is no longer needed) + handleSet(nullptr, cmdStr, false); // no stateUpdated() call here + stateUpdated(CALL_MODE_BUTTON); + parsed = true; + } + } else { + // command is JSON object (TODO: currently will not handle irApplyToAllSelected correctly) + deserializeState(jsonCmdObj, CALL_MODE_BUTTON); + parsed = true; + } + releaseJSONBufferLock(); + return parsed; +} + // Callback function that will be executed when data is received void handleRemote(uint8_t *incomingData, size_t len) { message_structure_t *incoming = reinterpret_cast(incomingData); @@ -125,24 +191,26 @@ void handleRemote(uint8_t *incomingData, size_t len) { return; } - DEBUG_PRINT(F("Incoming ESP Now Packet[")); + DEBUG_PRINT(F("Incoming ESP Now Packet [")); DEBUG_PRINT(cur_seq); - DEBUG_PRINT(F("] from sender[")); + DEBUG_PRINT(F("] from sender [")); DEBUG_PRINT(last_signal_src); DEBUG_PRINT(F("] button: ")); DEBUG_PRINTLN(incoming->button); - switch (incoming->button) { - case WIZMOTE_BUTTON_ON : setOn(); stateUpdated(CALL_MODE_BUTTON); break; - case WIZMOTE_BUTTON_OFF : setOff(); stateUpdated(CALL_MODE_BUTTON); break; - case WIZMOTE_BUTTON_ONE : presetWithFallback(1, FX_MODE_STATIC, 0); resetNightMode(); break; - case WIZMOTE_BUTTON_TWO : presetWithFallback(2, FX_MODE_BREATH, 0); resetNightMode(); break; - case WIZMOTE_BUTTON_THREE : presetWithFallback(3, FX_MODE_FIRE_FLICKER, 0); resetNightMode(); break; - case WIZMOTE_BUTTON_FOUR : presetWithFallback(4, FX_MODE_RAINBOW, 0); resetNightMode(); break; - case WIZMOTE_BUTTON_NIGHT : activateNightMode(); stateUpdated(CALL_MODE_BUTTON); break; - case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); stateUpdated(CALL_MODE_BUTTON); break; - case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); stateUpdated(CALL_MODE_BUTTON); break; - default: break; - } + + if (!remoteJson(incoming->button)) + switch (incoming->button) { + case WIZMOTE_BUTTON_ON : setOn(); break; + case WIZMOTE_BUTTON_OFF : setOff(); break; + case WIZMOTE_BUTTON_ONE : presetWithFallback(1, FX_MODE_STATIC, 0); break; + case WIZMOTE_BUTTON_TWO : presetWithFallback(2, FX_MODE_BREATH, 0); break; + case WIZMOTE_BUTTON_THREE : presetWithFallback(3, FX_MODE_FIRE_FLICKER, 0); break; + case WIZMOTE_BUTTON_FOUR : presetWithFallback(4, FX_MODE_RAINBOW, 0); break; + case WIZMOTE_BUTTON_NIGHT : activateNightMode(); break; + case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); break; + case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); break; + default: break; + } last_seq = cur_seq; }