Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions homeassistant/components/binary_sensor/bayesian.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,13 @@ def __init__(self, name, prior, observations, probability_threshold,

self.current_obs = OrderedDict({})

self.entity_obs = {obs['entity_id']: obs for obs in self._observations}
to_observe = set(obs['entity_id'] for obs in self._observations)

self.entity_obs = dict.fromkeys(to_observe, [])

for ind, obs in enumerate(self._observations):
obs["id"] = ind
self.entity_obs[obs['entity_id']].append(obs)

self.watchers = {
'numeric_state': self._process_numeric_state,
Expand All @@ -120,16 +126,17 @@ def async_threshold_sensor_state_listener(entity, old_state,
if new_state.state == STATE_UNKNOWN:
return

entity_obs = self.entity_obs[entity]
platform = entity_obs['platform']
entity_obs_list = self.entity_obs[entity]

for entity_obs in entity_obs_list:
platform = entity_obs['platform']

self.watchers[platform](entity_obs)
self.watchers[platform](entity_obs)

prior = self.prior
for obs in self.current_obs.values():
prior = update_probability(prior, obs['prob_true'],
obs['prob_false'])

self.probability = prior

self.hass.async_add_job(self.async_update_ha_state, True)
Expand All @@ -140,20 +147,20 @@ def async_threshold_sensor_state_listener(entity, old_state,

def _update_current_obs(self, entity_observation, should_trigger):
"""Update current observation."""
entity = entity_observation['entity_id']
obs_id = entity_observation['id']

if should_trigger:
prob_true = entity_observation['prob_given_true']
prob_false = entity_observation.get(
'prob_given_false', 1 - prob_true)

self.current_obs[entity] = {
self.current_obs[obs_id] = {
'prob_true': prob_true,
'prob_false': prob_false
}

else:
self.current_obs.pop(entity, None)
self.current_obs.pop(obs_id, None)

def _process_numeric_state(self, entity_observation):
"""Add entity to current_obs if numeric state conditions are met."""
Expand Down
68 changes: 66 additions & 2 deletions tests/components/binary_sensor/test_bayesian.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ def test_sensor_numeric_state(self):
'prob_false': 0.1,
'prob_true': 0.9
}], state.attributes.get('observations'))
self.assertAlmostEqual(0.77,
state.attributes.get('probability'))
self.assertAlmostEqual(0.77, state.attributes.get('probability'))

assert state.state == 'on'

Expand Down Expand Up @@ -155,6 +154,71 @@ def test_sensor_state(self):

assert state.state == 'off'

def test_multiple_observations(self):
"""Test sensor with multiple observations of same entity."""
config = {
'binary_sensor': {
'name':
'Test_Binary',
'platform':
'bayesian',
'observations': [{
'platform': 'state',
'entity_id': 'sensor.test_monitored',
'to_state': 'blue',
'prob_given_true': 0.8,
'prob_given_false': 0.4
}, {
'platform': 'state',
'entity_id': 'sensor.test_monitored',
'to_state': 'red',
'prob_given_true': 0.2,
'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', 'off')

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', 'blue')
self.hass.block_till_done()
self.hass.states.set('sensor.test_monitored', 'off')
self.hass.block_till_done()
self.hass.states.set('sensor.test_monitored', 'blue')
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.33, state.attributes.get('probability'))

assert state.state == 'on'

self.hass.states.set('sensor.test_monitored', 'blue')
self.hass.block_till_done()
self.hass.states.set('sensor.test_monitored', 'red')
self.hass.block_till_done()

state = self.hass.states.get('binary_sensor.test_binary')
self.assertAlmostEqual(0.11, 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]
Expand Down