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/equipmentMgr.js

456 lines
15 KiB
JavaScript

const log = Java.type('org.slf4j.LoggerFactory').getLogger('js.equipmentMgr');
log.info('Load equipmentMgr');
const {
lib,
timer,
watch
} = require('../utils');
class Equipment {
constructor(equipmentItem) {
// Fetch item if provided equipmentItem is a string
if (typeof equipmentItem === 'string' || equipmentItem instanceof String) {
this.equipmentItem = items[equipmentItem];
} else {
this.equipmentItem = equipmentItem;
}
// Throw error if item is not existing
if (this.equipmentItem === null) {
throw(`Item ${equipmentItem} not existing`);
}
// Throw error if item is not an equipment type
if (this.equipmentItem.semantics.semanticType != 'Equipment') {
throw(`Item ${this.equipmentItem.name} is not an equipment type`);
}
log.info(`Initialization of equipment ${this.equipmentItem.name} with type ${this.constructor.name}`);
// Initialization of properties
this.name = this.equipmentItem.name;
this.conf = new Object();
this.savedControlPoints = new Object();
this.timers = new timer.Timer();
let metadata = equipmentItem.getMetadata('eMgr');
if (metadata != null) {
for (const [key, value] of Object.entries(metadata.configuration)) {
log.debug(`Set configuration ${key} for ${this.name} with value ${value.toString()}`);
this.conf[key] = value;
}
}
// Check if equipment has LowBat item
if (this.hasProperty('LowBat')) {
watches.add({
item: this.getPropertyItemName('LowBat'),
targetState: 'ON',
alertFunc: () => { this.#notifyLowBat(); },
alertRepeat: 'PT23H'
});
}
// Check if equipment has Unreach item
if (this.hasProperty('Unreach')) {
watches.add({
item: this.getPropertyItemName('Unreach'),
targetState: 'ON',
alertFunc: () => { this.#notifyUnreach(); },
alertDelay: 'PT15M',
alertRepeat: 'PT1H'
});
}
}
getValue(propertyName, defaultValue = '') {
let valueItemName = this.name + '_' + propertyName;
let returnValue = defaultValue;
if (items[valueItemName] == null) { // Return default value if item is missing
log.warn('Item ' + valueItemName + ' is missing');
} else if (items[valueItemName]['state'] == 'NULL') { // Return default value if item state is null
log.warn('Item ' + valueItemName + ' is unset')
} else { // Return value from item
if (items[valueItemName].quantityState == null && items[valueItemName].numericState == null) {
returnValue = items[valueItemName]['state'];
} else if (items[valueItemName].quantityState == null) {
returnValue = items[valueItemName]['numericState']
} else {
returnValue = items[valueItemName].quantityState;
}
}
log.debug(`Return value for property ${propertyName} for ${this.name}: ${returnValue}`);
return returnValue;
}
getPropertyItemName(propertyName) {
if (this.hasProperty(propertyName)) {
return items[this.name + '_' + propertyName].name;
}
}
hasProperty(propertyName) {
if (items[this.name + '_' + propertyName] == null) {
log.debug(`Equipment ${this.name} has no property ${propertyName}`)
return false;
}
return true;
}
get controlPoints() {
log.debug('Fetch control points for ' + this.name);
return items[this.name].descendents.filter(element => { return (element.semantics.pointType == 'Control') });
}
saveControlPoints() {
for (let controlPoint of this.controlPoints) {
log.debug('Save ' + controlPoint.name + ' with state ' + controlPoint.state);
}
}
#notifyLowBat() {
log.warn(`${this.name} has a low battery level.`);
}
#notifyUnreach() {
log.warn(`${this.name} is offline.`);
}
gc() {
log.info('Denitialization of equipment ' + this.name);
// Cancel all timers
this.timers.cancelAll();
}
}
class Awning extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
}
}
class Car extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
}
}
class CCTV extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
// Watch
watches.add({
item: 'AP_ResidentsAreHome',
targetState: 'ON',
alertFunc: () => { this.setHomeMode('ON'); }
});
watches.add({
item: 'AP_ResidentsAreHome',
targetState: 'OFF',
alertFunc: () => { this.setHomeMode('OFF'); },
alertDelay: 'PT1M'
});
}
setHomeMode(mode) {
switch (mode) {
case 'ON':
items[this.getPropertyItemName('HomeMode')].sendCommandIfDifferent('ON');
break;
case 'OFF':
items[this.getPropertyItemName('HomeMode')].sendCommandIfDifferent('OFF');
break;
default:
log.info(mode + ' is not a valid mode for HomeMode');
}
}
}
class EnergyMeter extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
if (this.hasProperty('ConsumptionTotal')) {
log.info(`Create rule to trigger calculateConsumption function`);
this.consumptionTotalItem = items[this.getPropertyItemName('ConsumptionTotal')];
rules.JSRule({
id: String(utils.randomUUID()),
name: 'Trigger calculateConsumption function for ' + this.equipmentItem.name,
triggers: [triggers.ItemStateChangeTrigger(this.consumptionTotalItem)],
execute: (event) => { this.#calculateConsumptionValues(event) },
});
} else {
log.info(`Item ${this.equipmentItem.name} has no consumptionTotal item`)
}
}
#calculateConsumptionValues() {
log.debug('Calculate EnergyMeter consumption values');
let startOfHour = time.toZDT().withMinute(0).withSecond(0).withNano(0);
let startOfDay = startOfHour.withHour(0);
let startOfWeek = startOfDay.minusDays(time.toZDT().dayOfWeek().value() - 1);
let startOfMonth = startOfDay.withDayOfMonth(1);
let startOfYear = startOfDay.withMonth(1).withDayOfMonth(1);
let consumptionTotal = Quantity(this.consumptionTotalItem.state);
if (this.hasProperty('ConsumptionHour')) {
let consumptionHour = consumptionTotal.subtract((this.consumptionTotalItem.history.minimumSince(startOfHour).state));
items[this.getPropertyItemName('ConsumptionHour')].postUpdate(consumptionHour);
}
if (this.hasProperty('ConsumptionToday')) {
let consumptionToday = consumptionTotal.subtract((this.consumptionTotalItem.history.minimumSince(startOfDay).state));
items[this.getPropertyItemName('ConsumptionToday')].postUpdate(consumptionToday);
}
if (this.hasProperty('ConsumptionWeek')) {
let consumptionWeek = consumptionTotal.subtract((this.consumptionTotalItem.history.minimumSince(startOfWeek).state));
items[this.getPropertyItemName('ConsumptionWeek')].postUpdate(consumptionWeek);
}
if (this.hasProperty('ConsumptionMonth')) {
let consumptionMonth = consumptionTotal.subtract((this.consumptionTotalItem.history.minimumSince(startOfMonth).state));
items[this.getPropertyItemName('ConsumptionMonth')].postUpdate(consumptionMonth);
}
if (this.hasProperty('ConsumptionYear')) {
let consumptionYear = consumptionTotal.subtract((this.consumptionTotalItem.history.minimumSince(startOfYear).state));
items[this.getPropertyItemName('ConsumptionYear')].postUpdate(consumptionYear);
}
}
}
class Doorbell extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
watches.add({
item: this.getPropertyItemName('State'),
targetState: 'ON',
alertFunc: () => { items.getItem("OF_Alexa_Desk_TTS").sendCommand('Es hat geklingelt'); items.getItem("LR_Alexa_TV_TTS").sendCommand('Es hat geklingelt'); }
});
}
}
class Irrigation extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
// Initialization of properties
this.valves = new Object();
// Add on/off watch rule
watches.add({
item: this.getPropertyItemName('State'),
targetState: 'ON',
alertFunc: () => { this.#irrigationOn(); },
endAlertFunc: () => { this.#irrigationOff(); },
});
// Fetch irrigation valves and initialize as subequipment
for (let valve of this.equipmentItem.members.filter(item => { return item.tags.includes('IrrigationValve'); } )) {
try {
this.valves[valve.name] = new IrrigationValve(valve);
} catch (error) {
log.error(error);
}
}
}
gc() {
log.info('Deinitialization of irrigation valves for ' + this.name);
// Delete all watchObjects
for (let valve of Object.keys(this.valves)) {
this.valves[valve].gc();
}
// Call function to deinitialize irrigation
super.gc();
}
#irrigationOn() {
log.info(`Irrigation ${this.name} received command on`);
// Fetch valves with AutoMode enabled
let autoValves = Object.keys(this.valves).filter(element => { return this.valves[element].getValue('AutoMode', 'OFF') == 'ON'} );
// Skip if no valves with AutoMode enabled are available
if (autoValves.length == 0) {
log.info('No valves with AutoMode enabled available');
this.stateItem.sendCommandIfDifferent('OFF');
return;
}
let totalDuration = 0;
for (let i = 0; i < autoValves.length; i++) {
let currentValve = autoValves[i];
let nextValve = autoValves[(i + 1)];
let duration = time.toZDT(this.valves[currentValve].getValue('Duration', 'PT30S')).getMillisFromNow();
// calculate start and endtimes for valve
let startTime = time.toZDT(totalDuration)
totalDuration = totalDuration + duration;
let endTime = time.toZDT(totalDuration);
// Set timers for valve
this.valves[currentValve].timers.create(
'autoOn',
startTime,
() => {
this.valves[currentValve].stateItem.sendCommandIfDifferent('ON');
}
);
this.valves[currentValve].timers.create(
'autoOff',
endTime,
() => {
this.valves[currentValve].stateItem.sendCommandIfDifferent('OFF');
}
);
// Stop irrigation after the last valve
if (nextValve == undefined) {
log.info('No further valves with autoMode enabled available');
this.timers.create(
'autoOff',
endTime.plusSeconds(5),
() => {
log.debug(`Switch irrigation ${this.name} to off`)
this.stateItem.sendCommandIfDifferent('OFF');
}
);
}
}
}
#irrigationOff() {
log.info(`Irrigation ${this.name} received command off`);
this.timers.cancel('autoOff')
for (let valve of Object.keys(this.valves)) {
this.valves[valve].timers.cancel('autoOn');
this.valves[valve].timers.cancel('autoOff');
this.valves[valve].stateItem.sendCommandIfDifferent('OFF')
}
}
}
class IrrigationValve extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
watches.add({
item: this.getPropertyItemName('State'),
targetState: 'ON',
alertFunc: () => { this.stateItem.sendCommand('OFF'); },
alertDelay: 'PT30M'
});
}
}
class Light extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
}
}
class Lightbulb extends Light {
constructor(equipmentItem) {
super(equipmentItem);
}
}
class PresenceSensor extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
}
}
class TowelRadiator extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
watches.add({
item: this.getPropertyItemName('State'),
targetState: 'ON',
alertFunc: () => { this.stateItem.sendCommand('OFF'); },
alertDelay: 'PT59M'
});
}
}
class VoiceAssistant extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
}
}
class WeatherService extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
}
}
class Window extends Equipment {
constructor(equipmentItem) {
super(equipmentItem);
}
}
const watches = new watch.Watch();
// Initialization of eMgr
const eMgr = new Object();
cache.shared.put('eMgr', eMgr);
// Initialization of equipment items
for (let equipmentItem of items.getItems().filter(element => { return (element.semantics.isEquipment == true) && (element.semantics.equipment == null) })) {
// Get equipmentType
let equipmentType = equipmentItem.tags.filter(element => { return !(element == 'Equipment') } );
// Set default equipmentType if item does not provide a type
if (equipmentType == '') {
equipmentType = 'Equipment';
}
// Initialize equipment class
try {
eMgr[equipmentItem.name] = eval(`new ${equipmentType}(equipmentItem)`);
} catch (error) {
log.warn('Equipment ' + equipmentType + ' is not defined');
eMgr[equipmentItem.name] = new Equipment(equipmentItem);
}
}
require('@runtime').lifecycleTracker.addDisposeHook(() => {
log.info('Deinitialization of equipmentMgr');
watches.deleteAll();
for (let equipmentItemName of Object.keys(eMgr)) {
eMgr[equipmentItemName].gc();
}
});