Compare commits

..

15 Commits

Author SHA1 Message Date
d6b7f3b6e6 Removing drone yaml
All checks were successful
Build homebridge-flux / Version (push) Successful in 7s
Build homebridge-flux / Build (push) Successful in 14s
Build homebridge-flux / Publish Latest (push) Successful in 7s
Build homebridge-flux / Deploy (push) Successful in 12s
2024-06-06 10:32:41 -05:00
f69f2a3ca5 Adding ci action
All checks were successful
Build homebridge-flux / Build (push) Successful in 16s
Build homebridge-flux / Version (push) Successful in 7s
Build homebridge-flux / Publish Latest (push) Successful in 9s
Build homebridge-flux / Deploy (push) Successful in 13s
2024-06-06 10:25:45 -05:00
Brandon Watson
1d39c725af Adding separate configuration for wiz vs hue lights
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-30 12:14:10 -05:00
Brandon Watson
417f017f45 Using command line to publish libs
Some checks failed
continuous-integration/drone Build is passing
continuous-integration/drone/push Build is failing
WIP

WIP
2023-01-27 16:11:23 -06:00
Brandon Watson
9cf8ef3c60 Using gitea npm registry
Some checks failed
continuous-integration/drone Build is failing
WIP

WIP
2023-01-26 17:14:57 -06:00
Brandon Watson
e1ac0a3a5b Only control wiz lights if they are already on
All checks were successful
continuous-integration/drone Build is passing
2022-10-30 10:47:08 -05:00
Brandon Watson
e61ec0cc3c Stability fixes
Updating longitude type

Only updating wiz bulb if pilot is not undefined

Fixing issue where lights will turn off after receiving power

Fixing bug where wiz lights would go dark instead of maintaining the current brightness
2022-10-29 13:59:36 -05:00
Brandon Watson
abb66eb26f Bump wiz-lib
All checks were successful
continuous-integration/drone/push Build is passing
2022-09-07 22:10:50 -05:00
Brandon Watson
c378e46cb3 Making default cron schedule configurable
All checks were successful
continuous-integration/drone/push Build is passing
asdf
2022-09-07 22:04:17 -05:00
Brandon Watson
a9833729f7 Fixing bugs
All checks were successful
continuous-integration/drone/push Build is passing
- Only RGB lights
- Fixing on/off bug
2022-09-07 21:41:26 -05:00
Brandon Watson
51b82fc8d2 Updating config schema
All checks were successful
continuous-integration/drone/push Build is passing
asdf
2022-09-07 14:45:36 -05:00
Brandon Watson
feb5533419 Adding new features
All checks were successful
continuous-integration/drone/push Build is passing
- Adding ability to specify delay times per light
- Adding config.schema.json
2022-09-07 14:29:51 -05:00
Brandon Watson
8fbfc51276 Bump wiz-lib
All checks were successful
continuous-integration/drone/push Build is passing
Bump
2022-09-06 20:44:31 -05:00
Brandon Watson
c79b776ec5 Publishing bin dir
All checks were successful
continuous-integration/drone/push Build is passing
2022-09-06 20:33:25 -05:00
Brandon Watson
da83a94742 Squashed commit of the following:
All checks were successful
continuous-integration/drone/push Build is passing
commit 440a4d62a1
Author: Brandon Watson <brandon@watsonlabs.net>
Date:   Tue Sep 6 20:23:31 2022 -0500

    Fixing issue where button becomes unresponsive | Updating homebridge

commit d73dead5d4
Author: Brandon Watson <brandon@watsonlabs.net>
Date:   Tue Sep 6 18:25:38 2022 -0500

    Successfully controlling wiz bulbs
2022-09-06 20:28:49 -05:00
10 changed files with 1193 additions and 1214 deletions

View File

