From 3564a34502cc6ac31afca0fbad35bf5f2eb86916 Mon Sep 17 00:00:00 2001 From: Brandon Watson Date: Thu, 23 Dec 2021 15:58:25 -0500 Subject: [PATCH] Updating drone config --- .drone.yml | 69 ++++++--- package.json | 69 ++++----- src/chase.ts | 406 +++++++++++++++++++++++++++------------------------ src/index.ts | 176 ++++++++++++---------- 4 files changed, 396 insertions(+), 324 deletions(-) diff --git a/.drone.yml b/.drone.yml index 6f234b6..489f2aa 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,40 +2,69 @@ kind: pipeline type: docker name: default -clone: - disable: true - steps: - - name: clone - image: alpine/git - commands: - - git clone https://gitea.watsonlabs.net/watsonb8/homebridge-hue-chase.git . - - git checkout $DRONE_COMMIT - - name: build image: node commands: - npm install - npm run build - - name: publish + - name: version + image: node + commands: + - export version=`node -p "require('./package.json').version"` + - export commit=`echo $DRONE_COMMIT | cut -c1-5` + - npm version prerelease --preid=$commit --git-tag-version=false --allow-same-version=true + when: + event: + exclude: + - tag + - pull_request + branch: + include: + - master + + - name: publish pre + image: plugins/npm + settings: + username: + from_secret: npm_username + password: + from_secret: npm_password + email: brandon@watsonlabs.net + registry: "http://10.44.1.6:4873/" + when: + event: + exclude: + - tag + - pull_request + branch: + include: + - master + + - name: publish tagged version image: plugins/npm settings: username: admin password: from_secret: npm_password email: brandon@watsonlabs.net - registry: "http://linuxhost.me:4873/" + registry: "http://10.44.1.6:4873/" when: event: - tag + exclude: + - pull_request -notify: - image: drillster/drone-email - host: smtp.watsonlabs.net - username: srvGitea - password: - from_secret: smtp_password - from: drone@watsonlabs.net - when: - status: [failure] + - name: Notify + image: drillster/drone-email + settings: + host: 10.44.1.13 + username: srvGitea + password: + from_secret: smtp_password + from: drone@watsonlabs.net + skip_verify: true + when: + status: + - failure diff --git a/package.json b/package.json index d868023..83f0700 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,37 @@ { - "name": "homebridge-hue-chase", - "version": "1.1.0", - "description": "A Phillips Hue add on for creating chase sequences.", - "main": "bin/index.js", - "scripts": { - "build": "tsc", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "ssh://git@watsonb8.ddns.net:2122/home/homebridge-hue-chase.git" - }, - "keywords": [ - "homebridge-plugin", - "homebridge", - "homekit", - "hue", - "phillips", - "typescript" - ], - "author": "Brandon Watson", - "license": "ISC", - "engines": { - "homebridge": ">=0.4.21", - "node": ">=7.6.0" - }, - "dependencies": { - "@types/node": "^13.1.2", - "@types/node-hue-api": "^2.3.0", - "homebridge": "^0.4.50", - "node-hue-api": "^4.0.0" - } -} + "name": "@watsonb8/homebridge-hue-chase", + "version": "1.1.0", + "description": "A Phillips Hue add on for creating chase sequences.", + "main": "bin/index.js", + "publishConfig": { + "registry": "http://10.44.1.6:4873/" + }, + "scripts": { + "build": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "ssh://git@watsonb8.ddns.net:2122/home/homebridge-hue-chase.git" + }, + "keywords": [ + "homebridge-plugin", + "homebridge", + "homekit", + "hue", + "phillips", + "typescript" + ], + "author": "Brandon Watson", + "license": "ISC", + "engines": { + "homebridge": ">=0.4.21", + "node": ">=7.6.0" + }, + "dependencies": { + "@types/node": "^13.1.2", + "@types/node-hue-api": "^2.3.0", + "homebridge": "^0.4.50", + "node-hue-api": "^4.0.0" + } +} \ No newline at end of file diff --git a/src/chase.ts b/src/chase.ts index 1d81adc..6570d5b 100644 --- a/src/chase.ts +++ b/src/chase.ts @@ -9,215 +9,241 @@ let Service: HAPNodeJS.Service; let Characteristic: HAPNodeJS.Characteristic; export interface IChaseProps { - name: string; - api: any; - log: any; - homebridge: any; - sequence: ISequence; - hue: Api + name: string; + api: any; + log: any; + homebridge: any; + sequence: ISequence; + hue: Api; } export class Chase implements IAccessory { - private _api: any; - private _homebridge: any; - private _log: any = {}; + private _api: any; + private _homebridge: any; + private _log: any = {}; - //Service fields - private _lightbulbService: HAPNodeJS.Service; - private _infoService: HAPNodeJS.Service; + //Service fields + private _lightbulbService: HAPNodeJS.Service; + private _infoService: HAPNodeJS.Service; - private _sequence: ISequence; + private _sequence: ISequence; - private _isActive: boolean; - private _hue: Api; + private _isActive: boolean; + private _hue: Api; - private _lights: Array = []; + private _lights: Array = []; - private _brightness: number = 0; + private _brightness: number = 0; - constructor(props: IChaseProps) { - //Assign class variables - this._log = props.log; - this._api = props.api; - Service = props.api.hap.Service; - Characteristic = props.api.hap.Characteristic; - this.name = props.name; - this._homebridge = props.homebridge; + constructor(props: IChaseProps) { + //Assign class variables + this._log = props.log; + this._api = props.api; + Service = props.api.hap.Service; + Characteristic = props.api.hap.Characteristic; + this.name = props.name; + this._homebridge = props.homebridge; - this._sequence = props.sequence; + this._sequence = props.sequence; - this._isActive = false; - this._hue = props.hue; + this._isActive = false; + this._hue = props.hue; - this.platformAccessory = new this._homebridge.platformAccessory(this.name, this.generateUUID(), this._homebridge.hap.Accessory.Categories.SWITCH); + this.platformAccessory = new this._homebridge.platformAccessory( + this.name, + this.generateUUID(), + this._homebridge.hap.Accessory.Categories.SWITCH + ); + //@ts-ignore + this._infoService = new Service.AccessoryInformation(); + this._infoService.setCharacteristic( + Characteristic.Manufacturer, + "Brandon Watson" + ); + this._infoService.setCharacteristic(Characteristic.Model, "Hue Chase"); + this._infoService.setCharacteristic( + Characteristic.SerialNumber, + "123-456-789" + ); + + this._lightbulbService = new Service.Lightbulb( + this.name, + "lightbulbService" + ); + + this._lightbulbService + .getCharacteristic(Characteristic.On) + //@ts-ignore + .on("set", this.onPowerSet) + .on("get", this.onPowerGet); + + this._lightbulbService + .getCharacteristic(Characteristic.Brightness) + //@ts-ignore + .on("set", this.onBrightnessSet) + .on("get", this.onBrightnessGet); + } + + /** + * Required by homebridge. + */ + public name: string; + + public platformAccessory: any; + + /** + * Called by homebridge to gather services. + */ + public getServices = (): Array => { + return [this._infoService, this._lightbulbService!]; + }; + + /** + * Handler for switch set event + * @param callback The callback function to call when complete + */ + private onPowerSet = async ( + activeState: boolean, + callback: (error?: Error | null | undefined) => void + ) => { + if (this._isActive != activeState) { + activeState ? this.chase() : this.stop(); + } + return callback(); + }; + + /** + * Handler for switch get event + * @param callback The callback function to call when complete + */ + private onPowerGet = () => { + return this._isActive; + }; + + private onBrightnessSet = async ( + newValue: number, + callback: (error?: Error | null | undefined) => void + ) => { + this._brightness = newValue; + const lightState = new LightState(); + lightState.on(true).brightness(this._brightness); + + await this.setLights(lightState); + + return callback(); + }; + + private onBrightnessGet = () => { + return this._brightness; + }; + + /** + * Popuplates internal lights array using the configuration values + */ + private getLights = async () => { + //Get lights + const lightPromises: Array> = this._sequence.lights.map( + async (value: string) => { //@ts-ignore - this._infoService = new Service.AccessoryInformation(); - this._infoService.setCharacteristic(Characteristic.Manufacturer, "Brandon Watson") - this._infoService.setCharacteristic(Characteristic.Model, "Hue Chase") - this._infoService.setCharacteristic(Characteristic.SerialNumber, "123-456-789"); - - this._lightbulbService = new Service.Lightbulb( - this.name, - 'lightbulbService' - ) - - this._lightbulbService.getCharacteristic(Characteristic.On) - //@ts-ignore - .on("set", this.onPowerSet) - .on("get", this.onPowerGet); - - this._lightbulbService.getCharacteristic(Characteristic.Brightness) - //@ts-ignore - .on("set", this.onBrightnessSet) - .on("get", this.onBrightnessGet); + const light: Light = await this._hue.lights.getLightByName(value); + this._lights.push(light); + } + ); + await Promise.all(lightPromises); + }; + /** + * Execute chase sequence + */ + private chase = async () => { + if (this._lights.length === 0) { + await this.getLights(); } + const lightState = new LightState(); + let idx = 0; + this._isActive = true; + while (this._isActive) { + const rgb = this.hexToRgb(this._sequence.colors[idx]); + lightState + .on(true) + .brightness(this._brightness) + .transitionInMillis(this._sequence.transitionTime) + .rgb(rgb?.red, rgb?.green, rgb?.blue); - /** - * Required by homebridge. - */ - public name: string; + await this.setLights(lightState); - public platformAccessory: any; - - /** - * Called by homebridge to gather services. - */ - public getServices = (): Array => { - return [this._infoService, this._lightbulbService!]; + await Sleep(this._sequence.transitionTime); + if (idx == this._sequence.colors.length) { + idx = 0; + } else { + idx++; + } } + }; - /** - * Handler for switch set event - * @param callback The callback function to call when complete - */ - private onPowerSet = async (activeState: boolean, callback: (error?: Error | null | undefined) => void) => { - if (this._isActive != activeState) { - activeState ? this.chase() : this.stop(); + /** + * Stop chase sequence + */ + private stopAndSetOff = async () => { + this._isActive = false; + const lightState: LightState = new LightState().off(); + + await this.setLights(lightState); + }; + + private stop = () => { + this._isActive = false; + }; + + private setLights = async (state: LightState) => { + try { + await Promise.all( + this._lights.map(async (value: Light) => { + await this._hue.lights.setLightState(value.id, state); + }) + ); + } catch (err) { + this._log(`Error while setting brightness: ${err}`); + } + }; + + /** + * Helper function to convert a hex string to rgb + * @param hex hex string starting with "#" + */ + private hexToRgb = ( + hex: string + ): { red: number; green: number; blue: number } | null => { + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + red: parseInt(result[1], 16), + green: parseInt(result[2], 16), + blue: parseInt(result[3], 16), } - return callback(); + : null; + }; + + /** + * Helper function to generate a UUID + */ + private generateUUID(): string { + // Public Domain/MIT + var d = new Date().getTime(); + if ( + typeof performance !== "undefined" && + typeof performance.now === "function" + ) { + d += performance.now(); //use high-precision timer if available } - - /** - * Handler for switch get event - * @param callback The callback function to call when complete - */ - private onPowerGet = (callback: (error: Error | null, value: boolean) => void) => { - return callback(null, this._isActive); - } - - private onBrightnessSet = async (newValue: number, callback: (error?: Error | null | undefined) => void) => { - this._brightness = newValue; - const lightState = new LightState(); - lightState - .on(true) - .brightness(this._brightness) - - await this.setLights(lightState); - - return callback(); - } - - private onBrightnessGet = (callback: (eror: Error | null, value: number) => void) => { - return callback(null, this._brightness); - } - - /** - * Popuplates internal lights array using the configuration values - */ - private getLights = async () => { - //Get lights - const lightPromises: Array> = this._sequence.lights.map(async (value: string) => { - //@ts-ignore - const light: Light = await this._hue.lights.getLightByName(value) - this._lights.push(light); - }); - await Promise.all(lightPromises); - } - - /** - * Execute chase sequence - */ - private chase = async () => { - if (this._lights.length === 0) { - await this.getLights(); - } - const lightState = new LightState(); - let idx = 0; - this._isActive = true; - while (this._isActive) { - const rgb = this.hexToRgb(this._sequence.colors[idx]) - lightState - .on(true) - .brightness(this._brightness) - .transitionInMillis(this._sequence.transitionTime) - .rgb(rgb?.red, rgb?.green, rgb?.blue); - - await this.setLights(lightState); - - - await Sleep(this._sequence.transitionTime); - if (idx == this._sequence.colors.length) { - idx = 0; - } else { - idx++; - } - } - } - - /** - * Stop chase sequence - */ - private stopAndSetOff = async () => { - this._isActive = false; - const lightState: LightState = new LightState() - .off(); - - await this.setLights(lightState); - } - - private stop = () => { - this._isActive = false; - } - - private setLights = async (state: LightState) => { - try { - await Promise.all( - this._lights.map(async (value: Light) => { - await this._hue.lights.setLightState(value.id, state); - }) - ); - } catch (err) { - this._log(`Error while setting brightness: ${err}`) - } - } - - /** - * Helper function to convert a hex string to rgb - * @param hex hex string starting with "#" - */ - private hexToRgb = (hex: string): { red: number, green: number, blue: number } | null => { - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - red: parseInt(result[1], 16), - green: parseInt(result[2], 16), - blue: parseInt(result[3], 16) - } : null; - } - - /** - * Helper function to generate a UUID - */ - private generateUUID(): string { // Public Domain/MIT - var d = new Date().getTime(); - if (typeof performance !== 'undefined' && typeof performance.now === 'function') { - d += performance.now(); //use high-precision timer if available - } - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - var r = (d + Math.random() * 16) % 16 | 0; - d = Math.floor(d / 16); - return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); - }); - } -} \ No newline at end of file + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( + /[xy]/g, + function (c) { + var r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); + } + ); + } +} diff --git a/src/index.ts b/src/index.ts index 9e00dd5..9c9b411 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import { Chase } from "./chase"; import { IConfig, ISequence } from "./models/iConfig"; -import { v3 } from 'node-hue-api'; +import { v3 } from "node-hue-api"; import LocalBootstrap = require("node-hue-api/lib/api/http/LocalBootstrap"); import Api = require("node-hue-api/lib/api/Api"); import { Sleep } from "./sleep"; @@ -10,97 +10,111 @@ let Homebridge: any; /** * Main entry. - * @param homebridge + * @param homebridge */ export default function (homebridge: any) { - Homebridge = homebridge; - Accessory = homebridge.platformAccessory; - homebridge.registerPlatform( - 'homebridge-hue-chase', - 'HueChase', - HueChasePlatform, - true - ); -}; + Homebridge = homebridge; + Accessory = homebridge.platformAccessory; + homebridge.registerPlatform( + "homebridge-hue-chase", + "HueChase", + HueChasePlatform, + true + ); +} class HueChasePlatform { - log: any = {}; - api: any; - chaseList: Array = []; - config: IConfig; - hue: Api | undefined; + log: any = {}; + api: any; + chaseList: Array = []; + config: IConfig; + hue: Api | undefined; + constructor(log: any, config: any, api: any) { + this.log = log; + this.api = api; + this.config = config; + this.log("INFO - Registering Hue Chase platform"); + this.api.on("didFinishLaunching", this.didFinishLaunching.bind(this)); + } - constructor(log: any, config: any, api: any) { - this.log = log; - this.api = api; - this.config = config; - this.log('INFO - Registering Hue Chase platform'); - this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this)); + private connectHue = async () => { + if (!this.config) { + return; } - private connectHue = async () => { - if (!this.config) { - return; - } - - if (this.config.userName && this.config.clientKey) { - this.hue = await v3.api.createLocal(this.config.ipAddress).connect(this.config.userName, this.config.clientKey, null); - this.log("Using existing connection info"); - } else { - const unauthenticatedApi = await v3.api.createLocal(this.config.ipAddress).connect(null, null, null); - let createdUser; - let connected = false - while (!connected) { - try { - this.log("Creating hue user. Push link button") - createdUser = await unauthenticatedApi.users.createUser("homebridge", "HueChase"); - - this.hue = await v3.api.createLocal(this.config.ipAddress).connect(createdUser.username, createdUser.clientKey, null); - this.log("Connected to Hue Bridge"); - this.log(`UserName: ${createdUser.username}, ClientKey: ${createdUser.clientkey}`) - connected = true; - - } catch (err) { - if (err.getHueErrorType() === 101) { - this.log('The Link button on the bridge was not pressed. Please press the Link button and try again.'); - Sleep(5000); - } else { - this.log(`Unexpected Error: ${err.message}`); - break; - } - - } - } + if (this.config.userName && this.config.clientKey) { + this.hue = await v3.api + .createLocal(this.config.ipAddress) + .connect(this.config.userName, this.config.clientKey, null); + this.log("Using existing connection info"); + } else { + const unauthenticatedApi = await v3.api + .createLocal(this.config.ipAddress) + .connect(null, null, null); + let createdUser; + let connected = false; + while (!connected) { + try { + this.log("Creating hue user. Push link button"); + createdUser = await unauthenticatedApi.users.createUser( + "homebridge", + "HueChase" + ); + + this.hue = await v3.api + .createLocal(this.config.ipAddress) + .connect(createdUser.username, createdUser.clientKey, null); + this.log("Connected to Hue Bridge"); + this.log( + `UserName: ${createdUser.username}, ClientKey: ${createdUser.clientkey}` + ); + connected = true; + } catch (err: any) { + if (err.getHueErrorType() === 101) { + this.log( + "The Link button on the bridge was not pressed. Please press the Link button and try again." + ); + Sleep(5000); + } else { + this.log(`Unexpected Error: ${err.message}`); + break; + } } + } } + }; - /** - * Handler for didFinishLaunching - * Happens after constructor - */ - private didFinishLaunching() { - this.log(`INFO - Done registering Hue Chase platform`); - } + /** + * Handler for didFinishLaunching + * Happens after constructor + */ + private didFinishLaunching() { + this.log(`INFO - Done registering Hue Chase platform`); + } - /** - * Called by homebridge to gather accessories. - * @param callback - */ - public accessories = async (callback: (accessories: Array) => void) => { - //Connect to hue bridge - await this.connectHue(); + /** + * Called by homebridge to gather accessories. + * @param callback + */ + public accessories = async ( + callback: (accessories: Array) => void + ) => { + //Connect to hue bridge + await this.connectHue(); - this.config.sequences.forEach((sequence: ISequence) => { - this.chaseList.push(new Chase({ - name: sequence.name, - api: this.api, - log: this.log, - homebridge: Homebridge, - sequence: sequence, - hue: this.hue!, - })) + this.config.sequences.forEach((sequence: ISequence) => { + this.chaseList.push( + new Chase({ + name: sequence.name, + api: this.api, + log: this.log, + homebridge: Homebridge, + sequence: sequence, + hue: this.hue!, }) - callback(this.chaseList); - } -} \ No newline at end of file + ); + }); + callback(this.chaseList); + }; +}