From 5aa399d7fe68d113df571931dd32c2ac8f31d47b Mon Sep 17 00:00:00 2001 From: Christian Weimann Date: Sat, 26 Aug 2023 16:48:49 +0200 Subject: [PATCH] Rework equipmentMgr --- equipmentMgr.js | 268 ++++++++++++++++++++++++++++++++++++++++++++- utils/equipment.js | 262 -------------------------------------------- 2 files changed, 262 insertions(+), 268 deletions(-) delete mode 100644 utils/equipment.js diff --git a/equipmentMgr.js b/equipmentMgr.js index 7955521..20079b4 100644 --- a/equipmentMgr.js +++ b/equipmentMgr.js @@ -2,25 +2,281 @@ console.loggerName = 'js.equipmentMgr'; console.log('Load equipmentMgr'); const { - equipment, 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 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 TowelRadiator extends Equipment { + constructor(equipmentItem) { + super(equipmentItem); + + this.watch['State'].add({ + targetState: 'ON', + alertFunc: () => { this.stateItem.sendCommand('OFF'); }, + alertDelay: 'PT59M' + }); + } +} + +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) })) { - // Fetch equipment type - let equipmentType = equipmentItem.tags.filter(element => Object.keys(equipment).includes(element)); - if (equipmentType.length > 1) { - equipmentType.splice(equipmentType.indexOf('Equipment'), 1); + + // 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] = new equipment[equipmentType](equipmentItem); + eMgr[equipmentItem.name] = eval(`new ${equipmentType}(equipmentItem)`); } catch (error) { console.error(error); } diff --git a/utils/equipment.js b/utils/equipment.js deleted file mode 100644 index 44a1c76..0000000 --- a/utils/equipment.js +++ /dev/null @@ -1,262 +0,0 @@ -console.loggerName = 'js.equipment'; -console.log('Load equipment modules'); - -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 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 TowelRadiator extends Equipment { - constructor(equipmentItem) { - super(equipmentItem); - - this.watch['State'].add({ - targetState: 'ON', - alertFunc: () => { this.stateItem.sendCommand('OFF'); }, - alertDelay: 'PT59M' - }); - } -} - -class WeatherService extends Equipment { - constructor(equipmentItem) { - super(equipmentItem); - } -} - -module.exports = { - Equipment, - Irrigation, - IrrigationValve, - TowelRadiator, - WeatherService -}; \ No newline at end of file