From a7e76b586952dd364716a6bdd8961d8ce897f31b Mon Sep 17 00:00:00 2001 From: mfletcher_2 <21361170-mfletcher_2@users.noreply.gitlab.com> Date: Sun, 2 Jun 2024 00:18:35 +0000 Subject: [PATCH] Add support for Kasa Smart LED light strips --- .../KasaSmartController.cpp | 78 +++++++++++++++++-- .../KasaSmartController/KasaSmartController.h | 28 +++++-- .../RGBController_KasaSmart.cpp | 33 ++++++-- 3 files changed, 123 insertions(+), 16 deletions(-) diff --git a/Controllers/KasaSmartController/KasaSmartController.cpp b/Controllers/KasaSmartController/KasaSmartController.cpp index 50ffde2a..f7db72a4 100644 --- a/Controllers/KasaSmartController/KasaSmartController.cpp +++ b/Controllers/KasaSmartController/KasaSmartController.cpp @@ -111,6 +111,32 @@ bool KasaSmartController::Initialize() return is_initialized; } + std::string model; + if(system_information["system"]["get_sysinfo"].contains("model")) + { + model = system_information["system"]["get_sysinfo"]["model"]; + } + else + { + /*-----------------------*\ + | Can't find device model | + \*-----------------------*/ + return is_initialized; + } + + if(model.find("KL420") != std::string::npos) + { + kasa_type = KASA_SMART_TYPE_KL420; + } + else if(model.find("KL4") != std::string::npos) + { + kasa_type = KASA_SMART_TYPE_OTHER_LEDSTRIP; + } + else + { + kasa_type = KASA_SMART_TYPE_LIGHT; + } + firmware_version = system_information["system"]["get_sysinfo"]["sw_ver"]; module_name = system_information["system"]["get_sysinfo"]["model"]; device_id = system_information["system"]["get_sysinfo"]["deviceId"]; @@ -152,7 +178,12 @@ std::string KasaSmartController::GetUniqueID() return(device_id); } -void KasaSmartController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +int KasaSmartController::GetKasaType() +{ + return(kasa_type); +} + +void KasaSmartController::SetColor(unsigned char red, unsigned char green, unsigned char blue, int device_type) { if(!is_initialized) { @@ -192,14 +223,22 @@ void KasaSmartController::SetColor(unsigned char red, unsigned char green, unsig \*----------------------------*/ if(normalized_saturation == 0 && normalized_value == 0) { - TurnOff(); + TurnOff(device_type); return; } /*------------------------------*\ | Format set light state command | \*------------------------------*/ - const std::string set_lightstate_command_format(KASA_SMART_SET_LIGHT_STATE_COMMAND_FORMAT); + std::string set_lightstate_command_format; + if(device_type == DEVICE_TYPE_LIGHT) + { + set_lightstate_command_format = KASA_SMART_LIGHT_SET_LIGHT_STATE_COMMAND_FORMAT; + } + else if(device_type == DEVICE_TYPE_LEDSTRIP) + { + set_lightstate_command_format = KASA_SMART_LEDSTRIP_SET_LIGHT_STATE_COMMAND_FORMAT; + } int size = std::snprintf(nullptr, 0, set_lightstate_command_format.c_str(), normalized_hue, normalized_saturation, normalized_value) + 1; if(size <= 0) { @@ -219,14 +258,43 @@ void KasaSmartController::SetColor(unsigned char red, unsigned char green, unsig port.tcp_close(); } -void KasaSmartController::TurnOff() +void KasaSmartController::SetEffect(std::string effect) { if(!is_initialized) { return; } - const std::string turn_off_command(KASA_SMART_OFF_COMMAND); + /*-------------------*\ + | Open TCP connection | + \*-------------------*/ + if(!port.connected && !port.tcp_client_connect() && ++retry_count >= KASA_SMART_MAX_CONNECTION_ATTEMPTS) + { + is_initialized = false; + return; + } + + std::string response; + KasaSmartController::SendCommand(effect, response); + port.tcp_close(); +} + +void KasaSmartController::TurnOff(int device_type) +{ + if(!is_initialized) + { + return; + } + + std::string turn_off_command; + if(device_type == DEVICE_TYPE_LIGHT) + { + turn_off_command = KASA_SMART_LIGHT_OFF_COMMAND; + } + else if(device_type == DEVICE_TYPE_LEDSTRIP) + { + turn_off_command = KASA_SMART_LEDSTRIP_OFF_COMMAND; + } if(!port.connected && !port.tcp_client_connect() && ++retry_count >= KASA_SMART_MAX_CONNECTION_ATTEMPTS) { diff --git a/Controllers/KasaSmartController/KasaSmartController.h b/Controllers/KasaSmartController/KasaSmartController.h index 4d6c3f78..7bc72ded 100644 --- a/Controllers/KasaSmartController/KasaSmartController.h +++ b/Controllers/KasaSmartController/KasaSmartController.h @@ -19,8 +19,16 @@ enum { - KASA_SMART_MODE_DIRECT = 0x00, - KASA_SMART_MODE_OFF = 0x01 + KASA_SMART_MODE_DIRECT = 0x00, + KASA_SMART_MODE_OFF = 0x01, + KASA_SMART_MODE_RAINBOW = 0x02 +}; + +enum +{ + KASA_SMART_TYPE_KL420 = 0x00, + KASA_SMART_TYPE_OTHER_LEDSTRIP = 0x01, + KASA_SMART_TYPE_LIGHT = 0x02 }; #define KASA_SMART_INITIALIZATION_VECTOR 0xAB @@ -31,9 +39,14 @@ enum | Kasa Smart Light Commands | \*-------------------------*/ #define KASA_SMART_SYSTEM_INFO_QUERY "{\"system\": {\"get_sysinfo\": {}}}" -#define KASA_SMART_OFF_COMMAND "{\"smartlife.iot.smartbulb.lightingservice\": {\"transition_light_state\": {\"transition_period\": 0, \"on_off\":0, \"mode\":\"normal\"}}}" -const char KASA_SMART_SET_LIGHT_STATE_COMMAND_FORMAT[] = "{\"smartlife.iot.smartbulb.lightingservice\": {\"transition_light_state\": {\"transition_period\": 0, \"on_off\"" +#define KASA_SMART_LIGHT_OFF_COMMAND "{\"smartlife.iot.smartbulb.lightingservice\": {\"transition_light_state\": {\"transition_period\": 0, \"on_off\":0, \"mode\":\"normal\"}}}" +const char KASA_SMART_LIGHT_SET_LIGHT_STATE_COMMAND_FORMAT[] = "{\"smartlife.iot.smartbulb.lightingservice\": {\"transition_light_state\": {\"transition_period\": 0, \"on_off\"" ":1, \"mode\":\"normal\", \"hue\": %u, \"saturation\": %u, \"brightness\": %u, \"color_temp\": 0}}}"; +#define KASA_SMART_LEDSTRIP_OFF_COMMAND "{\"smartlife.iot.lightStrip\": {\"set_light_state\": {\"transition\": 0, \"on_off\":0, \"mode\":\"normal\"}}}" +const char KASA_SMART_LEDSTRIP_SET_LIGHT_STATE_COMMAND_FORMAT[] = "{\"smartlife.iot.lightStrip\": {\"set_light_state\": {\"transition\": 0, \"on_off\"" + ":1, \"mode\":\"normal\", \"hue\": %u, \"saturation\": %u, \"brightness\": %u, \"color_temp\": 0}}}"; +#define KASA_SMART_EFFECT_RAINBOW_COMMAND "{\"smartlife.iot.lighting_effect\":{\"set_lighting_effect\":{\"custom\":0,\"direction\":1,\"duration\":0,\"enable\":1,\"expansion_strategy\":1,\"name\":\"Rainbow\",\"repeat_times\":0,\"segments\":[0],\"sequence\":[[0,100,100],[100,100,100],[200,100,100],[300,100,100]],\"spread\":12,\"transition\":1500,\"type\":\"sequence\"}}}}" + class KasaSmartController { @@ -46,10 +59,12 @@ public: std::string GetVersion(); std::string GetManufacturer(); std::string GetUniqueID(); + int GetKasaType(); bool Initialize(); - void SetColor(unsigned char red, unsigned char green, unsigned char blue); - void TurnOff(); + void SetColor(unsigned char red, unsigned char green, unsigned char blue, int device_type); + void SetEffect(std::string effect); + void TurnOff(int device_type); private: net_port port; @@ -60,6 +75,7 @@ private: std::string module_name; std::string device_id; std::string location; + int kasa_type; bool SendCommand(std::string command, std::string &response); static unsigned char* Encrypt(const std::string request); static std::string Decrypt(const unsigned char*, int length, std::string &response); diff --git a/Controllers/KasaSmartController/RGBController_KasaSmart.cpp b/Controllers/KasaSmartController/RGBController_KasaSmart.cpp index 02359f19..51565762 100644 --- a/Controllers/KasaSmartController/RGBController_KasaSmart.cpp +++ b/Controllers/KasaSmartController/RGBController_KasaSmart.cpp @@ -28,12 +28,21 @@ RGBController_KasaSmart::RGBController_KasaSmart(KasaSmartController* controller name = controller->GetManufacturer() + " " + controller->GetName(); vendor = controller->GetManufacturer(); - type = DEVICE_TYPE_LIGHT; version = controller->GetVersion(); description = "Kasa Smart Device"; serial = controller->GetUniqueID(); location = controller->GetLocation(); + if(controller->GetKasaType() == KASA_SMART_TYPE_LIGHT) + { + type = DEVICE_TYPE_LIGHT; + } + else if(controller->GetKasaType() == KASA_SMART_TYPE_OTHER_LEDSTRIP + || controller->GetKasaType() == KASA_SMART_TYPE_KL420) + { + type = DEVICE_TYPE_LEDSTRIP; + } + mode Direct; Direct.name = "Direct"; Direct.value = KASA_SMART_MODE_DIRECT; @@ -41,6 +50,16 @@ RGBController_KasaSmart::RGBController_KasaSmart(KasaSmartController* controller Direct.color_mode = MODE_COLORS_PER_LED; modes.push_back(Direct); + if(controller->GetKasaType() == KASA_SMART_TYPE_KL420) + { + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = KASA_SMART_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Rainbow.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Rainbow); + } + mode Off; Off.name = "Off"; Off.value = KASA_SMART_MODE_OFF; @@ -93,7 +112,7 @@ void RGBController_KasaSmart::DeviceUpdateLEDs() unsigned char grn = RGBGetGValue(colors[0]); unsigned char blu = RGBGetBValue(colors[0]); - controller->SetColor(red, grn, blu); + controller->SetColor(red, grn, blu, type); } void RGBController_KasaSmart::UpdateZoneLEDs(int /*zone*/) @@ -108,9 +127,13 @@ void RGBController_KasaSmart::UpdateSingleLED(int /*led*/) void RGBController_KasaSmart::DeviceUpdateMode() { - if(modes[active_mode].value == KASA_SMART_MODE_OFF) + switch(modes[active_mode].value) { - controller->TurnOff(); - return; + case KASA_SMART_MODE_OFF: + controller->TurnOff(type); + break; + case KASA_SMART_MODE_RAINBOW: + controller->SetEffect(KASA_SMART_EFFECT_RAINBOW_COMMAND); + break; } }