1
0
This repository has been archived on 2025-01-10. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
openhab-js-automation-old/utils/watch.js

241 lines
10 KiB
JavaScript

console.loggerName = 'js.watch';
console.log('Load watch module');
class Watch {
#item
#watchItemName
#watchObjects = new Object();
constructor(itemName) {
console.log(`Create new Watch instance for ${itemName}`);
// Check if item to watch is existing
this.#item = items.getItem(itemName, true);
if (this.#item == null) {
throw(`Item ${itemName} not existing`);
}
this.#watchItemName = itemName;
// Create watch rule
this.#createWatchRule();
}
add(params) {
// Generate watchUUID
let watchUUID = utils.randomUUID();
// Validate config for watchObject
if (!this.validateWatchConfig(params)) {
console.warn(`Failed to add watch object for ${this.#watchItemName} because no valid config was provided`);
return;
}
// Set default values if no config provided
let operator = (params['operator'] !== undefined) ? params['operator'] : '==';
// Create watch object and return UUID
console.log(`Add watch object for item ${this.#watchItemName} with state ${params['targetState']} and operator ${operator} with UUID ${watchUUID}`);
this.#watchObjects[watchUUID] = {
targetState: params['targetState'],
operator: operator,
alertFunc: params['alertFunc'],
alertDelay: (params['alertDelay'] !== undefined) ? params['alertDelay'] : 0,
alertRepeat: (params['alertRepeat'] !== undefined) ? params['alertRepeat'] : 0,
initialAlertFunc: (params['initialAlertFunc'] !== undefined) ? params['initialAlertFunc'] : '',
endAlertFunc: (params['endAlertFunc'] !== undefined) ? params['endAlertFunc'] : '',
hysteresis: (params['hysteresis'] !== undefined) ? lib.convertValue(params['hysteresis']) : '',
alert: false
}
// Check if watchObject is already triggered by currentState
this.#checkAlertState(watchUUID);
// return id of watchObject
return watchUUID;
}
delete(watchUUID) {
console.log(`Delete watch object for item ${this.#watchItemName} with watchUUID ${watchUUID}`);
// End repeatAlertTimer if existing
if (this.#watchObjects[watchUUID].hasOwnProperty('repeatAlertTimer') && this.#watchObjects[watchUUID].repeatAlertTimer.isActive()) {
console.log('Cancel repeatAlertTimer for ' + watchUUID);
this.#watchObjects[watchUUID].repeatAlertTimer.cancel();
}
// End startAlertTimer if existing
if (this.#watchObjects[watchUUID].hasOwnProperty('startAlertTimer') && this.#watchObjects[watchUUID].startAlertTimer.isActive()) {
console.log('Cancel startAlertTimer for ' + watchUUID);
this.#watchObjects[watchUUID].startAlertTimer.cancel();
}
delete this.#watchObjects[watchUUID];
}
deleteAll() {
for (let watchUUID of Object.keys(this.#watchObjects)) {
this.delete(watchUUID);
}
}
validateWatchConfig(params) {
if (params['targetState'] === undefined) {
console.error('No targetState set');
return false;
}
if (params['alertFunc'] === undefined) {
console.error('No alertFunc set');
return false;
}
// return true if config is valid
return true
}
#applyHysteresis(currentState, targetState, hysteresis) {
console.log(`Applying hysteresis with: ${currentState}, ${targetState}, ${hysteresis}`);
let delta = Math.abs(targetState - currentState);
console.log(delta);
if (delta < hysteresis) {
return false;
}
return true;
}
#checkAlertState(watchUUID) {
console.debug(`Check if item is in alert state for watchObject ${watchUUID}`)
// Convert currentState for comparison
let currentState = lib.convertValue(this.#item.state);
// Do comparison
if (lib.compare(currentState, this.#watchObjects[watchUUID].targetState, this.#watchObjects[watchUUID].operator)) { // Comparison successful
console.log(`State ${currentState} is ${this.#watchObjects[watchUUID].operator} ${this.#watchObjects[watchUUID].targetState} triggered by ${watchUUID}`);
if (this.#watchObjects[watchUUID].alert == true) { // Comparison successful and alert is already active
this.#rescheduleAlert(watchUUID);
} else { // Comparison successful and alert is not active
// Start alert
this.#startAlert(watchUUID);
}
} else if (this.#watchObjects[watchUUID].alert == true) { // Comparison failed but alert is active
// Skip if currentState fails hysteresis test
if (this.#watchObjects[watchUUID].hysteresis != '' && !this.#applyHysteresis(currentState, this.#watchObjects[watchUUID].targetState, this.#watchObjects[watchUUID].hysteresis)) {
console.log('CurrentState is still in boundaries provided by hysteresis value');
return;
}
// End alert
this.#endAlert(watchUUID);
}
}
#createWatchRule() {
// Create openHAB rule
console.log(`Create openHAB watch rule for item ${this.#watchItemName}`);
let ruleID = rules.JSRule({
name: 'Watch rule for ' + this.#watchItemName,
triggers: [triggers.ItemStateUpdateTrigger(this.#watchItemName)],
execute: (event) => { this.#processItemEvent(event) },
});
}
#endAlert(watchUUID) {
console.log(`End alert for watchObject ${watchUUID} triggered`);
this.#watchObjects[watchUUID].alert = false;
// Run end alert function if existing
if (this.#watchObjects[watchUUID].endAlertFunc != '') {
console.log(`Run end alert function for watchObject ${watchUUID}`);
this.#watchObjects[watchUUID].endAlertFunc();
}
// End repeatAlertTimer if existing
if (this.#watchObjects[watchUUID].hasOwnProperty('repeatAlertTimer') && this.#watchObjects[watchUUID].repeatAlertTimer.isActive()) {
console.log('Cancel repeatAlertTimer');
this.#watchObjects[watchUUID].repeatAlertTimer.cancel();
}
// End startAlertTimer if startAlert started timer with delay
if (this.#watchObjects[watchUUID].hasOwnProperty('startAlertTimer') && this.#watchObjects[watchUUID].startAlertTimer.isActive()) {
console.log('Cancel startAlertTimer');
this.#watchObjects[watchUUID].startAlertTimer.cancel();
}
}
#processItemEvent(event) {
// Skip if function is triggered without openHAB event
if (event === undefined || event.eventType === undefined) {
console.warn(`ProcessItemEvent for ${this.#watchItemName} triggered without openHAB event`);
return;
}
console.log(`Processing state ${this.#item.state} for ${this.#watchItemName}`);
// Iterate through watchObjetcs // todo: rework to only fetch UUID
for (let [watchUUID, watchObject] of Object.entries(this.#watchObjects)) {
this.#checkAlertState(watchUUID);
}
}
#rescheduleAlert(watchUUID) {
console.log(`Subsequent alert for ${watchUUID}`);
}
#startAlert(watchUUID) {
console.log(`Initial alert for watchObject ${watchUUID} triggered`);
// Set alertFunc as inital alert function if no initialAlertFunc is set
let initialAlertFunc = this.#watchObjects[watchUUID].alertFunc;
if (this.#watchObjects[watchUUID].initialAlertFunc != '') {
initialAlertFunc = this.#watchObjects[watchUUID].initialAlertFunc;
}
let alertDelay = this.#watchObjects[watchUUID].alertDelay;
let alertRepeat = this.#watchObjects[watchUUID].alertRepeat;
let alertDelayAbsolute = 0;
// Set alert state
this.#watchObjects[watchUUID].alert = true;
// Execute initial alert function or create timer to run initial alert rule if required
if (alertDelay == '') {
console.log(`Run initial alert function for watchObject ${watchUUID}`);
initialAlertFunc();
} else {
console.log(`Shedule initial alert function for watchObject ${watchUUID} with delay setting ${alertDelay}`);
let alertDelayZDT = time.toZDT(alertDelay);
alertDelayAbsolute = (alertDelayZDT.getMillisFromNow() / 1000);
this.#watchObjects[watchUUID].startAlertTimer = actions.ScriptExecution.createTimer('startAlert ' + watchUUID,
alertDelayZDT,
() => {
console.log('Run initial alert function for watchObject ' + watchUUID);
initialAlertFunc();
}
);
}
// Create timer to run repeat alert rule if required
if (alertRepeat != '') {
console.log(`Shedule repeat alert function for watchObject ${watchUUID} with repeat setting ${alertRepeat}`);
this.#watchObjects[watchUUID].repeatAlertTimer = actions.ScriptExecution.createTimer('repeatAlarm ' + watchUUID,
time.toZDT(alertRepeat).plusSeconds(alertDelayAbsolute),
() => {
console.log('Run repeat alert function for watchObject ' + watchUUID);
this.#watchObjects[watchUUID].alertFunc();
this.#watchObjects[watchUUID].repeatAlertTimer.reschedule(time.toZDT(alertRepeat));
}
);
}
}
}
module.exports = {
Watch,
};