Skip to content

Commit

Permalink
Update HueLight.js
Browse files Browse the repository at this point in the history
- Changes to handling colour temperature, see #814.
- First step towards implemening adaptive lighting, for now only for Hue LCT015.
  • Loading branch information
ebaauw committed Nov 14, 2020
1 parent 434bd56 commit 3ea1ec4
Showing 1 changed file with 125 additions and 12 deletions.
137 changes: 125 additions & 12 deletions lib/HueLight.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ const knownLights = {
LCT011: { gamut: 'C' }, // Hue BR30
LCT012: { gamut: 'C' }, // Hue Color Candle
LCT014: { gamut: 'C' }, // Hue bulb A19
LCT015: { gamut: 'C' }, // Hue bulb A19
LCT015: { gamut: 'C', adaptiveLighting: true }, // Hue bulb A19
LCT016: { gamut: 'C' }, // Hue bulb A19
LLC005: { gamut: 'A' }, // Living Colors Gen3 Bloom, Aura
LLC006: { gamut: 'A' }, // Living Colors Gen3 Iris
Expand All @@ -217,7 +217,7 @@ const knownLights = {
}
}

knownLights['Signify Netherlands B.V'] = knownLights.Philips
knownLights['Signify Netherlands B.V.'] = knownLights.Philips

// ===== Colour Conversion =====================================================

Expand Down Expand Up @@ -396,6 +396,48 @@ function invHueSat (hue, sat, gamut) {
return [Math.round(q.x * 10000) / 10000, Math.round(q.y * 10000) / 10000]
}

// ct to xy
// From deCONZ REST API plugin.
// Results don't match xy values as retuned by Hue LCT015 exactly, but seem
// to be close enough.
function invCt (ct) {
const kelvin = 1000000 / ct
let x, y

if (kelvin < 4000) {
x = 11790 +
57520658 / kelvin +
-15358885888 / kelvin / kelvin +
-17440695910400 / kelvin / kelvin / kelvin
} else {
x = 15754 +
14590587 / kelvin +
138086835814 / kelvin / kelvin +
-198301902438400 / kelvin / kelvin / kelvin
}
if (kelvin < 2222) {
y = -3312 +
35808 * x / 0x10000 +
-22087 * x * x / 0x100000000 +
-18126 * x * x * x / 0x1000000000000
} else if (kelvin < 4000) {
y = -2744 +
34265 * x / 0x10000 +
-22514 * x * x / 0x100000000 +
-15645 * x * x * x / 0x1000000000000
} else {
y = -6062 +
61458 * x / 0x10000 +
-96229 * x * x / 0x100000000 +
50491 * x * x * x / 0x1000000000000
}
y *= 4
x /= 0xFFFF
y /= 0xFFFF

return [Math.round(x * 10000) / 10000, Math.round(y * 10000) / 10000]
}