@ -1,130 +0,0 @@
kind: pipeline
type: docker
name: default
node:
lan: internal
steps:
- name: build
image: node
commands:
- npm install
- npm run build
- 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:1.0.0
settings:
username:
from_secret: npm_username
password:
from_secret: npm_password
email: b.watson@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:1.0.0
settings:
username:
from_secret: npm_username
password:
from_secret: npm_password
email: b.watson@watsonlabs.net
registry: "http://10.44.1.6:4873/"
when:
event:
- tag
exclude:
- pull_request
- name: remove old package
image: appleboy/drone-ssh
environment:
SSH_USER:
from_secret: ssh_user
settings:
host: homebridge.me
envs:
- SSH_USER
username:
from_secret: ssh_user
key:
from_secret: ssh_key
port: 22
script:
- rm -r /home/$SSH_USER/.npm-global/lib/node_modules/@watsonb8/homebridge-flux
when:
event:
- tag
exclude:
- pull_request
- name: deploy
image: appleboy/drone-ssh
settings:
host: homebridge.me
username:
from_secret: ssh_user
key:
from_secret: ssh_key
port: 22
script:
- npm install -g @watsonb8/homebridge-flux --registry http://10.44.1.6:4873
when:
event:
- tag
exclude:
- pull_request
- name: restart homebridge
image: appleboy/drone-ssh
settings:
host: homebridge.me
username:
from_secret: elevated_ssh_user
key:
from_secret: ssh_key
port: 22
script:
- systemctl restart homebridge
when:
event:
- tag
exclude:
- pull_request
- 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

85
.gitea/workflows/ci.yaml Normal file
View File

@ -0,0 +1,85 @@
name: Build homebridge-flux
on:
workflow_dispatch:
push:
branches:
- master
jobs:
build:
name: Build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: |
npm ci
npm run build
version:
name: Version
outputs:
version: ${{ steps.get_version.outputs.version }}
commit: ${{ steps.get_version.outputs.commit }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- id: get_version
name: Set Version
run: |
export version=`node -p "require('./package.json').version"`
export commit=`echo $GITHUB_SHA | cut -c1-5`
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "commit=$commit" >> "$GITHUB_OUTPUT"
publish_tagged:
name: Publish Latest
needs:
- build
- version
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm config set @watsonb8:registry https://gitea.watsonlabs.net/api/packages/watsonb8/npm/
- name: Publish
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
COMMIT: ${{ needs.version.outputs.commit }}
run: |
npm config set -- '//gitea.watsonlabs.net/api/packages/watsonb8/npm/:_authToken' "$NPM_TOKEN"
npm version prerelease --preid="$COMMIT" --git-tag-version=false --allow-same-version=true
npm publish
deploy:
runs-on:
- ubuntu-latest
- internal
name: Deploy
needs:
- publish_tagged
- version
steps:
- name: Set up SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.ELEVATED_HOMEBRIDGE_SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -p 22 homebridge.me >> ~/.ssh/known_hosts
sudo apt update
sudo apt install sshpass
- name: Remove old Package
run: |
sshpass -p '${{ secrets.ELEVATED_HOMEBRIDGE_PASSWORD }}' ssh -v -o StrictHostKeyChecking=no ${{ secrets.ELEVATED_HOMEBRIDGE_USER }}@${{ secrets.HOMEBRIDGE_HOST }} <<'ENDSSH'
rm -r /home/${{ secrets.HOMEBRIDGE_USER }}/.npm-global/lib/node_modules/@watsonb8/homebridge-flux
ENDSSH
- name: Deploy
env:
COMMIT: ${{ needs.version.outputs.commit }}
run: |
sshpass -p '${{ secrets.ELEVATED_HOMEBRIDGE_PASSWORD }}' ssh -v -o StrictHostKeyChecking=no ${{ secrets.ELEVATED_HOMEBRIDGE_USER }}@${{ secrets.HOMEBRIDGE_HOST }} <<'ENDSSH'
npm install -g @watsonb8/homebridge-flux@$COMMIT
ENDSSH

154
config.schema.json Normal file
View File

@ -0,0 +1,154 @@
{
"pluginAlias": "Flux",
"pluginType": "platform",
"singular": true,
"schema": {
"type": "object",
"properties": {
"name": {
"title": "Switch Name",
"type": "string",
"required": true
},
"ipAddress": {
"title": "IP Address",
"type": "string",
"required": true
},
"userName": {
"title": "User Name",
"type": "string",
"required": false
},
"clientKey": {
"title": "Client Key",
"type": "string",
"required": false
},
"latitude": {
"title": "Latitude",
"type": "number",
"required": true
},
"longitude": {
"title": "Longitude",
"type": "number",
"required": true
},
"testNowDateString": {
"title": "Test Date Time",
"type": "string",
"required": false
},
"hueLights": {
"title": "Hue Lights",
"type": "array",
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"name": {
"title": "Name",
"type": "string",
"required": true
},
"cron": {
"title": "Cron Schedule",
"type": "string",
"required": false
},
"on": {
"title": "On",
"type": "boolean",
"required": false
}
}
},
"required": true
},
"wizLights": {
"title": "Wiz Lights",
"type": "array",
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"ip": {
"title": "Ip Address",
"type": "string",
"required": true
},
"mac": {
"title": "Mac Address",
"type": "string",
"required": true
},
"cron": {
"title": "Cron Schedule",
"type": "string",
"required": false
},
"on": {
"title": "On",
"type": "boolean",
"required": false
}
}
},
"required": false
},
"wizDiscoveryEnabled": {
"title": "Wiz Discovery Enabled",
"type": "boolean",
"required": true
},
"hueCeilingColorTemp": {
"title": "Hue Ceiling Color Temperature",
"type": "number",
"required": true
},
"hueSunsetColorTemp": {
"title": "Hue Sunset Color Temperature",
"type": "number",
"required": true
},
"hueFloorColorTemp": {
"title": "Hue Floor Color Temperature",
"type": "number",
"required": true
},
"wizCeilingColorTemp": {
"title": "Wiz Ceiling Color Temperature",
"type": "number",
"required": true
},
"wizSunsetColorTemp": {
"title": "Wiz Sunset Color Temperature",
"type": "number",
"required": true
},
"wizFloorColorTemp": {
"title": "Wiz Floor Color Temperature",
"type": "number",
"required": true
},
"sunsetDuration": {
"title": "Sunset Duration",
"type": "number",
"required": true
},
"transition": {
"title": "Transition Time",
"type": "number",
"required": true
},
"cron": {
"title": "Default Cron",
"type": "string",
"required": false
}
}
},
"form": null,
"display": null
}

