mirror of
https://github.com/dtwardow/homebridge-multi-tap-switch.git
synced 2026-11-01 20:18:30 +00:00
Introduce new switch-off trigger and debounce timer.
This commit is contained in:
1 parent
3ef3435417
commit
b5ddbf488f
2 files changed
+83
-15
No files matched your search
@@ -0,0 +1,45 @@
|
|||||||
|
import { Logging } from './logger';
|
||||||
|
import { clearTimeout } from 'node:timers';
|
||||||
|
|
||||||
|
export default class TimeoutHandler {
|
||||||
|
private timeoutObject: NodeJS.Timeout | undefined;
|
||||||
|
public timeoutMs: number = 0;
|
||||||
|
private log: Logging | undefined;
|
||||||
|
|
||||||
|
private isRunning: boolean = false;
|
||||||
|
|
||||||
|
constructor(timeoutMs: number, logger: Logging) {
|
||||||
|
this.log = logger;
|
||||||
|
this.timeoutMs = timeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public trigger(): void {
|
||||||
|
if (!this.isRunning) {
|
||||||
|
this.timeoutStarted();
|
||||||
|
this.timeoutObject = setTimeout(this.timeoutTriggered.bind(this), this.timeoutMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel(): void {
|
||||||
|
if (!this.isRunning) {
|
||||||
|
clearTimeout(this.timeoutObject);
|
||||||
|
this.isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public elapsedAndTrigger(): boolean {
|
||||||
|
if (!this.isRunning) {
|
||||||
|
this.trigger();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private timeoutStarted(): void {
|
||||||
|
this.isRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async timeoutTriggered(): Promise<void> {
|
||||||
|
this.isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
-15
@@ -6,6 +6,7 @@ import { VERSION } from './settings';
|
|||||||
import { attachCharacteristic_ConfiguredScenes } from './types/characteristicConfiguredScenes';
|
import { attachCharacteristic_ConfiguredScenes } from './types/characteristicConfiguredScenes';
|
||||||
import { attachCharacteristic_TriggerTimeout } from './types/characteristicTimeout';
|
import { attachCharacteristic_TriggerTimeout } from './types/characteristicTimeout';
|
||||||
import { attachCharacteristic_CurrentScene } from './types/characteristicCurrentScene';
|
import { attachCharacteristic_CurrentScene } from './types/characteristicCurrentScene';
|
||||||
|
import TimeoutHandler from './helper/timeoutHandler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Platform Accessory
|
* Platform Accessory
|
||||||
@@ -23,6 +24,7 @@ export class DeviceAccessory {
|
|||||||
private SwitchResetTimer: NodeJS.Timeout | undefined;
|
private SwitchResetTimer: NodeJS.Timeout | undefined;
|
||||||
private SwitchOn: boolean = false;
|
private SwitchOn: boolean = false;
|
||||||
private NextItemToTrigger: number = 0;
|
private NextItemToTrigger: number = 0;
|
||||||
|
private LastTriggerReceived: TimeoutHandler;
|
||||||
|
|
||||||
private Log: Logging;
|
private Log: Logging;
|
||||||
|
|
||||||
@@ -51,6 +53,8 @@ export class DeviceAccessory {
|
|||||||
this.Log = new Logging(platform.log, this.Config.Name(), this.Config.isLogging());
|
this.Log = new Logging(platform.log, this.Config.Name(), this.Config.isLogging());
|
||||||
this.Log.log('Initialize Accessory ->', this.Config.Name());
|
this.Log.log('Initialize Accessory ->', this.Config.Name());
|
||||||
|
|
||||||
|
this.LastTriggerReceived = new TimeoutHandler(this.Config.DebounceTimeoutMs(), this.Log);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accessory information
|
* Accessory information
|
||||||
*/
|
*/
|
||||||
@@ -106,13 +110,13 @@ export class DeviceAccessory {
|
|||||||
// Register handlers for the CurrentScene characteristic & restore previous value
|
// Register handlers for the CurrentScene characteristic & restore previous value
|
||||||
attachCharacteristic_CurrentScene(this.serviceIn, this.api)
|
attachCharacteristic_CurrentScene(this.serviceIn, this.api)
|
||||||
.setProps({
|
.setProps({
|
||||||
'minValue': 1,
|
'minValue': 0,
|
||||||
'maxValue': this.Config.NumOfScenes(),
|
'maxValue': this.Config.NumOfScenes(),
|
||||||
'minStep': 1,
|
'minStep': 1,
|
||||||
})
|
})
|
||||||
.onSet(this.setCurrentScene.bind(this))
|
.onSet(this.setCurrentScene.bind(this))
|
||||||
.onGet(this.getCurrentScene.bind(this))
|
.onGet(this.getCurrentScene.bind(this))
|
||||||
.updateValue(this.NextItemToTrigger);
|
.updateValue(this.NextItemToTrigger = this.Config.FirstTriggerScene());
|
||||||
|
|
||||||
// Set the service name, this is what is displayed as the default name on the Home app
|
// Set the service name, this is what is displayed as the default name on the Home app
|
||||||
this.serviceIn.setCharacteristic(this.platform.Characteristic.Name, this.Config.Name());
|
this.serviceIn.setCharacteristic(this.platform.Characteristic.Name, this.Config.Name());
|
||||||
@@ -165,14 +169,13 @@ export class DeviceAccessory {
|
|||||||
/**
|
/**
|
||||||
* Trigger/Fire the next programmable switch event
|
* Trigger/Fire the next programmable switch event
|
||||||
*/
|
*/
|
||||||
triggerNext() {
|
trigger(index: number) {
|
||||||
if (this.NextItemToTrigger >= this.State.numberConfiguredScenes) {
|
if ((index >= 0) && (index < this.State.numberConfiguredScenes)) {
|
||||||
this.NextItemToTrigger = 0;
|
this.Log.log('Trigger Event -> ', index);
|
||||||
|
this.TriggerButtons[index].updateCharacteristic(this.platform.Characteristic.ProgrammableSwitchEvent, 0);
|
||||||
|
} else {
|
||||||
|
this.Log.error('Trigger Event -> Index ', index, ' out of bounds (min = 0, max = ', this.State.numberConfiguredScenes - 1, ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger Scene Button
|
|
||||||
this.Log.log('Trigger Event -> ', this.NextItemToTrigger);
|
|
||||||
this.TriggerButtons[this.NextItemToTrigger++].updateCharacteristic(this.platform.Characteristic.ProgrammableSwitchEvent, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,19 +184,25 @@ export class DeviceAccessory {
|
|||||||
resetTrigger() {
|
resetTrigger() {
|
||||||
this.Log.log('Reset Scene Trigger');
|
this.Log.log('Reset Scene Trigger');
|
||||||
this.serviceIn.updateCharacteristic(this.platform.Characteristic.On, this.SwitchOn = false);
|
this.serviceIn.updateCharacteristic(this.platform.Characteristic.On, this.SwitchOn = false);
|
||||||
this.NextItemToTrigger = 0;
|
this.NextItemToTrigger = this.Config.FirstTriggerScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =====================
|
||||||
|
// = Handler Functions =
|
||||||
|
// =====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle state changes of the input switch
|
* Handle state changes of the input switch
|
||||||
* This function does all the necessary work.
|
* This function does all the necessary work.
|
||||||
* @param value Target switch state from Automation or manual state change
|
* @param value Target switch state from Automation or manual state change
|
||||||
*/
|
*/
|
||||||
async setOn(value: CharacteristicValue) {
|
async setOn(value: CharacteristicValue) {
|
||||||
// implement your own code to turn your device on/off
|
// Check if debounce timer has elapsed. If not, ignore the command.
|
||||||
|
if (this.LastTriggerReceived.elapsedAndTrigger()) {
|
||||||
this.SwitchOn = value as boolean;
|
this.SwitchOn = value as boolean;
|
||||||
|
|
||||||
if (this.SwitchOn) {
|
if (this.SwitchOn) { // Switch is set to 'ON'
|
||||||
|
// Disable an active reset timer, before starting a new one.
|
||||||
if (this.SwitchResetTimer) {
|
if (this.SwitchResetTimer) {
|
||||||
clearTimeout(this.SwitchResetTimer);
|
clearTimeout(this.SwitchResetTimer);
|
||||||
}
|
}
|
||||||
@@ -204,17 +213,31 @@ export class DeviceAccessory {
|
|||||||
this.SwitchResetTimer = setTimeout(this.resetTrigger.bind(this), this.State.triggerTimeout * 1000);
|
this.SwitchResetTimer = setTimeout(this.resetTrigger.bind(this), this.State.triggerTimeout * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger Scene
|
// Determine next item to trigger
|
||||||
this.triggerNext();
|
if (this.NextItemToTrigger >= this.State.numberConfiguredScenes) {
|
||||||
} else if (this.Config.isResetWhenOff() && !this.SwitchOn) {
|
this.NextItemToTrigger = this.Config.FirstTriggerScene();
|
||||||
|
}
|
||||||
|
// Trigger Switch
|
||||||
|
this.trigger(this.NextItemToTrigger++);
|
||||||
|
} else { // Switch is set to 'OFF'
|
||||||
|
// If configured: Disable any active reset timer, when the switch is manually turned off
|
||||||
|
if (this.Config.isResetWhenOff()) {
|
||||||
if (this.SwitchResetTimer) {
|
if (this.SwitchResetTimer) {
|
||||||
clearTimeout(this.SwitchResetTimer);
|
clearTimeout(this.SwitchResetTimer);
|
||||||
}
|
}
|
||||||
this.resetTrigger();
|
this.resetTrigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If configured: Trigger stateless switch / scene when manually switched off
|
||||||
|
if (this.Config.isTriggerSceneWhenOff()) {
|
||||||
|
// Trigger scene 0
|
||||||
|
this.trigger(this.Config.TriggerOffSceneIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.Log.debug('Set Switch State ->', value);
|
this.Log.debug('Set Switch State ->', value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report state of the input switch back to HomeKit (for visualization)
|
* Report state of the input switch back to HomeKit (for visualization)
|
||||||
|
|||||||
Reference in new issue
Block a user