diff --git a/src/auth.ts b/src/auth.ts index 96c8ba9..db98702 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -1,9 +1,27 @@ +/** + * This file contains the auth functions. + * + * @file src\auth.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import crypto from 'crypto'; -/* -import { AnsiLogger, TimestampFormat, db, idn, rs, zb } from 'node-ansi-logger'; -import { Shelly } from './shelly.js'; -import { ShellyDevice } from './shellyDevice.js'; -*/ export interface AuthParams { realm: string; // device_id diff --git a/src/ble.ts b/src/ble.ts index 75a1a20..3fa9ee4 100644 --- a/src/ble.ts +++ b/src/ble.ts @@ -1,3 +1,26 @@ +/** + * This file contains the ble functions. + * + * @file src\ble.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + /* // eslint-disable-next-line @typescript-eslint/no-unused-vars import { AnsiLogger, TimestampFormat, db, debugStringify, dn, hk, nf, rs, wr, zb } from 'node-ansi-logger'; diff --git a/src/coapServer.ts b/src/coapServer.ts index 128298e..9cdbdc3 100644 --- a/src/coapServer.ts +++ b/src/coapServer.ts @@ -1,3 +1,26 @@ +/** + * This file contains the class CoapServer. + * + * @file src\coapServer.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import { AnsiLogger, BLUE, CYAN, MAGENTA, RESET, TimestampFormat, db, debugStringify, idn, rs } from 'node-ansi-logger'; import coap, { Server, IncomingMessage, OutgoingMessage, globalAgent } from 'coap'; import EventEmitter from 'events'; diff --git a/src/colorUtils.ts b/src/colorUtils.ts new file mode 100644 index 0000000..53864af --- /dev/null +++ b/src/colorUtils.ts @@ -0,0 +1,380 @@ +/** + * This file contains the color utilities. + * + * @file colorUtils.ts + * @author Luca Liguori + * @date 2023-10-05 + * @version 1.2.6 + * + * Copyright 2023, 2024 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + +import { assert } from 'console'; + +export interface RGB { + r: number; + g: number; + b: number; +} + +export interface XY { + x: number; + y: number; +} + +export interface HSL { + h: number; + s: number; + l: number; +} + +export function hslColorToRgbColor(hue: number, saturation: number, luminance: number): RGB { + if (hue === 360) { + hue = 0; + } + assert(hue >= 0 && hue <= 359, 'hslColorToRgbColor Hue error'); + assert(saturation >= 0 && saturation <= 100, 'hslColorToRgbColor Saturation error'); + assert(luminance === 50, 'hslColorToRgbColor Luminance error'); + + saturation /= 100; + luminance /= 100; + let r: number, g: number, b: number; + + if (saturation === 0) { + r = g = b = luminance; // achromatic + } else { + const hue2rgb = (p: number, q: number, t: number): number => { + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t; + } + if (t < 1 / 2) { + return q; + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6; + } + return p; + }; + + const q = luminance < 0.5 ? luminance * (1 + saturation) : luminance + saturation - luminance * saturation; + const p = 2 * luminance - q; + + r = hue2rgb(p, q, hue / 360 + 1 / 3); + g = hue2rgb(p, q, hue / 360); + b = hue2rgb(p, q, hue / 360 - 1 / 3); + } + + return { + r: Math.ceil(r * 255), + g: Math.ceil(g * 255), + b: Math.ceil(b * 255), + }; +} + +// Converts from RGB to the XY color (CIE 1931 color space) +export function rgbColorToXYColor(rgb: RGB): XY { + let r = rgb.r / 255; + let g = rgb.g / 255; + let b = rgb.b / 255; + + // Apply gamma correction + r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92; + g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92; + b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92; + + // Scale the values to the D65 illuminant + r = r * 100; + g = g * 100; + b = b * 100; + + // Convert RGB to XYZ + const X = r * 0.664511 + g * 0.154324 + b * 0.162028; + const Y = r * 0.283881 + g * 0.668433 + b * 0.047685; + const Z = r * 0.000088 + g * 0.07231 + b * 0.986039; + + // Normalization + let x = X / (X + Y + Z); + let y = Y / (X + Y + Z); + + // Round to 4 digits + x = Math.round(x * 10000) / 10000; + y = Math.round(y * 10000) / 10000; + + return { x, y }; +} + +export function xyColorToRgbColor(x: number, y: number, brightness = 254): RGB { + const z = 1.0 - x - y; + const Y = (brightness / 254).toFixed(2); + const X = (Number(Y) / y) * x; + const Z = (Number(Y) / y) * z; + + // Convert to RGB using Wide RGB D65 conversion + let red = X * 1.656492 - Number(Y) * 0.354851 - Z * 0.255038; + let green = -X * 0.707196 + Number(Y) * 1.655397 + Z * 0.036152; + let blue = X * 0.051713 - Number(Y) * 0.121364 + Z * 1.01153; + + // If red, green or blue is larger than 1.0 set it back to the maximum of 1.0 + if (red > blue && red > green && red > 1.0) { + green = green / red; + blue = blue / red; + red = 1.0; + } else if (green > blue && green > red && green > 1.0) { + red = red / green; + blue = blue / green; + green = 1.0; + } else if (blue > red && blue > green && blue > 1.0) { + red = red / blue; + green = green / blue; + blue = 1.0; + } + + // Reverse gamma correction + red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * Math.pow(red, 1.0 / 2.4) - 0.055; + green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * Math.pow(green, 1.0 / 2.4) - 0.055; + blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * Math.pow(blue, 1.0 / 2.4) - 0.055; + + // Convert normalized decimal to decimal + red = Math.round(red * 255); + green = Math.round(green * 255); + blue = Math.round(blue * 255); + + // Normalize + if (isNaN(red) || red < 0) { + red = 0; + } + if (isNaN(green) || green < 0) { + green = 0; + } + if (isNaN(blue) || blue < 0) { + blue = 0; + } + + return { r: red, g: green, b: blue }; +} + +export function rgbColorToHslColor(rgb: RGB): HSL { + const r = rgb.r / 255; + const g = rgb.g / 255; + const b = rgb.b / 255; + + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + let h = 0, + s = 0; + const l = (max + min) / 2; + + if (max === min) { + h = s = 0; // achromatic + } else { + const d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + + return { + h: Math.round(h * 360), + s: Math.round(s * 100), + l: Math.round(l * 100), + }; +} + +export function xyToHsl(x: number, y: number): HSL { + const rgb = xyColorToRgbColor(x, y); + return rgbColorToHslColor(rgb); +} + +export function testColors(): void { + // this table has been checked with different apps and sites and is correct 100% + const colors = [ + { name: 'Pure Red 0', hsl: { h: 0, s: 100, l: 50 }, rgb: { r: 255, g: 0, b: 0 }, xy: { x: 0.7006, y: 0.2993 } }, + { name: 'Bright Orange 30', hsl: { h: 30, s: 100, l: 50 }, rgb: { r: 255, g: 128, b: 0 }, xy: { x: 0.6112, y: 0.375 } }, + { name: 'Pure Yellow 60', hsl: { h: 60, s: 100, l: 50 }, rgb: { r: 255, g: 255, b: 0 }, xy: { x: 0.4442, y: 0.5166 } }, + { name: 'Lime Green 90', hsl: { h: 90, s: 100, l: 50 }, rgb: { r: 128, g: 255, b: 0 }, xy: { x: 0.2707, y: 0.6635 } }, + { name: 'Pure Green 120', hsl: { h: 120, s: 100, l: 50 }, rgb: { r: 0, g: 255, b: 0 }, xy: { x: 0.1724, y: 0.7468 } }, + { name: 'Light Sea Green 150', hsl: { h: 150, s: 100, l: 50 }, rgb: { r: 0, g: 255, b: 128 }, xy: { x: 0.1642, y: 0.5886 } }, + { name: 'Pure Cyan 180', hsl: { h: 180, s: 100, l: 50 }, rgb: { r: 0, g: 255, b: 255 }, xy: { x: 0.1513, y: 0.3425 } }, + { name: 'Deep Sky Blue 210', hsl: { h: 210, s: 100, l: 50 }, rgb: { r: 0, g: 128, b: 255 }, xy: { x: 0.1406, y: 0.1382 } }, + { name: 'Pure Blue 240', hsl: { h: 240, s: 100, l: 50 }, rgb: { r: 0, g: 0, b: 255 }, xy: { x: 0.1355, y: 0.0399 } }, + { name: 'Blue Violet 270', hsl: { h: 270, s: 100, l: 50 }, rgb: { r: 128, g: 0, b: 255 }, xy: { x: 0.2181, y: 0.0778 } }, + { name: 'Pure Magenta 300', hsl: { h: 300, s: 100, l: 50 }, rgb: { r: 255, g: 0, b: 255 }, xy: { x: 0.3855, y: 0.1546 } }, + { name: 'Deep Pink 330', hsl: { h: 330, s: 100, l: 50 }, rgb: { r: 255, g: 0, b: 128 }, xy: { x: 0.5797, y: 0.2438 } }, + + { name: 'Pure Red 50% 0', hsl: { h: 0, s: 50, l: 50 }, rgb: { r: 192, g: 64, b: 64 }, xy: { x: 0.6036, y: 0.3069 } }, + { name: 'Bright Orange 50% 30', hsl: { h: 30, s: 50, l: 50 }, rgb: { r: 192, g: 128, b: 64 }, xy: { x: 0.5194, y: 0.3928 } }, + { name: 'Pure Yellow 50% 60', hsl: { h: 60, s: 50, l: 50 }, rgb: { r: 192, g: 192, b: 64 }, xy: { x: 0.4258, y: 0.4883 } }, + { name: 'Lime Green 50% 90', hsl: { h: 90, s: 50, l: 50 }, rgb: { r: 128, g: 192, b: 64 }, xy: { x: 0.3159, y: 0.5639 } }, + { name: 'Pure Green 50% 120', hsl: { h: 120, s: 50, l: 50 }, rgb: { r: 64, g: 192, b: 64 }, xy: { x: 0.2127, y: 0.6349 } }, + { name: 'Light Sea Green 50% 150', hsl: { h: 150, s: 50, l: 50 }, rgb: { r: 64, g: 192, b: 128 }, xy: { x: 0.1932, y: 0.4845 } }, + { name: 'Pure Cyan 50% 180', hsl: { h: 180, s: 50, l: 50 }, rgb: { r: 64, g: 192, b: 192 }, xy: { x: 0.1745, y: 0.3407 } }, + { name: 'Deep Sky Blue 50% 210', hsl: { h: 210, s: 50, l: 50 }, rgb: { r: 64, g: 128, b: 192 }, xy: { x: 0.1752, y: 0.211 } }, + { name: 'Pure Blue 50% 240', hsl: { h: 240, s: 50, l: 50 }, rgb: { r: 64, g: 64, b: 192 }, xy: { x: 0.1758, y: 0.102 } }, + { name: 'Blue Violet 50% 270', hsl: { h: 270, s: 50, l: 50 }, rgb: { r: 128, g: 64, b: 192 }, xy: { x: 0.2688, y: 0.137 } }, + { name: 'Pure Magenta 50% 300', hsl: { h: 300, s: 50, l: 50 }, rgb: { r: 192, g: 64, b: 192 }, xy: { x: 0.3772, y: 0.1777 } }, + { name: 'Deep Pink 50% 330', hsl: { h: 330, s: 50, l: 50 }, rgb: { r: 192, g: 64, b: 128 }, xy: { x: 0.489, y: 0.2416 } }, + ]; + + colors.forEach((color) => { + // eslint-disable-next-line no-console + console.log( + `\x1b[48;2;${color.rgb.r};${color.rgb.g};${color.rgb.b}mColor: ${color.name}\x1b[0m, hsl: { h: ${color.hsl.h}, s: ${color.hsl.s}, l: ${color.hsl.l} }, rgb: { r: ${color.rgb.r}, g: ${color.rgb.g}, b: ${color.rgb.b} }, xy: { x: ${color.xy.x}, y: ${color.xy.y} }`, + ); + + const rgb = hslColorToRgbColor(color.hsl.h, color.hsl.s, color.hsl.l); + assert( + rgb.r === color.rgb.r && rgb.g === color.rgb.g && rgb.b === color.rgb.b, + `\x1b[48;2;${rgb.r};${rgb.g};${rgb.b}mColor: ${color.name}\x1b[0m hslColorToRgbColor { r: ${rgb.r}, g: ${rgb.g}, b: ${rgb.b} } conversion error`, + ); + + const hsl = rgbColorToHslColor({ r: color.rgb.r, g: color.rgb.g, b: color.rgb.b }); + assert(hsl.h === color.hsl.h && hsl.s === color.hsl.s && hsl.l === color.hsl.l, `Color: ${color.name} rgbColorToHslColor conversion error`); + + const xy = rgbColorToXYColor({ r: color.rgb.r, g: color.rgb.g, b: color.rgb.b }); + assert(xy.x === color.xy.x && xy.y === color.xy.y, `Color: ${color.name} rgbColorToXYColor conversion error got x ${xy.x} y ${xy.y}`); + + const rgb2 = xyColorToRgbColor(color.xy.x, color.xy.y); + assert( + rgb2.r === color.rgb.r && rgb2.g === color.rgb.g && rgb2.b === color.rgb.b, + `\x1b[48;2;${rgb2.r};${rgb2.g};${rgb2.b}mColor: ${color.name}\x1b[0m xyColorToRgbColor(${color.xy.x}, ${color.xy.y}) conversion error -> r: ${rgb2.r} g: ${rgb2.g} b: ${rgb2.g}`, + ); + }); +} + +/** + * Converts CIE color space to RGB color space + * @param {Number} x + * @param {Number} y + * @param {Number} brightness - Ranges from 1 to 254 + * @return {Array} Array that contains the color values for red, green and blue + * From: https://github.com/usolved/cie-rgb-converter/blob/master/cie_rgb_converter.js + */ +export function cie_to_rgb(x: number, y: number, brightness = 254): RGB { + // Set to maximum brightness if no custom value was given (Not the slick ECMAScript 6 way for compatibility reasons) + + const z = 1.0 - x - y; + const Y = (brightness / 254).toFixed(2); + const X = (Number(Y) / y) * x; + const Z = (Number(Y) / y) * z; + + // Convert to RGB using Wide RGB D65 conversion + let red = X * 1.656492 - Number(Y) * 0.354851 - Z * 0.255038; + let green = -X * 0.707196 + Number(Y) * 1.655397 + Z * 0.036152; + let blue = X * 0.051713 - Number(Y) * 0.121364 + Z * 1.01153; + + // If red, green or blue is larger than 1.0 set it back to the maximum of 1.0 + if (red > blue && red > green && red > 1.0) { + green = green / red; + blue = blue / red; + red = 1.0; + } else if (green > blue && green > red && green > 1.0) { + red = red / green; + blue = blue / green; + green = 1.0; + } else if (blue > red && blue > green && blue > 1.0) { + red = red / blue; + green = green / blue; + blue = 1.0; + } + + // Reverse gamma correction + red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * Math.pow(red, 1.0 / 2.4) - 0.055; + green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * Math.pow(green, 1.0 / 2.4) - 0.055; + blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * Math.pow(blue, 1.0 / 2.4) - 0.055; + + // Convert normalized decimal to decimal + red = Math.round(red * 255); + green = Math.round(green * 255); + blue = Math.round(blue * 255); + + // Normalize + if (isNaN(red) || red < 0) { + red = 0; + } + if (isNaN(green) || green < 0) { + green = 0; + } + if (isNaN(blue) || blue < 0) { + blue = 0; + } + + return { r: red, g: green, b: blue }; +} + +/** + * Converts RGB color space to CIE color space + * @param {Number} red + * @param {Number} green + * @param {Number} blue + * @return {Array} Array that contains the CIE color values for x and y + * From: https://github.com/usolved/cie-rgb-converter/blob/master/cie_rgb_converter.js + */ +export function rgb_to_cie(red: number, green: number, blue: number): XY { + // Apply a gamma correction to the RGB values, which makes the color more vivid and more the like the color displayed on the screen of your device + red = red > 0.04045 ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : red / 12.92; + green = green > 0.04045 ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : green / 12.92; + blue = blue > 0.04045 ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : blue / 12.92; + + // RGB values to XYZ using the Wide RGB D65 conversion formula + const X = red * 0.664511 + green * 0.154324 + blue * 0.162028; + const Y = red * 0.283881 + green * 0.668433 + blue * 0.047685; + const Z = red * 0.000088 + green * 0.07231 + blue * 0.986039; + + // Calculate the xy values from the XYZ values + let x = (X / (X + Y + Z)).toFixed(4); + let y = (Y / (X + Y + Z)).toFixed(4); + + if (isNaN(Number(x))) { + x = '0'; + } + + if (isNaN(Number(y))) { + y = '0'; + } + + return { x: Number(x), y: Number(y) }; +} + +/* +testColors(); +console.log('rgb_to_cie(0, 128, 255)', rgb_to_cie(0, 128, 255)); +console.log('cie_to_rgb(0.1401, 0.1284, 254)', cie_to_rgb(0.1401, 0.1284, 254)); +console.log('cie_to_rgb(0.1406, 0.1382, 254)', cie_to_rgb(0.1406, 0.1382, 254)); +for (let h = 0; h < 360; h++) { + const rgb = hslColorToRgbColor(h, 100, 50); + const xy = rgbColorToXYColor({ r: rgb.r, g: rgb.g, b: rgb.b }); + const rgb2 = cie_to_rgb(xy.x, xy.y, 254); + const hsl = rgbColorToHslColor({ r: rgb2.r, g: rgb2.g, b: rgb2.b }); + assert(rgb.r === rgb2.r && rgb.g === rgb2.g && rgb.b === rgb2.b, 'Color rgb conversion error'); + assert(h === hsl.h, 'Color hsl conversion error'); + console.log(`\x1b[48;2;${rgb.r};${rgb.g};${rgb.b}mColor: r:${rgb.r} g:${rgb.g} b:${rgb.b}\x1b[0m => x:${xy.x} y:${xy.y} => \x1b[48;2;${rgb2.r};${rgb2.g};${rgb2.b}mColor: r:${rgb2.r} g:${rgb2.g} b:${rgb2.b} h:${hsl.h} s:${hsl.s}\x1b[0m\x1b[K`); +} +*/ diff --git a/src/index.ts b/src/index.ts index 9f0ab52..c2d6b2e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,26 @@ +/** + * This file contains the main entryPoint for the plugin. + * + * @file src\index.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import { Matterbridge, PlatformConfig } from 'matterbridge'; import { AnsiLogger } from 'node-ansi-logger'; import { ShellyPlatform } from './platform.js'; @@ -12,6 +35,7 @@ import { ShellyPlatform } from './platform.js'; * @returns {ShellyPlatform} - An instance of the SomfyTahomaPlatform. This is the main interface for interacting with the Somfy Tahoma system. * */ + export default function initializePlugin(matterbridge: Matterbridge, log: AnsiLogger, config: PlatformConfig): ShellyPlatform { return new ShellyPlatform(matterbridge, log, config); } diff --git a/src/mcastServer.ts b/src/mcastServer.ts index 7474397..b9dc300 100644 --- a/src/mcastServer.ts +++ b/src/mcastServer.ts @@ -1,3 +1,26 @@ +/** + * This file contains the class Multicast. + * + * @file src\mcastServer.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import { AnsiLogger, TimestampFormat } from 'node-ansi-logger'; import { getIpv4InterfaceAddress } from 'matterbridge'; import dgram from 'dgram'; diff --git a/src/mdnsScanner.ts b/src/mdnsScanner.ts index b7608c8..88e07f3 100644 --- a/src/mdnsScanner.ts +++ b/src/mdnsScanner.ts @@ -1,3 +1,26 @@ +/** + * This file contains the class MdnsScanner. + * + * @file src\mdnsScanner.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import { AnsiLogger, BLUE, CYAN, TimestampFormat, db, debugStringify, hk, idn, nf, rs, zb } from 'node-ansi-logger'; import mdns, { ResponsePacket } from 'multicast-dns'; import EventEmitter from 'events'; diff --git a/src/platform.ts b/src/platform.ts index 8b778a8..7d2c95f 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1,3 +1,26 @@ +/** + * This file contains the class ShellyPlatform. + * + * @file src\platform.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import { Matterbridge, MatterbridgeDevice, @@ -34,6 +57,7 @@ import { DiscoveredDevice } from './mdnsScanner.js'; import { ShellyDevice } from './shellyDevice.js'; import { ShellyCoverComponent, ShellySwitchComponent } from './shellyComponent.js'; import { ShellyData, ShellyDataType } from './shellyTypes.js'; +import { rgbColorToHslColor } from './colorUtils.js'; type ConfigDeviceIp = Record; @@ -612,6 +636,24 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform { cluster?.setCurrentLevelAttribute(matterLevel); shellyDevice.log.info(`${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}LevelControl-currentLevel${db} ${YELLOW}${matterLevel}${db}`); } + // Update color + const colors = ['red', 'green', 'blue', 'white']; + if (shellyComponent.name === 'Light' && colors.includes(property)) { + // shellyDevice.getComponent(component)?.setValue(property, value); + const red = property === 'red' ? (value as number) : (shellyComponent.getValue('red') as number); + const green = property === 'green' ? (value as number) : (shellyComponent.getValue('green') as number); + const blue = property === 'blue' ? (value as number) : (shellyComponent.getValue('blue') as number); + const white = property === 'white' ? (value as number) : (shellyComponent.getValue('white') as number); + this.log.warn(`Color: R: ${red} G: ${green} B: ${blue} W: ${white}`); + const cluster = endpoint.getClusterServer(ColorControl.Complete); + const hsl = rgbColorToHslColor({ r: red, g: green, b: blue }); + cluster?.setCurrentHueAttribute(hsl.h); + cluster?.setCurrentSaturationAttribute(hsl.s); + cluster?.setColorModeAttribute(ColorControl.ColorMode.CurrentHueAndCurrentSaturation); + shellyDevice.log.info( + `${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}ColorControl.currentHue:${YELLOW}${hsl.h}${db} ${hk}ColorControl.currentSaturation:${YELLOW}${hsl.s}${db}`, + ); + } // Update cover if (shellyComponent.name === 'Cover' || shellyComponent.name === 'Roller') { const windowCoveringCluster = endpoint.getClusterServer( diff --git a/src/shelly.ts b/src/shelly.ts index 9ab9a74..0c4517a 100644 --- a/src/shelly.ts +++ b/src/shelly.ts @@ -1,6 +1,31 @@ +/** + * This file contains the class Shelly. + * + * @file src\shelly.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + +import { AnsiLogger, CYAN, MAGENTA, BRIGHT, hk, db, nf, wr, zb, er } from 'node-ansi-logger'; + import EventEmitter from 'events'; + import { ShellyDevice } from './shellyDevice.js'; -import { AnsiLogger, CYAN, MAGENTA, BRIGHT, hk, db, nf, wr, zb, er } from 'node-ansi-logger'; import { DiscoveredDevice, MdnsScanner } from './mdnsScanner.js'; import { CoapServer } from './coapServer.js'; diff --git a/src/shellyComponent.ts b/src/shellyComponent.ts index df93c74..33440f9 100644 --- a/src/shellyComponent.ts +++ b/src/shellyComponent.ts @@ -1,3 +1,26 @@ +/** + * This file contains the class SwitchComponent. + * + * @file src\shellyComponent.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import { BLUE, CYAN, GREEN, GREY, YELLOW, db, debugStringify, er } from 'node-ansi-logger'; import { ShellyData, ShellyDataType } from './shellyTypes.js'; diff --git a/src/shellyDevice.ts b/src/shellyDevice.ts index 633c1f0..f7649d3 100644 --- a/src/shellyDevice.ts +++ b/src/shellyDevice.ts @@ -1,3 +1,26 @@ +/** + * This file contains the class ShellyDevice. + * + * @file src\shellyDevice.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import { AnsiLogger, BLUE, CYAN, GREEN, GREY, MAGENTA, RED, RESET, db, debugStringify, er, hk, nf, wr, zb } from 'node-ansi-logger'; import { EventEmitter } from 'events'; import fetch, { RequestInit } from 'node-fetch'; diff --git a/src/shellyProperty.ts b/src/shellyProperty.ts index c3f3c02..1ae206f 100644 --- a/src/shellyProperty.ts +++ b/src/shellyProperty.ts @@ -1,3 +1,26 @@ +/** + * This file contains the class ShellyProperty. + * + * @file src\shellyProperty.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + import { ShellyComponent } from './shellyComponent.js'; import { ShellyDataType } from './shellyTypes.js'; diff --git a/src/shellyTypes.ts b/src/shellyTypes.ts index ac623b0..7ccf6dd 100644 --- a/src/shellyTypes.ts +++ b/src/shellyTypes.ts @@ -1,3 +1,26 @@ +/** + * This file contains the shelly types. + * + * @file src\shellyTypes.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + export declare type ParamsTypes = boolean | number | string; export type ShellyData = Record; diff --git a/src/wsClient.ts b/src/wsClient.ts index bdf092e..147faeb 100644 --- a/src/wsClient.ts +++ b/src/wsClient.ts @@ -1,6 +1,27 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * This file contains the class WsClient. + * + * @file src\wsClient.ts + * @author Luca Liguori + * @date 2024-05-01 + * @version 1.0.0 + * + * Copyright 2024, 2025 Luca Liguori. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ -import { AnsiLogger, BLUE, CYAN, GREEN, TimestampFormat, db, debugStringify, dn, er, hk, idn, nf, rs, wr, zb } from 'node-ansi-logger'; +import { AnsiLogger, BLUE, CYAN, TimestampFormat, db, er, nf, rs, wr, zb } from 'node-ansi-logger'; import WebSocket from 'ws'; import crypto from 'crypto'; import EventEmitter from 'events'; @@ -30,6 +51,7 @@ interface RequestFrameWithAuth { auth: AuthParams; } +// eslint-disable-next-line @typescript-eslint/no-unused-vars interface ResponseError { id: number; src: string; @@ -50,6 +72,7 @@ interface ResponseErrorMessage { type Params = Record; +// eslint-disable-next-line @typescript-eslint/no-unused-vars interface ResponseNotifyStatus { src: string; dst: string; @@ -57,6 +80,7 @@ interface ResponseNotifyStatus { params: Params; } +// eslint-disable-next-line @typescript-eslint/no-unused-vars interface Response { id: number; src: string; @@ -136,6 +160,7 @@ export class WsClient extends EventEmitter { }); // Handle messages from the WebSocket + // eslint-disable-next-line @typescript-eslint/no-unused-vars this.wsClient.on('message', (data: WebSocket.RawData, isBinary: boolean) => { const response = JSON.parse(data.toString()); this.id = response.src;