1382
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,16 @@
{
"name": "@watsonb8/homebridge-flux",
"version": "1.1.3",
"version": "1.2.0",
"description": "",
"main": "bin/index.js",
"publishConfig": {
"registry": "http://10.44.1.6:4873/"
"registry": "https://gitea.watsonlabs.net"
},
"files": [
"bin",
"src",
"config.schema.json"
],
"scripts": {
"build": "tsc",
"test": "echo \"Error: no test specified\" && exit 1"
@ -29,7 +34,7 @@
"dependencies": {
"@types/node-cron": "^2.0.3",
"@types/suncalc": "^1.8.0",
"@watsonb8/wiz-lib": "^1.0.1-62427.0",
"@watsonb8/wiz-lib": "^1.0.1-ae175.0",
"node-cron": "^2.0.3",
"node-hue-api": "^4.0.5",
"suncalc": "^1.8.0"
@ -39,4 +44,4 @@
"homebridge": "^1.5.0",
"typescript": "^4.5.4"
}
}
}

View File

@ -6,11 +6,12 @@ import { IConfig } from "./models/iConfig";
//@ts-ignore
import { GetTimesResult, getTimes } from "suncalc";
import HueError = require("node-hue-api/lib/HueError");
import cron from "node-cron";
import cron, { ScheduledTask } 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";
import { colorTempToRgb } from "./util/colorUtil";
const SECONDS_IN_DAY = 86400000;
const MINUTES_IN_MILLISECOND = 60000;
@ -29,6 +30,10 @@ export class FluxAccessory {
private readonly _accessory: PlatformAccessory;
private _config: IConfig;
private _isActive: boolean;
private _hueRGB: RGB;
private _wizRGB: RGB;
private _fade: number;
private _cron: string;
//Service fields
private _switchService;
@ -39,6 +44,7 @@ export class FluxAccessory {
private _wizLights: Array<WizBulb> = [];
private _times: GetTimesResult;
private _tasks: Array<ScheduledTask> = [];
constructor(props: IFluxProps) {
//Assign class variables
@ -47,6 +53,12 @@ export class FluxAccessory {
this._config = props.config;
this._isActive = false;
this._wizLights = props.wizBulbs;
this._hue = props.hue;
this.name = this._config.name;
this._hueRGB = { r: 0, g: 0, b: 0 };
this._wizRGB = { r: 0, g: 0, b: 0 };
this._fade = this._config.transition ?? 30000;
this._cron = this._config.cron ?? "*/30 * * * * *";
this._times = getTimes(
new Date(),
@ -70,8 +82,20 @@ export class FluxAccessory {
}
).start();
this._hue = props.hue;
this.name = this._config.name;
//Schedule job to refresh hues every minute
this.updateRGB();
cron.schedule(
"* * * * *",
() => {
this.updateRGB();
this._platform.log.info("Updated hues");
},
{
scheduled: true,
}
).start();
this.scheduleLights();
this._accessory
.getService(this._platform.api.hap.Service.AccessoryInformation)!
@ -106,6 +130,8 @@ export class FluxAccessory {
.on("set", this.onSetEnabled)
//@ts-ignore
.on("get", this.onGetEnabled);
// this.test();
}
public name: string = "Flux";
@ -126,9 +152,11 @@ export class FluxAccessory {
this._config.latitude,
this._config.longitude
);
this.update();
this._isActive = true;
this.enable();
} else {
this._isActive = false;
this.disable();
}
return callback();
};
@ -148,112 +176,19 @@ export class FluxAccessory {
* Populates internal lights array using the configuration values
*/
private getLights = async (): Promise<void> => {
for (const value of this._config.lights) {
for (const value of this._config.hueLights) {
//@ts-ignore
const light: Light = await this._hue.lights.getLightByName(value);
const light: Light = await this._hue.lights.getLightByName(
value.name
);
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;
}
private isHueError = (object: any): object is HueError => {
return "_hueError" in object;
};
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}`
);
}
}
});
await Promise.all(promises);
};
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);
})
);
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);
}
);
}
/**
* Gets adjusted color temperature.
*/
@ -283,84 +218,208 @@ export class FluxAccessory {
}
}
private update = async (): Promise<void> => {
this._isActive = true;
while (this._isActive) {
if (this._lights.length === 0) {
await this.getLights();
private updateRGB = (): void => {
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 hueStartColorTemp = this._config.hueCeilingColorTemp ?? 4000;
const hueSunsetColorTemp = this._config.hueSunsetColorTemp ?? 2800;
const hueFloorColorTemp = this._config.hueFloorColorTemp ?? 1900;
const wizStartColorTemp = this._config.wizCeilingColorTemp ?? 4000;
const wizSunsetColorTemp = this._config.wizSunsetColorTemp ?? 2800;
const wizFloorColorTemp = this._config.wizFloorColorTemp ?? 1900;
let newHueTemp = this._config.hueCeilingColorTemp;
let newWizTemp = this._config.wizCeilingColorTemp;
if (start < now && now < sunsetStart) {
newHueTemp = this.getTempOffset(
hueStartColorTemp,
hueSunsetColorTemp,
start,
sunsetStart
);
newWizTemp = this.getTempOffset(
wizStartColorTemp,
wizSunsetColorTemp,
start,
sunsetStart
);
} else if (sunsetStart < now && now < sunsetEnd) {
newHueTemp = this._config.hueSunsetColorTemp;
newWizTemp = this._config.wizSunsetColorTemp;
} else if (sunsetEnd < now && now < nightStart) {
newHueTemp = this.getTempOffset(
hueSunsetColorTemp,
hueFloorColorTemp,
sunsetEnd,
nightStart
);
newWizTemp = this.getTempOffset(
wizSunsetColorTemp,
wizFloorColorTemp,
sunsetEnd,
nightStart
);
} else if (nightStart < now && now < sunrise) {
newHueTemp = this._config.hueFloorColorTemp;
newWizTemp = this._config.wizFloorColorTemp;
}
//Set RGB
this._hueRGB = colorTempToRgb(newHueTemp);
this._wizRGB = colorTemperature2rgb(newWizTemp);
};
private scheduleLights = async (): Promise<void> => {
if (this._lights.length === 0) {
await this.getLights();
}
this._tasks = [...this.getHueTasks(), ...this.getWizTasks()];
};
private getHueTasks(): Array<ScheduledTask> {
return this._config.hueLights.map((hueLightConfig) => {
let light = this._lights.find((x) => x.name == hueLightConfig.name);
let schedule: string = hueLightConfig.cron ?? this._cron;
this._platform.log.info(
`Scheduling task for ${light?.name}: ${schedule}`
);
return cron.schedule(
schedule,
async () => {
await this.updateHueLight(
light,
hueLightConfig?.on ?? false
);
this._platform.log.info("Updated hues");
},
{
scheduled: false,
}
);
});
}
private getWizTasks(): Array<ScheduledTask> {
return this._wizLights.map((wizBulb) => {
let wizLightConfig = this._config.wizLights.find(
(x) => x.ip == wizBulb.getIp()
);
let schedule: string = wizLightConfig?.cron ?? this._cron;
this._platform.log.info(
`Scheduling task for ${wizBulb.getMac()}: ${schedule}`
);
return cron.schedule(
schedule,
async () => {
await this.updateWizLight(
wizBulb,
wizLightConfig?.on ?? false
);
this._platform.log.info("Updated hues");
},
{
scheduled: false,
}
);
});
}
private updateWizLight = async (
wizBulb: WizBulb,
on: Boolean
): Promise<void> => {
let pilot;
try {
pilot = await wizBulb.get();
} catch (err: any) {
this._platform.log.error(err.message);
}
if (pilot && pilot.state) {
this._platform.log.info(`Adjusting wiz bulb: ${wizBulb.getMac()}`);
await wizBulb.set(
this._wizRGB,
on ? 100 : pilot.dimming,
this._fade
);
}
};
private updateHueLight = async (
hueLight: Light | undefined,
on: Boolean
): Promise<void> => {
if (!hueLight) {
return;
}
this._platform.log.info(`Adjusting wiz bulb: ${hueLight.name}`);
const lightState = new LightState();
lightState
.transitionInMillis(this._fade)
.rgb(this._hueRGB.r ?? 0, this._hueRGB.g ?? 0, this._hueRGB.b ?? 0);
if (on) {
lightState.brightness(100).on(true);
}
try {
await this._hue.lights.setLightState(hueLight.id, lightState);
} 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}`);
}
}
};
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
);
private enable() {
this._tasks.forEach((task) => task.start());
}
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;
private disable() {
this._tasks.forEach((task) => task.stop());
}
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
);
private test = async () => {
for (let i = 2500; i > 0; i--) {
this._platform.log.info(`i: ${i}`);
for (const wizBulb of this._wizLights) {
let pilot;
try {
pilot = await wizBulb.get();
} catch (err: any) {
this._platform.log.error(err.message);
}
this._platform.log.info(
`Adjusting light temp to ${newTemp}, ${JSON.stringify(
hueRGB
)}`
`Adjusting wiz bulb: ${wizBulb.getMac()}`
);
wizBulb.set(colorTemperature2rgb(i), 100, this._fade);
}
await Sleep(this._config.delay ? this._config.delay : 60000);
await Sleep(100);
}
};
}

