Squashed commit of the following:
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			commit440a4d62a1Author: Brandon Watson <brandon@watsonlabs.net> Date: Tue Sep 6 20:23:31 2022 -0500 Fixing issue where button becomes unresponsive | Updating homebridge commitd73dead5d4Author: Brandon Watson <brandon@watsonlabs.net> Date: Tue Sep 6 18:25:38 2022 -0500 Successfully controlling wiz bulbs
This commit is contained in:
		
							
								
								
									
										4
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|     "tabWidth": 4, | ||||
|     "useTabs": false | ||||
| } | ||||
							
								
								
									
										35
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,19 +1,20 @@ | ||||
| { | ||||
|   // Use IntelliSense to learn about possible attributes. | ||||
|   // Hover to view descriptions of existing attributes. | ||||
|   // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|   "version": "0.2.0", | ||||
|   "configurations": [ | ||||
|     { | ||||
|       "type": "node", | ||||
|       "request": "launch", | ||||
|       "name": "Launch Program", | ||||
|       "preLaunchTask": "build", | ||||
|       "program": "/Users/brandonwatson/.nvm/versions/node/v14.15.0/lib/node_modules/homebridge/bin/homebridge", | ||||
|       "env": { | ||||
|         "HOMEBRIDGE_OPTS": "/Users/brandonwatson/.homebridge" | ||||
|       }, | ||||
|       "sourceMaps": true | ||||
|     } | ||||
|   ] | ||||
|     // Use IntelliSense to learn about possible attributes. | ||||
|     // Hover to view descriptions of existing attributes. | ||||
|     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|     "version": "0.2.0", | ||||
|     "configurations": [ | ||||
|         { | ||||
|             "type": "node", | ||||
|             "request": "launch", | ||||
|             "name": "Launch Program", | ||||
|             "preLaunchTask": "build", | ||||
|             "program": "/Users/brandonwatson/.nvm/versions/node/v14.15.0/lib/node_modules/homebridge/bin/homebridge", | ||||
|             "env": { | ||||
|                 "HOMEBRIDGE_OPTS": "/Users/brandonwatson/.homebridge", | ||||
|                 "LOG_LEVEL": "debug" | ||||
|             }, | ||||
|             "sourceMaps": true | ||||
|         } | ||||
|     ] | ||||
| } | ||||
|   | ||||
							
								
								
									
										2662
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2662
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -21,7 +21,7 @@ | ||||
|         "typescript" | ||||
|     ], | ||||
|     "engines": { | ||||
|         "homebridge": ">=0.4.21", | ||||
|         "homebridge": ">=1.1.6", | ||||
|         "node": ">=7.6.0" | ||||
|     }, | ||||
|     "author": "Brandon Watson", | ||||
| @@ -29,13 +29,14 @@ | ||||
|     "dependencies": { | ||||
|         "@types/node-cron": "^2.0.3", | ||||
|         "@types/suncalc": "^1.8.0", | ||||
|         "@watsonb8/wiz-lib": "^1.0.1-62427.0", | ||||
|         "node-cron": "^2.0.3", | ||||
|         "node-hue-api": "^4.0.5", | ||||
|         "suncalc": "^1.8.0" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@types/node": "^13.11.1", | ||||
|         "homebridge": "^1.3.9", | ||||
|         "homebridge": "^1.5.0", | ||||
|         "typescript": "^4.5.4" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import { IAccessory } from "./models/iAccessory"; | ||||
| import Api = require("node-hue-api/lib/api/Api"); | ||||
| import Light = require("node-hue-api/lib/model/Light"); | ||||
| import LightState = require("node-hue-api/lib/model/lightstate/LightState"); | ||||
| @@ -8,347 +7,360 @@ import { IConfig } from "./models/iConfig"; | ||||
| import { GetTimesResult, getTimes } from "suncalc"; | ||||
| import HueError = require("node-hue-api/lib/HueError"); | ||||
| import cron from "node-cron"; | ||||
| import { WizBulb } from "@watsonb8/wiz-lib/build/wizBulb"; | ||||
| import { colorTemperature2rgb, RGB } from "@watsonb8/wiz-lib"; | ||||
| import { PlatformAccessory } from "homebridge"; | ||||
| import { Platform } from "./platform"; | ||||
|  | ||||
| let Service: HAPNodeJS.Service; | ||||
| let Characteristic: HAPNodeJS.Characteristic; | ||||
| const SECONDS_IN_DAY = 86400000; | ||||
| const MINUTES_IN_MILLISECOND = 60000; | ||||
| const SECONDS_IN_HOUR = 3600; | ||||
|  | ||||
| export interface IFluxProps { | ||||
|   api: any; | ||||
|   log: any; | ||||
|   homebridge: any; | ||||
|   hue: Api; | ||||
|   config: IConfig; | ||||
|     platform: Platform; | ||||
|     accessory: PlatformAccessory; | ||||
|     hue: Api; | ||||
|     wizBulbs: Array<WizBulb>; | ||||
|     config: IConfig; | ||||
| } | ||||
|  | ||||
| export class FluxAccessory implements IAccessory { | ||||
|   private _api: any; | ||||
|   private _homebridge: any; | ||||
|   private _log: any = {}; | ||||
|   private _config: IConfig; | ||||
|   private _isActive: boolean; | ||||
| export class FluxAccessory { | ||||
|     private readonly _platform: Platform; | ||||
|     private readonly _accessory: PlatformAccessory; | ||||
|     private _config: IConfig; | ||||
|     private _isActive: boolean; | ||||
|  | ||||
|   //Service fields | ||||
|   private _switchService: HAPNodeJS.Service; | ||||
|   private _infoService: HAPNodeJS.Service; | ||||
|     //Service fields | ||||
|     private _switchService; | ||||
|  | ||||
|   private _hue: Api; | ||||
|     private _hue: Api; | ||||
|  | ||||
|   private _lights: Array<Light> = []; | ||||
|     private _lights: Array<Light> = []; | ||||
|     private _wizLights: Array<WizBulb> = []; | ||||
|  | ||||
|   private _times: GetTimesResult; | ||||
|     private _times: GetTimesResult; | ||||
|  | ||||
|   constructor(props: IFluxProps) { | ||||
|     //Assign class variables | ||||
|     this._log = props.log; | ||||
|     this._api = props.api; | ||||
|     this._config = props.config; | ||||
|     Service = props.api.hap.Service; | ||||
|     Characteristic = props.api.hap.Characteristic; | ||||
|     this._homebridge = props.homebridge; | ||||
|     this._isActive = false; | ||||
|     constructor(props: IFluxProps) { | ||||
|         //Assign class variables | ||||
|         this._platform = props.platform; | ||||
|         this._accessory = props.accessory; | ||||
|         this._config = props.config; | ||||
|         this._isActive = false; | ||||
|         this._wizLights = props.wizBulbs; | ||||
|  | ||||
|     this._times = getTimes( | ||||
|       new Date(), | ||||
|       this._config.latitude, | ||||
|       this._config.longitude | ||||
|     ); | ||||
|  | ||||
|     //Schedule job to refresh times | ||||
|     cron | ||||
|       .schedule( | ||||
|         "0 12 * * *", | ||||
|         () => { | ||||
|           this._times = getTimes( | ||||
|         this._times = getTimes( | ||||
|             new Date(), | ||||
|             this._config.latitude, | ||||
|             this._config.longitude | ||||
|           ); | ||||
|           this._log("Updated sunset times"); | ||||
|         }, | ||||
|         { | ||||
|           scheduled: true, | ||||
|         } | ||||
|       ) | ||||
|       .start(); | ||||
|         ); | ||||
|  | ||||
|     this._hue = props.hue; | ||||
|     this.name = this._config.name; | ||||
|         //Schedule job to refresh times | ||||
|         cron.schedule( | ||||
|             "0 12 * * *", | ||||
|             () => { | ||||
|                 this._times = getTimes( | ||||
|                     new Date(), | ||||
|                     this._config.latitude, | ||||
|                     this._config.longitude | ||||
|                 ); | ||||
|                 this._platform.log.info("Updated sunset times"); | ||||
|             }, | ||||
|             { | ||||
|                 scheduled: true, | ||||
|             } | ||||
|         ).start(); | ||||
|  | ||||
|     this.platformAccessory = new this._homebridge.platformAccessory( | ||||
|       this.name, | ||||
|       this.generateUUID(), | ||||
|       this._homebridge.hap.Accessory.Categories.SWITCH | ||||
|     ); | ||||
|         this._hue = props.hue; | ||||
|         this.name = this._config.name; | ||||
|  | ||||
|     //@ts-ignore | ||||
|     this._infoService = new Service.AccessoryInformation(); | ||||
|     this._infoService.setCharacteristic( | ||||
|       Characteristic.Manufacturer, | ||||
|       "Brandon Watson" | ||||
|     ); | ||||
|     this._infoService.setCharacteristic(Characteristic.Model, "F.lux"); | ||||
|     this._infoService.setCharacteristic( | ||||
|       Characteristic.SerialNumber, | ||||
|       "123-456-789" | ||||
|     ); | ||||
|         this._accessory | ||||
|             .getService(this._platform.api.hap.Service.AccessoryInformation)! | ||||
|             .setCharacteristic( | ||||
|                 this._platform.api.hap.Characteristic.Manufacturer, | ||||
|                 "Brandon Watson" | ||||
|             ) | ||||
|             .setCharacteristic( | ||||
|                 this._platform.api.hap.Characteristic.Model, | ||||
|                 "F.lux" | ||||
|             ) | ||||
|             .setCharacteristic( | ||||
|                 this._platform.api.hap.Characteristic.SerialNumber, | ||||
|                 "123-456-789" | ||||
|             ); | ||||
|  | ||||
|     this._switchService = new Service.Switch(this.name, "fluxService"); | ||||
|         const switchUUID = this._platform.api.hap.uuid.generate( | ||||
|             `${this._accessory.displayName} Switch` | ||||
|         ); | ||||
|  | ||||
|     this._switchService | ||||
|       .getCharacteristic(Characteristic.On) | ||||
|       //@ts-ignore | ||||
|       .on("set", this.onSetEnabled) | ||||
|       //@ts-ignore | ||||
|       .on("get", this.onGetEnabled); | ||||
|   } | ||||
|         this._switchService = | ||||
|             this._accessory.getService(this._platform.api.hap.Service.Switch) || | ||||
|             this._accessory.addService( | ||||
|                 this._platform.api.hap.Service.Switch, | ||||
|                 this._accessory.displayName, | ||||
|                 switchUUID | ||||
|             ); | ||||
|  | ||||
|   public name: string = "Flux"; | ||||
|  | ||||
|   public platformAccessory: any; | ||||
|  | ||||
|   /** | ||||
|    * Handler for switch set event | ||||
|    * @param callback The callback function to call when complete | ||||
|    */ | ||||
|   private onSetEnabled = async ( | ||||
|     activeState: boolean, | ||||
|     callback: (error?: Error | null | undefined) => void | ||||
|   ) => { | ||||
|     if (activeState) { | ||||
|       this._times = getTimes( | ||||
|         new Date(), | ||||
|         this._config.latitude, | ||||
|         this._config.longitude | ||||
|       ); | ||||
|       this.update(); | ||||
|     } else { | ||||
|       this._isActive = false; | ||||
|         this._switchService | ||||
|             .getCharacteristic(this._platform.api.hap.Characteristic.On) | ||||
|             //@ts-ignore | ||||
|             .on("set", this.onSetEnabled) | ||||
|             //@ts-ignore | ||||
|             .on("get", this.onGetEnabled); | ||||
|     } | ||||
|     return callback(); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * Handler for switch get event | ||||
|    * @param callback The callback function to call when complete | ||||
|    */ | ||||
|   private onGetEnabled( | ||||
|     callback: (error: Error | null, value: boolean) => void | ||||
|   ): void { | ||||
|     callback(null, this._isActive); | ||||
|     // return this._isActive; | ||||
|   } | ||||
|     public name: string = "Flux"; | ||||
|  | ||||
|   /** | ||||
|    * Called by homebridge to gather services. | ||||
|    */ | ||||
|   public getServices = (): Array<HAPNodeJS.Service> => { | ||||
|     return [this._infoService, this._switchService!]; | ||||
|   }; | ||||
|     public platformAccessory: any; | ||||
|  | ||||
|   /** | ||||
|    * Popuplates internal lights array using the configuration values | ||||
|    */ | ||||
|   private getLights = async (): Promise<void> => { | ||||
|     for (const value of this._config.lights) { | ||||
|       //@ts-ignore | ||||
|       const light: Light = await this._hue.lights.getLightByName(value); | ||||
|       this._lights.push(light); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   private colorTempToRgb = ( | ||||
|     kelvin: number | ||||
|   ): { red: number; green: number; blue: number } => { | ||||
|     var temp = kelvin / 100; | ||||
|     var red, green, blue; | ||||
|     if (temp <= 66) { | ||||
|       red = 255; | ||||
|       green = temp; | ||||
|       green = 99.4708025861 * Math.log(green) - 161.1195681661; | ||||
|  | ||||
|       if (temp <= 19) { | ||||
|         blue = 0; | ||||
|       } else { | ||||
|         blue = temp - 10; | ||||
|         blue = 138.5177312231 * Math.log(blue) - 305.0447927307; | ||||
|       } | ||||
|     } else { | ||||
|       red = temp - 60; | ||||
|       red = 329.698727446 * Math.pow(red, -0.1332047592); | ||||
|  | ||||
|       green = temp - 60; | ||||
|       green = 288.1221695283 * Math.pow(green, -0.0755148492); | ||||
|  | ||||
|       blue = 255; | ||||
|     } | ||||
|     return { | ||||
|       red: this.clamp(red, 0, 255), | ||||
|       green: this.clamp(green, 0, 255), | ||||
|       blue: this.clamp(blue, 0, 255), | ||||
|     }; | ||||
|   }; | ||||
|  | ||||
|   private clamp(x: number, min: number, max: number) { | ||||
|     if (x < min) { | ||||
|       return min; | ||||
|     } | ||||
|     if (x > max) { | ||||
|       return max; | ||||
|     } | ||||
|     return x; | ||||
|   } | ||||
|  | ||||
|   private isHueError = (object: any): object is HueError => { | ||||
|     return "_hueError" in object; | ||||
|   }; | ||||
|  | ||||
|   private setLights = async (state: LightState) => { | ||||
|     const promises: Array<Promise<unknown> | PromiseLike<unknown>> = []; | ||||
|     this._lights.map(async (light: Light) => { | ||||
|       try { | ||||
|         await this._hue.lights.setLightState(light.id, state); | ||||
|       } catch (err) { | ||||
|         if ( | ||||
|           this.isHueError(err) && | ||||
|           err.message === | ||||
|             "parameter, xy, is not modifiable. Device is set to off." | ||||
|         ) { | ||||
|           //Eat this | ||||
|     /** | ||||
|      * Handler for switch set event | ||||
|      * @param callback The callback function to call when complete | ||||
|      */ | ||||
|     private onSetEnabled = async ( | ||||
|         activeState: boolean, | ||||
|         callback: (error?: Error | null | undefined) => void | ||||
|     ) => { | ||||
|         if (activeState) { | ||||
|             this._times = getTimes( | ||||
|                 new Date(), | ||||
|                 this._config.latitude, | ||||
|                 this._config.longitude | ||||
|             ); | ||||
|             this.update(); | ||||
|         } else { | ||||
|           this._log(`Error while setting lights: ${err}`); | ||||
|             this._isActive = false; | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|         return callback(); | ||||
|     }; | ||||
|  | ||||
|     await Promise.all(promises); | ||||
|   }; | ||||
|     /** | ||||
|      * Handler for switch get event | ||||
|      * @param callback The callback function to call when complete | ||||
|      */ | ||||
|     private onGetEnabled = ( | ||||
|         callback: (error: Error | null, value: boolean) => void | ||||
|     ): void => { | ||||
|         callback(null, this._isActive); | ||||
|         // return this._isActive; | ||||
|     }; | ||||
|  | ||||
|   /** | ||||
|    * 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 | ||||
|     /** | ||||
|      * Populates internal lights array using the configuration values | ||||
|      */ | ||||
|     private getLights = async (): Promise<void> => { | ||||
|         for (const value of this._config.lights) { | ||||
|             //@ts-ignore | ||||
|             const light: Light = await this._hue.lights.getLightByName(value); | ||||
|             this._lights.push(light); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     private colorTempToRgb = (kelvin: number): RGB => { | ||||
|         var temp = kelvin / 100; | ||||
|         var red, green, blue; | ||||
|         if (temp <= 66) { | ||||
|             red = 255; | ||||
|             green = temp; | ||||
|             green = 99.4708025861 * Math.log(green) - 161.1195681661; | ||||
|  | ||||
|             if (temp <= 19) { | ||||
|                 blue = 0; | ||||
|             } else { | ||||
|                 blue = temp - 10; | ||||
|                 blue = 138.5177312231 * Math.log(blue) - 305.0447927307; | ||||
|             } | ||||
|         } else { | ||||
|             red = temp - 60; | ||||
|             red = 329.698727446 * Math.pow(red, -0.1332047592); | ||||
|  | ||||
|             green = temp - 60; | ||||
|             green = 288.1221695283 * Math.pow(green, -0.0755148492); | ||||
|  | ||||
|             blue = 255; | ||||
|         } | ||||
|         return { | ||||
|             r: this.clamp(red, 0, 255), | ||||
|             g: this.clamp(green, 0, 255), | ||||
|             b: this.clamp(blue, 0, 255), | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     private clamp(x: number, min: number, max: number) { | ||||
|         if (x < min) { | ||||
|             return min; | ||||
|         } | ||||
|         if (x > max) { | ||||
|             return max; | ||||
|         } | ||||
|         return x; | ||||
|     } | ||||
|     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); | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Gets adjusted color temperature. | ||||
|    */ | ||||
|   private getTempOffset = ( | ||||
|     startTemp: number, | ||||
|     endTemp: number, | ||||
|     startTime: Date, | ||||
|     endTime: Date | ||||
|   ) => { | ||||
|     const now = this.getNow().getTime(); | ||||
|     const percentComplete = | ||||
|       (now - startTime.getTime()) / (endTime.getTime() - startTime.getTime()); | ||||
|     const tempRange = Math.abs(startTemp - endTemp); | ||||
|     const tempOffset = tempRange * percentComplete; | ||||
|     return startTemp - tempOffset; | ||||
|   }; | ||||
|     private isHueError = (object: any): object is HueError => { | ||||
|         return "_hueError" in object; | ||||
|     }; | ||||
|  | ||||
|   /** | ||||
|    * Get the current time. Use test time if present. | ||||
|    */ | ||||
|   private getNow() { | ||||
|     if (this._config.testNowDateString) { | ||||
|       return new Date(this._config.testNowDateString); | ||||
|     } else { | ||||
|       return new Date(); | ||||
|     } | ||||
|   } | ||||
|     private setHueLights = async (state: LightState) => { | ||||
|         const promises: Array<Promise<unknown> | PromiseLike<unknown>> = []; | ||||
|         this._lights.map(async (light: Light) => { | ||||
|             try { | ||||
|                 await this._hue.lights.setLightState(light.id, state); | ||||
|             } catch (err) { | ||||
|                 if ( | ||||
|                     this.isHueError(err) && | ||||
|                     err.message === | ||||
|                         "parameter, xy, is not modifiable. Device is set to off." | ||||
|                 ) { | ||||
|                     //Eat this | ||||
|                 } else { | ||||
|                     this._platform.log.info( | ||||
|                         `Error while setting lights: ${err}` | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|   private update = async (): Promise<void> => { | ||||
|     this._isActive = true; | ||||
|     while (this._isActive) { | ||||
|       if (this._lights.length === 0) { | ||||
|         await this.getLights(); | ||||
|       } | ||||
|         await Promise.all(promises); | ||||
|     }; | ||||
|  | ||||
|       const now = this.getNow(); | ||||
|       //Pad start time by an hour before sunset | ||||
|       const start = new Date( | ||||
|         this._times.sunset.getTime() - 60 * MINUTES_IN_MILLISECOND | ||||
|       ); | ||||
|       const sunsetStart = this._times.sunsetStart; | ||||
|       const sunsetEnd = new Date( | ||||
|         this._times.sunset.getTime() + this._config.sunsetDuration | ||||
|       ); | ||||
|       const nightStart = new Date( | ||||
|         sunsetEnd.getTime() + 60 * MINUTES_IN_MILLISECOND | ||||
|       ); | ||||
|       const sunrise = new Date( | ||||
|         this._times.sunrise.getTime() + 1 * SECONDS_IN_DAY | ||||
|       ); | ||||
|  | ||||
|       const startColorTemp = this._config.ceilingColorTemp | ||||
|         ? this._config.ceilingColorTemp | ||||
|         : 4000; | ||||
|       const sunsetColorTemp = this._config.sunsetColorTemp | ||||
|         ? this._config.sunsetColorTemp | ||||
|         : 2800; | ||||
|       const floorColorTemp = this._config.floorColorTemp | ||||
|         ? this._config.floorColorTemp | ||||
|         : 1900; | ||||
|  | ||||
|       let newTemp = 0; | ||||
|  | ||||
|       if (start < now && now < sunsetStart) { | ||||
|         newTemp = this.getTempOffset( | ||||
|           startColorTemp, | ||||
|           sunsetColorTemp, | ||||
|           start, | ||||
|           sunsetStart | ||||
|     private setWizLights = async (rgb: RGB, fade: number): Promise<void> => { | ||||
|         await Promise.all( | ||||
|             this._wizLights.map(async (bulb) => { | ||||
|                 const pilot = await bulb.get(); | ||||
|                 bulb.set(rgb, pilot?.dimming, fade); | ||||
|             }) | ||||
|         ); | ||||
|       } else if (sunsetStart < now && now < sunsetEnd) { | ||||
|         newTemp = this._config.sunsetColorTemp; | ||||
|       } else if (sunsetEnd < now && now < nightStart) { | ||||
|         newTemp = this.getTempOffset( | ||||
|           sunsetColorTemp, | ||||
|           floorColorTemp, | ||||
|           sunsetEnd, | ||||
|           nightStart | ||||
|         return; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * 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); | ||||
|             } | ||||
|         ); | ||||
|       } else if (nightStart < now && now < sunrise) { | ||||
|         newTemp = this._config.floorColorTemp; | ||||
|       } | ||||
|  | ||||
|       //Set lights | ||||
|       const rgb = this.colorTempToRgb(newTemp); | ||||
|       if (rgb && newTemp !== 0) { | ||||
|         const lightState = new LightState(); | ||||
|         lightState | ||||
|           .transitionInMillis( | ||||
|             this._config.transition ? this._config.transition : 5000 | ||||
|           ) | ||||
|           .rgb( | ||||
|             rgb.red ? rgb.red : 0, | ||||
|             rgb.green ? rgb.green : 0, | ||||
|             rgb.blue ? rgb.blue : 0 | ||||
|           ); | ||||
|         await this.setLights(lightState); | ||||
|         this._log(`Adjusting light temp to ${newTemp}, ${JSON.stringify(rgb)}`); | ||||
|       } | ||||
|  | ||||
|       await Sleep(this._config.delay ? this._config.delay : 60000); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|     /** | ||||
|      * Gets adjusted color temperature. | ||||
|      */ | ||||
|     private getTempOffset = ( | ||||
|         startTemp: number, | ||||
|         endTemp: number, | ||||
|         startTime: Date, | ||||
|         endTime: Date | ||||
|     ) => { | ||||
|         const now = this.getNow().getTime(); | ||||
|         const percentComplete = | ||||
|             (now - startTime.getTime()) / | ||||
|             (endTime.getTime() - startTime.getTime()); | ||||
|         const tempRange = Math.abs(startTemp - endTemp); | ||||
|         const tempOffset = tempRange * percentComplete; | ||||
|         return startTemp - tempOffset; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Get the current time. Use test time if present. | ||||
|      */ | ||||
|     private getNow() { | ||||
|         if (this._config.testNowDateString) { | ||||
|             return new Date(this._config.testNowDateString); | ||||
|         } else { | ||||
|             return new Date(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private update = async (): Promise<void> => { | ||||
|         this._isActive = true; | ||||
|         while (this._isActive) { | ||||
|             if (this._lights.length === 0) { | ||||
|                 await this.getLights(); | ||||
|             } | ||||
|  | ||||
|             const now = this.getNow(); | ||||
|             //Pad start time by an hour before sunset | ||||
|             const start = new Date( | ||||
|                 this._times.sunset.getTime() - 60 * MINUTES_IN_MILLISECOND | ||||
|             ); | ||||
|             const sunsetStart = this._times.sunsetStart; | ||||
|             const sunsetEnd = new Date( | ||||
|                 this._times.sunset.getTime() + this._config.sunsetDuration | ||||
|             ); | ||||
|             const nightStart = new Date( | ||||
|                 sunsetEnd.getTime() + 60 * MINUTES_IN_MILLISECOND | ||||
|             ); | ||||
|             const sunrise = new Date( | ||||
|                 this._times.sunrise.getTime() + 1 * SECONDS_IN_DAY | ||||
|             ); | ||||
|  | ||||
|             const startColorTemp = this._config.ceilingColorTemp | ||||
|                 ? this._config.ceilingColorTemp | ||||
|                 : 4000; | ||||
|             const sunsetColorTemp = this._config.sunsetColorTemp | ||||
|                 ? this._config.sunsetColorTemp | ||||
|                 : 2800; | ||||
|             const floorColorTemp = this._config.floorColorTemp | ||||
|                 ? this._config.floorColorTemp | ||||
|                 : 1900; | ||||
|  | ||||
|             let newTemp = 0; | ||||
|  | ||||
|             if (start < now && now < sunsetStart) { | ||||
|                 newTemp = this.getTempOffset( | ||||
|                     startColorTemp, | ||||
|                     sunsetColorTemp, | ||||
|                     start, | ||||
|                     sunsetStart | ||||
|                 ); | ||||
|             } else if (sunsetStart < now && now < sunsetEnd) { | ||||
|                 newTemp = this._config.sunsetColorTemp; | ||||
|             } else if (sunsetEnd < now && now < nightStart) { | ||||
|                 newTemp = this.getTempOffset( | ||||
|                     sunsetColorTemp, | ||||
|                     floorColorTemp, | ||||
|                     sunsetEnd, | ||||
|                     nightStart | ||||
|                 ); | ||||
|             } else if (nightStart < now && now < sunrise) { | ||||
|                 newTemp = this._config.floorColorTemp; | ||||
|             } | ||||
|  | ||||
|             //Set lights | ||||
|             const hueRGB = this.colorTempToRgb(newTemp); | ||||
|             const wizRGB = colorTemperature2rgb(newTemp); | ||||
|             if (hueRGB && newTemp !== 0) { | ||||
|                 const lightState = new LightState(); | ||||
|                 lightState | ||||
|                     .transitionInMillis( | ||||
|                         this._config.transition ? this._config.transition : 5000 | ||||
|                     ) | ||||
|                     .rgb(hueRGB.r ?? 0, hueRGB.g ?? 0, hueRGB.b ?? 0); | ||||
|                 await this.setHueLights(lightState); | ||||
|                 await this.setWizLights( | ||||
|                     wizRGB, | ||||
|                     this._config.transition ? this._config.transition / 1000 : 5 | ||||
|                 ); | ||||
|                 this._platform.log.info( | ||||
|                     `Adjusting light temp to ${newTemp}, ${JSON.stringify( | ||||
|                         hueRGB | ||||
|                     )}` | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             await Sleep(this._config.delay ? this._config.delay : 60000); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|   | ||||
							
								
								
									
										217
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								src/index.ts
									
									
									
									
									
								
							| @@ -1,105 +1,146 @@ | ||||
| import { IConfig } from "./models/iConfig"; | ||||
| 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"; | ||||
| import { IAccessory } from "./models/iAccessory"; | ||||
| import { FluxAccessory } from "./fluxAccessory"; | ||||
| import { API } from "homebridge"; | ||||
|  | ||||
| let Accessory: any; | ||||
| let Homebridge: any; | ||||
| import { PLATFORM_NAME } from "./settings"; | ||||
| import { Platform } from "./platform"; | ||||
|  | ||||
| /** | ||||
|  * Main entry. | ||||
|  * @param homebridge  | ||||
|  * This method registers the platform with Homebridge | ||||
|  */ | ||||
| export default function (homebridge: any) { | ||||
|     Homebridge = homebridge; | ||||
|     Accessory = homebridge.platformAccessory; | ||||
|     homebridge.registerPlatform( | ||||
|         'homebridge-flux', | ||||
|         'Flux', | ||||
|         FluxPlatform, | ||||
|         true | ||||
|     ); | ||||
| export = (api: API) => { | ||||
|     api.registerPlatform(PLATFORM_NAME, Platform); | ||||
| }; | ||||
|  | ||||
| class FluxPlatform { | ||||
|     log: any = {}; | ||||
|     api: any; | ||||
|     accessoryList: Array<IAccessory> = []; | ||||
|     config: IConfig; | ||||
|     hue: Api | undefined; | ||||
| // import { IConfig } from "./models/iConfig"; | ||||
| // 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"; | ||||
| // import { IAccessory } from "./models/iAccessory"; | ||||
| // import { FluxAccessory } from "./fluxAccessory"; | ||||
| // import { WizBulb } from "@watsonb8/wiz-lib/build/wizBulb"; | ||||
| // import discover from "@watsonb8/wiz-lib/build/discovery"; | ||||
|  | ||||
| // let Accessory: any; | ||||
| // let Homebridge: any; | ||||
|  | ||||
|     constructor(log: any, config: any, api: any) { | ||||
|         this.log = log; | ||||
|         this.api = api; | ||||
|         this.config = config; | ||||
|         this.log('INFO - Registering Flux platform'); | ||||
|         this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this)); | ||||
|     } | ||||
| // /** | ||||
| //  * Main entry. | ||||
| //  * @param homebridge | ||||
| //  */ | ||||
| // export default function (homebridge: any) { | ||||
| //     Homebridge = homebridge; | ||||
| //     Accessory = homebridge.platformAccessory; | ||||
| //     homebridge.registerPlatform("homebridge-flux", "Flux", FluxPlatform, true); | ||||
| // } | ||||
|  | ||||
|     private connectHue = async () => { | ||||
|         if (!this.config) { | ||||
|             return; | ||||
|         } | ||||
| // class FluxPlatform { | ||||
| //     log: any = {}; | ||||
| //     api: any; | ||||
| //     accessoryList: Array<IAccessory> = []; | ||||
| //     config: IConfig; | ||||
| //     hue: Api | undefined; | ||||
|  | ||||
|         if (this.config.userName && this.config.clientKey) { | ||||
|             this.hue = await v3.api.createLocal(this.config.ipAddress).connect(this.config.userName, this.config.clientKey, undefined); | ||||
|             this.log("Using existing connection info"); | ||||
|         } else { | ||||
|             const unauthenticatedApi = await v3.api.createLocal(this.config.ipAddress).connect(undefined, undefined, undefined); | ||||
|             let createdUser; | ||||
|             let connected = false | ||||
|             while (!connected) { | ||||
|                 try { | ||||
|                     this.log("Creating hue user. Push link button") | ||||
|                     createdUser = await unauthenticatedApi.users.createUser("homebridge", "HueChase"); | ||||
| //     constructor(log: any, config: any, api: any) { | ||||
| //         this.log = log; | ||||
| //         this.api = api; | ||||
| //         this.config = config; | ||||
| //         this.log("INFO - Registering Flux platform"); | ||||
| //         this.api.on("didFinishLaunching", this.didFinishLaunching.bind(this)); | ||||
| //     } | ||||
|  | ||||
|                     this.hue = await v3.api.createLocal(this.config.ipAddress).connect(createdUser.username, createdUser.clientKey, undefined); | ||||
|                     this.log("Connected to Hue Bridge"); | ||||
|                     this.log(`UserName: ${createdUser.username}, ClientKey: ${createdUser.clientkey}`) | ||||
|                     connected = true; | ||||
| //     private connectWiz = async () => { | ||||
| //         if (!this.config) { | ||||
| //             return; | ||||
| //         } | ||||
|  | ||||
|                 } 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; | ||||
|                     } | ||||
| //         return await discover(); | ||||
| //     }; | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| //     private connectHue = async () => { | ||||
| //         if (!this.config) { | ||||
| //             return; | ||||
| //         } | ||||
|  | ||||
|     /** | ||||
|      * Handler for didFinishLaunching | ||||
|      * Happens after constructor | ||||
|      */ | ||||
|     private didFinishLaunching() { | ||||
|         this.log(`INFO - Done registering Flux platform`); | ||||
|     } | ||||
| //         if (this.config.userName && this.config.clientKey) { | ||||
| //             this.hue = await v3.api | ||||
| //                 .createLocal(this.config.ipAddress) | ||||
| //                 .connect( | ||||
| //                     this.config.userName, | ||||
| //                     this.config.clientKey, | ||||
| //                     undefined | ||||
| //                 ); | ||||
| //             this.log("Using existing connection info"); | ||||
| //         } else { | ||||
| //             const unauthenticatedApi = await v3.api | ||||
| //                 .createLocal(this.config.ipAddress) | ||||
| //                 .connect(undefined, undefined, undefined); | ||||
| //             let createdUser; | ||||
| //             let connected = false; | ||||
| //             while (!connected) { | ||||
| //                 try { | ||||
| //                     this.log("Creating hue user. Push link button"); | ||||
| //                     createdUser = await unauthenticatedApi.users.createUser( | ||||
| //                         "homebridge", | ||||
| //                         "HueChase" | ||||
| //                     ); | ||||
|  | ||||
|     /** | ||||
|      * Called by homebridge to gather accessories. | ||||
|      * @param callback  | ||||
|      */ | ||||
|     public accessories = async (callback: (accessories: Array<IAccessory>) => void) => { | ||||
|         //Connect to hue bridge | ||||
|         await this.connectHue(); | ||||
| //                     this.hue = await v3.api | ||||
| //                         .createLocal(this.config.ipAddress) | ||||
| //                         .connect( | ||||
| //                             createdUser.username, | ||||
| //                             createdUser.clientKey, | ||||
| //                             undefined | ||||
| //                         ); | ||||
| //                     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; | ||||
| //                     } | ||||
| //                 } | ||||
| //             } | ||||
| //         } | ||||
| //     }; | ||||
|  | ||||
|         this.accessoryList.push(new FluxAccessory({ | ||||
|             api: this.api, | ||||
|             log: this.log, | ||||
|             homebridge: Homebridge, | ||||
|             hue: this.hue!, | ||||
|             config: this.config | ||||
|         })); | ||||
| //     /** | ||||
| //      * Handler for didFinishLaunching | ||||
| //      * Happens after constructor | ||||
| //      */ | ||||
| //     private didFinishLaunching() { | ||||
| //         this.log(`INFO - Done registering Flux platform`); | ||||
| //     } | ||||
|  | ||||
|         callback(this.accessoryList); | ||||
|     } | ||||
| } | ||||
| //     /** | ||||
| //      * Called by homebridge to gather accessories. | ||||
| //      * @param callback | ||||
| //      */ | ||||
| //     public accessories = async ( | ||||
| //         callback: (accessories: Array<IAccessory>) => void | ||||
| //     ) => { | ||||
| //         //Connect to hue bridge | ||||
| //         await this.connectHue(); | ||||
| //         const wizBulbs = await this.connectWiz(); | ||||
|  | ||||
| //         this.accessoryList.push( | ||||
| //             new FluxAccessory({ | ||||
| //                 api: this.api, | ||||
| //                 log: this.log, | ||||
| //                 homebridge: Homebridge, | ||||
| //                 hue: this.hue!, | ||||
| //                 wizBulbs: wizBulbs ?? [], | ||||
| //                 config: this.config, | ||||
| //             }) | ||||
| //         ); | ||||
|  | ||||
| //         callback(this.accessoryList); | ||||
| //     }; | ||||
| // } | ||||
|   | ||||
							
								
								
									
										456
									
								
								src/models/hapNodeJS.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										456
									
								
								src/models/hapNodeJS.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,456 +0,0 @@ | ||||
| declare namespace HAPNodeJS { | ||||
|  | ||||
|     export interface uuid { | ||||
|         generate(data: string): string; | ||||
|         isValid(UUID: string): boolean; | ||||
|         unparse(bug: string, offset: number): string; | ||||
|     } | ||||
|  | ||||
|     type EventService = "characteristic-change" | "service-configurationChange" | ||||
|  | ||||
|     export interface IEventEmitterAccessory { | ||||
|         addListener(event: EventService, listener: Function): this; | ||||
|         on(event: EventService, listener: Function): this; | ||||
|         once(event: EventService, listener: Function): this; | ||||
|         removeListener(event: EventService, listener: Function): this; | ||||
|         removeAllListeners(event?: EventService): this; | ||||
|         setMaxListeners(n: number): this; | ||||
|         getMaxListeners(): number; | ||||
|         listeners(event: EventService): Function[]; | ||||
|         emit(event: EventService, ...args: any[]): boolean; | ||||
|         listenerCount(type: string): number; | ||||
|     } | ||||
|  | ||||
|     export interface Service extends IEventEmitterAccessory { | ||||
|         new(displayName: string, UUID: string, subtype: string): Service; | ||||
|  | ||||
|         displayName: string; | ||||
|         UUID: string; | ||||
|         subtype: string; | ||||
|         iid: string; | ||||
|         characteristics: Characteristic[]; | ||||
|         optionalCharacteristics: Characteristic[]; | ||||
|  | ||||
|         addCharacteristic(characteristic: Characteristic | Function): Characteristic; | ||||
|         removeCharacteristic(characteristic: Characteristic): void; | ||||
|         getCharacteristic(name: string | Function): Characteristic; | ||||
|         testCharacteristic(name: string | Function): boolean; | ||||
|         setCharacteristic(name: string | Function, value: CharacteristicValue): Service; | ||||
|         updateCharacteristic(name: string | Function, value: CharacteristicValue): Service; | ||||
|         addOptionalCharacteristic(characteristic: Characteristic | Function): void; | ||||
|         getCharacteristicByIID(iid: string): Characteristic; | ||||
|  | ||||
|         toHAP(opt: any): JSON; | ||||
|  | ||||
|         AccessoryInformation: PredefinedService; | ||||
|         AirPurifier: PredefinedService; | ||||
|         AirQualitySensor: PredefinedService; | ||||
|         BatteryService: PredefinedService; | ||||
|         BridgeConfiguration: PredefinedService; | ||||
|         BridgingState: PredefinedService; | ||||
|         CameraControl: PredefinedService; | ||||
|         CameraRTPStreamManagement: PredefinedService; | ||||
|         CarbonDioxideSensor: PredefinedService; | ||||
|         CarbonMonoxideSensor: PredefinedService; | ||||
|         ContactSensor: PredefinedService; | ||||
|         Door: PredefinedService; | ||||
|         Doorbell: PredefinedService; | ||||
|         Fan: PredefinedService; | ||||
|         Fanv2: PredefinedService; | ||||
|         Faucet: PredefinedService; | ||||
|         FilterMaintenance: PredefinedService; | ||||
|         GarageDoorOpener: PredefinedService; | ||||
|         HeaterCooler: PredefinedService; | ||||
|         HumidifierDehumidifier: PredefinedService; | ||||
|         HumiditySensor: PredefinedService; | ||||
|         InputSource: PredefinedService; | ||||
|         IrrigationSystem: PredefinedService; | ||||
|         LeakSensor: PredefinedService; | ||||
|         LightSensor: PredefinedService; | ||||
|         Lightbulb: PredefinedService; | ||||
|         LockManagement: PredefinedService; | ||||
|         LockMechanism: PredefinedService; | ||||
|         Microphone: PredefinedService; | ||||
|         MotionSensor: PredefinedService; | ||||
|         OccupancySensor: PredefinedService; | ||||
|         Outlet: PredefinedService; | ||||
|         Pairing: PredefinedService; | ||||
|         ProtocolInformation: PredefinedService; | ||||
|         Relay: PredefinedService; | ||||
|         SecuritySystem: PredefinedService; | ||||
|         ServiceLabel: PredefinedService; | ||||
|         Slat: PredefinedService; | ||||
|         SmokeSensor: PredefinedService; | ||||
|         Speaker: PredefinedService; | ||||
|         StatefulProgrammableSwitch: PredefinedService; | ||||
|         StatelessProgrammableSwitch: PredefinedService; | ||||
|         Switch: PredefinedService; | ||||
|         Television: PredefinedService; | ||||
|         TelevisionSpeaker: PredefinedService; | ||||
|         TemperatureSensor: PredefinedService; | ||||
|         Thermostat: PredefinedService; | ||||
|         TimeInformation: PredefinedService; | ||||
|         TunneledBTLEAccessoryService: PredefinedService; | ||||
|         Valve: PredefinedService; | ||||
|         Window: PredefinedService; | ||||
|         WindowCovering: PredefinedService; | ||||
|     } | ||||
|  | ||||
|     export interface PredefinedService { | ||||
|         new(displayName: string, subtype: string): Service; | ||||
|     } | ||||
|  | ||||
|     export interface CameraSource { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     type EventAccessory = "service-configurationChange" | "service-characteristic-change" | "identify" | ||||
|  | ||||
|     export interface IEventEmitterAccessory { | ||||
|         addListener(event: EventAccessory, listener: Function): this; | ||||
|         on(event: EventAccessory, listener: Function): this; | ||||
|         once(event: EventAccessory, listener: Function): this; | ||||
|         removeListener(event: EventAccessory, listener: Function): this; | ||||
|         removeAllListeners(event?: EventAccessory): this; | ||||
|         setMaxListeners(n: number): this; | ||||
|         getMaxListeners(): number; | ||||
|         listeners(event: EventAccessory): Function[]; | ||||
|         emit(event: EventAccessory, ...args: any[]): boolean; | ||||
|         listenerCount(type: string): number; | ||||
|     } | ||||
|  | ||||
|     export interface CharacteristicProps { | ||||
|         format: Characteristic.Formats; | ||||
|         unit: Characteristic.Units, | ||||
|         minValue: number, | ||||
|         maxValue: number, | ||||
|         minStep: number, | ||||
|         perms: Characteristic.Perms[] | ||||
|     } | ||||
|  | ||||
|     type EventCharacteristic = "get" | "set" | ||||
|     type CharacteristicValue = boolean | string | number | ||||
|  | ||||
|     export type CharacteristicGetCallback<T = CharacteristicValue> = (error: Error | null, value: T) => void | ||||
|     export type CharacteristicSetCallback = (error?: Error | null) => void | ||||
|     export type CharacteristicCallback = CharacteristicGetCallback | CharacteristicSetCallback | ||||
|  | ||||
|     export interface IEventEmitterCharacteristic { | ||||
|         addListener(event: EventCharacteristic, listener: CharacteristicCallback): this; | ||||
|         on(event: EventCharacteristic, listener: CharacteristicCallback): this; | ||||
|         once(event: EventCharacteristic, listener: CharacteristicCallback): this; | ||||
|         removeListener(event: EventCharacteristic, listener: CharacteristicCallback): this; | ||||
|         removeAllListeners(event?: EventCharacteristic): this; | ||||
|         setMaxListeners(n: number): this; | ||||
|         getMaxListeners(): number; | ||||
|         listeners(event: EventCharacteristic): CharacteristicCallback[]; | ||||
|         emit(event: EventCharacteristic, ...args: any[]): boolean; | ||||
|         listenerCount(type: string): number; | ||||
|     } | ||||
|  | ||||
|     export interface Characteristic extends IEventEmitterCharacteristic { | ||||
|         new(displayName: string, UUID: string, props?: CharacteristicProps): Characteristic; | ||||
|  | ||||
|         Formats: typeof Characteristic.Formats; | ||||
|         Units: typeof Characteristic.Units; | ||||
|         Perms: typeof Characteristic.Perms; | ||||
|  | ||||
|         setProps(props: CharacteristicProps): Characteristic | ||||
|         getValue(callback?: CharacteristicGetCallback, context?: any, connectionID?: string): void; | ||||
|         setValue(newValue: CharacteristicValue, callback?: CharacteristicSetCallback, context?: any, connectionID?: string): Characteristic; | ||||
|         updateValue(newValue: CharacteristicValue, callback?: () => void, context?: any): Characteristic; | ||||
|         getDefaultValue(): CharacteristicValue; | ||||
|         toHAP(opt: any): JSON; | ||||
|  | ||||
|         AccessoryFlags: Characteristic; | ||||
|         AccessoryIdentifier: Characteristic; | ||||
|         Active: Characteristic; | ||||
|         ActiveIdentifier: Characteristic; | ||||
|         AdministratorOnlyAccess: Characteristic; | ||||
|         AirParticulateDensity: Characteristic; | ||||
|         AirParticulateSize: Characteristic; | ||||
|         AirQuality: Characteristic; | ||||
|         AppMatchingIdentifier: Characteristic; | ||||
|         AudioFeedback: Characteristic; | ||||
|         BatteryLevel: Characteristic; | ||||
|         Brightness: Characteristic; | ||||
|         CarbonDioxideDetected: Characteristic; | ||||
|         CarbonDioxideLevel: Characteristic; | ||||
|         CarbonDioxidePeakLevel: Characteristic; | ||||
|         CarbonMonoxideDetected: Characteristic; | ||||
|         CarbonMonoxideLevel: Characteristic; | ||||
|         CarbonMonoxidePeakLevel: Characteristic; | ||||
|         Category: Characteristic; | ||||
|         ChargingState: Characteristic; | ||||
|         ClosedCaptions: Characteristic; | ||||
|         ColorTemperature: Characteristic; | ||||
|         ConfigureBridgedAccessory: Characteristic; | ||||
|         ConfigureBridgedAccessoryStatus: Characteristic; | ||||
|         ConfiguredName: Characteristic; | ||||
|         ContactSensorState: Characteristic; | ||||
|         CoolingThresholdTemperature: Characteristic; | ||||
|         CurrentAirPurifierState: Characteristic; | ||||
|         CurrentAmbientLightLevel: Characteristic; | ||||
|         CurrentDoorState: Characteristic; | ||||
|         CurrentFanState: Characteristic; | ||||
|         CurrentHeaterCoolerState: Characteristic; | ||||
|         CurrentHeatingCoolingState: Characteristic; | ||||
|         CurrentHorizontalTiltAngle: Characteristic; | ||||
|         CurrentHumidifierDehumidifierState: Characteristic; | ||||
|         CurrentMediaState: Characteristic; | ||||
|         CurrentPosition: Characteristic; | ||||
|         CurrentRelativeHumidity: Characteristic; | ||||
|         CurrentSlatState: Characteristic; | ||||
|         CurrentTemperature: Characteristic; | ||||
|         CurrentTiltAngle: Characteristic; | ||||
|         CurrentTime: Characteristic; | ||||
|         CurrentVerticalTiltAngle: Characteristic; | ||||
|         CurrentVisibilityState: Characteristic; | ||||
|         DayoftheWeek: Characteristic; | ||||
|         DigitalZoom: Characteristic; | ||||
|         DiscoverBridgedAccessories: Characteristic; | ||||
|         DiscoveredBridgedAccessories: Characteristic; | ||||
|         DisplayOrder: Characteristic; | ||||
|         FilterChangeIndication: Characteristic; | ||||
|         FilterLifeLevel: Characteristic; | ||||
|         FirmwareRevision: Characteristic; | ||||
|         HardwareRevision: Characteristic; | ||||
|         HeatingThresholdTemperature: Characteristic; | ||||
|         HoldPosition: Characteristic; | ||||
|         Hue: Characteristic; | ||||
|         Identifier: Characteristic; | ||||
|         Identify: Characteristic; | ||||
|         ImageMirroring: Characteristic; | ||||
|         ImageRotation: Characteristic; | ||||
|         InUse: Characteristic; | ||||
|         InputDeviceType: Characteristic; | ||||
|         InputSourceType: Characteristic; | ||||
|         IsConfigured: Characteristic; | ||||
|         LeakDetected: Characteristic; | ||||
|         LinkQuality: Characteristic; | ||||
|         LockControlPoint: Characteristic; | ||||
|         LockCurrentState: Characteristic; | ||||
|         LockLastKnownAction: Characteristic; | ||||
|         LockManagementAutoSecurityTimeout: Characteristic; | ||||
|         LockPhysicalControls: Characteristic; | ||||
|         LockTargetState: Characteristic; | ||||
|         Logs: Characteristic; | ||||
|         Manufacturer: Characteristic; | ||||
|         Model: Characteristic; | ||||
|         MotionDetected: Characteristic; | ||||
|         Mute: Characteristic; | ||||
|         Name: Characteristic; | ||||
|         NightVision: Characteristic; | ||||
|         NitrogenDioxideDensity: Characteristic; | ||||
|         ObstructionDetected: Characteristic; | ||||
|         OccupancyDetected: Characteristic; | ||||
|         On: Characteristic; | ||||
|         OpticalZoom: Characteristic; | ||||
|         OutletInUse: Characteristic; | ||||
|         OzoneDensity: Characteristic; | ||||
|         PM10Density: Characteristic; | ||||
|         PM2_5Density: Characteristic; | ||||
|         PairSetup: Characteristic; | ||||
|         PairVerify: Characteristic; | ||||
|         PairingFeatures: Characteristic; | ||||
|         PairingPairings: Characteristic; | ||||
|         PictureMode: Characteristic; | ||||
|         PositionState: Characteristic; | ||||
|         PowerModeSelection: Characteristic; | ||||
|         ProgramMode: Characteristic; | ||||
|         ProgrammableSwitchEvent: Characteristic; | ||||
|         ProgrammableSwitchOutputState: Characteristic; | ||||
|         Reachable: Characteristic; | ||||
|         RelativeHumidityDehumidifierThreshold: Characteristic; | ||||
|         RelativeHumidityHumidifierThreshold: Characteristic; | ||||
|         RelayControlPoint: Characteristic; | ||||
|         RelayEnabled: Characteristic; | ||||
|         RelayState: Characteristic; | ||||
|         RemainingDuration: Characteristic; | ||||
|         RemoteKey: Characteristic; | ||||
|         ResetFilterIndication: Characteristic; | ||||
|         RotationDirection: Characteristic; | ||||
|         RotationSpeed: Characteristic; | ||||
|         Saturation: Characteristic; | ||||
|         SecuritySystemAlarmType: Characteristic; | ||||
|         SecuritySystemCurrentState: Characteristic; | ||||
|         SecuritySystemTargetState: Characteristic; | ||||
|         SelectedRTPStreamConfiguration: Characteristic; | ||||
|         SerialNumber: Characteristic; | ||||
|         ServiceLabelIndex: Characteristic; | ||||
|         ServiceLabelNamespace: Characteristic; | ||||
|         SetDuration: Characteristic; | ||||
|         SetupEndpoints: Characteristic; | ||||
|         SlatType: Characteristic; | ||||
|         SleepDiscoveryMode: Characteristic; | ||||
|         SmokeDetected: Characteristic; | ||||
|         SoftwareRevision: Characteristic; | ||||
|         StatusActive: Characteristic; | ||||
|         StatusFault: Characteristic; | ||||
|         StatusJammed: Characteristic; | ||||
|         StatusLowBattery: Characteristic; | ||||
|         StatusTampered: Characteristic; | ||||
|         StreamingStatus: Characteristic; | ||||
|         SulphurDioxideDensity: Characteristic; | ||||
|         SupportedAudioStreamConfiguration: Characteristic; | ||||
|         SupportedRTPConfiguration: Characteristic; | ||||
|         SupportedVideoStreamConfiguration: Characteristic; | ||||
|         SwingMode: Characteristic; | ||||
|         TargetAirPurifierState: Characteristic; | ||||
|         TargetAirQuality: Characteristic; | ||||
|         TargetDoorState: Characteristic; | ||||
|         TargetFanState: Characteristic; | ||||
|         TargetHeaterCoolerState: Characteristic; | ||||
|         TargetHeatingCoolingState: Characteristic; | ||||
|         TargetHorizontalTiltAngle: Characteristic; | ||||
|         TargetHumidifierDehumidifierState: Characteristic; | ||||
|         TargetMediaState: Characteristic; | ||||
|         TargetPosition: Characteristic; | ||||
|         TargetRelativeHumidity: Characteristic; | ||||
|         TargetSlatState: Characteristic; | ||||
|         TargetTemperature: Characteristic; | ||||
|         TargetTiltAngle: Characteristic; | ||||
|         TargetVerticalTiltAngle: Characteristic; | ||||
|         TargetVisibilityState: Characteristic; | ||||
|         TemperatureDisplayUnits: Characteristic; | ||||
|         TimeUpdate: Characteristic; | ||||
|         TunnelConnectionTimeout: Characteristic; | ||||
|         TunneledAccessoryAdvertising: Characteristic; | ||||
|         TunneledAccessoryConnected: Characteristic; | ||||
|         TunneledAccessoryStateNumber: Characteristic; | ||||
|         VOCDensity: Characteristic; | ||||
|         ValveType: Characteristic; | ||||
|         Version: Characteristic; | ||||
|         Volume: Characteristic; | ||||
|         VolumeControlType: Characteristic; | ||||
|         VolumeSelector: Characteristic; | ||||
|         WaterLevel: Characteristic; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     module Characteristic { | ||||
|         export enum Formats { | ||||
|             BOOL, | ||||
|             INT, | ||||
|             FLOAT, | ||||
|             STRING, | ||||
|             ARRAY, // unconfirmed | ||||
|             DICTIONARY, // unconfirmed | ||||
|             UINT8, | ||||
|             UINT16, | ||||
|             UINT32, | ||||
|             UINT64, | ||||
|             DATA, // unconfirmed | ||||
|             TLV8 | ||||
|         } | ||||
|  | ||||
|         export enum Units { | ||||
|             // HomeKit only defines Celsius, for Fahrenheit, it requires iOS app to do the conversion. | ||||
|             CELSIUS, | ||||
|             PERCENTAGE, | ||||
|             ARC_DEGREE, | ||||
|             LUX, | ||||
|             SECONDS | ||||
|         } | ||||
|  | ||||
|         export enum Perms { | ||||
|             READ, | ||||
|             WRITE, | ||||
|             NOTIFY, | ||||
|             HIDDEN | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     export interface PublishInfo { | ||||
|         port: number; | ||||
|         username: string; | ||||
|         pincode: string; | ||||
|         category: number; | ||||
|     } | ||||
|  | ||||
|     export interface Accessory extends IEventEmitterAccessory { | ||||
|         new(displayName: string, UUID: string): Accessory; | ||||
|         displayName: string; | ||||
|         username: string; | ||||
|         pincode: string; | ||||
|         UUID: string; | ||||
|         aid: string; | ||||
|         bridged: boolean; | ||||
|         bridgedAccessories: Accessory[]; | ||||
|         reachable: boolean; | ||||
|         category: Accessory.Categories; | ||||
|         services: Service[]; | ||||
|         cameraSource: CameraSource; | ||||
|         Categories: typeof Accessory.Categories | ||||
|         addService(service: Service | Function): Service; | ||||
|         removeService(service: Service): void; | ||||
|         getService(name: string | Function): Service; | ||||
|         updateReachability(reachable: boolean): void; | ||||
|         addBridgedAccessory(accessory: Accessory, deferUpdate: boolean): Accessory; | ||||
|         addBridgedAccessories(accessories: Accessory[]): void | ||||
|         removeBridgedAccessory(accessory: Accessory, deferUpdate: boolean): void; | ||||
|         removeBridgedAccessories(accessories: Accessory[]): void; | ||||
|         getCharacteristicByIID(iid: string): Characteristic; | ||||
|         getBridgedAccessoryByAID(aid: string): Accessory; | ||||
|         findCharacteristic(aid: string, iid: string): Accessory; | ||||
|         configureCameraSource(cameraSource: CameraSource): void; | ||||
|         toHAP(opt: any): JSON; | ||||
|         publish(info: PublishInfo, allowInsecureRequest: boolean): void; | ||||
|         destroy(): void; | ||||
|         setupURI(): string; | ||||
|     } | ||||
|  | ||||
|     module Accessory { | ||||
|         export enum Categories { | ||||
|             OTHER = 1, | ||||
|             BRIDGE = 2, | ||||
|             FAN = 3, | ||||
|             GARAGE_DOOR_OPENER = 4, | ||||
|             LIGHTBULB = 5, | ||||
|             DOOR_LOCK = 6, | ||||
|             OUTLET = 7, | ||||
|             SWITCH = 8, | ||||
|             THERMOSTAT = 9, | ||||
|             SENSOR = 10, | ||||
|             ALARM_SYSTEM = 11, | ||||
|             SECURITY_SYSTEM = 11, | ||||
|             DOOR = 12, | ||||
|             WINDOW = 13, | ||||
|             WINDOW_COVERING = 14, | ||||
|             PROGRAMMABLE_SWITCH = 15, | ||||
|             RANGE_EXTENDER = 16, | ||||
|             CAMERA = 17, | ||||
|             IP_CAMERA = 17, | ||||
|             VIDEO_DOORBELL = 18, | ||||
|             AIR_PURIFIER = 19, | ||||
|             AIR_HEATER = 20, | ||||
|             AIR_CONDITIONER = 21, | ||||
|             AIR_HUMIDIFIER = 22, | ||||
|             AIR_DEHUMIDIFIER = 23, | ||||
|             APPLE_TV = 24, | ||||
|             SPEAKER = 26, | ||||
|             AIRPORT = 27, | ||||
|             SPRINKLER = 28, | ||||
|             FAUCET = 29, | ||||
|             SHOWER_HEAD = 30, | ||||
|             TELEVISION = 31, | ||||
|             TARGET_CONTROLLER = 32 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     export interface HAPNodeJS { | ||||
|         init(storagePath?: string): void, | ||||
|         uuid: uuid, | ||||
|         Accessory: Accessory, | ||||
|         Service: Service, | ||||
|         Characteristic: Characteristic | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| declare var hapNodeJS: HAPNodeJS.HAPNodeJS; | ||||
|  | ||||
| declare module "hap-nodejs" { | ||||
|     export = hapNodeJS; | ||||
| } | ||||
| @@ -1,14 +0,0 @@ | ||||
|  | ||||
| /** | ||||
|  * Interface to describe homebridge required elements. | ||||
|  */ | ||||
| export interface IAccessory { | ||||
|     /** | ||||
|      * Required by homebridge. | ||||
|      */ | ||||
|     name: string, | ||||
|     /** | ||||
|      * Called by homebridge to gather services. | ||||
|      */ | ||||
|     getServices(): Array<HAPNodeJS.Service>, | ||||
| } | ||||
							
								
								
									
										137
									
								
								src/platform.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/platform.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| import discover from "@watsonb8/wiz-lib/build/discovery"; | ||||
| import { | ||||
|     API, | ||||
|     Characteristic, | ||||
|     DynamicPlatformPlugin, | ||||
|     Logger, | ||||
|     Logging, | ||||
|     PlatformAccessory, | ||||
|     PlatformConfig, | ||||
|     Service, | ||||
|     UnknownContext, | ||||
| } from "homebridge"; | ||||
| import { v3 } from "node-hue-api"; | ||||
| import Api from "node-hue-api/lib/api/Api"; | ||||
| import { FluxAccessory } from "./fluxAccessory"; | ||||
| import { IConfig } from "./models/iConfig"; | ||||
| import { PLATFORM_NAME, PLUGIN_NAME } from "./settings"; | ||||
| import { Sleep } from "./sleep"; | ||||
|  | ||||
| export class Platform implements DynamicPlatformPlugin { | ||||
|     private hue: Api | undefined; | ||||
|     private accessory: PlatformAccessory | undefined = undefined; | ||||
|     private config: IConfig; | ||||
|     constructor( | ||||
|         public readonly log: Logger, | ||||
|         config: PlatformConfig, | ||||
|         public readonly api: API | ||||
|     ) { | ||||
|         this.config = config as unknown as IConfig; | ||||
|         this.log.info("INFO - Registering Flux platform"); | ||||
|         this.api.on("didFinishLaunching", this.didFinishLaunching.bind(this)); | ||||
|     } | ||||
|  | ||||
|     async configureAccessory(accessory: PlatformAccessory<UnknownContext>) { | ||||
|         this.log.info("Loading accessory from cache:", accessory.displayName); | ||||
|  | ||||
|         this.accessory = accessory; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handler for didFinishLaunching | ||||
|      * Happens after constructor | ||||
|      */ | ||||
|     private async didFinishLaunching() { | ||||
|         this.log.info(`INFO - Done registering Flux platform`); | ||||
|  | ||||
|         await this.connectHue(); | ||||
|         const wizBulbs = await this.connectWiz(); | ||||
|  | ||||
|         this.log.info("Registering accessory: " + this.config.name); | ||||
|         const uuid = this.api.hap.uuid.generate(this.config.name); | ||||
|  | ||||
|         // Load accessory if not cached | ||||
|         if (!this.accessory) { | ||||
|             this.accessory = new this.api.platformAccessory( | ||||
|                 this.config.name, | ||||
|                 uuid | ||||
|             ); | ||||
|             this.accessory.context["DeviceName"] = this.config.name; | ||||
|             this.accessory.context["Type"] = typeof "Flux"; | ||||
|             this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [ | ||||
|                 this.accessory, | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         new FluxAccessory({ | ||||
|             platform: this, | ||||
|             accessory: this.accessory, | ||||
|             hue: this.hue!, | ||||
|             wizBulbs: wizBulbs ?? [], | ||||
|             config: this.config, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private connectWiz = async () => { | ||||
|         if (!this.config) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         return await discover(); | ||||
|     }; | ||||
|  | ||||
|     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, | ||||
|                     undefined | ||||
|                 ); | ||||
|             this.log.info("Using existing connection info"); | ||||
|         } else { | ||||
|             const unauthenticatedApi = await v3.api | ||||
|                 .createLocal(this.config.ipAddress) | ||||
|                 .connect(undefined, undefined, undefined); | ||||
|             let createdUser; | ||||
|             let connected = false; | ||||
|             while (!connected) { | ||||
|                 try { | ||||
|                     this.log.info("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, | ||||
|                             undefined | ||||
|                         ); | ||||
|                     this.log.info("Connected to Hue Bridge"); | ||||
|                     this.log.info( | ||||
|                         `UserName: ${createdUser.username}, ClientKey: ${createdUser.clientkey}` | ||||
|                     ); | ||||
|                     connected = true; | ||||
|                 } catch (err: any) { | ||||
|                     if (err.getHueErrorType() === 101) { | ||||
|                         this.log.info( | ||||
|                             "The Link button on the bridge was not pressed. Please press the Link button and try again." | ||||
|                         ); | ||||
|                         Sleep(5000); | ||||
|                     } else { | ||||
|                         this.log.info(`Unexpected Error: ${err.message}`); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										9
									
								
								src/settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/settings.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| /** | ||||
|  * This is the name of the platform that users will use to register the plugin in the Homebridge config.json | ||||
|  */ | ||||
| export const PLATFORM_NAME = "Flux"; | ||||
|  | ||||
| /** | ||||
|  * This must match the name of your plugin as defined the package.json | ||||
|  */ | ||||
| export const PLUGIN_NAME = "@watsonb8/homebridge-flux"; | ||||
| @@ -2,18 +2,18 @@ | ||||
|     "compilerOptions": { | ||||
|         /* Basic Options */ | ||||
|         // "incremental": true,                   /* Enable incremental compilation */ | ||||
|         "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ | ||||
|         "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ | ||||
|         "target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, | ||||
|         "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, | ||||
|         // "lib": [],                             /* Specify library files to be included in the compilation. */ | ||||
|         // "allowJs": true,                       /* Allow javascript files to be compiled. */ | ||||
|         // "checkJs": true,                       /* Report errors in .js files. */ | ||||
|         // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ | ||||
|         // "declaration": true,                   /* Generates corresponding '.d.ts' file. */ | ||||
|         // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */ | ||||
|         "sourceMap": true, /* Generates corresponding '.map' file. */ | ||||
|         "sourceMap": true /* Generates corresponding '.map' file. */, | ||||
|         // "outFile": "./",                       /* Concatenate and emit output to single file. */ | ||||
|         "outDir": "./bin", /* Redirect output structure to the directory. */ | ||||
|         // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ | ||||
|         "outDir": "./bin" /* Redirect output structure to the directory. */, | ||||
|         "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | ||||
|         // "composite": true,                     /* Enable project compilation */ | ||||
|         // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */ | ||||
|         // "removeComments": true,                /* Do not emit comments to output. */ | ||||
| @@ -22,7 +22,7 @@ | ||||
|         // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ | ||||
|         // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ | ||||
|         /* Strict Type-Checking Options */ | ||||
|         "strict": true, /* Enable all strict type-checking options. */ | ||||
|         "strict": true /* Enable all strict type-checking options. */, | ||||
|         // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */ | ||||
|         // "strictNullChecks": true,              /* Enable strict null checks. */ | ||||
|         // "strictFunctionTypes": true,           /* Enable strict checking of function types. */ | ||||
| @@ -36,14 +36,14 @@ | ||||
|         // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */ | ||||
|         // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */ | ||||
|         /* Module Resolution Options */ | ||||
|         // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ | ||||
|         // "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, | ||||
|         // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */ | ||||
|         // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ | ||||
|         // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */ | ||||
|         // "typeRoots": [],                       /* List of folders to include type definitions from. */ | ||||
|         // "types": [],                           /* Type declaration files to be included in compilation. */ | ||||
|         "types": [] /* Type declaration files to be included in compilation. */, | ||||
|         // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | ||||
|         "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | ||||
|         "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, | ||||
|         // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */ | ||||
|         // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */ | ||||
|         /* Source Map Options */ | ||||
| @@ -55,6 +55,7 @@ | ||||
|         // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */ | ||||
|         // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */ | ||||
|         /* Advanced Options */ | ||||
|         "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ | ||||
|         "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, | ||||
|         "skipLibCheck": false | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user