293 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
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 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) })) {
 | 
						|
    
 | 
						|
    // 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();
 | 
						|
    }
 | 
						|
}); |