View File

@ -9,138 +9,3 @@ import { Platform } from "./platform";
export = (api: API) => {
api.registerPlatform(PLATFORM_NAME, Platform);
};
// 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;
// /**
// * Main entry.
// * @param homebridge
// */
// export default function (homebridge: any) {
// Homebridge = homebridge;
// Accessory = homebridge.platformAccessory;
// homebridge.registerPlatform("homebridge-flux", "Flux", FluxPlatform, true);
// }
// class FluxPlatform {
// log: any = {};
// api: any;
// accessoryList: Array<IAccessory> = [];
// 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 Flux platform");
// this.api.on("didFinishLaunching", this.didFinishLaunching.bind(this));
// }
// 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("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"
// );
// 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;
// }
// }
// }
// }
// };
// /**
// * Handler for didFinishLaunching
// * Happens after constructor
// */
// private didFinishLaunching() {
// this.log(`INFO - Done registering Flux platform`);
// }
// /**
// * 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);
// };
// }

View File

@ -17,7 +17,14 @@ export interface IConfig {
/**
* The list of lights to affect
*/
lights: Array<string>;
hueLights: Array<{ name: string; cron?: string; on?: boolean }>;
/**
* The list of wiz lights to affect
*/
wizLights: Array<{ ip: string; mac: string; cron?: string; on?: boolean }>;
wizDiscoveryEnabled: boolean;
/**
* The name of the enable switch in homekit
@ -27,17 +34,32 @@ export interface IConfig {
/**
* The color temperature at the start of sunset transition
*/
ceilingColorTemp: number;
hueCeilingColorTemp: number;
/**
* The color temp during the night
*/
floorColorTemp: number;
hueFloorColorTemp: number;
/**
* The color temp at sunet
*/
sunsetColorTemp: number;
hueSunsetColorTemp: number;
/**
* The color temperature at the start of sunset transition
*/
wizCeilingColorTemp: number;
/**
* The color temp during the night
*/
wizFloorColorTemp: number;
/**
* The color temp at sunet
*/
wizSunsetColorTemp: number;
/**
* The time in milliseconds the lights should remain at sunset temperature.
@ -52,10 +74,10 @@ export interface IConfig {
/**
* The number of milliseconds to wait btw updates
*/
delay?: number;
cron?: string;
/**
* The current formatted date and time to use with testing
*/
testNowDateString?: string;
}
}

