Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement dynamic scenes selector #74

Merged
merged 3 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/accessories/WizBulb/AdaptiveLighting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function initAdaptiveLighting(
// get pilot will disable for us :)
getPilot(
wiz,
service,
accessory,
device,
() => {},
() => {}
Expand Down
39 changes: 24 additions & 15 deletions src/accessories/WizBulb/characteristics/color.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
CharacteristicSetCallback,
CharacteristicValue,
Service as WizService,
PlatformAccessory,
} from "homebridge";
import HomebridgeWizLan from "../../../wiz";
import { Device } from "../../../types";
Expand All @@ -22,16 +22,22 @@ import {
export function transformHue(pilot: Pilot) {
return pilotToColor(pilot).hue;
}
function initHue(service: WizService, device: Device, wiz: HomebridgeWizLan) {
const { Characteristic } = wiz;
function initHue(
accessory: PlatformAccessory,
device: Device,
wiz: HomebridgeWizLan
) {
const { Characteristic, Service } = wiz;
const service = accessory.getService(Service.Lightbulb)!;
const scenesService = new Service.Television("Scenes");
service
.getCharacteristic(Characteristic.Hue)
.on("get", (callback) =>
.on("get", callback =>
getPilot(
wiz,
service,
accessory,
device,
(pilot) => callback(null, transformHue(pilot)),
pilot => callback(null, transformHue(pilot)),
callback
)
)
Expand All @@ -40,6 +46,7 @@ function initHue(service: WizService, device: Device, wiz: HomebridgeWizLan) {
(newValue: CharacteristicValue, next: CharacteristicSetCallback) => {
setPilot(
wiz,
accessory,
device,
{
temp: undefined,
Expand All @@ -49,7 +56,7 @@ function initHue(service: WizService, device: Device, wiz: HomebridgeWizLan) {
wiz
),
},
updateColorTemp(device, service, wiz, next)
updateColorTemp(device, accessory, wiz, next)
);
}
);
Expand All @@ -59,19 +66,20 @@ export function transformSaturation(pilot: Pilot) {
return pilotToColor(pilot).saturation;
}
function initSaturation(
service: WizService,
accessory: PlatformAccessory,
device: Device,
wiz: HomebridgeWizLan
) {
const { Characteristic } = wiz;
const { Characteristic, Service } = wiz;
const service = accessory.getService(Service.Lightbulb)!;
service
.getCharacteristic(Characteristic.Saturation)
.on("get", (callback) =>
getPilot(
wiz,
service,
accessory,
device,
(pilot) => callback(null, transformSaturation(pilot)),
pilot => callback(null, transformSaturation(pilot)),
callback
)
)
Expand All @@ -80,6 +88,7 @@ function initSaturation(
(newValue: CharacteristicValue, next: CharacteristicSetCallback) => {
setPilot(
wiz,
accessory,
device,
{
temp: undefined,
Expand All @@ -89,17 +98,17 @@ function initSaturation(
wiz
),
},
updateColorTemp(device, service, wiz, next)
updateColorTemp(device, accessory, wiz, next)
);
}
);
}

export function initColor(
service: WizService,
accessory: PlatformAccessory,
device: Device,
wiz: HomebridgeWizLan
) {
initHue(service, device, wiz);
initSaturation(service, device, wiz);
initHue(accessory, device, wiz);
initSaturation(accessory, device, wiz);
}
9 changes: 6 additions & 3 deletions src/accessories/WizBulb/characteristics/dimming.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
CharacteristicSetCallback,
CharacteristicValue,
PlatformAccessory,
Service as WizService,
} from "homebridge";
import HomebridgeWizLan from "../../../wiz";
Expand All @@ -15,17 +16,18 @@ export function transformDimming(pilot: Pilot) {
return Number(Math.round((Math.max(10, Number(pilot.dimming)) - 100) * 1.1 + 100));
}
export function initDimming(
service: WizService,
accessory: PlatformAccessory,
device: Device,
wiz: HomebridgeWizLan
) {
const { Characteristic } = wiz;
const { Characteristic, Service } = wiz;
const service = accessory.getService(Service.Lightbulb)!;
service
.getCharacteristic(Characteristic.Brightness)
.on("get", (callback) =>
getPilot(
wiz,
service,
accessory,
device,
(pilot) => callback(null, transformDimming(pilot)),
callback
Expand All @@ -36,6 +38,7 @@ export function initDimming(
(newValue: CharacteristicValue, next: CharacteristicSetCallback) => {
setPilot(
wiz,
accessory,
device,
// for some reason < 10% is invalid, so we gotta fit it into 10% <-> 100%
{ dimming: Math.round((Math.max(1, Number(newValue)) + 10) / 1.1) },
Expand Down
14 changes: 8 additions & 6 deletions src/accessories/WizBulb/characteristics/onOff.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
CharacteristicSetCallback,
CharacteristicValue,
PlatformAccessory,
Service as WizService,
} from "homebridge";
import HomebridgeWizLan from "../../../wiz";
Expand All @@ -15,26 +16,27 @@ export function transformOnOff(pilot: Pilot) {
return Number(pilot.state);
}
export function initOnOff(
service: WizService,
accessory: PlatformAccessory,
device: Device,
wiz: HomebridgeWizLan
) {
const { Characteristic } = wiz;
const { Characteristic, Service } = wiz;
const service = accessory.getService(Service.Lightbulb)!;
service
.getCharacteristic(Characteristic.On)
.on("get", (callback) =>
.on("get", callback =>
getPilot(
wiz,
service,
accessory,
device,
(pilot) => callback(null, transformOnOff(pilot)),
pilot => callback(null, transformOnOff(pilot)),
callback
)
)
.on(
"set",
(newValue: CharacteristicValue, next: CharacteristicSetCallback) => {
setPilot(wiz, device, { state: Boolean(newValue) }, next);
setPilot(wiz, accessory, device, { state: Boolean(newValue) }, next);
}
);
}
172 changes: 172 additions & 0 deletions src/accessories/WizBulb/characteristics/scenes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import {
CharacteristicSetCallback,
CharacteristicValue,
PlatformAccessory,
Service as WizService,
} from "homebridge";
import HomebridgeWizLan from "../../../wiz";
import { Device } from "../../../types";
import {
getPilot as _getPilot,
setPilot as _setPilot,
} from "../../../util/network";
import { getPilot, setPilot } from "../pilot";
import { Pilot } from "../pilot";
import { isRGB, isTW, turnOffIfNeeded } from "../util";

enum SCENES {
"Ocean" = 1,
"Romance",
"Sunset",
"Party",
"Fireplace",
"Cozy",
"Forest",
"Pastel Colors",
"Wake up",
"Bedtime",
"Warm White",
"Daylight",
"Cool white",
"Night light",
"Focus",
"Relax",
"True colors",
"TV time",
"Plantgrowth",
"Spring",
"Summer",
"Fall",
"Deepdive",
"Jungle",
"Mojito",
"Club",
"Christmas",
"Halloween",
"Candlelight",
"Golden white",
"Pulse",
"Steampunk",
}

const DW_BULBS_SUPPORTED_SCENES_IDS = [9, 10, 13, 14, 29, 30, 31, 32];
const TW_BULBS_SUPPORTED_SCENES_IDS = [
6, 9, 10, 11, 12, 13, 14, 15, 16, 18, 29, 30, 31, 32,
];
const RGB_BULBS_SUPPORTED_SCENES_IDS = Object.keys(SCENES).reduce(
(ids, key) => (isNaN(Number(key)) ? ids : [...ids, Number(key)]),
[] as number[]
);

/**
* Returns supported scenes for device. Based on https://bit.ly/3hLImPa.
* @param device
* @return array of ids of scenes supported by the particular light type
*/
function supportedScenesIdsForDevice(device: Device) {
if (isTW(device)) return TW_BULBS_SUPPORTED_SCENES_IDS;
else if (isRGB(device)) return RGB_BULBS_SUPPORTED_SCENES_IDS;
return DW_BULBS_SUPPORTED_SCENES_IDS;
}

export function transformEffectId(pilot: Pilot): number {
return Number(pilot.sceneId);
}

export function transformEffectActive(pilot: Pilot): boolean {
return Number(pilot.sceneId) > 0;
}

export function initScenes(
wiz: HomebridgeWizLan,
accessory: PlatformAccessory,
device: Device
) {
const { Characteristic, Service } = wiz;

let scenesService = accessory.getService(Service.Television);
const service = accessory.getService(Service.Lightbulb)!;
if (!scenesService) {
scenesService = new Service.Television("Scenes");
accessory.addService(scenesService);

scenesService
.getCharacteristic(Characteristic.ActiveIdentifier)
.on("get", callback =>
getPilot(
wiz,
accessory,
device,
pilot => callback(null, transformEffectId(pilot)),
callback
)
)
.on(
"set",
(newValue: CharacteristicValue, next: CharacteristicSetCallback) => {
const sceneId = Number(newValue);
setPilot(wiz, accessory, device, { sceneId }, next);
if (sceneId !== 0) device.lastSelectedSceneId = sceneId;
}
);

scenesService
.getCharacteristic(Characteristic.Active)
.on("get", callback =>
getPilot(
wiz,
accessory,
device,
pilot => callback(null, transformEffectActive(pilot)),
callback
)
)
.on(
"set",
(newValue: CharacteristicValue, next: CharacteristicSetCallback) => {
console.log("active: ", newValue);
if (newValue === 0) {
scenesService!.setCharacteristic(
Characteristic.ActiveIdentifier,
0
);

next(null);
} else {
scenesService!.setCharacteristic(
Characteristic.ActiveIdentifier,
device.lastSelectedSceneId ?? 1
);
turnOffIfNeeded(Characteristic.Hue, service);
turnOffIfNeeded(Characteristic.Saturation, service);

next(null);
}
}
);

const scenesIds = supportedScenesIdsForDevice(device);

scenesIds.forEach((sceneId: number) => {
const sceneName = SCENES[sceneId];
console.log(sceneName);
const effectInputSource = accessory.addService(
Service.InputSource,
sceneId,
sceneName
);
effectInputSource
.setCharacteristic(Characteristic.Identifier, sceneId)
.setCharacteristic(Characteristic.ConfiguredName, sceneName)
.setCharacteristic(
Characteristic.IsConfigured,
Characteristic.IsConfigured.CONFIGURED
)
.setCharacteristic(
Characteristic.InputSourceType,
Characteristic.InputSourceType.HDMI
);
scenesService!.addLinkedService(effectInputSource);
});
}
}
Loading