Add support for TP-Link's Kasa Smart light bulbs
This commit is contained in:
parent
b1f2231bc7
commit
de43a02a1b
14 changed files with 950 additions and 0 deletions
329
Controllers/KasaSmartController/KasaSmartController.cpp
Normal file
329
Controllers/KasaSmartController/KasaSmartController.cpp
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
/*----------------------------------------------*\
|
||||
| Driver for Kasa Smart light bulbs |
|
||||
| |
|
||||
| Devin Wendt (umbreon222@gmail.com), 2/16/2023 |
|
||||
\*----------------------------------------------*/
|
||||
|
||||
#include "KasaSmartController.h"
|
||||
#include "json.hpp"
|
||||
#include "hsv.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
KasaSmartController::KasaSmartController(std::string ipAddress, std::string name)
|
||||
{
|
||||
this->name = name;
|
||||
|
||||
/*------------------------------------------------*\
|
||||
| Fill in location string with device's IP address |
|
||||
\*------------------------------------------------*/
|
||||
location = "IP: " + ipAddress;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Create a TCP client sending to the device's IP, port 9999 |
|
||||
\*---------------------------------------------------------*/
|
||||
port.tcp_client(ipAddress.c_str(), "9999");
|
||||
}
|
||||
|
||||
bool KasaSmartController::Initialize()
|
||||
{
|
||||
is_initialized = false;
|
||||
retry_count = 0;
|
||||
|
||||
/*--------------*\
|
||||
| Try to connect |
|
||||
\*--------------*/
|
||||
while(!port.connected && !port.tcp_client_connect() && retry_count < KASA_SMART_MAX_CONNECTION_ATTEMPTS)
|
||||
{
|
||||
++retry_count;
|
||||
}
|
||||
if(!port.connected)
|
||||
{
|
||||
/*----------------*\
|
||||
| Couldn't connect |
|
||||
\*----------------*/
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
const std::string system_info_query(KASA_SMART_SYSTEM_INFO_QUERY);
|
||||
std::string system_info_json;
|
||||
bool command_sent = KasaSmartController::SendCommand(system_info_query, system_info_json);
|
||||
port.tcp_close();
|
||||
if(!command_sent || system_info_json.empty())
|
||||
{
|
||||
/*---------------------------------------*\
|
||||
| Send command failed or no data returned |
|
||||
\*---------------------------------------*/
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
json system_information;
|
||||
try
|
||||
{
|
||||
system_information = json::parse(system_info_json);
|
||||
}
|
||||
catch (json::parse_error&)
|
||||
{
|
||||
/*-----------------------*\
|
||||
| Can't parse system info |
|
||||
\*-----------------------*/
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
std::string device_type;
|
||||
if(system_information["system"]["get_sysinfo"].contains("type"))
|
||||
{
|
||||
device_type = system_information["system"]["get_sysinfo"]["type"];
|
||||
}
|
||||
else if(system_information["system"]["get_sysinfo"].contains("mic_type"))
|
||||
{
|
||||
device_type = system_information["system"]["get_sysinfo"]["mic_type"];
|
||||
}
|
||||
else
|
||||
{
|
||||
/*----------------------*\
|
||||
| Can't find device type |
|
||||
\*----------------------*/
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
std::transform(device_type.begin(), device_type.end(), device_type.begin(),
|
||||
[](unsigned char c){ return std::tolower(c); });
|
||||
if(device_type.find("smartbulb") == std::string::npos)
|
||||
{
|
||||
/*----------------------------*\
|
||||
| Device type not a smart bulb |
|
||||
\*----------------------------*/
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
if(system_information["system"]["get_sysinfo"].contains("is_color") && system_information["system"]["get_sysinfo"]["is_color"] != 1)
|
||||
{
|
||||
/*--------------------------------*\
|
||||
| Smart bulb doesn't support color |
|
||||
\*--------------------------------*/
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
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"];
|
||||
|
||||
is_initialized = true;
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
KasaSmartController::~KasaSmartController()
|
||||
{
|
||||
if(port.connected)
|
||||
{
|
||||
port.tcp_close();
|
||||
}
|
||||
}
|
||||
|
||||
std::string KasaSmartController::GetLocation()
|
||||
{
|
||||
return(location);
|
||||
}
|
||||
|
||||
std::string KasaSmartController::GetName()
|
||||
{
|
||||
return(name);
|
||||
}
|
||||
|
||||
std::string KasaSmartController::GetVersion()
|
||||
{
|
||||
return(module_name + " " + firmware_version);
|
||||
}
|
||||
|
||||
std::string KasaSmartController::GetManufacturer()
|
||||
{
|
||||
return("Kasa Smart");
|
||||
}
|
||||
|
||||
std::string KasaSmartController::GetUniqueID()
|
||||
{
|
||||
return(device_id);
|
||||
}
|
||||
|
||||
void KasaSmartController::SetColor(unsigned char red, unsigned char green, unsigned char blue)
|
||||
{
|
||||
if(!is_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RGBColor color = ToRGBColor(red, green, blue);
|
||||
hsv_t hsv;
|
||||
rgb2hsv(color, &hsv);
|
||||
|
||||
/*------------------------------------------*\
|
||||
| Normalize case where hue is "-1" undefined |
|
||||
\*------------------------------------------*/
|
||||
unsigned int normalized_hue = hsv.hue;
|
||||
if(hsv.hue == -1)
|
||||
{
|
||||
normalized_hue = 0;
|
||||
}
|
||||
/*--------------------------------------------------*\
|
||||
| Kasa smart lights take values out of 100 for these |
|
||||
\*--------------------------------------------------*/
|
||||
unsigned int normalized_saturation = hsv.saturation * 100 / 255;
|
||||
unsigned int normalized_value = hsv.value * 100 / 255;
|
||||
|
||||
|
||||
/*-------------------*\
|
||||
| Open TCP connection |
|
||||
\*-------------------*/
|
||||
if(!port.connected && !port.tcp_client_connect() && ++retry_count >= KASA_SMART_MAX_CONNECTION_ATTEMPTS)
|
||||
{
|
||||
is_initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/*----------------------------*\
|
||||
| Hack to handle/emulate black |
|
||||
\*----------------------------*/
|
||||
if(normalized_saturation == 0 && normalized_value == 0)
|
||||
{
|
||||
TurnOff();
|
||||
return;
|
||||
}
|
||||
|
||||
/*------------------------------*\
|
||||
| Format set light state command |
|
||||
\*------------------------------*/
|
||||
const std::string set_lightstate_command_format(KASA_SMART_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)
|
||||
{
|
||||
port.tcp_close();
|
||||
return;
|
||||
}
|
||||
char* buf = new char[size];
|
||||
std::snprintf(buf, size, set_lightstate_command_format.c_str(), normalized_hue, normalized_saturation, normalized_value);
|
||||
std::string set_lightstate_command(buf, buf + size - 1);
|
||||
delete[] buf;
|
||||
|
||||
/*-----------------------------*\
|
||||
| Send command, ignore response |
|
||||
\*-----------------------------*/
|
||||
std::string response;
|
||||
KasaSmartController::SendCommand(set_lightstate_command, response);
|
||||
port.tcp_close();
|
||||
}
|
||||
|
||||
void KasaSmartController::TurnOff()
|
||||
{
|
||||
if(!is_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string turn_off_command(KASA_SMART_OFF_COMMAND);
|
||||
|
||||
if(!port.connected && !port.tcp_client_connect() && ++retry_count >= KASA_SMART_MAX_CONNECTION_ATTEMPTS)
|
||||
{
|
||||
is_initialized = false;
|
||||
return;
|
||||
}
|
||||
std::string response;
|
||||
KasaSmartController::SendCommand(turn_off_command, response);
|
||||
port.tcp_close();
|
||||
}
|
||||
|
||||
bool KasaSmartController::SendCommand(std::string command, std::string &response)
|
||||
{
|
||||
const unsigned char* encrypted_payload = KasaSmartController::Encrypt(command);
|
||||
port.tcp_client_write((char*)encrypted_payload, command.length() + sizeof(unsigned long));
|
||||
delete[] encrypted_payload;
|
||||
|
||||
unsigned char* receive_buffer = new unsigned char[KASA_SMART_RECEIVE_BUFFER_SIZE];
|
||||
int response_length = port.tcp_listen((char*)receive_buffer, KASA_SMART_RECEIVE_BUFFER_SIZE);
|
||||
if(response_length > KASA_SMART_RECEIVE_BUFFER_SIZE) {
|
||||
/*-------------------------------------------------------------*\
|
||||
| Small fail safes to prevent decrypting bad or empty responses |
|
||||
\*-------------------------------------------------------------*/
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long received_length = response_length;
|
||||
unsigned long response_full_length = 0;
|
||||
if(response_length > 0)
|
||||
{
|
||||
response_full_length = ntohl(*(unsigned long*)receive_buffer);
|
||||
}
|
||||
|
||||
if(response_full_length > KASA_SMART_RECEIVE_BUFFER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*--------------------------*\
|
||||
| Fetch entirety of response |
|
||||
\*--------------------------*/
|
||||
while(received_length < response_full_length)
|
||||
{
|
||||
received_length += port.tcp_listen((char*)receive_buffer + received_length, KASA_SMART_RECEIVE_BUFFER_SIZE - received_length);
|
||||
}
|
||||
|
||||
if(received_length > 0)
|
||||
{
|
||||
/*------------------------------------------------*\
|
||||
| Decrypt payload data preceeding the payload size |
|
||||
\*------------------------------------------------*/
|
||||
KasaSmartController::Decrypt(receive_buffer + sizeof(unsigned long), received_length - sizeof(unsigned long), response);
|
||||
}
|
||||
delete[] receive_buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char* KasaSmartController::Encrypt(const std::string request)
|
||||
{
|
||||
/*----------------------------------------------------------------*\
|
||||
| "Encrypted" payload consists of size as a uint32 + XOR'd payload |
|
||||
\*----------------------------------------------------------------*/
|
||||
unsigned long size = htonl(request.length());
|
||||
int payload_size = request.length() + sizeof(size);
|
||||
unsigned char* payload = new unsigned char[payload_size];
|
||||
memcpy(payload, &size, sizeof(size));
|
||||
unsigned char* request_data = new unsigned char[request.length()];
|
||||
memcpy(request_data, request.data(), request.length());
|
||||
KasaSmartController::XorPayload(request_data, request.length());
|
||||
memcpy(payload + sizeof(size), request_data, request.length());
|
||||
delete[] request_data;
|
||||
return payload;
|
||||
}
|
||||
|
||||
std::string KasaSmartController::Decrypt(const unsigned char* encrypted, int length, std::string &response)
|
||||
{
|
||||
unsigned char* temp_encrypted = new unsigned char[length];
|
||||
memcpy(temp_encrypted, encrypted, length);
|
||||
KasaSmartController::XorEncryptedPayload(temp_encrypted, length);
|
||||
for(int i = 0; i < length; ++i)
|
||||
{
|
||||
response += temp_encrypted[i];
|
||||
}
|
||||
delete[] temp_encrypted;
|
||||
return response;
|
||||
}
|
||||
|
||||
void KasaSmartController::XorPayload(unsigned char* encrypted, int length)
|
||||
{
|
||||
unsigned char key = KASA_SMART_INITIALIZATION_VECTOR;
|
||||
for(int i = 0; i < length; ++i)
|
||||
{
|
||||
key ^= encrypted[i];
|
||||
encrypted[i] = key;
|
||||
}
|
||||
}
|
||||
|
||||
void KasaSmartController::XorEncryptedPayload(unsigned char* encrypted, int length)
|
||||
{
|
||||
unsigned char key = KASA_SMART_INITIALIZATION_VECTOR;
|
||||
for(int i = 0; i < length; ++i)
|
||||
{
|
||||
unsigned char plain_byte = key ^ encrypted[i];
|
||||
key = encrypted[i];
|
||||
encrypted[i] = plain_byte;
|
||||
}
|
||||
}
|
||||
64
Controllers/KasaSmartController/KasaSmartController.h
Normal file
64
Controllers/KasaSmartController/KasaSmartController.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*---------------------------------------------------------*\
|
||||
| Definitions for Kasa Smart |
|
||||
| |
|
||||
| Devin Wendt (umbreon222@gmail.com), 2/16/2023 |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "net_port.h"
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#pragma once
|
||||
|
||||
enum
|
||||
{
|
||||
KASA_SMART_MODE_DIRECT = 0x00,
|
||||
KASA_SMART_MODE_OFF = 0x01
|
||||
};
|
||||
|
||||
#define KASA_SMART_INITIALIZATION_VECTOR 0xAB
|
||||
#define KASA_SMART_RECEIVE_BUFFER_SIZE 4096
|
||||
#define KASA_SMART_MAX_CONNECTION_ATTEMPTS 3
|
||||
|
||||
/*-------------------------*\
|
||||
| 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\""
|
||||
":1, \"mode\":\"normal\", \"hue\": %u, \"saturation\": %u, \"brightness\": %u, \"color_temp\": 0}}}";
|
||||
|
||||
class KasaSmartController
|
||||
{
|
||||
public:
|
||||
KasaSmartController(std::string ipAddress, std::string name);
|
||||
~KasaSmartController();
|
||||
|
||||
std::string GetLocation();
|
||||
std::string GetName();
|
||||
std::string GetVersion();
|
||||
std::string GetManufacturer();
|
||||
std::string GetUniqueID();
|
||||
|
||||
bool Initialize();
|
||||
void SetColor(unsigned char red, unsigned char green, unsigned char blue);
|
||||
void TurnOff();
|
||||
|
||||
private:
|
||||
net_port port;
|
||||
std::string name;
|
||||
bool is_initialized;
|
||||
unsigned int retry_count;
|
||||
std::string firmware_version;
|
||||
std::string module_name;
|
||||
std::string device_id;
|
||||
std::string location;
|
||||
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);
|
||||
static void XorPayload(unsigned char* encrypted, int length);
|
||||
static void XorEncryptedPayload(unsigned char* encrypted, int length);
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#include "Detector.h"
|
||||
#include "KasaSmartController.h"
|
||||
#include "RGBController.h"
|
||||
#include "RGBController_KasaSmart.h"
|
||||
#include "SettingsManager.h"
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* DetectKasaSmartControllers *
|
||||
* *
|
||||
* Detect Kasa Smart devices *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
|
||||
void DetectKasaSmartControllers()
|
||||
{
|
||||
json kasa_smart_settings;
|
||||
|
||||
/*---------------------------------------------*\
|
||||
| Get Kasa Smart settings from settings manager |
|
||||
\*---------------------------------------------*/
|
||||
kasa_smart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("KasaSmartDevices");
|
||||
|
||||
/*---------------------------------------------*\
|
||||
| If the Wiz settings contains devices, process |
|
||||
\*---------------------------------------------*/
|
||||
if(kasa_smart_settings.contains("devices"))
|
||||
{
|
||||
for(unsigned int device_idx = 0; device_idx < kasa_smart_settings["devices"].size(); device_idx++)
|
||||
{
|
||||
if(kasa_smart_settings["devices"][device_idx].contains("ip"))
|
||||
{
|
||||
std::string kasa_smart_ip = kasa_smart_settings["devices"][device_idx]["ip"];
|
||||
std::string name = kasa_smart_settings["devices"][device_idx]["name"];
|
||||
|
||||
KasaSmartController* controller = new KasaSmartController(kasa_smart_ip, name);
|
||||
if(!controller->Initialize())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RGBController_KasaSmart* rgb_controller = new RGBController_KasaSmart(controller);
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* DetectKasaSmartControllers() */
|
||||
|
||||
REGISTER_DETECTOR("KasaSmart", DetectKasaSmartControllers);
|
||||
113
Controllers/KasaSmartController/RGBController_KasaSmart.cpp
Normal file
113
Controllers/KasaSmartController/RGBController_KasaSmart.cpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*-----------------------------------------*\
|
||||
| RGBController_KasaSmart.cpp |
|
||||
| |
|
||||
| Generic RGB Interface for KasaSmart |
|
||||
| |
|
||||
| Devin Wendt (umbreon222) 2/16/2023 |
|
||||
\*-----------------------------------------*/
|
||||
|
||||
#include "RGBController_KasaSmart.h"
|
||||
|
||||
/**------------------------------------------------------------------*\
|
||||
@name Kasa Smart Bulbs
|
||||
@category Light
|
||||
@type USB
|
||||
@save :x:
|
||||
@direct :white_check_mark:
|
||||
@effects :x:
|
||||
@detectors DetectKasaSmartControllers
|
||||
@comment
|
||||
\*-------------------------------------------------------------------*/
|
||||
|
||||
RGBController_KasaSmart::RGBController_KasaSmart(KasaSmartController* controller_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
|
||||
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();
|
||||
|
||||
mode Direct;
|
||||
Direct.name = "Direct";
|
||||
Direct.value = KASA_SMART_MODE_DIRECT;
|
||||
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Direct.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Direct);
|
||||
|
||||
mode Off;
|
||||
Off.name = "Off";
|
||||
Off.value = KASA_SMART_MODE_OFF;
|
||||
Off.flags = 0;
|
||||
Off.color_mode = MODE_COLORS_NONE;
|
||||
modes.push_back(Off);
|
||||
|
||||
SetupZones();
|
||||
}
|
||||
|
||||
RGBController_KasaSmart::~RGBController_KasaSmart()
|
||||
{
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void RGBController_KasaSmart::SetupZones()
|
||||
{
|
||||
zone led_zone;
|
||||
led_zone.name = "RGB Light";
|
||||
led_zone.type = ZONE_TYPE_SINGLE;
|
||||
led_zone.leds_min = 1;
|
||||
led_zone.leds_max = 1;
|
||||
led_zone.leds_count = 1;
|
||||
led_zone.matrix_map = NULL;
|
||||
zones.push_back(led_zone);
|
||||
|
||||
led new_led;
|
||||
new_led.name = "RGB Light";
|
||||
|
||||
leds.push_back(new_led);
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_KasaSmart::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
/*-------------------------------------------*\
|
||||
| This device does not support resizing zones |
|
||||
\*-------------------------------------------*/
|
||||
}
|
||||
|
||||
void RGBController_KasaSmart::DeviceUpdateLEDs()
|
||||
{
|
||||
if(modes[active_mode].value != KASA_SMART_MODE_DIRECT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char red = RGBGetRValue(colors[0]);
|
||||
unsigned char grn = RGBGetGValue(colors[0]);
|
||||
unsigned char blu = RGBGetBValue(colors[0]);
|
||||
|
||||
controller->SetColor(red, grn, blu);
|
||||
}
|
||||
|
||||
void RGBController_KasaSmart::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_KasaSmart::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_KasaSmart::DeviceUpdateMode()
|
||||
{
|
||||
if(modes[active_mode].value == KASA_SMART_MODE_OFF)
|
||||
{
|
||||
controller->TurnOff();
|
||||
return;
|
||||
}
|
||||
}
|
||||
31
Controllers/KasaSmartController/RGBController_KasaSmart.h
Normal file
31
Controllers/KasaSmartController/RGBController_KasaSmart.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*-----------------------------------------*\
|
||||
| RGBController_KasaSmart.h |
|
||||
| |
|
||||
| Generic RGB Interface for Kasa Smart |
|
||||
| |
|
||||
| Devin Wendt (umbreon222) 2/16/2023 |
|
||||
\*-----------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
#include "RGBController.h"
|
||||
#include "KasaSmartController.h"
|
||||
|
||||
class RGBController_KasaSmart : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_KasaSmart(KasaSmartController* controller_ptr);
|
||||
~RGBController_KasaSmart();
|
||||
|
||||
void SetupZones();
|
||||
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
private:
|
||||
KasaSmartController* controller;
|
||||
};
|
||||
11
OpenRGB.pro
11
OpenRGB.pro
|
|
@ -219,6 +219,8 @@ HEADERS +=
|
|||
qt/OpenRGBDialog2.h \
|
||||
qt/OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsEntry.h \
|
||||
qt/OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsPage.h \
|
||||
qt/OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsEntry.h \
|
||||
qt/OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsPage.h \
|
||||
qt/OpenRGBPluginContainer.h \
|
||||
qt/OpenRGBPluginsPage/OpenRGBPluginsEntry.h \
|
||||
qt/OpenRGBPluginsPage/OpenRGBPluginsList.h \
|
||||
|
|
@ -469,6 +471,8 @@ HEADERS +=
|
|||
Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.h \
|
||||
Controllers/IonicoController/IonicoController.h \
|
||||
Controllers/IonicoController/RGBController_Ionico.h \
|
||||
Controllers/KasaSmartController/KasaSmartController.h \
|
||||
Controllers/KasaSmartController/RGBController_KasaSmart.h \
|
||||
Controllers/KeychronKeyboardController/KeychronKeyboardController.h \
|
||||
Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.h \
|
||||
Controllers/LEDStripController/LEDStripController.h \
|
||||
|
|
@ -748,6 +752,8 @@ SOURCES +=
|
|||
qt/OpenRGBDialog2.cpp \
|
||||
qt/OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsEntry.cpp \
|
||||
qt/OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsPage.cpp \
|
||||
qt/OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsEntry.cpp \
|
||||
qt/OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsPage.cpp \
|
||||
qt/OpenRGBPluginContainer.cpp \
|
||||
qt/OpenRGBPluginsPage/OpenRGBPluginsEntry.cpp \
|
||||
qt/OpenRGBPluginsPage/OpenRGBPluginsList.cpp \
|
||||
|
|
@ -1062,6 +1068,9 @@ SOURCES +=
|
|||
Controllers/IonicoController/IonicoControllerDetect.cpp \
|
||||
Controllers/IonicoController/RGBController_Ionico.cpp \
|
||||
Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.cpp \
|
||||
Controllers/KasaSmartController/KasaSmartController.cpp \
|
||||
Controllers/KasaSmartController/KasaSmartControllerDetect.cpp \
|
||||
Controllers/KasaSmartController/RGBController_KasaSmart.cpp \
|
||||
Controllers/KeychronKeyboardController/KeychronKeyboardController.cpp \
|
||||
Controllers/KeychronKeyboardController/KeychronKeyboardControllerDetect.cpp \
|
||||
Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.cpp \
|
||||
|
|
@ -1364,6 +1373,8 @@ FORMS +=
|
|||
qt/OpenRGBE131SettingsPage/OpenRGBE131SettingsPage.ui \
|
||||
qt/OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsEntry.ui \
|
||||
qt/OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsPage.ui \
|
||||
qt/OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsEntry.ui \
|
||||
qt/OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsPage.ui \
|
||||
qt/OpenRGBLIFXSettingsPage/OpenRGBLIFXSettingsEntry.ui \
|
||||
qt/OpenRGBLIFXSettingsPage/OpenRGBLIFXSettingsPage.ui \
|
||||
qt/OpenRGBNanoleafSettingsPage/OpenRGBNanoleafSettingsPage.ui \
|
||||
|
|
|
|||
|
|
@ -437,6 +437,11 @@ OpenRGBDialog2::OpenRGBDialog2(QWidget *parent) : QMainWindow(parent), ui(new Op
|
|||
\*-----------------------------------------------------*/
|
||||
AddE131SettingsPage();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Add the Kasa Smart settings page |
|
||||
\*-----------------------------------------------------*/
|
||||
AddKasaSmartSettingsPage();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Add the LIFX settings page |
|
||||
\*-----------------------------------------------------*/
|
||||
|
|
@ -766,6 +771,34 @@ void OpenRGBDialog2::AddE131SettingsPage()
|
|||
ui->SettingsTabBar->tabBar()->setTabButton(ui->SettingsTabBar->tabBar()->count() - 1, QTabBar::LeftSide, SettingsTabLabel);
|
||||
}
|
||||
|
||||
void OpenRGBDialog2::AddKasaSmartSettingsPage()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Create the Settings page |
|
||||
\*-----------------------------------------------------*/
|
||||
KasaSmartSettingsPage = new OpenRGBKasaSmartSettingsPage();
|
||||
|
||||
ui->SettingsTabBar->addTab(KasaSmartSettingsPage, "");
|
||||
|
||||
QString SettingsLabelString;
|
||||
|
||||
if(OpenRGBThemeManager::IsDarkTheme())
|
||||
{
|
||||
SettingsLabelString = "light_dark.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
SettingsLabelString = "light.png";
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Create the tab label |
|
||||
\*-----------------------------------------------------*/
|
||||
TabLabel* SettingsTabLabel = new TabLabel(SettingsLabelString, tr("Kasa Smart Devices"), (char *)"Kasa Smart Devices", (char *)context);
|
||||
|
||||
ui->SettingsTabBar->tabBar()->setTabButton(ui->SettingsTabBar->tabBar()->count() - 1, QTabBar::LeftSide, SettingsTabLabel);
|
||||
}
|
||||
|
||||
void OpenRGBDialog2::AddLIFXSettingsPage()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "OpenRGBSettingsPage.h"
|
||||
#include "OpenRGBE131SettingsPage/OpenRGBE131SettingsPage.h"
|
||||
#include "OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsPage.h"
|
||||
#include "OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsPage.h"
|
||||
#include "OpenRGBLIFXSettingsPage/OpenRGBLIFXSettingsPage.h"
|
||||
#include "OpenRGBPhilipsHueSettingsPage/OpenRGBPhilipsHueSettingsPage.h"
|
||||
#include "OpenRGBPhilipsWizSettingsPage/OpenRGBPhilipsWizSettingsPage.h"
|
||||
|
|
@ -84,6 +85,7 @@ private:
|
|||
OpenRGBSettingsPage *SettingsPage;
|
||||
OpenRGBE131SettingsPage *E131SettingsPage;
|
||||
OpenRGBElgatoKeyLightSettingsPage *ElgatoKeyLightSettingsPage;
|
||||
OpenRGBKasaSmartSettingsPage *KasaSmartSettingsPage;
|
||||
OpenRGBLIFXSettingsPage *LIFXSettingsPage;
|
||||
OpenRGBPhilipsHueSettingsPage *PhilipsHueSettingsPage;
|
||||
OpenRGBPhilipsWizSettingsPage *PhilipsWizSettingsPage;
|
||||
|
|
@ -111,6 +113,7 @@ private:
|
|||
void AddSettingsPage();
|
||||
void AddE131SettingsPage();
|
||||
void AddElgatoKeyLightSettingsPage();
|
||||
void AddKasaSmartSettingsPage();
|
||||
void AddLIFXSettingsPage();
|
||||
void AddPhilipsHueSettingsPage();
|
||||
void AddPhilipsWizSettingsPage();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
#include "OpenRGBKasaSmartSettingsEntry.h"
|
||||
#include "ui_OpenRGBKasaSmartSettingsEntry.h"
|
||||
|
||||
using namespace Ui;
|
||||
|
||||
OpenRGBKasaSmartSettingsEntry::OpenRGBKasaSmartSettingsEntry(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OpenRGBKasaSmartSettingsEntryUi)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
OpenRGBKasaSmartSettingsEntry::~OpenRGBKasaSmartSettingsEntry()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OpenRGBKasaSmartSettingsEntry::changeEvent(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef OPENRGBKASASMARTSETTINGSENTRY_H
|
||||
#define OPENRGBKASASMARTSETTINGSENTRY_H
|
||||
|
||||
#include "ui_OpenRGBKasaSmartSettingsEntry.h"
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class OpenRGBKasaSmartSettingsEntry;
|
||||
}
|
||||
|
||||
class Ui::OpenRGBKasaSmartSettingsEntry : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenRGBKasaSmartSettingsEntry(QWidget *parent = nullptr);
|
||||
~OpenRGBKasaSmartSettingsEntry();
|
||||
Ui::OpenRGBKasaSmartSettingsEntryUi *ui;
|
||||
|
||||
private slots:
|
||||
void changeEvent(QEvent *event);
|
||||
};
|
||||
|
||||
#endif // OPENRGBKASASMARTSETTINGSENTRY_H
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OpenRGBKasaSmartSettingsEntryUi</class>
|
||||
<widget class="QWidget" name="OpenRGBKasaSmartSettingsEntryUi">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>190</width>
|
||||
<height>89</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="5">
|
||||
<widget class="QLineEdit" name="NameEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="IPLabel">
|
||||
<property name="text">
|
||||
<string>IP:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLineEdit" name="IPEdit"/>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="NameLabel">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>IPEdit</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
120
qt/OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsPage.cpp
Normal file
120
qt/OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsPage.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#include "OpenRGBKasaSmartSettingsPage.h"
|
||||
#include "ui_OpenRGBKasaSmartSettingsPage.h"
|
||||
#include "ResourceManager.h"
|
||||
|
||||
using namespace Ui;
|
||||
|
||||
OpenRGBKasaSmartSettingsPage::OpenRGBKasaSmartSettingsPage(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OpenRGBKasaSmartSettingsPageUi)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
json KasaSmart_settings;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Get KasaSmart settings |
|
||||
\*-------------------------------------------------*/
|
||||
KasaSmart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("KasaSmartDevices");
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| If the Wiz settings contains devices, process |
|
||||
\*-------------------------------------------------*/
|
||||
if(KasaSmart_settings.contains("devices"))
|
||||
{
|
||||
for(unsigned int device_idx = 0; device_idx < KasaSmart_settings["devices"].size(); device_idx++)
|
||||
{
|
||||
OpenRGBKasaSmartSettingsEntry* entry = new OpenRGBKasaSmartSettingsEntry;
|
||||
|
||||
if(KasaSmart_settings["devices"][device_idx].contains("ip"))
|
||||
{
|
||||
entry->ui->IPEdit->setText(QString::fromStdString(KasaSmart_settings["devices"][device_idx]["ip"]));
|
||||
}
|
||||
|
||||
if(KasaSmart_settings["devices"][device_idx].contains("name"))
|
||||
{
|
||||
entry->ui->IPEdit->setText(QString::fromStdString(KasaSmart_settings["devices"][device_idx]["name"]));
|
||||
}
|
||||
|
||||
entries.push_back(entry);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem;
|
||||
|
||||
item->setSizeHint(entry->sizeHint());
|
||||
|
||||
ui->KasaSmartDeviceList->addItem(item);
|
||||
ui->KasaSmartDeviceList->setItemWidget(item, entry);
|
||||
ui->KasaSmartDeviceList->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpenRGBKasaSmartSettingsPage::~OpenRGBKasaSmartSettingsPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OpenRGBKasaSmartSettingsPage::changeEvent(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Ui::OpenRGBKasaSmartSettingsPage::on_AddKasaSmartDeviceButton_clicked()
|
||||
{
|
||||
OpenRGBKasaSmartSettingsEntry* entry = new OpenRGBKasaSmartSettingsEntry;
|
||||
entry->ui->NameEdit->setText(QString("KasaSmart%1").arg(entries.size()));
|
||||
entries.push_back(entry);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem;
|
||||
|
||||
item->setSizeHint(entry->sizeHint());
|
||||
|
||||
ui->KasaSmartDeviceList->addItem(item);
|
||||
ui->KasaSmartDeviceList->setItemWidget(item, entry);
|
||||
ui->KasaSmartDeviceList->show();
|
||||
}
|
||||
|
||||
void Ui::OpenRGBKasaSmartSettingsPage::on_RemoveKasaSmartDeviceButton_clicked()
|
||||
{
|
||||
int cur_row = ui->KasaSmartDeviceList->currentRow();
|
||||
|
||||
if(cur_row < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem* item = ui->KasaSmartDeviceList->takeItem(cur_row);
|
||||
|
||||
ui->KasaSmartDeviceList->removeItemWidget(item);
|
||||
delete item;
|
||||
|
||||
delete entries[cur_row];
|
||||
entries.erase(entries.begin() + cur_row);
|
||||
}
|
||||
|
||||
void Ui::OpenRGBKasaSmartSettingsPage::on_SaveKasaSmartConfigurationButton_clicked()
|
||||
{
|
||||
json KasaSmart_settings;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Get KasaSmart settings |
|
||||
\*-------------------------------------------------*/
|
||||
KasaSmart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("KasaSmartDevices");
|
||||
|
||||
KasaSmart_settings["devices"].clear();
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < entries.size(); device_idx++)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Required parameters |
|
||||
\*-------------------------------------------------*/
|
||||
KasaSmart_settings["devices"][device_idx]["ip"] = entries[device_idx]->ui->IPEdit->text().toStdString();
|
||||
KasaSmart_settings["devices"][device_idx]["name"] = entries[device_idx]->ui->NameEdit->text().toStdString();
|
||||
}
|
||||
|
||||
ResourceManager::get()->GetSettingsManager()->SetSettings("KasaSmartDevices", KasaSmart_settings);
|
||||
ResourceManager::get()->GetSettingsManager()->SaveSettings();
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef OPENRGBKASASMARTSETTINGSPAGE_H
|
||||
#define OPENRGBKASASMARTSETTINGSPAGE_H
|
||||
|
||||
#include "ui_OpenRGBKasaSmartSettingsPage.h"
|
||||
#include <QWidget>
|
||||
|
||||
#include "OpenRGBKasaSmartSettingsEntry.h"
|
||||
|
||||
namespace Ui {
|
||||
class OpenRGBKasaSmartSettingsPage;
|
||||
}
|
||||
|
||||
class Ui::OpenRGBKasaSmartSettingsPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenRGBKasaSmartSettingsPage(QWidget *parent = nullptr);
|
||||
~OpenRGBKasaSmartSettingsPage();
|
||||
|
||||
private slots:
|
||||
void changeEvent(QEvent *event);
|
||||
void on_AddKasaSmartDeviceButton_clicked();
|
||||
|
||||
void on_RemoveKasaSmartDeviceButton_clicked();
|
||||
|
||||
void on_SaveKasaSmartConfigurationButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::OpenRGBKasaSmartSettingsPageUi *ui;
|
||||
std::vector<OpenRGBKasaSmartSettingsEntry*> entries;
|
||||
|
||||
};
|
||||
|
||||
#endif // OPENRGBKASASMARTSETTINGSPAGE_H
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OpenRGBKasaSmartSettingsPageUi</class>
|
||||
<widget class="QWidget" name="OpenRGBKasaSmartSettingsPageUi">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="AddKasaSmartDeviceButton">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="RemoveKasaSmartDeviceButton">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="SaveKasaSmartConfigurationButton">
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QListWidget" name="KasaSmartDeviceList">
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Loading…
Add table
Add a link
Reference in a new issue