Add multizone support for LIFX devices

This commit is contained in:
Devin Wendt 2025-07-03 00:47:07 +00:00 committed by Adam Honse
parent fc6306f44a
commit e4c43548eb
7 changed files with 710 additions and 164 deletions

View file

@ -16,19 +16,22 @@
using json = nlohmann::json;
using namespace std::chrono_literals;
LIFXController::LIFXController(std::string ip, std::string name)
LIFXController::LIFXController(std::string ip, std::string name, bool multizone, bool extended_multizone)
{
this->name = name;
this->name = name;
zone_count = 1;
this->multizone = multizone;
this->extended_multizone = extended_multizone;
/*-----------------------------------------------------------------*\
| Fill in location string with device's IP address |
\*-----------------------------------------------------------------*/
location = "IP: " + ip;
location = "IP: " + ip;
/*-----------------------------------------------------------------*\
| Open a UDP client sending to the device's IP, port 56700 |
\*-----------------------------------------------------------------*/
port.udp_client(ip.c_str(), "56700");
port.udp_client(ip.c_str(), LIFX_UDP_PORT);
}
LIFXController::~LIFXController()
@ -53,7 +56,7 @@ std::string LIFXController::GetVersion()
std::string LIFXController::GetManufacturer()
{
return("LIFX");
return(LIFX_MANUFACTURER);
}
std::string LIFXController::GetUniqueID()
@ -61,97 +64,417 @@ std::string LIFXController::GetUniqueID()
return(module_mac);
}
void LIFXController::SetColor(unsigned char red, unsigned char green, unsigned char blue)
unsigned int LIFXController::GetZoneCount()
{
return(zone_count);
}
void LIFXController::SetColors(std::vector<RGBColor> colors)
{
/*-------------------------*\
| Non-multizone lifx device |
\*-------------------------*/
if(!multizone)
{
SetColor(colors[0]);
return;
}
/*-------------------------------------------*\
| Multizone lifx device with extended support |
\*-------------------------------------------*/
if(extended_multizone)
{
SetZoneColors(colors);
return;
}
/*----------------------------------------------*\
| Multizone lifx device without extended support |
\*----------------------------------------------*/
for(size_t i = 0; i < zone_count; i++)
{
/*-----------------------------------------------------------------*\
| Utilize caching to avoid setting all zones when 1 zone is changed |
\*-----------------------------------------------------------------*/
if(cached_colors[i] == colors[i])
{
continue;
}
SetZoneColor(colors[i], i);
cached_colors[i] = colors[i];
}
}
void LIFXController::FetchZoneCount()
{
if(!multizone)
{
return;
}
/*---------------------------*\
| Send get color zones packet |
\*---------------------------*/
data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_GET_COLOR_ZONES_PACKET_LENGTH;
data = new unsigned char[data_buf_size];
memset(data, 0, data_buf_size);
HeaderPacketSetDefaults(LIFX_PACKET_TYPE_GET_COLOR_ZONES);
GetColorZonesPacketSetStartIndex(0);
GetColorZonesPacketSetEndIndex(0);
port.udp_write((char*)data, data_buf_size);
delete[] data;
/*----------------------------*\
| Listen for state zone packet |
\*----------------------------*/
data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_STATE_ZONE_PACKET_LENGTH;
data = new unsigned char[data_buf_size];
memset(data, 0, data_buf_size);
port.set_receive_timeout(5, 0);
port.udp_listen((char*)data, data_buf_size);
/*-----------------*\
| Validate response |
\*-----------------*/
if(HeaderPacketGetSize() != data_buf_size || HeaderPacketGetProtocol() != LIFX_PROTOCOL || HeaderPacketGetPacketType() != LIFX_PACKET_TYPE_STATE_ZONE)
{
return;
}
zone_count = StateZonePacketGetZonesCount();
delete[] data;
}
void LIFXController::SetColor(RGBColor color)
{
/*---------------------*\
| Send set color packet |
\*---------------------*/
data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_SET_COLOR_PACKET_LENGTH;
data = new unsigned char[data_buf_size];
memset(data, 0, data_buf_size);
HeaderPacketSetDefaults(LIFX_PACKET_TYPE_SET_COLOR);
hsbk_t hsbk;
RGBColorToHSBK(color, &hsbk);
SetColorPacketSetHSBK(&hsbk);
SetColorPacketSetDuration(0);
port.udp_write((char*)data, data_buf_size);
delete[] data;
}
void LIFXController::SetZoneColor(RGBColor color, unsigned int zone)
{
/*---------------------------*\
| Send set color zones packet |
\*---------------------------*/
data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_SET_COLOR_ZONES_PACKET_LENGTH;
data = new unsigned char[data_buf_size];
memset(data, 0, data_buf_size);
HeaderPacketSetDefaults(LIFX_PACKET_TYPE_SET_COLOR_ZONES);
SetColorZonesPacketSetStartIndex(zone);
SetColorZonesPacketSetEndIndex(zone);
hsbk_t hsbk;
RGBColorToHSBK(color, &hsbk);
SetColorZonesPacketSetHSBK(&hsbk);
SetColorZonesPacketSetDuration(0);
SetColorZonesPacketSetApply(LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY);
port.udp_write((char*)data, data_buf_size);
delete[] data;
}
void LIFXController::SetZoneColors(std::vector<RGBColor> colors)
{
/*------------------------------------*\
| Send set extended color zones packet |
\*------------------------------------*/
data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_LENGTH;
data = new unsigned char[data_buf_size];
memset(data, 0, data_buf_size);
HeaderPacketSetDefaults(LIFX_PACKET_TYPE_SET_EXTENDED_COLOR_ZONES);
SetExtendedColorZonesPacketSetDuration(0);
SetExtendedColorZonesPacketSetApply(LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY);
SetExtendedColorZonesPacketSetZoneIndex(0);
SetExtendedColorZonesPacketSetColors(colors);
port.udp_write((char*)data, data_buf_size);
delete[] data;
}
void LIFXController::RGBColorToHSBK(RGBColor color, hsbk_t* hsbk)
{
RGBColor color = ToRGBColor(red, green, blue);
hsv_t hsv;
rgb2hsv(color, &hsv);
data = data_buf;
memset( data, 0, 49 );
source = 2;
sequence = 1;
unsigned char target[8] = {0};
FrameHeader( 49, true, false, 0, source );
FrameAddress( target, false, false, sequence );
ProtocolAddress( 102 );
unsigned char * set_color = &data[36];
unsigned short hue = hsv.hue * (65536/360);
unsigned short saturation = hsv.saturation * (65536/256);
unsigned short brightness = hsv.value * (65536/256);
unsigned short kelvin = 3500;
unsigned int duration = 0;
memcpy(&set_color[LIFX_SET_COLOR_OFFSET_HUE], &hue, sizeof(unsigned short));
memcpy(&set_color[LIFX_SET_COLOR_OFFSET_SATURATION], &saturation, sizeof(unsigned short));
memcpy(&set_color[LIFX_SET_COLOR_OFFSET_BRIGHTNESS], &brightness, sizeof(unsigned short));
memcpy(&set_color[LIFX_SET_COLOR_OFFSET_KELVIN], &kelvin, sizeof(unsigned short));
memcpy(&set_color[LIFX_SET_COLOR_OFFSET_DURATION], &duration, sizeof(unsigned int));
port.udp_write((char *)data, 49);
hsbk->hue = hsv.hue * (USHRT_MAX/360);
hsbk->saturation = hsv.saturation * (USHRT_MAX/256);
hsbk->brightness = hsv.value * (USHRT_MAX/256);
hsbk->kelvin = DEFAULT_KELVIN;
}
void LIFXController::FrameHeader
(
unsigned short size,
bool addressable,
bool tagged,
unsigned char origin,
unsigned int source
)
/*----------------------------*\
| Header packet helper methods |
\*----------------------------*/
void LIFXController::HeaderPacketSetDefaults(unsigned short packet_type)
{
unsigned short protocol = 1024;
memcpy(&data[LIFX_FRAME_HEADER_OFFSET_SIZE], &size, sizeof(unsigned short));
memcpy(&data[LIFX_FRAME_HEADER_OFFSET_PROTOCOL], &protocol, sizeof(unsigned short));
/*-----*\
| Frame |
\*-----*/
HeaderPacketSetSize(data_buf_size);
HeaderPacketSetProtocol();
HeaderPacketSetAddressable(true);
HeaderPacketSetTagged(false);
HeaderPacketSetOrigin(0);
HeaderPacketSetSource(2);
/*-------------*\
| Frame address |
\*-------------*/
unsigned char target[TARGET_LENGTH] = {0};
HeaderPacketSetTarget(target);
HeaderPacketSetResponseRequired(false);
HeaderPacketSetAcknowledgeRequired(false);
HeaderPacketSetSequence(++sequence);
/*---------------*\
| Protocol header |
\*---------------*/
HeaderPacketSetPacketType(packet_type);
}
unsigned short LIFXController::HeaderPacketGetSize()
{
return data[LIFX_HEADER_PACKET_OFFSET_SIZE];
}
void LIFXController::HeaderPacketSetSize(unsigned short size)
{
memcpy(&data[LIFX_HEADER_PACKET_OFFSET_SIZE], &size, sizeof(unsigned short));
}
unsigned short LIFXController::HeaderPacketGetProtocol()
{
unsigned short protocol;
memcpy(&protocol, &data[LIFX_HEADER_PACKET_OFFSET_PROTOCOL], sizeof(unsigned short));
return protocol & 0x0FFF;
}
void LIFXController::HeaderPacketSetProtocol(unsigned short protocol)
{
data[LIFX_HEADER_PACKET_OFFSET_PROTOCOL] = protocol & 0xFF;
unsigned char current = data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN];
data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] = (current & 0xF0) | ((protocol >> 8) & 0x0F);
}
void LIFXController::HeaderPacketSetAddressable(bool addressable)
{
if(addressable)
{
data[LIFX_FRAME_HEADER_OFFSET_FLAGS] |= (1 << 4);
data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] |= 0x10;
}
else
{
data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] &= ~0x10;
}
}
void LIFXController::HeaderPacketSetTagged(bool tagged)
{
if(tagged)
{
data[LIFX_FRAME_HEADER_OFFSET_FLAGS] |= (1 << 5);
data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] |= 0x20;
}
data[LIFX_FRAME_HEADER_OFFSET_FLAGS] |= (origin << 6);
memcpy(&data[LIFX_FRAME_HEADER_OFFSET_SOURCE], &source, sizeof(unsigned int));
}
void LIFXController::FrameAddress
(
unsigned char * target,
bool res_required,
bool ack_required,
unsigned char sequence
)
{
memcpy(&data[LIFX_FRAME_ADDRESS_OFFSET_TARGET], target, 8);
data[LIFX_FRAME_ADDRESS_OFFSET_FLAGS] = 0;
if(res_required)
else
{
data[LIFX_FRAME_ADDRESS_OFFSET_FLAGS] |= (1 << 0);
data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] &= ~0x20;
}
if(ack_required)
{
data[LIFX_FRAME_ADDRESS_OFFSET_FLAGS] |= (1 << 1);
}
data[LIFX_FRAME_ADDRESS_OFFSET_SEQUENCE] = sequence;
}
void LIFXController::ProtocolAddress
(
unsigned short type
)
void LIFXController::HeaderPacketSetOrigin(unsigned char origin)
{
memcpy(&data[LIFX_PROTOCOL_HEADER_OFFSET_TYPE], &type, sizeof(unsigned short));
data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] =
(data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] & 0xFC) | (origin & 0x03);
}
void LIFXController::HeaderPacketSetSource(unsigned int source)
{
memcpy(&data[LIFX_HEADER_PACKET_OFFSET_SOURCE], &source, sizeof(unsigned int));
}
void LIFXController::HeaderPacketSetTarget(unsigned char* target)
{
memcpy(&data[LIFX_HEADER_PACKET_OFFSET_TARGET], target, TARGET_LENGTH);
}
void LIFXController::HeaderPacketSetResponseRequired(bool response_required)
{
if(response_required)
{
data[LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED] |= 0x01;
}
else
{
data[LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED] &= ~0x01;
}
}
void LIFXController::HeaderPacketSetAcknowledgeRequired(bool acknowledge_required)
{
if(acknowledge_required)
{
data[LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED] |= 0x02;
}
else
{
data[LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED] &= ~0x02;
}
}
void LIFXController::HeaderPacketSetSequence(unsigned char sequence)
{
data[LIFX_HEADER_PACKET_OFFSET_SEQUENCE] = sequence;
}
unsigned short LIFXController::HeaderPacketGetPacketType()
{
unsigned short packet_type_value;
memcpy(&packet_type_value, &data[LIFX_HEADER_PACKET_OFFSET_PACKET_TYPE], sizeof(unsigned short));
return packet_type_value;
}
void LIFXController::HeaderPacketSetPacketType(unsigned short packet_type)
{
memcpy(&data[LIFX_HEADER_PACKET_OFFSET_PACKET_TYPE], &packet_type, sizeof(unsigned short));
}
/*-------------------------------*\
| Set color packet helper methods |
\*-------------------------------*/
void LIFXController::SetColorPacketSetHSBK(hsbk_t* hsbk)
{
memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_HUE], &hsbk->hue, sizeof(unsigned short));
memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_SATURATION], &hsbk->saturation, sizeof(unsigned short));
memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_BRIGHTNESS], &hsbk->brightness, sizeof(unsigned short));
memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_KELVIN], &hsbk->kelvin, sizeof(unsigned short));
}
void LIFXController::SetColorPacketSetDuration(unsigned int duration)
{
memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_DURATION], &duration, sizeof(unsigned int));
}
/*-------------------------------------*\
| Set color zones packet helper methods |
\*-------------------------------------*/
void LIFXController::SetColorZonesPacketSetStartIndex(unsigned char start_index)
{
memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_START_INDEX], &start_index, sizeof(unsigned char));
}
void LIFXController::SetColorZonesPacketSetEndIndex(unsigned char end_index)
{
memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_END_INDEX], &end_index, sizeof(unsigned char));
}
void LIFXController::SetColorZonesPacketSetHSBK(hsbk_t* hsbk)
{
memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_HUE], &hsbk->hue, sizeof(unsigned short));
memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_SATURATION], &hsbk->saturation, sizeof(unsigned short));
memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_BRIGHTNESS], &hsbk->brightness, sizeof(unsigned short));
memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_KELVIN], &hsbk->kelvin, sizeof(unsigned short));
}
void LIFXController::SetColorZonesPacketSetDuration(unsigned int duration)
{
memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_DURATION], &duration, sizeof(unsigned int));
}
void LIFXController::SetColorZonesPacketSetApply(unsigned char apply)
{
memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_APPLY], &apply, sizeof(unsigned char));
}
/*-------------------------------------*\
| Get color zones packet helper methods |
\*-------------------------------------*/
void LIFXController::GetColorZonesPacketSetStartIndex(unsigned char start_index)
{
memcpy(&data[LIFX_GET_COLOR_ZONES_PACKET_OFFSET_START_INDEX], &start_index, sizeof(unsigned char));
}
void LIFXController::GetColorZonesPacketSetEndIndex(unsigned char end_index)
{
memcpy(&data[LIFX_GET_COLOR_ZONES_PACKET_OFFSET_END_INDEX], &end_index, sizeof(unsigned char));
}
/*--------------------------------*\
| State zone packet helper methods |
\*--------------------------------*/
unsigned char LIFXController::StateZonePacketGetZonesCount()
{
unsigned char zones_count;
memcpy(&zones_count, &data[LIFX_STATE_ZONE_PACKET_OFFSET_ZONES_COUNT], sizeof(unsigned char));
return zones_count;
}
/*----------------------------------------------*\
| Set extended color zones packet helper methods |
\*----------------------------------------------*/
void LIFXController::SetExtendedColorZonesPacketSetDuration(unsigned int duration)
{
memcpy(&data[LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_DURATION], &duration, sizeof(unsigned int));
}
void LIFXController::SetExtendedColorZonesPacketSetApply(unsigned char apply)
{
memcpy(&data[LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_APPLY], &apply, sizeof(unsigned char));
}
void LIFXController::SetExtendedColorZonesPacketSetZoneIndex(unsigned short zone_index)
{
memcpy(&data[LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_ZONE_INDEX], &zone_index, sizeof(unsigned short));
}
void LIFXController::SetExtendedColorZonesPacketSetColors(std::vector<RGBColor> colors)
{
unsigned char colors_count = colors.size();
memcpy(&data[LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_COLORS_COUNT], &colors_count, sizeof(unsigned char));
for(size_t i = 0; i < colors.size(); i++)
{
hsbk_t hsbk;
RGBColorToHSBK(colors[i], &hsbk);
size_t current_color_offset = LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_COLORS + (i * HSBK_LENGTH);
size_t hue_offset = current_color_offset;
size_t saturation_offset = hue_offset + sizeof(unsigned short);
size_t brightness_offset = saturation_offset + sizeof(unsigned short);
size_t kelvin_offset = brightness_offset + sizeof(unsigned short);
memcpy(&data[hue_offset], &hsbk.hue, sizeof(unsigned short));
memcpy(&data[saturation_offset], &hsbk.saturation, sizeof(unsigned short));
memcpy(&data[brightness_offset], &hsbk.brightness, sizeof(unsigned short));
memcpy(&data[kelvin_offset], &hsbk.kelvin, sizeof(unsigned short));
}
}

