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
102 changes: 102 additions & 0 deletions src/accessories/RGBCWBulb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
clamp,
convertHSLtoRGB,
convertMiredToTempInKelvin,
convertRGBtoHSL,
convertTempInKelvinToWhiteValues,
} from '../magichome-interface/utils';
import { HomebridgeMagichomeDynamicPlatformAccessory } from '../platformAccessory';

export class RGBCWBulb extends HomebridgeMagichomeDynamicPlatformAccessory {
async updateDeviceState(_timeout = 200) {
//**** local variables ****\\
const hsl = this.lightState.HSL;
const isColorTempChange = this.setColortemp;
let [red, green, blue] = [0, 0, 0];
if (!isColorTempChange) {
[red, green, blue] = convertHSLtoRGB(hsl); //convert HSL to RGB
}
const brightness = this.lightState.brightness;
const mask = isColorTempChange ? 0x0f : 0xf0; // the 'mask' byte tells the controller which LEDs to turn on color(0xF0), white (0x0F), or both (0xFF)
//we default the mask to turn on color. Other values can still be set, they just wont turn on

//sanitize our color/white values with Math.round and clamp between 0 and 255, not sure if either is needed
//next determine brightness by dividing by 100 and multiplying it back in as brightness (0-100)
const r = Math.round((clamp(red, 0, 255) / 100) * brightness);
const g = Math.round((clamp(green, 0, 255) / 100) * brightness);
const b = Math.round((clamp(blue, 0, 255) / 100) * brightness);

const temperatureInKelvin = convertMiredToTempInKelvin(this.lightState.CCT);
const brightnessPercentage = brightness / 100;

const whiteValues = convertTempInKelvinToWhiteValues(
temperatureInKelvin,
brightnessPercentage,
);

await this.send(
[0x31, r, g, b, whiteValues.warmWhite, whiteValues.coldWhite, mask, mask],
true,
_timeout,
); //9th byte checksum calculated later in send()
}

async updateHomekitState() {
this.service.updateCharacteristic(
this.platform.Characteristic.On,
this.lightState.isOn,
);
this.service.updateCharacteristic(
this.platform.Characteristic.Hue,
this.lightState.HSL.hue,
);
this.service.updateCharacteristic(
this.platform.Characteristic.Saturation,
this.lightState.HSL.saturation,
);
if (this.lightState.HSL.luminance > 0 && this.lightState.isOn) {
this.service.updateCharacteristic(
this.platform.Characteristic.Brightness,
this.lightState.HSL.luminance * 2,
);
} else if (this.lightState.isOn) {
this.service.updateCharacteristic(
this.platform.Characteristic.Brightness,
clamp(
this.lightState.whiteValues.coldWhite / 2.55 +
this.lightState.whiteValues.warmWhite / 2.55,
0,
100,
),
);
if (
this.lightState.whiteValues.warmWhite >
this.lightState.whiteValues.coldWhite
) {
this.service.updateCharacteristic(
this.platform.Characteristic.Saturation,
this.colorWhiteThreshold -
this.colorWhiteThreshold *
(this.lightState.whiteValues.coldWhite / 255),
);
this.service.updateCharacteristic(this.platform.Characteristic.Hue, 0);
} else {
this.service.updateCharacteristic(
this.platform.Characteristic.Saturation,
this.colorWhiteThreshold -
this.colorWhiteThreshold *
(this.lightState.whiteValues.warmWhite / 255),
);
this.service.updateCharacteristic(
this.platform.Characteristic.Hue,
180,
);
}
}
this.service.updateCharacteristic(
this.platform.Characteristic.ColorTemperature,
this.lightState.CCT,
);
this.cacheCurrentLightState();
}
}
11 changes: 11 additions & 0 deletions src/magichome-interface/LightMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ const lightTypesMap: Map<number, ILightParameters> = new Map([
hasBrightness: true,
},
],
[
0x0e,
{
controllerLogicType: ControllerTypes.RGBCWBulb,
convenientName: 'RGBCW Bulb',
simultaneousCCT: false,
hasColor: true,
hasCCT: true,
hasBrightness: true,
},
],
[
0x21,
{
Expand Down
1 change: 1 addition & 0 deletions src/magichome-interface/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export enum ControllerTypes {
DimmerStrip = 'DimmerStrip',
GRBStrip = 'GRBStrip',
RGBWWBulb = 'RGBWWBulb',
RGBCWBulb = 'RGBCWBulb',
RGBWBulb = 'RGBWBulb',
Switch = 'Switch',
RGBStrip = 'RGBStrip'
Expand Down
57 changes: 57 additions & 0 deletions src/magichome-interface/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,63 @@ export function convertHSLtoRGB ({hue, saturation, luminance}) {
//=================================================
// End Convert HSLtoRGB //

const MIN_TEMPERATURE_IN_KELVIN = 2000;
const MAX_TEMPERATURE_IN_KELVIN = 7200;

/**
* Converts white values to temperature in degrees Kelvin and associated brightness percentage.
* @param whiteValues byte values for warm white and cold white
* @returns temperature in degrees Kelvin and brightness percentage
*/
export function convertWhiteValuesToTempInKelvinAndBrightness(
whiteValues: { warmWhite: number; coldWhite: number },
minTemp = MIN_TEMPERATURE_IN_KELVIN,
maxTemp = MAX_TEMPERATURE_IN_KELVIN,
): { temperature: number; brightnessPercentage: number } {
let temperature = minTemp;
const warm = whiteValues.warmWhite / 255;
const cold = whiteValues.coldWhite / 255;
const brightness = cold + warm;
if (brightness !== 0) {
temperature = (cold / brightness) * (maxTemp - minTemp) + minTemp;
}
return { temperature: temperature, brightnessPercentage: brightness * 100 };
}

/**
* Converts temperature in degrees Kelvin and associated brightness percentage to byte values for warm white and cold white
* @param kelvin temperature in degrees Kelvin
* @param brightnessPercentage brightness as a percentage value
* @returns byte values for warm white and cold white
*/
export function convertTempInKelvinToWhiteValues(
kelvin: number,
brightnessPercentage: number,
minTemp = MIN_TEMPERATURE_IN_KELVIN,
maxTemp = MAX_TEMPERATURE_IN_KELVIN,
): { warmWhite: number; coldWhite: number } {
const warm =
((maxTemp - kelvin) / (maxTemp - minTemp)) * brightnessPercentage;
const cold = brightnessPercentage - warm;
const ww = Math.round(255 * warm);
const cw = Math.round(255 * cold);
return { warmWhite: ww, coldWhite: cw };
}

/**
* Converts from temperature in Kelvin (ex. 6500) to mired (micro-reciprocal degrees), as used by HomeKit
*/
export function convertTempInKelvinToMired(kelvin: number): number {
return 1000000 / kelvin;
}

/**
* Converts from mired (micro-reciprocal degrees), as used by HomeKit, to temperature in Kelvin (ex. 6500)
*/
export function convertMiredToTempInKelvin(mired: number) {
return 1000000 / mired;
}

export function parseJson<T>(value: string, replacement: T): T {
try {
return <T>JSON.parse(value);
Expand Down
2 changes: 2 additions & 0 deletions src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { RGBStrip } from './accessories/RGBStrip';
import { GRBStrip } from './accessories/GRBStrip';
import { RGBWBulb } from './accessories/RGBWBulb';
import { RGBWWBulb } from './accessories/RGBWWBulb';
import { RGBCWBulb } from './accessories/RGBCWBulb';
import { RGBWStrip } from './accessories/RGBWStrip';
import { RGBWWStrip } from './accessories/RGBWWStrip';
import { CCTStrip } from './accessories/CCTStrip';
Expand All @@ -30,6 +31,7 @@ const accessoryType = {
RGBStrip,
RGBWBulb,
RGBWWBulb,
RGBCWBulb,
RGBWStrip,
RGBWWStrip,
CCTStrip,
Expand Down
36 changes: 33 additions & 3 deletions src/platformAccessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import type {
Service, PlatformConfig, PlatformAccessory, CharacteristicValue,
CharacteristicSetCallback, CharacteristicGetCallback,
} from 'homebridge';
import { clamp, convertHSLtoRGB, convertRGBtoHSL } from './magichome-interface/utils';
import {
clamp,
convertHSLtoRGB,
convertRGBtoHSL,
convertTempInKelvinToMired,
convertWhiteValuesToTempInKelvinAndBrightness,
} from './magichome-interface/utils';
import { HomebridgeMagichomeDynamicPlatform } from './platform';
import { Transport } from './magichome-interface/Transport';
import { getLogs } from './logs';
Expand Down Expand Up @@ -249,7 +255,7 @@ export class HomebridgeMagichomeDynamicPlatformAccessory {
const CCT = this.lightState.CCT;

//update state with actual values asynchronously
this.logs.debug('Get Characteristic Hue -> %o for device: %o ', CCT, this.myDevice.displayName);
this.logs.debug('Get Characteristic Color Temperature -> %o for device: %o ', CCT, this.myDevice.displayName);
if(this.setColortemp){
this.updateLocalState();
}
Expand Down Expand Up @@ -323,6 +329,7 @@ export class HomebridgeMagichomeDynamicPlatformAccessory {
this.updateLocalHSL(convertRGBtoHSL(this.lightState.RGB));
this.updateLocalWhiteValues(state.whiteValues);
this.updateLocalIsOn(state.isOn);
this.updateLocalColorTemperature(state.whiteValues);
this.updateHomekitState();

} catch (error) {
Expand All @@ -340,10 +347,20 @@ export class HomebridgeMagichomeDynamicPlatformAccessory {
this.service.updateCharacteristic(this.platform.Characteristic.On, this.lightState.isOn);
this.service.updateCharacteristic(this.platform.Characteristic.Hue, this.lightState.HSL.hue);
this.service.updateCharacteristic(this.platform.Characteristic.Saturation, this.lightState.HSL.saturation);
if(this.lightState.HSL.luminance > 0 && this.lightState.isOn){
if (
!this.myDevice.lightParameters.hasCCT &&
this.lightState.HSL.luminance > 0 &&
this.lightState.isOn
) {
this.updateLocalBrightness(this.lightState.HSL.luminance * 2);
Copy link
Author

@ali-hk ali-hk Oct 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unclear to me why the local brightness value gets updated here, but this seems to cause HomeKit to show the wrong color value when navigating away and back to the accessory settings, if the color value was set to a color temperature rather than RGB, so I've added a condition to skip it for CCT cases.

}
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.lightState.brightness);
if (this.myDevice.lightParameters.hasCCT) {
this.service.updateCharacteristic(
this.platform.Characteristic.ColorTemperature,
this.lightState.CCT,
);
}
}

updateLocalHSL(_hsl){
Expand All @@ -366,6 +383,19 @@ export class HomebridgeMagichomeDynamicPlatformAccessory {
this.lightState.brightness = _brightness;
}

updateLocalColorTemperature(_whiteValues: {
warmWhite: number;
coldWhite: number;
}) {
const tempKelvinAndBrightness =
convertWhiteValuesToTempInKelvinAndBrightness(_whiteValues);

// convert to mired (micro-reciprocal degrees)
this.lightState.CCT = convertTempInKelvinToMired(
tempKelvinAndBrightness.temperature,
);
this.lightState.brightness = tempKelvinAndBrightness.brightnessPercentage;
}

/**
** @updateDeviceState
Expand Down