function dateToString (date, utc = true) {
if (date == null || date === 'none') {
return 'n/a'
Expand All @@ -411,13 +453,11 @@ function dateToString (date, utc = true) {
let Service
let Characteristic
let my
let eve

function setHomebridge (homebridge, _my, _eve) {
Service = homebridge.hap.Service
Characteristic = homebridge.hap.Characteristic
my = _my
eve = _eve
}

// ===== HueLight ==============================================================
Expand Down Expand Up @@ -547,11 +587,9 @@ function HueLight (accessory, id, obj, type = 'light') {
.on('set', this.setBriChange.bind(this))
}
if (this.config.ct) {
this.colorTemperatureCharacteristic = this.config.xy
? eve.Characteristics.ColorTemperature
: Characteristic.ColorTemperature
this.service.addOptionalCharacteristic(this.colorTemperatureCharacteristic)
this.service.getCharacteristic(this.colorTemperatureCharacteristic)
this.colorTemperatureCharacteristic = Characteristic.ColorTemperature
this.service.addOptionalCharacteristic(Characteristic.ColorTemperature)
this.service.getCharacteristic(Characteristic.ColorTemperature)
.on('set', this.setCT.bind(this))
.setProps({
minValue: this.config.minCT,
Expand All @@ -576,6 +614,18 @@ function HueLight (accessory, id, obj, type = 'light') {
this.service.getCharacteristic(my.Characteristics.ColorLoop)
.on('set', this.setColorLoop.bind(this))
}
if (this.config.adaptiveLighting) {
this.service.addOptionalCharacteristic(my.Characteristics.SupportedTransitionConfiguration)
this.service.getCharacteristic(my.Characteristics.SupportedTransitionConfiguration)
.on('get', this.getSupportedTransitionConfiguration.bind(this))
this.service.addOptionalCharacteristic(my.Characteristics.TransitionControl)
this.service.getCharacteristic(my.Characteristics.TransitionControl)
.on('get', this.getTransitionControl.bind(this))
.on('set', this.setTransitionControl.bind(this))
this.service.addOptionalCharacteristic(my.Characteristics.ActiveTransitionCount)
this.service.getCharacteristic(my.Characteristics.ActiveTransitionCount)
.updateValue(0)
}
}
if (this.type === 'light') {
this.service.addOptionalCharacteristic(Characteristic.StatusFault)
Expand Down Expand Up @@ -773,6 +823,9 @@ HueLight.prototype.setConfig = function () {
if (model.noWaitUpdate) {
this.config.waitTimeUpdate = 0
}
if (model.adaptiveLighting) {
this.config.adaptiveLighting = true
}
this.log.debug('%s: %s: config: %j', this.bridge.name, this.resource, this.config)
}

Expand Down Expand Up @@ -1071,7 +1124,7 @@ HueLight.prototype.checkCT = function (ct) {
)
}
this.hk.ct = hkCT
this.service.getCharacteristic(this.colorTemperatureCharacteristic)
this.service.getCharacteristic(Characteristic.ColorTemperature)
.updateValue(this.hk.ct)
}
}
Expand Down Expand Up @@ -1190,7 +1243,7 @@ HueLight.prototype.checkReachable = function (reachable) {
}
}

HueLight.prototype.checkXY = function (xy) {
HueLight.prototype.checkXY = function (xy, fromCt = false) {
if (!this.config.xy) {
return
}
Expand All @@ -1206,7 +1259,7 @@ HueLight.prototype.checkXY = function (xy) {
this.obj.state.colormode, this.obj.state.xy, xy
)
}
if (this.recentlyUpdated) {
if (this.recentlyUpdated && !fromCt) {
this.log.debug('%s: recently updated - ignore changed xy', this.name)
return
}
Expand Down Expand Up @@ -1660,6 +1713,7 @@ HueLight.prototype.setCT = function (ct, callback) {
const newCT = this.hk.ct
this.put({ ct: newCT }).then(() => {
this.obj.state.ct = newCT
this.checkXY(invCt(this.obj.state.ct), true)
callback()
}).catch((error) => {
this.hk.ct = oldCT
Expand Down Expand Up @@ -1883,6 +1937,65 @@ HueLight.prototype.didSetActive = function () {
}, 500)
}

HueLight.prototype.getSupportedTransitionConfiguration = function (callback) {
// The SuppotedTransitionConfiguration value is constant, but the iid values
// for the characteristics are only assigned when the HAP server is started,
// so we cannot set the value while defining the service.
const bri = this.service.getCharacteristic(Characteristic.Brightness).iid
const ct = this.service.getCharacteristic(Characteristic.ColorTemperature).iid
const buf = Buffer.alloc(38)
let i = 0

buf[i++] = 1 // T0
buf[i++] = 16 // L0
buf[i++] = 1 // V0 T0
buf[i++] = 8 // V0 L0
buf.writeUint32LE(bri, i) // V0 V0
i += 8
buf[i++] = 2 // V0 T1
buf[i++] = 4 // V0 L1
buf.writeUint16LE(1, i) // V0 V1
i += 4

buf[i++] = 0 // T1
buf[i++] = 0 // L1

buf[i++] = 1 // T2
buf[i++] = 16 // L2
buf[i++] = 1 // V2 T0
buf[i++] = 8 // V2 L0
buf.writeUint32LE(ct, i) // V2 V0
i += 8
buf[i++] = 2 // V2 T1
buf[i++] = 4 // V2 L1
buf.writeUint16LE(2, i) // V2 V1
i += 4

this.log.debug(
'%s: brightness idd: %d, color temperature idd: %d', this.name, bri, ct
)
this.log.debug(
'%s: supported transition configuration: %s', this.name,
buf.toString('base64')
)
callback(null, buf.toString('base64'))

// Remove the event handler, since we now have the value.
this.service.getCharacteristic(my.Characteristics.SupportedTransitionConfiguration)
.setValue(buf.toString('base64'))
.removeAllListeners('get')
}

HueLight.prototype.getTransitionControl = function (callback) {
this.log.warn('get control')
callback(null, Buffer.alloc(0).toString('base64'))
}

HueLight.prototype.setTransitionControl = function (control, callback) {
this.log.warn('set control to: %j', control)
callback()
}

// Collect changes into a combined request.
HueLight.prototype.put = function (state) {
return new Promise((resolve, reject) => {
Expand Down

0 comments on commit 3ea1ec4

Please sign in to comment.