View file

@ -12,85 +12,229 @@
#pragma once
#include <string>
#include <thread>
#include <vector>
#include <climits>
#include "RGBController.h"
#include "net_port.h"
#define LIFX_MANUFACTURER "LIFX"
#define LIFX_UDP_PORT "56700"
#define LIFX_PROTOCOL 1024
#define TARGET_LENGTH 8
#define DEFAULT_KELVIN 3500
#define HSBK_LENGTH 8
/*---------------------*\
| Packet size constants |
\*---------------------*/
#define LIFX_PACKET_HEADER_LENGTH 36
#define LIFX_SET_COLOR_PACKET_LENGTH 13
#define LIFX_SET_COLOR_ZONES_PACKET_LENGTH 15
#define LIFX_GET_COLOR_ZONES_PACKET_LENGTH 2
#define LIFX_STATE_ZONE_PACKET_LENGTH 10
#define LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_LENGTH 664
/*---------------------------------------------------------------------------*\
| https://lan.developer.lifx.com/docs/field-types#multizoneapplicationrequest |
\*---------------------------------------------------------------------------*/
enum
{
LIFX_FRAME_HEADER_OFFSET_SIZE = 0, /* 2 bytes, size of the entire message in bytes */
LIFX_FRAME_HEADER_OFFSET_PROTOCOL = 2, /* Protocol number, must be 1024 */
LIFX_FRAME_HEADER_OFFSET_FLAGS = 3, /* Bits 0-3 are part of Protocol */
/* Bit 4, addressable flag */
/* Bit 5, tagged flag */
/* Bit 6/7, origin value */
LIFX_FRAME_HEADER_OFFSET_SOURCE = 4, /* Source identifier, unique value set by client*/
LIFX_FRAME_ADDRESS_OFFSET_TARGET = 8, /* 6 byte device address (MAC) or zero */
/* Last two bytes should be 0 */
LIFX_FRAME_ADDRESS_OFFSET_FLAGS = 22, /* Bit 0, res_required flag */
/* Bit 1, ack_required flag */
LIFX_FRAME_ADDRESS_OFFSET_SEQUENCE = 23, /* Wrap around message sequence number */
LIFX_PROTOCOL_HEADER_OFFSET_TYPE = 32, /* Message type determines the payload used */
LIFX_MULTIZONE_APPLICATION_REQUEST_NO_APPLY = 0,
LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY = 1,
LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY_ONLY = 2
};
/*----------------------------------------------------------------*\
| https://lan.developer.lifx.com/docs/representing-color-with-hsbk |
\*----------------------------------------------------------------*/
typedef struct
{
unsigned short hue; /* 0-360 value normalized to 0-65535 */
unsigned short saturation; /* 0-1 value normalized to 0-65535 */
unsigned short brightness; /* 0-1 value normalized to 0-65535 */
unsigned short kelvin; /* 0-65535 value */
/* Note: Devices may only support a subset of the full range. */
} hsbk_t;
/*-----------------*\
| LIFX packet types |
\*-----------------*/
enum
{
LIFX_SET_COLOR_OFFSET_HUE = 1, /* 16-bit hue value */
LIFX_SET_COLOR_OFFSET_SATURATION = 3, /* 16-bit saturation value */
LIFX_SET_COLOR_OFFSET_BRIGHTNESS = 5, /* 16-bit brightness value */
LIFX_SET_COLOR_OFFSET_KELVIN = 7, /* 16-bit kelvin value */
LIFX_SET_COLOR_OFFSET_DURATION = 9, /* 32-bit brightness value */
LIFX_PACKET_TYPE_SET_COLOR = 102,
LIFX_PACKET_TYPE_SET_COLOR_ZONES = 501,
LIFX_PACKET_TYPE_GET_COLOR_ZONES = 502,
LIFX_PACKET_TYPE_STATE_ZONE = 503,
LIFX_PACKET_TYPE_SET_EXTENDED_COLOR_ZONES = 510
};
/*-----------------------------------------------------*\
| LIFX header packet offsets |
| https://lan.developer.lifx.com/docs/encoding-a-packet |
\*-----------------------------------------------------*/
enum
{
LIFX_HEADER_PACKET_OFFSET_SIZE = 0, /* 2 bytes, size of the entire message in bytes */
LIFX_HEADER_PACKET_OFFSET_PROTOCOL = 2, /* Protocol number, must be 1024 */
LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN = 3, /* Bits 0-3 are part of Protocol */
/* Bit 4, addressable flag */
/* Bit 5, tagged flag */
/* Bit 6/7, origin value */
LIFX_HEADER_PACKET_OFFSET_SOURCE = 4, /* Source identifier, unique value set by client */
LIFX_HEADER_PACKET_OFFSET_TARGET = 8, /* 6 byte device address (MAC) or zero */
/* Last two bytes should be 0 */
LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED = 22, /* Bit 0, res_required flag */
/* Bit 1, ack_required flag */
LIFX_HEADER_PACKET_OFFSET_SEQUENCE = 23, /* Wrap around message sequence number */
LIFX_HEADER_PACKET_OFFSET_PACKET_TYPE = 32 /* Message type determines the payload used */
};
/*---------------------------------------------------------------------------*\
| LIFX set color packet offsets |
| https://lan.developer.lifx.com/docs/changing-a-device#setcolor---packet-102 |
\*---------------------------------------------------------------------------*/
enum
{
/* 1 byte, reserved */
LIFX_SET_COLOR_PACKET_OFFSET_HUE = LIFX_PACKET_HEADER_LENGTH + 1, /* 2 bytes, hue as a 0-65535 value */
LIFX_SET_COLOR_PACKET_OFFSET_SATURATION = LIFX_PACKET_HEADER_LENGTH + 3, /* 2 bytes, saturation as a 0-65535 value */
LIFX_SET_COLOR_PACKET_OFFSET_BRIGHTNESS = LIFX_PACKET_HEADER_LENGTH + 5, /* 2 bytes, brightness as a 0-65535 value */
LIFX_SET_COLOR_PACKET_OFFSET_KELVIN = LIFX_PACKET_HEADER_LENGTH + 7, /* 2 bytes, kelvin as a 0-65535 value. */
/* Note: The actual max for this is device specific */
LIFX_SET_COLOR_PACKET_OFFSET_DURATION = LIFX_PACKET_HEADER_LENGTH + 9, /* 4 bytes, transition time in ms */
};
/*--------------------------------------------------------------------------------*\
| LIFX set color zones packet offsets |
| https://lan.developer.lifx.com/docs/changing-a-device#setcolorzones---packet-501 |
\*--------------------------------------------------------------------------------*/
enum
{
LIFX_SET_COLOR_ZONES_PACKET_OFFSET_START_INDEX = LIFX_PACKET_HEADER_LENGTH + 0, /* 1 byte, the first zone in the segment we are changing */
LIFX_SET_COLOR_ZONES_PACKET_OFFSET_END_INDEX = LIFX_PACKET_HEADER_LENGTH + 1, /* 1 byte, the last zone in the segment we are changing */
LIFX_SET_COLOR_ZONES_PACKET_OFFSET_HUE = LIFX_PACKET_HEADER_LENGTH + 2, /* 2 bytes, hue as a 0-65535 value */
LIFX_SET_COLOR_ZONES_PACKET_OFFSET_SATURATION = LIFX_PACKET_HEADER_LENGTH + 4, /* 2 bytes, saturation as a 0-65535 value */
LIFX_SET_COLOR_ZONES_PACKET_OFFSET_BRIGHTNESS = LIFX_PACKET_HEADER_LENGTH + 6, /* 2 bytes, brightness as a 0-65535 value */
LIFX_SET_COLOR_ZONES_PACKET_OFFSET_KELVIN = LIFX_PACKET_HEADER_LENGTH + 8, /* 2 bytes, kelvin as a 0-65535 value. */
/* Note: The actual max for this is device specific */
LIFX_SET_COLOR_ZONES_PACKET_OFFSET_DURATION = LIFX_PACKET_HEADER_LENGTH + 10, /* 4 bytes, transition time in ms */
LIFX_SET_COLOR_ZONES_PACKET_OFFSET_APPLY = LIFX_PACKET_HEADER_LENGTH + 14 /* 1 byte, multizone application request */
};
/*-------------------------------------------------------------------------------------------*\
| LIFX get color zones packet offsets |
| https://lan.developer.lifx.com/docs/querying-the-device-for-data#getcolorzones---packet-502 |
\*-------------------------------------------------------------------------------------------*/
enum
{
LIFX_GET_COLOR_ZONES_PACKET_OFFSET_START_INDEX = LIFX_PACKET_HEADER_LENGTH + 0, /* 1 byte, The first zone you want to get information from */
LIFX_GET_COLOR_ZONES_PACKET_OFFSET_END_INDEX = LIFX_PACKET_HEADER_LENGTH + 1, /* 1 byte, The second zone you want to get information from */
};
/*-------------------------------------------------------------------------------*\
| LIFX state zone packet offsets |
| https://lan.developer.lifx.com/docs/information-messages#statezone---packet-503 |
\*-------------------------------------------------------------------------------*/
enum
{
LIFX_STATE_ZONE_PACKET_OFFSET_ZONES_COUNT = LIFX_PACKET_HEADER_LENGTH + 0, /* 1 byte, the total number of zones on the strip. */
LIFX_STATE_ZONE_PACKET_OFFSET_ZONE_INDEX = LIFX_PACKET_HEADER_LENGTH + 1, /* 1 byte, the zone this packet refers to. */
LIFX_STATE_ZONE_PACKET_OFFSET_HUE = LIFX_PACKET_HEADER_LENGTH + 2, /* 2 bytes, hue as a 0-65535 value */
LIFX_STATE_ZONE_PACKET_OFFSET_SATURATION = LIFX_PACKET_HEADER_LENGTH + 4, /* 2 bytes, saturation as a 0-65535 value */
LIFX_STATE_ZONE_PACKET_OFFSET_BRIGHTNESS = LIFX_PACKET_HEADER_LENGTH + 6, /* 2 bytes, brightness as a 0-65535 value */
LIFX_STATE_ZONE_PACKET_OFFSET_KELVIN = LIFX_PACKET_HEADER_LENGTH + 8, /* 2 bytes, kelvin as a 0-65535 value. */
/* Note: The actual max for this is device specific */
};
/*----------------------------------------------------------------------------------------*\
| LIFX set extended color zones packet offsets |
| https://lan.developer.lifx.com/docs/changing-a-device#setextendedcolorzones---packet-510 |
\*----------------------------------------------------------------------------------------*/
enum
{
LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_DURATION = LIFX_PACKET_HEADER_LENGTH + 0, /* 4 bytes, transition time in ms */
LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_APPLY = LIFX_PACKET_HEADER_LENGTH + 4, /* 1 byte, multizone application request */
LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_ZONE_INDEX = LIFX_PACKET_HEADER_LENGTH + 5, /* 2 bytes, The first zone to apply colors from. */
/* If the light has more than 82 zones, then */
/* send multiple messages with different indices */
/* to update the whole device. */
LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_COLORS_COUNT = LIFX_PACKET_HEADER_LENGTH + 7, /* 1 byte, The number of colors in the colors field */
LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_COLORS = LIFX_PACKET_HEADER_LENGTH + 8, /* 656 bytes (82 * 4 * 2), 82 HSBK values to change */
/* the device with */
};
class LIFXController
{
public:
LIFXController(std::string ip, std::string name);
LIFXController(std::string ip, std::string name, bool multizone, bool extended_multizone);
~LIFXController();
std::string GetLocation();
std::string GetName();
std::string GetVersion();
std::string GetManufacturer();
std::string GetUniqueID();
std::string GetLocation();
std::string GetName();
std::string GetVersion();
std::string GetManufacturer();
std::string GetUniqueID();
unsigned int GetZoneCount();
void SetColor(unsigned char red, unsigned char green, unsigned char blue);
void FetchZoneCount();
void SetColors(std::vector<RGBColor> colors);
private:
unsigned char data_buf[49];
unsigned char* data;
unsigned char sequence;
unsigned int source;
std::string name;
std::string firmware_version;
std::string module_name;
std::string module_mac;
std::string location;
net_port port;
RGBColor cached_colors[UCHAR_MAX];
unsigned int zone_count;
size_t data_buf_size;
unsigned char* data;
unsigned char sequence;
std::string name;
std::string firmware_version;
std::string module_name;
std::string module_mac;
std::string location;
net_port port;
bool multizone;
bool extended_multizone;
/*-----------------------------------------------------*\
| Functions for filling in LIFX header |
\*-----------------------------------------------------*/
void FrameHeader
(
unsigned short size,
bool addressable,
bool tagged,
unsigned char origin,
unsigned int source
);
void SetColor(RGBColor color);
void SetZoneColor(RGBColor color, unsigned int zone);
void SetZoneColors(std::vector<RGBColor> colors);
void RGBColorToHSBK(RGBColor color, hsbk_t* hsbk);
void FrameAddress
(
unsigned char * target,
bool res_required,
bool ack_required,
unsigned char sequence
);
/*---------------------*\
| Packet helper methods |
\*---------------------*/
void HeaderPacketSetDefaults(unsigned short packet_type);
unsigned short HeaderPacketGetSize();
void HeaderPacketSetSize(unsigned short size);
unsigned short HeaderPacketGetProtocol();
void HeaderPacketSetProtocol(unsigned short protocol=LIFX_PROTOCOL);
void HeaderPacketSetAddressable(bool addressable=true);
void HeaderPacketSetTagged(bool tagged=false);
void HeaderPacketSetOrigin(unsigned char origin=0);
void HeaderPacketSetSource(unsigned int source=2);
void HeaderPacketSetTarget(unsigned char* target);
void HeaderPacketSetResponseRequired(bool response_required=false);
void HeaderPacketSetAcknowledgeRequired(bool acknowledge_required=false);
void HeaderPacketSetSequence(unsigned char sequence);
unsigned short HeaderPacketGetPacketType();
void HeaderPacketSetPacketType(unsigned short packet_type);
void ProtocolAddress
(
unsigned short type
);
void SetColorPacketSetDuration(unsigned int duration=0);
void SetColorPacketSetHSBK(hsbk_t* hsbk);
void SetColorZonesPacketSetStartIndex(unsigned char start_index);
void SetColorZonesPacketSetEndIndex(unsigned char end_index);
void SetColorZonesPacketSetHSBK(hsbk_t* hsbk);
void SetColorZonesPacketSetDuration(unsigned int duration=0);
void SetColorZonesPacketSetApply(unsigned char apply=LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY);
void GetColorZonesPacketSetStartIndex(unsigned char start_index=0);
void GetColorZonesPacketSetEndIndex(unsigned char end_index=0);
unsigned char StateZonePacketGetZonesCount();
void SetExtendedColorZonesPacketSetDuration(unsigned int duration=0);
void SetExtendedColorZonesPacketSetApply(unsigned char apply=LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY);
void SetExtendedColorZonesPacketSetZoneIndex(unsigned short zone_index=0);
void SetExtendedColorZonesPacketSetColors(std::vector<RGBColor> colors);
};