View File

@ -1,13 +1,11 @@
import discover from "@watsonb8/wiz-lib/build/discovery";
import { Discover } from "@watsonb8/wiz-lib/build/discovery";
import { WizBulb } from "@watsonb8/wiz-lib/build/wizBulb";
import {
API,
Characteristic,
DynamicPlatformPlugin,
Logger,
Logging,
PlatformAccessory,
PlatformConfig,
Service,
UnknownContext,
} from "homebridge";
import { v3 } from "node-hue-api";
@ -19,6 +17,7 @@ import { Sleep } from "./sleep";
export class Platform implements DynamicPlatformPlugin {
private hue: Api | undefined;
private wiz: Discover;
private accessory: PlatformAccessory | undefined = undefined;
private config: IConfig;
constructor(
@ -27,6 +26,7 @@ export class Platform implements DynamicPlatformPlugin {
public readonly api: API
) {
this.config = config as unknown as IConfig;
this.wiz = new Discover();
this.log.info("INFO - Registering Flux platform");
this.api.on("didFinishLaunching", this.didFinishLaunching.bind(this));
}
@ -77,7 +77,27 @@ export class Platform implements DynamicPlatformPlugin {
return;
}
return await discover();
let bulbs: Array<WizBulb> = await this.wiz.createWizBulbs(
this.config.wizLights
);
if (this.config.wizDiscoveryEnabled) {
let discoveredBulbs: Array<WizBulb> = await this.wiz.discover();
let filtered = [];
for (const bulb of discoveredBulbs) {
if (
!bulbs.some(
(manualBulb) => manualBulb.getIp() === bulb.getIp()
) &&
bulb.isRGB()
) {
filtered.push(bulb);
}
}
bulbs.push(...filtered);
}
return bulbs;
};
private connectHue = async () => {

41
src/util/colorUtil.ts Normal file
View File

@ -0,0 +1,41 @@
import { RGB } from "@watsonb8/wiz-lib";
export const 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: clamp(red, 0, 255),
g: clamp(green, 0, 255),
b: clamp(blue, 0, 255),
};
};
const clamp = (x: number, min: number, max: number) => {
if (x < min) {
return min;
}
if (x > max) {
return max;
}
return x;
};