import { EventEmitter } from "events";
import { Lightbulb } from "../definitions";
import { ControllerIdentifier, ControllerServiceMap, SerializableController, StateChangeDelegate } from "./Controller";
/**
* @group Adaptive Lighting
*/
export interface ActiveAdaptiveLightingTransition {
/**
* The instance id for the characteristic for which this transition applies to (aka the ColorTemperature characteristic).
*/
iid: number;
/**
* Start of the transition in epoch time millis (as sent from the HomeKit controller).
* Additionally see {@link timeMillisOffset}.
*/
transitionStartMillis: number;
/**
* It is not necessarily given, that we have the same time (or rather the correct time) as the HomeKit controller
* who set up the transition schedule.
* Thus we record the delta between our current time and the the time sent with the setup request.
* timeMillisOffset
is defined as Date.now() - transitionStartMillis;
.
* So in the case were we actually have a correct local time, it most likely will be positive (due to network latency).
* But of course it can also be negative.
*/
timeMillisOffset: number;
/**
* Value is the same for ALL control write requests I have seen (even on other homes).
* @private
*/
transitionId: string;
/**
* Start of transition in milliseconds from 2001-01-01 00:00:00; unsigned 64 bit LE integer
* @private as it is a 64 bit integer, we just store the buffer to not have the struggle to encode/decode 64 bit int in JavaScript
*/
transitionStartBuffer: string;
/**
* Hex string of 8 bytes. Some kind of id (?). Sometimes it isn't supplied. Don't know the use for that.
* @private
*/
id3?: string;
transitionCurve: AdaptiveLightingTransitionCurveEntry[];
brightnessCharacteristicIID: number;
brightnessAdjustmentRange: BrightnessAdjustmentMultiplierRange;
/**
* Interval in milliseconds specifies how often the accessory should update the color temperature (internally).
* Typically this is 60000 aka 60 seconds aka 1 minute.
* Note {@link notifyIntervalThreshold}
*/
updateInterval: number;
/**
* Defines the interval in milliseconds on how often the accessory may send even notifications
* to subscribed HomeKit controllers (aka call {@link Characteristic.updateValue}.
* Typically this is 600000 aka 600 seconds aka 10 minutes or 300000 aka 300 seconds aka 5 minutes.
*/
notifyIntervalThreshold: number;
}
/**
* @group Adaptive Lighting
*/
export interface AdaptiveLightingTransitionPoint {
/**
* This is the time offset from the transition start to the {@link lowerBound}.
*/
lowerBoundTimeOffset: number;
transitionOffset: number;
lowerBound: AdaptiveLightingTransitionCurveEntry;
upperBound: AdaptiveLightingTransitionCurveEntry;
}
/**
* @group Adaptive Lighting
*/
export interface AdaptiveLightingTransitionCurveEntry {
/**
* The color temperature in mired.
*/
temperature: number;
/**
* The color temperature actually set to the color temperature characteristic is dependent
* on the current brightness value of the lightbulb.
* This means you will always need to query the current brightness when updating the color temperature
* for the next transition step.
* Additionally you will also need to correct the color temperature when the end user changes the
* brightness of the Lightbulb.
*
* The brightnessAdjustmentFactor is always a negative floating point value.
*
* To calculate the resulting color temperature you will need to do the following.
*
* In short: temperature + brightnessAdjustmentFactor * currentBrightness
*
* Complete example:
* ```js
* const temperature = ...; // next transition value, the above property
* // below query the current brightness while staying the the min/max brightness range (typically between 10-100 percent)
* const currentBrightness = Math.max(minBrightnessValue, Math.min(maxBrightnessValue, CHARACTERISTIC_BRIGHTNESS_VALUE));
*
* // as both temperature and brightnessAdjustmentFactor are floating point values it is advised to round to the next integer
* const resultTemperature = Math.round(temperature + brightnessAdjustmentFactor * currentBrightness);
* ```
*/
brightnessAdjustmentFactor: number;
/**
* The duration in milliseconds this exact temperature value stays the same.
* When we transition to to the temperature value represented by this entry, it stays for the specified
* duration on the exact same value (with respect to brightness adjustment) until we transition
* to the next entry (see {@link transitionTime}).
*/
duration?: number;
/**
* The time in milliseconds the color temperature should transition from the previous
* entry to this one.
* For example if we got the two values A and B, with A.temperature = 300 and B.temperature = 400 and
* for the current time we are at temperature value 300. Then we need to transition smoothly
* within the B.transitionTime to the B.temperature value.
* If this is the first entry in the Curve (this value is probably zero) and is the offset to the transitionStartMillis
* (the Date/Time were this transition curve was set up).
*/
transitionTime: number;
}
/**
* @group Adaptive Lighting
*/
export interface BrightnessAdjustmentMultiplierRange {
minBrightnessValue: number;
maxBrightnessValue: number;
}
/**
* @group Adaptive Lighting
*/
export interface AdaptiveLightingOptions {
/**
* Defines how the controller will operate.
* You can choose between automatic and manual mode.
* See {@link AdaptiveLightingControllerMode}.
*/
controllerMode?: AdaptiveLightingControllerMode;
/**
* Defines a custom temperature adjustment factor.
*
* This can be used to define a linear deviation from the HomeKit Controller defined
* ColorTemperature schedule.
*
* For example supplying a value of `-10` will reduce the ColorTemperature, which is
* calculated from the transition schedule, by 10 mired for every change.
*/
customTemperatureAdjustment?: number;
}
/**
* Defines in which mode the {@link AdaptiveLightingController} will operate in.
* @group Adaptive Lighting
*/
export declare const enum AdaptiveLightingControllerMode {
/**
* In automatic mode pretty much everything from setup to transition scheduling is done by the controller.
*/
AUTOMATIC = 1,
/**
* In manual mode setup is done by the controller but the actual transition must be done by the user.
* This is useful for lights which natively support transitions.
*/
MANUAL = 2
}
/**
* @group Adaptive Lighting
*/
export declare const enum AdaptiveLightingControllerEvents {
/**
* This event is called once a HomeKit controller enables Adaptive Lighting
* or a HomeHub sends an updated transition schedule for the next 24 hours.
* This is also called on startup when AdaptiveLighting was previously enabled.
*/
UPDATE = "update",
/**
* In yet unknown circumstances HomeKit may also send a dedicated disable command
* via the control point characteristic. You may want to handle that in manual mode as well.
* The current transition will still be associated with the controller object when this event is called.
*/
DISABLED = "disable"
}
/**
* @group Adaptive Lighting
* see {@link ActiveAdaptiveLightingTransition}.
*/
export interface AdaptiveLightingControllerUpdate {
transitionStartMillis: number;
timeMillisOffset: number;
transitionCurve: AdaptiveLightingTransitionCurveEntry[];
brightnessAdjustmentRange: BrightnessAdjustmentMultiplierRange;
updateInterval: number;
notifyIntervalThreshold: number;
}
/**
* @group Adaptive Lighting
*/
export declare interface AdaptiveLightingController {
/**
* See {@link AdaptiveLightingControllerEvents.UPDATE}
* Also see {@link AdaptiveLightingControllerUpdate}
*
* @param event
* @param listener
*/
on(event: "update", listener: (update: AdaptiveLightingControllerUpdate) => void): this;
/**
* See {@link AdaptiveLightingControllerEvents.DISABLED}
*
* @param event
* @param listener
*/
on(event: "disable", listener: () => void): this;
/**
* See {@link AdaptiveLightingControllerUpdate}
*/
emit(event: "update", update: AdaptiveLightingControllerUpdate): boolean;
emit(event: "disable"): boolean;
}
/**
* @group Adaptive Lighting
*/
export interface SerializedAdaptiveLightingControllerState {
activeTransition: ActiveAdaptiveLightingTransition;
}
/**
* This class allows adding Adaptive Lighting support to Lightbulb services.
* The Lightbulb service MUST have the {@link Characteristic.ColorTemperature} characteristic AND
* the {@link Characteristic.Brightness} characteristic added.
* The light may also expose {@link Characteristic.Hue} and {@link Characteristic.Saturation} characteristics
* (though additional work is required to keep them in sync with the color temperature characteristic. see below)
*
* How Adaptive Lighting works:
* When enabling AdaptiveLighting the iDevice will send a transition schedule for the next 24 hours.
* This schedule will be renewed all 24 hours by a HomeHub in your home
* (updating the schedule according to your current day/night situation).
* Once enabled the lightbulb will execute the provided transitions. The color temperature value set is always
* dependent on the current brightness value. Meaning brighter light will be colder and darker light will be warmer.
* HomeKit considers Adaptive Lighting to be disabled as soon a write happens to either the
* Hue/Saturation or the ColorTemperature characteristics.
* The AdaptiveLighting state must persist across reboots.
*
* The AdaptiveLightingController can be operated in two modes: {@link AdaptiveLightingControllerMode.AUTOMATIC} and
* {@link AdaptiveLightingControllerMode.MANUAL} with AUTOMATIC being the default.
* The goal would be that the color transition is done DIRECTLY on the light itself, thus not creating any
* additional/heavy traffic on the network.
* So if your light hardware/API supports transitions please go the extra mile and use MANUAL mode.
*
*
*
* Below is an overview what you need to or consider when enabling AdaptiveLighting (categorized by mode).
* The {@link AdaptiveLightingControllerMode} can be defined with the second constructor argument.
*
* AUTOMATIC (Default mode):
*
* This is the easiest mode to setup and needs less to no work form your side for AdaptiveLighting to work.
* The AdaptiveLightingController will go through setup procedure with HomeKit and automatically update
* the color temperature characteristic base on the current transition schedule.
* It is also adjusting the color temperature when a write to the brightness characteristic happens.
* Additionally, it will also handle turning off AdaptiveLighting, when it detects a write happening to the
* ColorTemperature, Hue or Saturation characteristic (though it can only detect writes coming from HomeKit and
* can't detect changes done to the physical devices directly! See below).
*
* So what do you need to consider in automatic mode:
* - Brightness and ColorTemperature characteristics MUST be set up. Hue and Saturation may be added for color support.
* - Color temperature will be updated all 60 seconds by calling the SET handler of the ColorTemperature characteristic.
* So every transition behaves like a regular write to the ColorTemperature characteristic.
* - Every transition step is dependent on the current brightness value. Try to keep the internal cache updated
* as the controller won't call the GET handler every 60 seconds.
* (The cached brightness value is updated on SET/GET operations or by manually calling {@link Characteristic.updateValue}
* on the brightness characteristic).
* - Detecting changes on the lightbulb side:
* Any manual change to ColorTemperature or Hue/Saturation is considered as a signal to turn AdaptiveLighting off.
* In order to notify the AdaptiveLightingController of such an event happening OUTSIDE of HomeKit
* you must call {@link disableAdaptiveLighting} manually!
* - Be aware that even when the light is turned off the transition will continue to call the SET handler
* of the ColorTemperature characteristic.
* - When using Hue/Saturation:
* When using Hue/Saturation in combination with the ColorTemperature characteristic you need to update the
* respective other in a particular way depending on if being in "color mode" or "color temperature mode".
* When a write happens to Hue/Saturation characteristic in is advised to set the internal value of the
* ColorTemperature to the minimal (NOT RAISING an event).
* When a write happens to the ColorTemperature characteristic just MUST convert to a proper representation
* in hue and saturation values, with RAISING an event.
* As noted above you MUST NOT call the {@link Characteristic.setValue} method for this, as this will be considered
* a write to the characteristic and will turn off AdaptiveLighting. Instead, you should use
* {@link Characteristic.updateValue} for this.
* You can and SHOULD use the supplied utility method {@link ColorUtils.colorTemperatureToHueAndSaturation}
* for converting mired to hue and saturation values.
*
*
* MANUAL mode:
*
* Manual mode is recommended for any accessories which support transitions natively on the devices end.
* Like for example ZigBee lights which support sending transitions directly to the lightbulb which
* then get executed ON the lightbulb itself reducing unnecessary network traffic.
* Here is a quick overview what you have to consider to successfully implement AdaptiveLighting support.
* The AdaptiveLightingController will also in manual mode do all the setup procedure.
* It will also save the transition schedule to disk to keep AdaptiveLighting enabled across reboots.
* The "only" thing you have to do yourself is handling the actual transitions, check that event notifications
* are only sent in the defined interval threshold, adjust the color temperature when brightness is changed
* and signal that Adaptive Lighting should be disabled if ColorTemperature, Hue or Saturation is changed manually.
*
* First step is to setup up an event handler for the {@link AdaptiveLightingControllerEvents.UPDATE}, which is called
* when AdaptiveLighting is enabled, the HomeHub updates the schedule for the next 24 hours or AdaptiveLighting
* is restored from disk on startup.
* In the event handler you can get the current schedule via {@link AdaptiveLightingController.getAdaptiveLightingTransitionCurve},
* retrieve current intervals like {@link AdaptiveLightingController.getAdaptiveLightingUpdateInterval} or
* {@link AdaptiveLightingController.getAdaptiveLightingNotifyIntervalThreshold} and get the date in epoch millis
* when the current transition curve started using {@link AdaptiveLightingController.getAdaptiveLightingStartTimeOfTransition}.
* Additionally {@link AdaptiveLightingController.getAdaptiveLightingBrightnessMultiplierRange} can be used
* to get the valid range for the brightness value to calculate the brightness adjustment factor.
* The method {@link AdaptiveLightingController.isAdaptiveLightingActive} can be used to check if AdaptiveLighting is enabled.
* Besides, actually running the transition (see {@link AdaptiveLightingTransitionCurveEntry}) you must correctly update
* the color temperature when the brightness of the lightbulb changes (see {@link AdaptiveLightingTransitionCurveEntry.brightnessAdjustmentFactor}),
* and signal when AdaptiveLighting got disabled by calling {@link AdaptiveLightingController.disableAdaptiveLighting}
* when ColorTemperature, Hue or Saturation where changed manually.
* Lastly you should set up a event handler for the {@link AdaptiveLightingControllerEvents.DISABLED} event.
* In yet unknown circumstances HomeKit may also send a dedicated disable command via the control point characteristic.
* Be prepared to handle that.
*
* @group Adaptive Lighting
*/
export declare class AdaptiveLightingController extends EventEmitter implements SerializableController {
private stateChangeDelegate?;
private readonly lightbulb;
private readonly mode;
private readonly customTemperatureAdjustment;
private readonly adjustmentFactorChangedListener;
private readonly characteristicManualWrittenChangeListener;
private supportedTransitionConfiguration?;
private transitionControl?;
private activeTransitionCount?;
private colorTemperatureCharacteristic?;
private brightnessCharacteristic?;
private saturationCharacteristic?;
private hueCharacteristic?;
private activeTransition?;
private didRunFirstInitializationStep;
private updateTimeout?;
private lastTransitionPointInfo?;
private lastEventNotificationSent;
private lastNotifiedTemperatureValue;
private lastNotifiedSaturationValue;
private lastNotifiedHueValue;
/**
* Creates a new instance of the AdaptiveLightingController.
* Refer to the {@link AdaptiveLightingController} documentation on how to use it.
*
* @param service - The lightbulb to which Adaptive Lighting support should be added.
* @param options - Optional options to define the operating mode (automatic vs manual).
*/
constructor(service: Lightbulb, options?: AdaptiveLightingOptions);
/**
* @private
*/
controllerId(): ControllerIdentifier;
/**
* Returns if a Adaptive Lighting transition is currently active.
*/
isAdaptiveLightingActive(): boolean;
/**
* This method can be called to manually disable the current active Adaptive Lighting transition.
* When using {@link AdaptiveLightingControllerMode.AUTOMATIC} you won't need to call this method.
* In {@link AdaptiveLightingControllerMode.MANUAL} you must call this method when Adaptive Lighting should be disabled.
* This is the case when the user manually changes the value of Hue, Saturation or ColorTemperature characteristics
* (or if any of those values is changed by physical interaction with the lightbulb).
*/
disableAdaptiveLighting(): void;
/**
* Returns the time where the current transition curve was started in epoch time millis.
* A transition curves is active for 24 hours typically and is renewed every 24 hours by a HomeHub.
* Additionally see {@link getAdaptiveLightingTimeOffset}.
*/
getAdaptiveLightingStartTimeOfTransition(): number;
/**
* It is not necessarily given, that we have the same time (or rather the correct time) as the HomeKit controller
* who set up the transition schedule.
* Thus we record the delta between our current time and the the time send with the setup request.
* timeOffset
is defined as Date.now() - getAdaptiveLightingStartTimeOfTransition();
.
* So in the case were we actually have a correct local time, it most likely will be positive (due to network latency).
* But of course it can also be negative.
*/
getAdaptiveLightingTimeOffset(): number;
getAdaptiveLightingTransitionCurve(): AdaptiveLightingTransitionCurveEntry[];
getAdaptiveLightingBrightnessMultiplierRange(): BrightnessAdjustmentMultiplierRange;
/**
* This method returns the interval (in milliseconds) in which the light should update its internal color temperature
* (aka changes it physical color).
* A lightbulb should ideally change this also when turned of in oder to have a smooth transition when turning the light on.
*
* Typically this evaluates to 60000 milliseconds (60 seconds).
*/
getAdaptiveLightingUpdateInterval(): number;
/**
* Returns the minimum interval threshold (in milliseconds) a accessory may notify HomeKit controllers about a new
* color temperature value via event notifications (what happens when you call {@link Characteristic.updateValue}).
* Meaning the accessory should only send event notifications to subscribed HomeKit controllers at the specified interval.
*
* Typically this evaluates to 600000 milliseconds (10 minutes).
*/
getAdaptiveLightingNotifyIntervalThreshold(): number;
private handleActiveTransitionUpdated;
private handleAdaptiveLightingEnabled;
private handleAdaptiveLightingDisabled;
private handleAdjustmentFactorChanged;
/**
* This method is called when a change happens to the Hue/Saturation or ColorTemperature characteristic.
* When such a write happens (caused by the user changing the color/temperature) Adaptive Lighting must be disabled.
*
* @param change
*/
private handleCharacteristicManualWritten;
/**
* Retrieve the {@link AdaptiveLightingTransitionPoint} for the current timestamp.
* Returns undefined if the current transition schedule reached its end.
*/
getCurrentAdaptiveLightingTransitionPoint(): AdaptiveLightingTransitionPoint | undefined;
private scheduleNextUpdate;
/**
* @private
*/
constructServices(): ControllerServiceMap;
/**
* @private
*/
initWithServices(serviceMap: ControllerServiceMap): void | ControllerServiceMap;
/**
* @private
*/
configureServices(): void;
/**
* @private
*/
handleControllerRemoved(): void;
/**
* @private
*/
handleFactoryReset(): void;
/**
* @private
*/
serialize(): SerializedAdaptiveLightingControllerState | undefined;
/**
* @private
*/
deserialize(serialized: SerializedAdaptiveLightingControllerState): void;
/**
* @private
*/
setupStateChangeDelegate(delegate?: StateChangeDelegate): void;
private handleSupportedTransitionConfigurationRead;
private buildTransitionControlResponseBuffer;
private handleTransitionControlWrite;
private handleTransitionControlReadTransition;
private handleTransitionControlUpdateTransition;
}
//# sourceMappingURL=AdaptiveLightingController.d.ts.map