Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Model #|Name|Compatible Bulbs
|FUT092|RGB/CCT|<ol><li>FUT012</li><li>FUT013</li><li>FUT014</li><li>FUT015</li><li>FUT103</li><li>FUT104</li><li>FUT105</li><li>Many RGB/CCT LED Strip Controllers</li></ol>|
|FUT091|CCT v2|Most newer dual white bulbs and controllers|
|FUT089|8-zone RGB/CCT|Most newer rgb + dual white bulbs and controllers|
|Casalux|CCT|sold by Aldi in Germany
|NLG|CCT|Neuhaus Lightning Group, Paul Neuhaus

Other remotes or bulbs, but have not been tested.

Expand Down
4 changes: 2 additions & 2 deletions dist/index.html.gz.h

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions lib/Helpers/bitstreamConvert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <Arduino.h>

#ifndef _BITSTREAMCONVERT_H
#define _BITSTREAMCONVERT_H

class BitstreamConvert {
public:
uint8_t sizeOut = 0;

BitstreamConvert(uint8_t sourceSize, uint8_t destSize){
src_bitlength = sourceSize;
dst_bitlength = destSize;
mask = (1 << dst_bitlength) -1;
mask <<= 32 -dst_bitlength;
}

bool convert(uint16_t input[], uint8_t size){
#ifdef DEBUG_PRINTF
printf("converting: %d values %d-bit values\n", size, src_bitlength);
#endif
for(uint8_t i = 0; i < size; i++){
push(input[i]);
}
return true;
}

bool convert(uint8_t input[], uint8_t size){
#ifdef DEBUG_PRINTF
printf("converting: %d %d-bit values\n", size, src_bitlength);
#endif
for(uint8_t i = 0; i < size; i++){
push(input[i]);
}
return true;
}


uint16_t * get(){
#ifdef DEBUG_PRINTF
printf("get %d %d-bit values\n", sizeOut, dst_bitlength);
#endif
return output;
};

private:
uint16_t output[32];
uint8_t bufferPos = 0;
uint32_t buffer = 0x00000000;

uint8_t src_bitlength;
uint8_t dst_bitlength;
uint32_t mask;
uint32_t outmask;

void push(uint16_t data){
bufferPos += src_bitlength;

buffer |= data << (32 -bufferPos);

while(bufferPos >= dst_bitlength){
uint32_t outbyte = (buffer & mask) >> (32 -dst_bitlength);
output[sizeOut++] = outbyte;
buffer <<= dst_bitlength;
bufferPos -= dst_bitlength;
}
}
};


#endif
198 changes: 198 additions & 0 deletions lib/MiLight/CasaluxPacketFormatter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#include <CasaluxPacketFormatter.h>
#include <MiLightCommands.h>

bool CasaluxPacketFormatter::canHandle(const uint8_t *packet, const size_t len) {
return len == packetLength && packet[1] == 0x01;
}

void CasaluxPacketFormatter::initializePacket(uint8_t* packet) {
size_t packetPtr = 0;

// Byte 0: Packet length = 10 bytes

// Byte 1: Bulb command, filled in later
packet[packetPtr++] = 0;

// Byte 2: Casalux protocol
packet[packetPtr++] = 0x01;

// Byte 3: 11
packet[packetPtr++] = 0x11;

// Byte 4 and 5: Device ID
packet[packetPtr++] = deviceId >> 8;
packet[packetPtr++] = deviceId & 0xFF;

// Byte 6: Zone
packet[packetPtr++] = groupToGroupId(groupId);

// Byte 7: Zero
packet[packetPtr++] = 0;

// Byte 8: Packet sequence number 0..255
packet[packetPtr++] = ++sequenceNum;

// Byte 9 +10: Checksum over previous bytes, including packet length = 7
// The checksum will be calculated when setting the command field
packet[packetPtr++] = 0;
packet[packetPtr++] = 0;

// Byte 11: CRC LSB
// Byte 12: CRC MSB
}

void CasaluxPacketFormatter::finalizePacket(uint8_t* packet) {
uint16_t checksum;

// Calculate checksum over packet length .. sequenceNum
checksum = packetLength; // Packet length is not part of packet
for (uint8_t i = 0; i < packetLength; i++) {
checksum += currentPacket[i];
}
// Store the checksum in the 9th, 10th bytes
currentPacket[8] = checksum >> 8;
currentPacket[9] = checksum & 0x00FF;
}

