
* Bayesian Binary Sensor Why: * It would be beneficial to leverage various sensor outputs in a Bayesian manner in order to sense more complex events. This change addresses the need by: * `BayesianBinarySensor` class in `./homeassistant/components/binary_sensor/bayesian.py` * Tests in `./tests/components/binary_sensor/test_bayesian.py` Caveats: This is my first time in this code-base. I did try to follow conventions that I was able to find, but I'm sure there will be some issues to straighten out. * minor cleanup * Address reviewer's comments This change addresses the need by: * Removing `CONF_SENSOR_CLASS` and its usage in `get_deprecated`. * Make probability update function a static method, and use single `_` to match project conventions. * Address linter failures * fix `device_class` declaration * Address Comments Why: * Not validating config schema enough. * Not following common practices for async initialization. * Naive implementation of Bayes' rule. This change addresses the need by: * Improving config validation for observations. * Moving initialization logic into `async_added_to_hass`. * Re-configuring Bayesian updates to allow true P|Q usage. * address linting issues * Improve DRYness by adding `_update_current_obs` method * update doc strings and ensure functions are set up properly for async * Make only 1 state change handle * fix style * fix style part 2 * fix lint
176 lines
5.7 KiB
Python
176 lines
5.7 KiB
Python
"""The test for the bayesian sensor platform."""
|
|
import unittest
|
|
|
|
from homeassistant.setup import setup_component
|
|
from homeassistant.components.binary_sensor import bayesian
|
|
|
|
from tests.common import get_test_home_assistant
|
|
|
|
|
|
class TestBayesianBinarySensor(unittest.TestCase):
|
|
"""Test the threshold sensor."""
|
|
|
|
def setup_method(self, method):
|
|
"""Set up things to be run when tests are started."""
|
|
self.hass = get_test_home_assistant()
|
|
|
|
def teardown_method(self, method):
|
|
"""Stop everything that was started."""
|
|
self.hass.stop()
|
|
|
|
def test_sensor_numeric_state(self):
|
|
"""Test sensor on numeric state platform observations."""
|
|
config = {
|
|
'binary_sensor': {
|
|
'platform':
|
|
'bayesian',
|
|
'name':
|
|
'Test_Binary',
|
|
'observations': [{
|
|
'platform': 'numeric_state',
|
|
'entity_id': 'sensor.test_monitored',
|
|
'below': 10,
|
|
'above': 5,
|
|
'prob_given_true': 0.6
|
|
}, {
|
|
'platform': 'numeric_state',
|
|
'entity_id': 'sensor.test_monitored1',
|
|
'below': 7,
|
|
'above': 5,
|
|
'prob_given_true': 0.9,
|
|
'prob_given_false': 0.1
|
|
}],
|
|
'prior':
|
|
0.2,
|
|
}
|
|
}
|
|
|
|
assert setup_component(self.hass, 'binary_sensor', config)
|
|
|
|
self.hass.states.set('sensor.test_monitored', 4)
|
|
self.hass.block_till_done()
|
|
|
|
state = self.hass.states.get('binary_sensor.test_binary')
|
|
|
|
self.assertEqual([], state.attributes.get('observations'))
|
|
self.assertEqual(0.2, state.attributes.get('probability'))
|
|
|
|
assert state.state == 'off'
|
|
|
|
self.hass.states.set('sensor.test_monitored', 6)
|
|
self.hass.block_till_done()
|
|
self.hass.states.set('sensor.test_monitored', 4)
|
|
self.hass.block_till_done()
|
|
self.hass.states.set('sensor.test_monitored', 6)
|
|
self.hass.states.set('sensor.test_monitored1', 6)
|
|
self.hass.block_till_done()
|
|
|
|
state = self.hass.states.get('binary_sensor.test_binary')
|
|
self.assertEqual([{
|
|
'prob_false': 0.4,
|
|
'prob_true': 0.6
|
|
}, {
|
|
'prob_false': 0.1,
|
|
'prob_true': 0.9
|
|
}], state.attributes.get('observations'))
|
|
self.assertAlmostEqual(0.7714285714285715,
|
|
state.attributes.get('probability'))
|
|
|
|
assert state.state == 'on'
|
|
|
|
self.hass.states.set('sensor.test_monitored', 6)
|
|
self.hass.states.set('sensor.test_monitored1', 0)
|
|
self.hass.block_till_done()
|
|
self.hass.states.set('sensor.test_monitored', 4)
|
|
self.hass.block_till_done()
|
|
|
|
state = self.hass.states.get('binary_sensor.test_binary')
|
|
self.assertEqual(0.2, state.attributes.get('probability'))
|
|
|
|
assert state.state == 'off'
|
|
|
|
self.hass.states.set('sensor.test_monitored', 15)
|
|
self.hass.block_till_done()
|
|
|
|
state = self.hass.states.get('binary_sensor.test_binary')
|
|
|
|
assert state.state == 'off'
|
|
|
|
def test_sensor_state(self):
|
|
"""Test sensor on state platform observations."""
|
|
config = {
|
|
'binary_sensor': {
|
|
'name':
|
|
'Test_Binary',
|
|
'platform':
|
|
'bayesian',
|
|
'observations': [{
|
|
'platform': 'state',
|
|
'entity_id': 'sensor.test_monitored',
|
|
'to_state': 'off',
|
|
'prob_given_true': 0.8,
|
|
'prob_given_false': 0.4
|
|
}],
|
|
'prior':
|
|
0.2,
|
|
'probability_threshold':
|
|
0.32,
|
|
}
|
|
}
|
|
|
|
assert setup_component(self.hass, 'binary_sensor', config)
|
|
|
|
self.hass.states.set('sensor.test_monitored', 'on')
|
|
|
|
state = self.hass.states.get('binary_sensor.test_binary')
|
|
|
|
self.assertEqual([], state.attributes.get('observations'))
|
|
self.assertEqual(0.2, state.attributes.get('probability'))
|
|
|
|
assert state.state == 'off'
|
|
|
|
self.hass.states.set('sensor.test_monitored', 'off')
|
|
self.hass.block_till_done()
|
|
self.hass.states.set('sensor.test_monitored', 'on')
|
|
self.hass.block_till_done()
|
|
self.hass.states.set('sensor.test_monitored', 'off')
|
|
self.hass.block_till_done()
|
|
|
|
state = self.hass.states.get('binary_sensor.test_binary')
|
|
self.assertEqual([{
|
|
'prob_true': 0.8,
|
|
'prob_false': 0.4
|
|
}], state.attributes.get('observations'))
|
|
self.assertAlmostEqual(0.33333333, state.attributes.get('probability'))
|
|
|
|
assert state.state == 'on'
|
|
|
|
self.hass.states.set('sensor.test_monitored', 'off')
|
|
self.hass.block_till_done()
|
|
self.hass.states.set('sensor.test_monitored', 'on')
|
|
self.hass.block_till_done()
|
|
|
|
state = self.hass.states.get('binary_sensor.test_binary')
|
|
self.assertAlmostEqual(0.2, state.attributes.get('probability'))
|
|
|
|
assert state.state == 'off'
|
|
|
|
def test_probability_updates(self):
|
|
"""Test probability update function."""
|
|
prob_true = [0.3, 0.6, 0.8]
|
|
prob_false = [0.7, 0.4, 0.2]
|
|
prior = 0.5
|
|
|
|
for pt, pf in zip(prob_true, prob_false):
|
|
prior = bayesian.update_probability(prior, pt, pf)
|
|
|
|
self.assertAlmostEqual(0.720000, prior)
|
|
|
|
prob_true = [0.8, 0.3, 0.9]
|
|
prob_false = [0.6, 0.4, 0.2]
|
|
prior = 0.7
|
|
|
|
for pt, pf in zip(prob_true, prob_false):
|
|
prior = bayesian.update_probability(prior, pt, pf)
|
|
|
|
self.assertAlmostEqual(0.9130434782608695, prior)
|