Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support 2 gang dimmer switches #176

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ class TuyaPlatform {
case 'xdd':
case 'dc':
case 'tgkg':
deviceAccessory = new LightAccessory(this, homebridgeAccessory, device);
var deviceData = new DataUtil().getSubService(device.status)
deviceAccessory = new LightAccessory(this, homebridgeAccessory, device, deviceData);
this.accessories.set(uuid, deviceAccessory.homebridgeAccessory);
this.deviceAccessories.set(uuid, deviceAccessory);
break;
Expand Down
144 changes: 107 additions & 37 deletions lib/light_accessory.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ let Characteristic;
let UUIDGen;

class LightAccessory extends BaseAccessory {
constructor(platform, homebridgeAccessory, deviceConfig) {
constructor(platform, homebridgeAccessory, deviceConfig, deviceData) {
({ Accessory, Characteristic, Service } = platform.api.hap);
super(
platform,
homebridgeAccessory,
deviceConfig,
Accessory.Categories.LIGHTBULB,
Service.Lightbulb
Service.Lightbulb,
deviceData.subType
);
this.statusArr = deviceConfig.status ? deviceConfig.status : [];
this.functionArr = deviceConfig.functions ? deviceConfig.functions : [];
Expand All @@ -35,87 +36,115 @@ class LightAccessory extends BaseAccessory {
//init Or refresh AccessoryService
refreshAccessoryServiceIfNeed(statusArr, isRefresh) {
this.isRefresh = isRefresh;

if (this.subServices.length == 0 || this.subServices.length == 1) {
this._refreshSingleAccessoryServiceIfNeed(this.service, statusArr, '1');
} else {
for (var subType of this.subServices) {
this._refreshSingleAccessoryServiceIfNeed(this.homebridgeAccessory.getService(subType), statusArr, this._getSubTypePostfix(subType));
}
}
}
_getSubTypeService(subType) {
let service
if (this.subServices.length == 0 || this.subServices.length == 1) {
service = this.service;
} else {
service = this.homebridgeAccessory.getService(subType);
}
return service;
}

_refreshSingleAccessoryServiceIfNeed(service, statusArr, postfix) {
for (var statusMap of statusArr) {
var codePostifx = this._getSubTypePostfix(statusMap.code);
if (!(codePostifx === postfix || (postfix === '1' && statusMap.code === codePostifx))) {
continue;
}

if (statusMap.code === 'work_mode') {
this.workMode = statusMap;
}
if (statusMap.code === 'switch_led' || statusMap.code === 'switch_led_1') {
this.switchLed = statusMap;
this.normalAsync(Characteristic.On, this.switchLed.value)
if (statusMap.code === 'switch_led' || statusMap.code === 'switch_led_1' || statusMap.code === 'switch_led_2') {
this._storeCommandCode(service, Characteristic.On, statusMap);
this.normalAsync(service, Characteristic.On, statusMap.value);
}
if (statusMap.code === 'bright_value' || statusMap.code === 'bright_value_v2' || statusMap.code === 'bright_value_1') {
this.brightValue = statusMap;
if (statusMap.code === 'bright_value' || statusMap.code === 'bright_value_v2' || statusMap.code === 'bright_value_1' || statusMap.code === 'bright_value_2') {
this._storeCommandCode(service, Characteristic.Brightness, statusMap);
var rawValue;
var percentage;
rawValue = this.brightValue.value;
rawValue = statusMap.value;
percentage = Math.floor((rawValue - this.function_dp_range.bright_range.min) * 100 / (this.function_dp_range.bright_range.max - this.function_dp_range.bright_range.min)); // $
this.normalAsync(Characteristic.Brightness, percentage > 100 ? 100 : percentage)
this.normalAsync(service, Characteristic.Brightness, percentage > 100 ? 100 : percentage)
}
if (statusMap.code === 'temp_value' || statusMap.code === 'temp_value_v2') {
this.tempValue = statusMap;
this._storeCommandCode(service, Characteristic.ColorTemperature, statusMap);

var rawValue;
var temperature;
rawValue = this.tempValue.value;
rawValue = statusMap.value;
temperature = Math.floor((rawValue - this.function_dp_range.temp_range.min) * 360 / (this.function_dp_range.temp_range.max - this.function_dp_range.temp_range.min) + 140); // ra$
if (temperature > 500) {
temperature = 500
}
if (temperature < 140) {
temperature = 140
}
this.normalAsync(Characteristic.ColorTemperature, temperature)
this.normalAsync(service, Characteristic.ColorTemperature, temperature)
}
if (statusMap.code === 'colour_data' || statusMap.code === 'colour_data_v2') {
this.colourData = statusMap;
this.colourObj = this.colourData.value === "" ? { "h": 100.0, "s": 100.0, "v": 100.0 } : JSON.parse(this.colourData.value)
this._storeCommandCode(service, Characteristic.Hue, statusMap);
this.colourObj = statusMap.value === "" ? { "h": 100.0, "s": 100.0, "v": 100.0 } : JSON.parse(statusMap.value)

if (!this._isHaveDPCodeOfBrightValue()) {
var percentage;
percentage = Math.floor((this.colourObj.v - this.function_dp_range.bright_range.min) * 100 / (this.function_dp_range.bright_range.max - this.function_dp_range.bright_range.min));
this.normalAsync(Characteristic.Brightness, percentage > 100 ? 100 : percentage)
this.normalAsync(service, Characteristic.Brightness, percentage > 100 ? 100 : percentage)
}

const hue = this.colourObj.h; // 0-359
this.normalAsync(Characteristic.Hue, hue)
this.normalAsync(service, Characteristic.Hue, hue)

var saturation;
saturation = Math.floor((this.colourObj.s - this.function_dp_range.saturation_range.min) * 100 / (this.function_dp_range.saturation_range.max - this.function_dp_range.saturation_range.min)); // saturation 0-100
this.normalAsync(Characteristic.Saturation, saturation > 100 ? 100 : saturation)
this.normalAsync(service, Characteristic.Saturation, saturation > 100 ? 100 : saturation)

}
}
}

normalAsync(name, hbValue) {
this.setCachedState(name, hbValue);
normalAsync(service, name, hbValue) {
this.setCachedState(service.displayName, name, hbValue);
if (this.isRefresh) {
this.service
service
.getCharacteristic(name)
.updateValue(hbValue);
} else {
this.getAccessoryCharacteristic(name);
this.getAccessoryCharacteristic(service, name);
}
}

getAccessoryCharacteristic(name) {
getAccessoryCharacteristic(service, name) {
//set Accessory service Characteristic
this.service.getCharacteristic(name)
service.getCharacteristic(name)
.on('get', callback => {
if (this.hasValidCache()) {
callback(null, this.getCachedState(name));
callback(null, this.getCachedState(service.displayName, name));
}
})
.on('set', (value, callback) => {
this.log.error('[value]: %s', value);

//Switching colors will trigger both hue and saturation to avoid the color command being sent repeatedly.
//So the saturation is saved and is sent with the hue
if (name == Characteristic.Saturation) {
this.setCachedState(name, value);
this.setCachedState(service.displayName, name, value);
callback();
return;
}
var param = this.getSendParam(name, value)
var param = this.getSendParam(service.displayName, name, value)
this.platform.tuyaOpenApi.sendCommand(this.deviceId, param).then(() => {
this.setCachedState(name, value);
this.setCachedState(service.displayName, name, value);
callback();
}).catch((error) => {
this.log.error('[SET][%s] Characteristic Error: %s', this.homebridgeAccessory.displayName, error);
Expand All @@ -126,33 +155,43 @@ class LightAccessory extends BaseAccessory {
}

//get Command SendData
getSendParam(name, value) {
getSendParam(serviceName, name, value) {

var code;
var value;
switch (name) {
case Characteristic.On:
const isOn = value ? true : false;
code = this.switchLed.code;
code = this._resolveCommandCode(serviceName, Characteristic.On);
value = isOn;
break;
case Characteristic.ColorTemperature:
var temperature;
temperature = Math.floor((value - 140) * (this.function_dp_range.temp_range.max - this.function_dp_range.temp_range.min) / 360 + this.function_dp_range.temp_range.min); // value 140~500
code = this.tempValue.code;
code = this._resolveCommandCode(serviceName, Characteristic.ColorTemperature);
value = temperature;
break;
case Characteristic.Brightness:
{
var percentage;
percentage = Math.floor((this.function_dp_range.bright_range.max - this.function_dp_range.bright_range.min) * value / 100 + this.function_dp_range.bright_range.min); // value 0~100

// turn light on/off in brightness goes to/from 0 percent
if(percentage == 0 && this.getCachedState(serviceName, Characteristic.On)) {
this.platform.tuyaOpenApi.sendCommand(this.deviceId, this.getSendParam(serviceName, Characteristic.On, false));
} else if (percentage > 0 && !this.getCachedState(serviceName, Characteristic.On)) {
this.platform.tuyaOpenApi.sendCommand(this.deviceId, this.getSendParam(serviceName, Characteristic.On, true));
}

// update brightness value
if ((!this.workMode || this.workMode.value === 'white' || this.workMode.value === 'light_white') && this._isHaveDPCodeOfBrightValue()) {
code = this.brightValue.code;
code = this._resolveCommandCode(serviceName, Characteristic.Brightness);
value = percentage;
} else {
var saturation;
saturation = Math.floor((this.function_dp_range.saturation_range.max - this.function_dp_range.saturation_range.min) * this.getCachedState(Characteristic.Saturation) / 100 + this.function_dp_range.saturation_range.min); // value 0~100
var hue = this.getCachedState(Characteristic.Hue);; // 0-359
code = this.colourData.code;
saturation = Math.floor((this.function_dp_range.saturation_range.max - this.function_dp_range.saturation_range.min) * this.getCachedState(serviceName, Characteristic.Saturation) / 100 + this.function_dp_range.saturation_range.min); // value 0~100
var hue = this.getCachedState(serviceName, Characteristic.Hue);; // 0-359
code = this._resolveCommandCode(serviceName, Characteristic.Hue);
value = {
"h": hue,
"s": saturation,
Expand All @@ -164,9 +203,9 @@ class LightAccessory extends BaseAccessory {
case Characteristic.Hue:
var bright;
var saturation;
bright = Math.floor((this.function_dp_range.bright_range.max - this.function_dp_range.bright_range.min) * this.getCachedState(Characteristic.Brightness) / 100 + this.function_dp_range.bright_range.min); // value 0~100
saturation = Math.floor((this.function_dp_range.saturation_range.max - this.function_dp_range.saturation_range.min) * this.getCachedState(Characteristic.Saturation) / 100 + this.function_dp_range.saturation_range.min);// value 0~100
code = this.colourData.code;
bright = Math.floor((this.function_dp_range.bright_range.max - this.function_dp_range.bright_range.min) * this.getCachedState(serviceName, Characteristic.Brightness) / 100 + this.function_dp_range.bright_range.min); // value 0~100
saturation = Math.floor((this.function_dp_range.saturation_range.max - this.function_dp_range.saturation_range.min) * this.getCachedState(serviceName, Characteristic.Saturation) / 100 + this.function_dp_range.saturation_range.min);// value 0~100
code = this._resolveCommandCode(serviceName, Characteristic.Hue);
value = {
"h": value,
"s": saturation,
Expand Down Expand Up @@ -248,6 +287,37 @@ class LightAccessory extends BaseAccessory {
}
}

_getSubTypePostfix(code) {
return code.substring(code.lastIndexOf('_') + 1);
}

_storeCommandCode(service, characteristic, statusMap) {
if(!this.commands) {
this.commands = {};
}
if(!this.commands[service.displayName]) {
this.commands[service.displayName] = {};
}
this.commands[service.displayName][characteristic] = statusMap.code;
}

_resolveCommandCode(serviceName, characteristic) {
if(!this.commands[serviceName][characteristic] && characteristic == Characteristic.On) {
return serviceName;
}
return this.commands[serviceName][characteristic];
}

getCachedState(serviceName, characteristic) {
return this.cachedState.get(serviceName + characteristic);
}

setCachedState(serviceName, characteristic, value) {
this.cachedState.set(serviceName + characteristic, value);
this.validCache = true;
}


// //return functionsdpRange
// getFunctionsDPRange() {
// let bright_range
Expand Down