void CasaluxPacketFormatter::updateBrightness(uint8_t value) {
const GroupState* state = this->stateStore->get(deviceId, groupId, MiLightRemoteType::REMOTE_TYPE_CASALUX);
int8_t knownValue = (state != NULL && state->isSetBrightness()) ? state->getBrightness() : -1;

Serial.printf("Brightness %d\n", knownValue);

valueByStepFunction(
&PacketFormatter::increaseBrightness,
&PacketFormatter::decreaseBrightness,
10,
value,
knownValue
);
}

void CasaluxPacketFormatter::updateTemperature(uint8_t value) {
const GroupState* state = this->stateStore->get(deviceId, groupId, MiLightRemoteType::REMOTE_TYPE_CASALUX);
int8_t knownValue = (state != NULL && state->isSetKelvin()) ? state->getKelvin() : -1;

Serial.printf("Temp %d\n", knownValue);

valueByStepFunction(
&PacketFormatter::increaseTemperature,
&PacketFormatter::decreaseTemperature,
10,
value,
knownValue
);
}

void CasaluxPacketFormatter::command(uint8_t command, uint8_t arg) {
pushPacket();
currentPacket[0] = command;
currentPacket[5] = groupToGroupId(arg);
}

void CasaluxPacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) {
if(status == ON) command(0x6F, groupId);
else command(0x60, groupId);
}


BulbId CasaluxPacketFormatter::parsePacket(const uint8_t* packet, JsonObject result) {
uint8_t command = packet[0];

uint8_t onOffGroup = groupIdToGroup(packet[5]);

BulbId bulbId(
(packet[3] << 8) | packet[4],
onOffGroup,
REMOTE_TYPE_CASALUX
);

sequenceNum = packet[7];

switch(command){
case 0x65:
result[GroupStateFieldNames::COMMAND] = "brightness_down";
break;

case 0x66:
result[GroupStateFieldNames::COMMAND] = "brightness_up";
break;

case 0x6A:
result[GroupStateFieldNames::COMMAND] = MiLightCommandNames::TEMPERATURE_DOWN;
break;

case 0x69:
result[GroupStateFieldNames::COMMAND] = MiLightCommandNames::TEMPERATURE_UP;
break;

default:
if(onOffGroup < 255) {
result[GroupStateFieldNames::STATE] = command == 0x6F ? "ON" : "OFF";
}

result["button_id"] = command;
break;
}

return bulbId;
}

void CasaluxPacketFormatter::increaseTemperature() {
command(0x69, 0);
}

void CasaluxPacketFormatter::decreaseTemperature() {
command(0x6A, 0);
}

void CasaluxPacketFormatter::increaseBrightness() {
command(0x66, 0);
}

void CasaluxPacketFormatter::decreaseBrightness() {
command(0x65, 0);
}


uint8_t CasaluxPacketFormatter::groupToGroupId(uint8_t group){
switch(group) {
case 1:
return 0xD1;
case 2:
return 0xD2;
case 3:
return 0xD4;
case 4:
return 0xD8;
default:
return 0xDF;
}
}

uint8_t CasaluxPacketFormatter::groupIdToGroup(uint8_t groupId){
switch(groupId) {
case 0xD1:
return 1;
case 0xD2:
return 2;
case 0xD4:
return 3;
case 0xD8:
return 4;
default:
return 255;
}
}


void CasaluxPacketFormatter::format(uint8_t const* packet, char* buffer) {
buffer += sprintf_P(buffer, PSTR("CMD : %02X\n"), packet[0]) ;
buffer += sprintf_P(buffer, PSTR("Byte 2 (01) : %02X\n"), packet[1]);
buffer += sprintf_P(buffer, PSTR("Byte 3 (11) : %02X\n"), packet[2]);
buffer += sprintf_P(buffer, PSTR("Device ID : %02X%02X\n"), packet[3], packet[4]);
buffer += sprintf_P(buffer, PSTR("Group : %02X\n"), packet[5]);
buffer += sprintf_P(buffer, PSTR("Byte 5(00) : %02X\n"), packet[6]);
buffer += sprintf_P(buffer, PSTR("Sequence Num. : %02X\n"), packet[7]);
buffer += sprintf_P(buffer, PSTR("Payload Chksum: %02X%02X\n"), packet[8], packet[9]);
}
30 changes: 30 additions & 0 deletions lib/MiLight/CasaluxPacketFormatter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <PacketFormatter.h>

#ifndef _CASALUX_PACKET_FORMATTER_H
#define _CASALUX_PACKET_FORMATTER_H

