diff --git a/src/hasp/hasp_attribute.cpp b/src/hasp/hasp_attribute.cpp index ef2395a1..bf3b3b8e 100644 --- a/src/hasp/hasp_attribute.cpp +++ b/src/hasp/hasp_attribute.cpp @@ -333,7 +333,8 @@ static void my_btnmatrix_map_create(lv_obj_t * obj, const char * payload) DeserializationError jsonError = deserializeJson(map_doc, payload); if(jsonError) { // Couldn't parse incoming JSON payload - return Log.warning(TAG_ATTR, F("JSON: Failed to parse incoming button map with error: %s"), jsonError.c_str()); + dispatch_json_error(TAG_ATTR, jsonError); + return; } JsonArray arr = map_doc.as(); // Parse payload @@ -404,7 +405,8 @@ static void line_set_points(lv_obj_t * obj, const char * payload) DeserializationError jsonError = deserializeJson(doc, payload); if(jsonError) { // Couldn't parse incoming JSON payload - return Log.warning(TAG_ATTR, F("JSON: Failed to parse incoming line points with error: %s"), jsonError.c_str()); + dispatch_json_error(TAG_ATTR, jsonError); + return; } JsonArray arr = doc.as(); // Parse payload @@ -441,7 +443,7 @@ static inline lv_color_t haspLogColor(lv_color_t color) } // OK -static bool haspPayloadToColor(const char * payload, lv_color_t & color) +bool haspPayloadToColor(const char * payload, lv_color_t & color) { /* HEX format #rrggbb or #rrggbbaa */ char pattern[4]; diff --git a/src/hasp/hasp_attribute.h b/src/hasp/hasp_attribute.h index 33023b5e..08fcbbcf 100644 --- a/src/hasp/hasp_attribute.h +++ b/src/hasp/hasp_attribute.h @@ -27,6 +27,8 @@ void line_clear_points(lv_obj_t * obj); void hasp_process_obj_attribute(lv_obj_t * obj, const char * attr_p, const char * payload, bool update); bool hasp_process_obj_attribute_val(lv_obj_t * obj, const char * attr, const char * payload, bool update); +bool haspPayloadToColor(const char * payload, lv_color_t & color); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/hasp/hasp_dispatch.cpp b/src/hasp/hasp_dispatch.cpp index 805898d2..5edd5651 100644 --- a/src/hasp/hasp_dispatch.cpp +++ b/src/hasp/hasp_dispatch.cpp @@ -8,6 +8,7 @@ #include "hasp_object.h" #include "hasp.h" #include "hasp_utilities.h" +#include "hasp_attribute.h" #if HASP_USE_DEBUG > 0 #include "StringStream.h" @@ -35,7 +36,14 @@ extern unsigned long debugLastMillis; // UpdateStatus timer extern uint8_t hasp_sleep_state; uint8_t nCommands = 0; -haspCommand_t commands[16]; +haspCommand_t commands[17]; + +struct moodlight_t +{ + byte power; + byte r, g, b; +}; +moodlight_t moodlight; static void dispatch_config(const char * topic, const char * payload); // void dispatch_group_value(uint8_t groupid, int16_t state, lv_obj_t * obj); @@ -77,6 +85,11 @@ bool dispatch_factory_reset() return formated && erased; } +void dispatch_json_error(uint8_t tag, DeserializationError & jsonError) +{ + Log.error(tag, F("JSON parsing failed: %s"), jsonError.c_str()); +} + // p[x].b[y].attr=value inline bool dispatch_process_button_attribute(const char * topic_p, const char * payload) { @@ -330,7 +343,7 @@ static void dispatch_config(const char * topic, const char * payload) } else { DeserializationError jsonError = deserializeJson(doc, payload); if(jsonError) { // Couldn't parse incoming JSON command - Log.warning(TAG_MSGR, F("JSON: Failed to parse incoming JSON command with error: %s"), jsonError.c_str()); + dispatch_json_error(TAG_MSGR, jsonError); return; } settings = doc.as(); @@ -568,7 +581,7 @@ void dispatch_parse_json(const char *, const char * payload) json.shrinkToFit(); if(jsonError) { // Couldn't parse incoming JSON command - Log.warning(TAG_MSGR, F("Failed to parse incoming JSON command with error: %s"), jsonError.c_str()); + dispatch_json_error(TAG_MSGR, jsonError); } else if(json.is()) { // handle json as an array of commands JsonArray arr = json.as(); @@ -718,6 +731,55 @@ void dispatch_dim(const char *, const char * level) dispatch_state_msg(F("dim"), payload); } +void dispatch_moodlight(const char * topic, const char * payload) +{ + // Set the current state + if(strlen(payload) != 0) { + + size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 512; + DynamicJsonDocument json(maxsize); + + // Note: Deserialization needs to be (const char *) so the objects WILL be copied + // this uses more memory but otherwise the mqtt receive buffer can get overwritten by the send buffer !! + DeserializationError jsonError = deserializeJson(json, payload); + json.shrinkToFit(); + + if(jsonError) { // Couldn't parse incoming JSON command + dispatch_json_error(TAG_MSGR, jsonError); + } else { + + if(!json[F("power")].isNull()) moodlight.power = hasp_util_is_true(json[F("power")].as().c_str()); + + if(!json[F("r")].isNull()) moodlight.r = json[F("r")].as(); + if(!json[F("g")].isNull()) moodlight.r = json[F("g")].as(); + if(!json[F("b")].isNull()) moodlight.r = json[F("b")].as(); + + if(!json[F("color")].isNull()) { + lv_color16_t color; + if(haspPayloadToColor(json[F("color")].as().c_str(), color)) { + lv_color32_t c32; + c32.full = lv_color_to32(color); + moodlight.r = c32.ch.red; + moodlight.g = c32.ch.green; + moodlight.b = c32.ch.blue; + } + } + + if(moodlight.power) + gpio_set_moodlight(moodlight.r, moodlight.g, moodlight.b); + else + gpio_set_moodlight(0, 0, 0); + } + } + + // Return the current state + char buffer[128]; + snprintf_P(buffer, sizeof(buffer), + PSTR("{\"power\":\"%u\",\"color\":\"#%02x%02x%02x\",\"r\":%u,\"g\":%u,\"b\":%u}"), moodlight.power, + moodlight.r, moodlight.g, moodlight.b, moodlight.r, moodlight.g, moodlight.b); + dispatch_state_msg(F("moodlight"), buffer); +} + void dispatch_backlight(const char *, const char * payload) { // Set the current state @@ -867,6 +929,7 @@ void dispatchSetup() dispatch_add_command(PSTR("dim"), dispatch_dim); dispatch_add_command(PSTR("brightness"), dispatch_dim); dispatch_add_command(PSTR("light"), dispatch_backlight); + dispatch_add_command(PSTR("moodlight"), dispatch_moodlight); dispatch_add_command(PSTR("calibrate"), dispatch_calibrate); dispatch_add_command(PSTR("update"), dispatch_web_update); dispatch_add_command(PSTR("reboot"), dispatch_reboot); diff --git a/src/hasp/hasp_dispatch.h b/src/hasp/hasp_dispatch.h index 2706d74a..b8a4c1b9 100644 --- a/src/hasp/hasp_dispatch.h +++ b/src/hasp/hasp_dispatch.h @@ -32,6 +32,7 @@ void dispatch_topic_payload(const char * topic, const char * payload); void dispatch_text_line(const char * cmnd); void dispatch_parse_jsonl(Stream & stream); void dispatch_clear_page(const char * page); +void dispatch_json_error(uint8_t tag, DeserializationError & jsonError); // void dispatchPage(uint8_t page); void dispatch_page_next(); diff --git a/src/hasp_gpio.cpp b/src/hasp_gpio.cpp index 17db78cc..28978dde 100644 --- a/src/hasp_gpio.cpp +++ b/src/hasp_gpio.cpp @@ -261,8 +261,8 @@ void gpioSetup() pinMode(gpioConfig[i].pin, OUTPUT); break; - case HASP_GPIO_LED: - case HASP_GPIO_LED_INVERTED: + case HASP_GPIO_LED ... HASP_GPIO_LED_CW_INVERTED: + // case HASP_GPIO_LED_INVERTED: case HASP_GPIO_PWM: case HASP_GPIO_PWM_INVERTED: // case HASP_GPIO_BACKLIGHT: @@ -290,27 +290,31 @@ void gpio_set_normalized_value(hasp_gpio_config_t gpio, uint16_t state) gpio.val = state >= 0x8000U ? LOW : HIGH; digitalWrite(gpio.pin, gpio.val); break; -#if defined(ARDUINO_ARCH_ESP32) case HASP_GPIO_LED: + case HASP_GPIO_LED_R: + case HASP_GPIO_LED_G: + case HASP_GPIO_LED_B: case HASP_GPIO_PWM: +#if defined(ARDUINO_ARCH_ESP32) gpio.val = map(state, 0, 0xFFFFU, 0, 4095); ledcWrite(gpio.group, gpio.val); // ledChannel and value +#else + analogWrite(gpio.pin, map(state, 0, 0xFFFFU, 0, 1023)); +#endif break; case HASP_GPIO_LED_INVERTED: + case HASP_GPIO_LED_R_INVERTED: + case HASP_GPIO_LED_G_INVERTED: + case HASP_GPIO_LED_B_INVERTED: case HASP_GPIO_PWM_INVERTED: +#if defined(ARDUINO_ARCH_ESP32) gpio.val = map(0xFFFFU - state, 0, 0xFFFFU, 0, 4095); ledcWrite(gpio.group, gpio.val); // ledChannel and value - break; #else - case HASP_GPIO_LED: - case HASP_GPIO_PWM: - analogWrite(gpio.pin, map(state, 0, 0xFFFFU, 0, 1023)); - break; - case HASP_GPIO_LED_INVERTED: - case HASP_GPIO_PWM_INVERTED: analogWrite(gpio.pin, map(0xFFFFU - state, 0, 0xFFFFU, 0, 1023)); - break; #endif + break; + default: return; } @@ -336,6 +340,23 @@ void gpio_set_normalized_group_value(uint8_t groupid, uint16_t state) } } +void gpio_set_moodlight(uint8_t r, uint8_t g, uint8_t b) +{ + for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { + switch(gpioConfig[i].type & 0xfe) { + case HASP_GPIO_LED_R: + gpio_set_normalized_value(gpioConfig[i], map(r, 0, 0xFF, 0, 0xFFFFU)); + break; + case HASP_GPIO_LED_G: + gpio_set_normalized_value(gpioConfig[i], map(g, 0, 0xFF, 0, 0xFFFFU)); + break; + case HASP_GPIO_LED_B: + gpio_set_normalized_value(gpioConfig[i], map(b, 0, 0xFF, 0, 0xFFFFU)); + break; + } + } +} + // not used // void gpio_set_gpio_value(uint8_t pin, uint16_t state) // { diff --git a/src/hasp_gpio.h b/src/hasp_gpio.h index bb3e4a8a..d2ddaa1a 100644 --- a/src/hasp_gpio.h +++ b/src/hasp_gpio.h @@ -26,6 +26,7 @@ void gpioEvery5Seconds(void); // void gpio_set_group_onoff(uint8_t groupid, bool ison); void gpio_set_normalized_group_value(uint8_t groupid, uint16_t state); // void gpio_set_gpio_state(uint8_t pin, uint16_t state); +void gpio_set_moodlight(uint8_t r, uint8_t g, uint8_t b); bool gpioSavePinConfig(uint8_t config_num, uint8_t pin, uint8_t type, uint8_t group, uint8_t pinfunc); bool gpioIsSystemPin(uint8_t gpio);