diff --git a/Controllers/YeelightController/RGBController_Yeelight.cpp b/Controllers/YeelightController/RGBController_Yeelight.cpp index 06be17d7d..47b9d908d 100644 --- a/Controllers/YeelightController/RGBController_Yeelight.cpp +++ b/Controllers/YeelightController/RGBController_Yeelight.cpp @@ -20,17 +20,31 @@ RGBController_Yeelight::RGBController_Yeelight(YeelightController* light_ptr) serial = light->GetUniqueID(); location = light->GetLocation(); - /*---------------------------------------------*\ - | Yeelight standard control does not support | - | fast refreshing. We need to implement music | - | mode, so for now name the mode "Static" | - \*---------------------------------------------*/ - mode Direct; - Direct.name = "Static"; - Direct.value = 0; - Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; - Direct.color_mode = MODE_COLORS_PER_LED; - modes.push_back(Direct); + /*---------------------------------------------------------*\ + | If using music mode, use mode name "Direct" as the music | + | mode interface can handle high speed updates from effects | + | engine software. If not using music mode, name the mode | + | "Static" to prevent effect engine use, as the standard | + | interface is limited to a very low update rate | + \*---------------------------------------------------------*/ + if(light->GetMusicMode()) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + } + else + { + mode Static; + Static.name = "Static"; + Static.value = 0; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + } SetupZones(); } diff --git a/Controllers/YeelightController/YeelightController.cpp b/Controllers/YeelightController/YeelightController.cpp index 81cf16a1b..fcc71d977 100644 --- a/Controllers/YeelightController/YeelightController.cpp +++ b/Controllers/YeelightController/YeelightController.cpp @@ -9,17 +9,38 @@ using json = nlohmann::json; -YeelightController::YeelightController(std::string ip) +YeelightController::YeelightController(std::string ip, bool music_mode_val) { /*-----------------------------------------------------------------*\ | Fill in location string with device's IP address | \*-----------------------------------------------------------------*/ location = "IP: " + ip; + music_mode = music_mode_val; /*-----------------------------------------------------------------*\ | Open a TCP client sending to the device's IP, port 38899 | \*-----------------------------------------------------------------*/ port.tcp_client(ip.c_str(), "55443"); + + SetPower(); + + if(music_mode) + { + /*-----------------------------------------------------------------*\ + | Open a TCP server for music mode if enabled | + \*-----------------------------------------------------------------*/ + music_mode_server.tcp_server("55444"); + + /*-----------------------------------------------------------------*\ + | Command bulb to connect to our TCP server | + \*-----------------------------------------------------------------*/ + SetMusicMode(); + + /*-----------------------------------------------------------------*\ + | Get the client socket for the music mode connection | + \*-----------------------------------------------------------------*/ + music_mode_sock = music_mode_server.tcp_server_listen(); + } } YeelightController::~YeelightController() @@ -51,11 +72,26 @@ std::string YeelightController::GetUniqueID() return(""); } -void YeelightController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +bool YeelightController::GetMusicMode() +{ + return(music_mode); +} + +void YeelightController::SetMusicMode() { json command; - unsigned int rgb = (red << 16) | (green << 8) | (blue << 0); + char hostname[256]; + char* ip_addr; + struct hostent* host_entry; + + /*-----------------------------------------------------------------*\ + | The Yeelight bulb requires this PC's local IP address for music | + | mode. Get the first IP address of this computer's hostname | + \*-----------------------------------------------------------------*/ + gethostname(hostname, 256); + host_entry = gethostbyname(hostname); + ip_addr = inet_ntoa(*((struct in_addr*) host_entry->h_addr_list[0])); /*-----------------------------------------------------------------*\ | Fill in the set_rgb command with RGB information. | @@ -63,10 +99,10 @@ void YeelightController::SetColor(unsigned char red, unsigned char green, unsign | set the state to off. Otherwise, set it to on. | \*-----------------------------------------------------------------*/ command["id"] = 1; - command["method"] = "set_rgb"; - command["params"][0] = rgb; - command["params"][1] = "sudden"; - command["params"][2] = 0; + command["method"] = "set_music"; + command["params"][0] = 1; + command["params"][1] = ip_addr; + command["params"][2] = 55444; /*-----------------------------------------------------------------*\ | Convert the JSON object to a string and write it | @@ -77,3 +113,92 @@ void YeelightController::SetColor(unsigned char red, unsigned char green, unsign port.tcp_client_write((char *)command_str.c_str(), command_str.length() + 1); port.tcp_close(); } + +void YeelightController::SetPower() +{ + json command; + + /*-----------------------------------------------------------------*\ + | Fill in the set_rgb command with RGB information. | + | The bulb will not respond to 0, 0, 0, so if all channels are zero,| + | set the state to off. Otherwise, set it to on. | + \*-----------------------------------------------------------------*/ + command["id"] = 1; + command["method"] = "set_power"; + command["params"][0] = 1; + command["params"][1] = "sudden"; + command["params"][2] = 0; + command["params"][3] = 2; + + /*-----------------------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------------------*/ + std::string command_str = command.dump().append("\r\n"); + + port.tcp_client_connect(); + port.tcp_client_write((char *)command_str.c_str(), command_str.length() + 1); + port.tcp_close(); +} + +void YeelightController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + json command; + + /*-----------------------------------------------------------------*\ + | Yeelight doesn't seem to support proper RGB, it just uses RGB to | + | calculate hue and saturation. It doesn't affect brightness. To | + | work around this, determine the highest value and scale to 100 to | + | use as brightness | + \*-----------------------------------------------------------------*/ + float bright = red; + + if(green > bright) + { + bright = green; + } + + if(blue > bright) + { + bright = blue; + } + + bright = (100.0f * (bright / 255.0f)); + + /*-----------------------------------------------------------------*\ + | Calculate the RGB field as 0x00RRGGBB | + \*-----------------------------------------------------------------*/ + unsigned int rgb = (red << 16) | (green << 8) | (blue << 0); + + /*-----------------------------------------------------------------*\ + | Because of Yeelight's weird quirks with true RGB, we have to use | + | the Color Flow option but configure only one frame. Because the | + | set_cf option provides both RGB and brightness in one command, it | + | allows better RGB control than the set_rgb function. | + \*-----------------------------------------------------------------*/ + std::string cf = "\"0,1," + std::to_string(rgb) +"," + std::to_string(bright) +"\""; + + /*-----------------------------------------------------------------*\ + | Fill in the set_cf command with the color flow string. | + \*-----------------------------------------------------------------*/ + command["id"] = 1; + command["method"] = "start_cf"; + command["params"][0] = 1; + command["params"][1] = 1; + command["params"][2] = cf; + + /*-----------------------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------------------*/ + std::string command_str = command.dump().append("\r\n"); + + if(music_mode) + { + send(*music_mode_sock, (char *)command_str.c_str(), command_str.length(), 0); + } + else + { + port.tcp_client_connect(); + port.tcp_client_write((char *)command_str.c_str(), command_str.length() + 1); + port.tcp_close(); + } +} diff --git a/Controllers/YeelightController/YeelightController.h b/Controllers/YeelightController/YeelightController.h index 43b59cbe1..bf3f312ae 100644 --- a/Controllers/YeelightController/YeelightController.h +++ b/Controllers/YeelightController/YeelightController.h @@ -16,7 +16,7 @@ class YeelightController { public: - YeelightController(std::string ip); + YeelightController(std::string ip, bool music_mode_val); ~YeelightController(); std::string GetLocation(); @@ -25,9 +25,16 @@ public: std::string GetManufacturer(); std::string GetUniqueID(); + bool GetMusicMode(); + + void SetMusicMode(); + void SetPower(); void SetColor(unsigned char red, unsigned char green, unsigned char blue); private: std::string location; net_port port; + bool music_mode; + net_port music_mode_server; + SOCKET * music_mode_sock; }; diff --git a/Controllers/YeelightController/YeelightControllerDetect.cpp b/Controllers/YeelightController/YeelightControllerDetect.cpp index 0d75c02ff..cc0708f9d 100644 --- a/Controllers/YeelightController/YeelightControllerDetect.cpp +++ b/Controllers/YeelightController/YeelightControllerDetect.cpp @@ -37,8 +37,14 @@ void DetectYeelightControllers(std::vector &rgb_controllers) if(yeelight_settings["devices"][device_idx].contains("ip")) { std::string yeelight_ip = yeelight_settings["devices"][device_idx]["ip"]; + bool music_mode = false; - new_controller = new YeelightController(yeelight_ip); + if(yeelight_settings["devices"][device_idx].contains("music_mode")) + { + music_mode = yeelight_settings["devices"][device_idx]["music_mode"]; + } + + new_controller = new YeelightController(yeelight_ip, music_mode); new_rgbcontroller = new RGBController_Yeelight(new_controller); rgb_controllers.push_back(new_rgbcontroller);