class CasaluxPacketFormatter : public PacketFormatter {
public:
CasaluxPacketFormatter()
: PacketFormatter(REMOTE_TYPE_CASALUX, 10, 20)
{ }

virtual bool canHandle(const uint8_t* packet, const size_t len);

virtual void command(uint8_t command, uint8_t arg);
virtual void updateStatus(MiLightStatus status, uint8_t groupId);
virtual void updateTemperature(uint8_t value);
virtual void increaseTemperature();
virtual void decreaseTemperature();
virtual void updateBrightness(uint8_t value);
virtual void increaseBrightness();
virtual void decreaseBrightness();
virtual void format(uint8_t const* packet, char* buffer);
virtual void initializePacket(uint8_t* packet);
virtual void finalizePacket(uint8_t* packet);
virtual BulbId parsePacket(const uint8_t* packet, JsonObject result);
static uint8_t groupToGroupId(uint8_t group);
static uint8_t groupIdToGroup(uint8_t groupId);
};

#endif
8 changes: 4 additions & 4 deletions lib/MiLight/CctPacketFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,26 @@ void CctPacketFormatter::finalizePacket(uint8_t* packet) {

void CctPacketFormatter::updateBrightness(uint8_t value) {
const GroupState* state = this->stateStore->get(deviceId, groupId, MiLightRemoteType::REMOTE_TYPE_CCT);
int8_t knownValue = (state != NULL && state->isSetBrightness()) ? state->getBrightness() / CCT_INTERVALS : -1;
int8_t knownValue = (state != NULL && state->isSetBrightness()) ? state->getBrightness() : -1;

valueByStepFunction(
&PacketFormatter::increaseBrightness,
&PacketFormatter::decreaseBrightness,
CCT_INTERVALS,
value / CCT_INTERVALS,
value,
knownValue
);
}

void CctPacketFormatter::updateTemperature(uint8_t value) {
const GroupState* state = this->stateStore->get(deviceId, groupId, MiLightRemoteType::REMOTE_TYPE_CCT);
int8_t knownValue = (state != NULL && state->isSetKelvin()) ? state->getKelvin() / CCT_INTERVALS : -1;
int8_t knownValue = (state != NULL && state->isSetKelvin()) ? state->getKelvin() : -1;

valueByStepFunction(
&PacketFormatter::increaseTemperature,
&PacketFormatter::decreaseTemperature,
CCT_INTERVALS,
value / CCT_INTERVALS,
value,
knownValue
);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/MiLight/FUT020PacketFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ void FUT020PacketFormatter::nextMode() {
void FUT020PacketFormatter::updateBrightness(uint8_t value) {
const GroupState* state = this->stateStore->get(deviceId, groupId, MiLightRemoteType::REMOTE_TYPE_FUT020);
int8_t knownValue = (state != NULL && state->isSetBrightness())
? state->getBrightness() / FUT02xPacketFormatter::NUM_BRIGHTNESS_INTERVALS
? state->getBrightness()
: -1;

valueByStepFunction(
&PacketFormatter::increaseBrightness,
&PacketFormatter::decreaseBrightness,
FUT02xPacketFormatter::NUM_BRIGHTNESS_INTERVALS,
value / FUT02xPacketFormatter::NUM_BRIGHTNESS_INTERVALS,
value,
knownValue
);
}
Expand Down
20 changes: 19 additions & 1 deletion lib/MiLight/MiLightRemoteConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const MiLightRemoteConfig* MiLightRemoteConfig::ALL_REMOTES[] = {
&FUT098Config, // rgb
&FUT089Config, // 8-group rgb+cct (b8, fut089)
&FUT091Config,
&FUT020Config
&FUT020Config,
&CasaluxConfig,
&NLGConfig
};

const size_t MiLightRemoteConfig::NUM_REMOTES = size(ALL_REMOTES);
Expand Down Expand Up @@ -105,4 +107,20 @@ const MiLightRemoteConfig FUT020Config(
REMOTE_TYPE_FUT020,
"fut020",
0
);

const MiLightRemoteConfig CasaluxConfig(
new CasaluxPacketFormatter(),
MiLightRadioConfig::ALL_CONFIGS[5],
REMOTE_TYPE_CASALUX,
"casalux",
4
);

const MiLightRemoteConfig NLGConfig(
new NLGPacketFormatter(),
MiLightRadioConfig::ALL_CONFIGS[6],
REMOTE_TYPE_NLG,
"nlg",
4
);
Loading