UNPKG

hap-homematic

Version:

provides a homekit bridge to the ccu

281 lines (252 loc) 8.43 kB
/* * File: HomeMaticRGBAccessory.js * Project: hap-homematic * File Created: Thursday, 9th April 2020 7:27:28 pm * Author: Thomas Kluge (th.kluge@me.com) * ----- * The MIT License (MIT) * * Copyright (c) Thomas Kluge <th.kluge@me.com> (https://github.com/thkl) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * ========================================================================== */ const path = require('path') const HomeMaticDimmerAccessory = require(path.join(__dirname, 'HomeMaticDimmerAccessory.js')) class HomeMaticRGBAccessory extends HomeMaticDimmerAccessory { publishServices(Service, Characteristic) { super.publishServices(Service, Characteristic) let self = this this.colorCharacteristic = this.service.getCharacteristic(Characteristic.Hue) .on('get', async (callback) => { let value = await self.getValueForDataPointNameWithSettingsKey('color', null, false) let hsv = self.homeMaticToHSV(value) self.hue = hsv.h self.sat = hsv.s self.level = hsv.v self.debugLog('get color %s HUE %s', value, hsv.h) if (callback) callback(null, hsv.h) }) .on('set', (value, callback) => { self.hue = parseInt(value) if (self.sat) { let hmColor = self.HSVtoHomeMatic({ h: self.hue, s: self.sat, v: self.level || 255 }) self.debugLog('Color %s set Hue to %s', value, hmColor) self.setValueForDataPointNameWithSettingsKey('color', null, hmColor) } callback() }) this.colorCharacteristic.eventEnabled = true this.saturationCharacteristic = this.service.getCharacteristic(Characteristic.Saturation) .on('get', async (callback) => { let value = await self.getValueForDataPointNameWithSettingsKey('color', null, false) let hsv = self.homeMaticToHSV(value) self.hue = hsv.h self.sat = hsv.s self.level = hsv.v self.debugLog('get color %s Sat %s', value, hsv.s) if (callback) { callback(null, hsv.s) } }) .on('set', (value, callback) => { self.sat = value if (self.hue) { let hmColor = self.HSVtoHomeMatic({ h: self.hue, s: self.sat, v: self.level || 255 }) self.setValueForDataPointNameWithSettingsKey('color', null, hmColor) } callback() }) this.registerAddressWithSettingsKeyForEventProcessingAtAccessory('color', null, async (newValue) => { self.debugLog('event on color %s', newValue) let hsv = self.homeMaticToHSV(newValue) self.hue = hsv.h self.sat = hsv.s self.debugLog('processed color %s HSV %s', newValue, JSON.stringify(hsv)) self.updateCharacteristic(self.colorCharacteristic, self.hue) self.updateCharacteristic(self.saturationCharacteristic, self.sat) let level = await self.getLevel() self.level = level self.updateCharacteristic(self.levelCharacteristic, (level * 100)) self.updateCharacteristic(self.isOnCharacteristic, (level > 0)) }) } getLevel() { let self = this return new Promise((resolve, reject) => { self.getValueForDataPointNameWithSettingsKey('level', null, true).then(newLevel => { self.level = newLevel self.debugLog('GetLevel %s', self.level) resolve(newLevel) }) }) } homeMaticToHSV(newValue) { let self = this let settings = self.deviceServiceSettings('color', null) let mode = settings['mode'] || 'HM' if (mode === 'RGB') { let result = newValue.match(/(\s*[0-9]{1,3}),(\s*[0-9]{1,3}),(\s*[0-9]{1,3})/) let r = parseInt(result[1].trim()) let g = parseInt(result[2].trim()) let b = parseInt(result[3].trim()) self.debugLog('RGB is %s,%s,%s', r, g, b) let hsv = self.RGBtoHSV([r, g, b]) let hsvr = { h: hsv[0], s: hsv[1], v: hsv[2] } self.debugLog('HSV is %s', JSON.stringify(hsvr)) return (hsvr) } else { // the HomeMatic Value if (newValue === 200) { return { h: 0, s: 1, v: 0 } } else { return { h: Math.round((newValue / 199) * 360), s: 100, v: 0 } } } } HSVtoHomeMatic(hsv) { let settings = this.deviceServiceSettings('color', null) let mode = settings['mode'] || 'HM' if (mode === 'RGB') { this.debugLog('HSV is %s', JSON.stringify(hsv)) let rgb = this.HSVtoRGB([hsv.h, hsv.s, hsv.v]) this.debugLog('RGB Result is %s', JSON.stringify(rgb)) let red = Math.floor(rgb[0] * 255) let green = Math.floor(rgb[1] * 255) let blue = Math.floor(rgb[2] * 255) this.debugLog('Homematic RGBstring is %s', `rgb(${red}, ${green}, ${blue})`) if (rgb) { return `rgb(${red}, ${green}, ${blue})` } else { return 'rgb(0,0,0)' } } else { let hmColor if (hsv.s < 10) { hmColor = 200 } else { hmColor = Math.round((hsv.h / 360) * 199) } return hmColor } } RGBtoHSV(rgb) { let rdif let gdif let bdif let h let s const r = rgb[0] / 255 const g = rgb[1] / 255 const b = rgb[2] / 255 const v = Math.max(r, g, b) const diff = v - Math.min(r, g, b) const diffc = function (c) { return (v - c) / 6 / diff + 1 / 2 } if (diff === 0) { h = 0 s = 0 } else { s = diff / v rdif = diffc(r) gdif = diffc(g) bdif = diffc(b) if (r === v) { h = bdif - gdif } else if (g === v) { h = (1 / 3) + rdif - bdif } else if (b === v) { h = (2 / 3) + gdif - rdif } if (h < 0) { h += 1 } else if (h > 1) { h -= 1 } } return [ h * 360, s * 100, v * 100 ] } HSVtoRGB(hsv) { const h = hsv[0] / 60 const s = hsv[1] / 100 let v = hsv[2] / 100 const hi = Math.floor(h) % 6 const f = h - Math.floor(h) const p = 255 * v * (1 - s) const q = 255 * v * (1 - (s * f)) const t = 255 * v * (1 - (s * (1 - f))) v *= 255 switch (hi) { case 0: return [v, t, p] case 1: return [q, v, p] case 2: return [p, v, t] case 3: return [p, q, v] case 4: return [t, p, v] case 5: return [v, p, q] } } static channelTypes() { return ['RGBW_COLOR', 'VIR-LG_RGB-DIM-CH', 'VIR-LG-RGB-DIM-CH', 'VIR-LG-RGBW-DIM-CH'] } initServiceSettings() { return { 'RGBW_COLOR': { level: { name: '1.LEVEL' }, working: { name: '1.WORKING' }, color: { name: '2.COLOR' } }, 'VIR-LG_RGB-DIM-CH': { level: { name: '1.LEVEL' }, working: { name: '1.WORKING' }, color: { name: '1.RGB', mode: 'RGB' } }, 'VIR-LG-RGB-DIM-CH': { level: { name: '1.LEVEL' }, working: { name: '1.WORKING' }, color: { name: '1.RGB', mode: 'RGB' } }, 'VIR-LG-RGBW-DIM-CH': { level: { name: '1.LEVEL' }, working: { name: '1.WORKING' }, color: { name: '1.RGBW', mode: 'RGB' } } } } static configurationItems() { return {} } static serviceDescription() { return 'This service provides a lightbulb where u can set level and color' } static validate(configurationItem) { return false } } module.exports = HomeMaticRGBAccessory