Add Yeelight music mode, enabled with "music_mode" option in Yeelight configuration. Probably only works for one light right now.

This commit is contained in:
Adam Honse
2021-01-20 10:32:13 -06:00
parent a5a9482206
commit 3e9993a6a4
4 changed files with 172 additions and 20 deletions

View File

@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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;
};

View File

@@ -37,8 +37,14 @@ void DetectYeelightControllers(std::vector<RGBController*> &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);