Last active
December 4, 2021 23:14
-
-
Save cweitkamp/d0a1f7a2c0186c0ce13390a036fa2f11 to your computer and use it in GitHub Desktop.
Bayesian Sensor Aggregation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Switch proxySwitch "I am ... [MAP(sleeping.map):%s]" | |
Number proxyNumber "Probability [%.5f %%]" | |
Group gBAYESIAN "Bayesian Sleep Sensor" { | |
bayesian="proxySwitch" [ prior=0.5, threshold=0.85, state_given_true="ON", state_given_false="OFF" ] | |
} | |
Switch testSwitch1 "I am at home ... [MAP(yes-no.map):%s]" <presence> (gBAYESIAN) { | |
bayesian="ON" [ prob_given_true=0.99, prob_given_false=0.5 ] | |
} | |
Switch testSwitch2 "It is after sunset ... [MAP(yes-no.map):%s]" <sunset> (gBAYESIAN) { | |
bayesian="ON" [ prob_given_true=0.75, prob_given_false=0.3 ] | |
} | |
String testString1 "The time of day is ... [%s]" <sunset> (gBAYESIAN) { | |
bayesian="NIGHT" [ prob_given_true=0.75, prob_given_false=0.3 ] | |
} | |
Switch testSwitch3 "The lights are ... [%s]" <light> (gBAYESIAN) { | |
bayesian="OFF" [ prob_given_true=0.9, prob_given_false=0.4 ] | |
} | |
Switch testSwitch4 "My phone is muted ... [MAP(yes-no.map):%s]" <soundvolume-muted> (gBAYESIAN) { | |
bayesian="ON" [ prob_given_true=0.95, prob_given_false=0.5 ] | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from core.rules import rule | |
from core.triggers import when | |
from core.actions import LogAction | |
from core.metadata import get_metadata | |
RULE_NAME = "bayesian" | |
GROUP_NAME = "gBAYESIAN" | |
def update_probability(prior, prob_given_true, prob_given_false): | |
numerator = prob_given_true * prior | |
denominator = numerator + prob_given_false * (1 - prior) | |
return numerator / denominator | |
@rule("Bayesian Sensor Aggregation") | |
@when("Member of " + GROUP_NAME + " changed") | |
def bayesianSensorAggregation(event): | |
LogAction.logDebug(RULE_NAME, "Item '{}' changed from '{}' to '{}'.", event.itemName, event.oldItemState, event.itemState) | |
group = ir.getItem(GROUP_NAME) | |
if group is None: | |
LogAction.logError(RULE_NAME, "Group '{}' is missing.", GROUP_NAME) | |
return | |
groupMetadata = get_metadata(GROUP_NAME, "bayesian") | |
LogAction.logDebug(RULE_NAME, "Group '{}': metadata = {}", GROUP_NAME, groupMetadata) | |
if groupMetadata is None: | |
LogAction.logError(RULE_NAME, "Group '{}': metadata is missing.", GROUP_NAME) | |
return | |
if ir.getItem(groupMetadata.value) is None: | |
LogAction.logError(RULE_NAME, "Group '{}': targetItem is missing.", GROUP_NAME) | |
return | |
if groupMetadata.configuration["prior"] is None: | |
LogAction.logError(RULE_NAME, "Group '{}': prior is missing.", GROUP_NAME) | |
return | |
prior = groupMetadata.configuration["prior"].floatValue() | |
LogAction.logDebug(RULE_NAME, "Group '{}': prior = {}", GROUP_NAME, prior) | |
threshold = groupMetadata.configuration["threshold"] | |
if threshold is None: | |
LogAction.logError(RULE_NAME, "Group '{}': threshold is missing.", GROUP_NAME) | |
return | |
# iterate group members | |
for item in ir.getItem(GROUP_NAME).members: | |
itemMetadata = get_metadata(item.name, "bayesian") | |
LogAction.logDebug(RULE_NAME, "Item '{}': metadata = {}", item.name, itemMetadata) | |
if itemMetadata is None: | |
LogAction.logError(RULE_NAME, "Item '{}': metadata is missing.", item.name) | |
continue | |
prob_given_true = itemMetadata.configuration["prob_given_true"] | |
if prob_given_true is None: | |
LogAction.logError(RULE_NAME, "Item '{}': prob_given_true is missing.", item.name) | |
continue | |
prob_given_false = itemMetadata.configuration["prob_given_false"] | |
if prob_given_false is None: | |
LogAction.logDebug(RULE_NAME, "Item '{}': prob_given_false is missing - using default: (1 - prob_given_true)", item.name) | |
prob_given_false = 1 - prob_given_true.floatValue() | |
else: | |
prob_given_false = prob_given_false.floatValue() | |
# apply bayesian rule | |
if str(item.state) == itemMetadata.value: | |
prior = update_probability(prior, prob_given_true.floatValue(), prob_given_false) | |
LogAction.logDebug(RULE_NAME, "Group '{}': posterior = {}", GROUP_NAME, prior) | |
if not ir.getItem("proxyNumber") is None: | |
# update posterior probability | |
events.sendCommand("proxyNumber", str(prior * 100)) | |
state_given_true = groupMetadata.configuration["state_given_true"] | |
if state_given_true is None: | |
LogAction.logDebug(RULE_NAME, "Group '{}': state_given_true is missing - using default: ON", GROUP_NAME) | |
state_given_true = "ON" | |
state_given_false = groupMetadata.configuration["state_given_false"] | |
if state_given_false is None: | |
LogAction.logDebug(RULE_NAME, "Group '{}': state_given_false is missing - using default: OFF", GROUP_NAME) | |
state_given_false = "OFF" | |
# update proxy item | |
events.sendCommand(groupMetadata.value, str(state_given_true if prior >= threshold.floatValue() else state_given_false)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sitemap bayesian label="Bayesian Sensor Aggregation" { | |
Frame label="Bayesian Sleep Sensor" { | |
Switch item=proxySwitch | |
Text item=proxyNumber | |
} | |
Frame label="Presence Sensor" { | |
Switch item=testSwitch1 | |
} | |
Frame label="Time Of Day Sensor" { | |
Switch item=testSwitch2 | |
Selection item=testString1 mappings=["DAY"="Day", "NIGHT"="Night"] | |
} | |
Frame label="Lights" { | |
Switch item=testSwitch3 | |
} | |
Frame label="Phone" { | |
Switch item=testSwitch4 | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ON=sleeping | |
OFF=not sleeping | |
NULL=- | |
UNDEF=- | |
-=- |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ON=YES | |
OFF=NO | |
NULL=- | |
UNDEF=- | |
-=- |
Hi @robitasmo,
Thanks for your interest in my demo. To use it you have to install OH 2.5.x and the following requirements: Map Transformation and Jython. It should be possible to use the demo setup out-of-the-box by dropping it into your OPENHAB_CONF/
folder.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Christoph i've found your presentation very intresting. Forgive my ignorance...may I ask you how to use the bayesian.pi file? I've an OH installation up and running on a ARMBIAN based hardware. I'd like to replicate your sensor aggregation idea!