View file

@ -40,10 +40,14 @@ void DetectLIFXControllers()
{
if(lifx_settings["devices"][device_idx].contains("ip"))
{
std::string lifx_ip = lifx_settings["devices"][device_idx]["ip"];
std::string name = lifx_settings["devices"][device_idx]["name"];
std::string lifx_ip = lifx_settings["devices"][device_idx]["ip"];
std::string name = lifx_settings["devices"][device_idx]["name"];
bool multizone = lifx_settings["devices"][device_idx]["multizone"];
bool extended_multizone = lifx_settings["devices"][device_idx]["extended_multizone"];
LIFXController* controller = new LIFXController(lifx_ip, name, multizone, extended_multizone);
controller->FetchZoneCount();
LIFXController* controller = new LIFXController(lifx_ip, name);
RGBController_LIFX* rgb_controller = new RGBController_LIFX(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);

View file

@ -52,18 +52,54 @@ RGBController_LIFX::~RGBController_LIFX()
void RGBController_LIFX::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";
unsigned int zone_count = controller->GetZoneCount();
leds.push_back(new_led);
/*---------------------------------------------------------*\
| If there is only one zone, set up a single LED |
\*---------------------------------------------------------*/
if(zone_count <= 1)
{
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);
}
else
{
/*---------------------------------------------------------*\
| Set up multiple LEDs |
\*---------------------------------------------------------*/
led_zone.name = "RGB Light Strip";
led_zone.type = ZONE_TYPE_LINEAR;
led_zone.leds_min = 1;
led_zone.leds_max = zone_count;
led_zone.leds_count = zone_count;
led_zone.matrix_map = NULL;
zones.push_back(led_zone);
for(size_t zone_idx = 0; zone_idx < zone_count; zone_idx++)
{
/*---------------------------------------------------------*\
| Set up LEDs |
\*---------------------------------------------------------*/
led new_led;
new_led.name = "LED " + std::to_string(zone_idx);
leds.push_back(new_led);
}
}
SetupColors();
}
@ -77,11 +113,7 @@ void RGBController_LIFX::ResizeZone(int /*zone*/, int /*new_size*/)
void RGBController_LIFX::DeviceUpdateLEDs()
{
unsigned char red = RGBGetRValue(colors[0]);
unsigned char grn = RGBGetGValue(colors[0]);
unsigned char blu = RGBGetBValue(colors[0]);
controller->SetColor(red, grn, blu);
controller->SetColors(colors);
}
void RGBController_LIFX::UpdateZoneLEDs(int /*zone*/)