uid: jag_rules:bayesian_sensor_calculation label: Bayesian Sensor Calculation description: Recalculate the probability of a bayesian sensor event when some input changes configDescriptions: - name: sensorgroup type: TEXT context: item label: Bayesian Group required: true description: Group item configured with the BayesianSensor metadata triggers: - id: "1" configuration: groupName: "{{sensorgroup}}" type: core.GroupStateChangeTrigger conditions: [] actions: - inputs: {} id: "2" configuration: type: application/javascript;version=ECMAScript-2021 script: > /* This Script provides functions for the sensor control system. */ //Initialize var runtime = require('@runtime'); var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.bayesianSensor'); var ZonedDateTime = Java.type("java.time.ZonedDateTime"); var Duration = Java.type("java.time.Duration"); logger.info('Bayesian Sensor - Recalculating') //Access MetadataRegistry var FrameworkUtil = Java.type('org.osgi.framework.FrameworkUtil'); this.ScriptHandler = Java.type("org.openhab.core.automation.module.script.rulesupport.shared.ScriptedHandler"); var _bundle = FrameworkUtil.getBundle(ScriptHandler.class); var bundle_context = _bundle.getBundleContext(); var MetadataRegistry_Ref = bundle_context.getServiceReference('org.openhab.core.items.MetadataRegistry'); var MetadataRegistry = bundle_context.getService(MetadataRegistry_Ref); var MetadataKey = Java.type('org.openhab.core.items.MetadataKey'); //Bayesian Probability Calculation function updatePrior (updPrior, updPTrue, updPFalse, updItem) { var numerator = updPTrue * updPrior; var denominator = numerator + updPFalse * (1 - updPrior); logger.info(updItem + ': ' + updPrior.toString() + ' -> ' + (numerator / denominator).toString()); return numerator / denominator; } //Find All Bayesian Sensor Groups for Event Item var parentGroups = items.getItem(event.itemName).groupNames; var sensorGroups = parentGroups.filter(x => items.getItem(x).tags.includes('Bayesian Sensor')); //Read BayesianSensor MetaData and Iterate Through Sensors for Each Impacted Sensor Group for (var sensorGroup of sensorGroups) { var metaList = MetadataRegistry.get(new MetadataKey('BayesianSensor',sensorGroup)); if (!metaList || !metaList.configuration) { logger.info(['Bayesian metadata not configured for ',sensorGroup].join('')); } else { var sensorConfig = metaList['configuration']; var sensorList = sensorConfig.sensors; var prior = sensorConfig.prior || 0.5; for (var checkItem in sensorList) { if (sensorList[checkItem].decay) { var now = ZonedDateTime.now(); var changedTime = items.getItem(sensorList[checkItem].decay.timestamp || checkItem + '_TS').rawState.getZonedDateTime(); var decayTime = Duration.between(changedTime,now).toMinutes(); var numSteps = Math.floor(decayTime / (sensorList[checkItem].decay.timeStep || 5)); var probTrue = Math.max((sensorList[checkItem].decay.decayMin || .6),sensorList[checkItem].pTrue - (numSteps * sensorList[checkItem].decay.decayStep)); } else { var probTrue = sensorList[checkItem].pTrue; } switch (sensorList[checkItem].testType) { case 'gt': if (runtime.items[checkItem] > sensorList[checkItem].testState) { prior = updatePrior(prior, probTrue,sensorList[checkItem].pFalse,checkItem); } break; case 'lt': if (runtime.items[checkItem] < sensorList[checkItem].testState) { prior = updatePrior(prior, probTrue,sensorList[checkItem].pFalse,checkItem); } break; case 'bt': if ((sensorList[checkItem].testState[0] > runtime.items[checkItem]) && (runtime.items[checkItem] > sensorList[checkItem].testState[1])) { prior = updatePrior(prior, probTrue,sensorList[checkItem].pFalse,checkItem); } break; case 'neq': if (runtime.items[checkItem].toString() != sensorList[checkItem].testState) { prior = updatePrior(prior, probTrue,sensorList[checkItem].pFalse,checkItem); } break; case 'list': if (sensorList[checkItem].testState.contains(runtime.items[checkItem])) { prior = updatePrior(prior, probTrue,sensorList[checkItem].pFalse,checkItem); } break; default: if (runtime.items[checkItem].toString() == sensorList[checkItem].testState) { prior = updatePrior(prior, probTrue,sensorList[checkItem].pFalse,checkItem); } break; } } if (sensorConfig.posterior) runtime.events.postUpdate(sensorConfig.posterior,prior.toString()); runtime.events.sendCommand(sensorConfig.proxy,(prior >= sensorConfig.threshold) ? (sensorConfig.trueState || 'ON') : (sensorConfig.falseState || 'OFF')); logger.info([sensorGroup + ' new probability of ',prior, (prior >= sensorConfig.threshold) ? ' is over ' : 'is under ', sensorConfig.threshold].join('')); } } type: script.ScriptAction