console.loggerName = 'js.equipmentMgr'; console.log('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`); } console.info(`Initialization of equipment ${this.equipmentItem.name} with type ${this.constructor.name}`); // Initialization of properties this.name = this.equipmentItem.name; this.watch = new Object(); this.timers = new timer.Timer(); // Check if equipment has state item if (this.hasProperty('State')) { this.stateItem = items[this.getPropertyItemName('State')]; this.watch['State'] = new watch.Watch(this.stateItem); } else { console.info(`Item ${this.equipmentItem.name} has no state item`) } // Check if equipment has LowBat item if (this.hasProperty('LowBat')) { this.watch['LowBat'] = new watch.Watch(this.getPropertyItemName('LowBat')); this.watch['LowBat'].add({ targetState: 'ON', alertFunc: () => { this.#notifyLowBat(); }, alertRepeat: 'PT23H' }); } // Check if equipment has Unreach item if (this.hasProperty('Unreach')) { this.watch['Unreach'] = new watch.Watch(this.getPropertyItemName('Unreach')); this.watch['Unreach'].add({ 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 console.warn('Item ' + valueItemName + ' is missing'); } else if (items[valueItemName]['state'] == 'NULL') { // Return default value if item state is null console.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; } } console.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) { console.debug(`Eqipment ${this.name} has no property ${propertyName}`) return false; } return true; } #notifyLowBat() { console.warn(`${this.name} has a low battery level.`); } #notifyUnreach() { console.warn(`${this.name} is offline.`); } gc() { console.log('Denitialization of eqipment ' + this.name); // Delete all watchObjects for (let watchItem of Object.keys(this.watch)) { this.watch[watchItem].deleteAll(); } // Cancel all timers this.timers.cancelAll(); } } class Awning extends Equipment { constructor(equipmentItem) { super(equipmentItem); } } class Car extends Equipment { constructor(equipmentItem) { super(equipmentItem); } } class Doorbell extends Equipment { constructor(equipmentItem) { super(equipmentItem); this.watch['State'].add({ 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 this.watch['State'].add({ 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) { console.error(error); } } } gc() { console.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() { console.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) { console.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) { console.log('No further valves with autoMode enabled available'); this.timers.create( 'autoOff', endTime.plusSeconds(5), () => { console.debug(`Switch irrigation ${this.name} to off`) this.stateItem.sendCommandIfDifferent('OFF'); } ); } } } #irrigationOff() { console.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); this.watch['State'].add({ targetState: 'ON', alertFunc: () => { this.stateItem.sendCommand('OFF'); }, alertDelay: 'PT30M' }); } } class PresenceSensor extends Equipment { constructor(equipmentItem) { super(equipmentItem); } } class TowelRadiator extends Equipment { constructor(equipmentItem) { super(equipmentItem); this.watch['State'].add({ 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); } } const eMgr = new Object(); for (let equipmentItem of items.getItems().filter(element => { return (element.semantics.isEquipment == true) && (element.semantics.equipment == null) })) { // Get equipmentType var equipmentType = equipmentItem.tags.filter(element => { return !(element == 'Equipment') } ); console.log(equipmentType); // 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) { console.error(error); } } cache.shared.put('eMgr', eMgr); require('@runtime').lifecycleTracker.addDisposeHook(() => { console.log('Deinitialization of equipmentMgr'); for (let equipmentItemName of Object.keys(eMgr)) { eMgr[equipmentItemName].gc(); } });