Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Bayesian Binary JSON Serialization #34540

Merged
merged 1 commit into from
Apr 22, 2020
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
10 changes: 9 additions & 1 deletion homeassistant/components/bayesian/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,16 @@ def device_class(self):
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""

attr_observations_list = list(
obs.copy() for obs in self.current_observations.values() if obs is not None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the bug here is that value_template is in current_observations to begin with.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the value_template is needed here:

elif "value_template" in obs:

I believe the bug was introduced because I changed the data structures to yield some simpler logic, and I didn't have a general awareness to check to make sure that any data structure exposed as a state attribute needs to be json serializable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually -- hmm digging a bit further

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that line referenced isn't exactly how it's used, but it is essential. Each handler defined here (

self.observation_handlers = {
) takes an "observation" dictionary that it can process. In the case of the template processor, a value_template must exist in that "observation".

These "observation" dictionaries were then being exposed directly via the current_observations attribute.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to resolve my issue with hanging at "loading data". Will continue testing, thanks.

)

for item in attr_observations_list:
item.pop("value_template", None)

return {
ATTR_OBSERVATIONS: list(self.current_observations.values()),
ATTR_OBSERVATIONS: attr_observations_list,
ATTR_OCCURRED_OBSERVATION_ENTITIES: list(
{
obs.get("entity_id")
Expand Down
67 changes: 62 additions & 5 deletions tests/components/bayesian/test_binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""The test for the bayesian sensor platform."""
import json
import unittest

from homeassistant.components.bayesian import binary_sensor as bayesian
Expand Down Expand Up @@ -76,7 +77,7 @@ def test_unknown_state_does_not_influence_probability(self):
assert setup_component(self.hass, "binary_sensor", config)

state = self.hass.states.get("binary_sensor.test_binary")
assert state.attributes.get("observations") == [None]
assert state.attributes.get("observations") == []

def test_sensor_numeric_state(self):
"""Test sensor on numeric state platform observations."""
Expand Down Expand Up @@ -112,7 +113,7 @@ def test_sensor_numeric_state(self):

state = self.hass.states.get("binary_sensor.test_binary")

assert [None, None] == state.attributes.get("observations")
assert [] == state.attributes.get("observations")
assert 0.2 == state.attributes.get("probability")

assert state.state == "off"
Expand Down Expand Up @@ -177,7 +178,7 @@ def test_sensor_state(self):

state = self.hass.states.get("binary_sensor.test_binary")

assert [None] == state.attributes.get("observations")
assert [] == state.attributes.get("observations")
assert 0.2 == state.attributes.get("probability")

assert state.state == "off"
Expand Down Expand Up @@ -231,7 +232,7 @@ def test_sensor_value_template(self):

state = self.hass.states.get("binary_sensor.test_binary")

assert [None] == state.attributes.get("observations")
assert [] == state.attributes.get("observations")
assert 0.2 == state.attributes.get("probability")

assert state.state == "off"
Expand Down Expand Up @@ -322,7 +323,9 @@ def test_multiple_observations(self):

state = self.hass.states.get("binary_sensor.test_binary")

assert [None, None] == state.attributes.get("observations")
for key, attrs in state.attributes.items():
json.dumps(attrs)
assert [] == state.attributes.get("observations")
assert 0.2 == state.attributes.get("probability")

assert state.state == "off"
Expand Down Expand Up @@ -422,3 +425,57 @@ def test_observed_entities(self):
assert ["sensor.test_monitored", "sensor.test_monitored1"] == sorted(
state.attributes.get("occurred_observation_entities")
)

def test_state_attributes_are_serializable(self):
"""Test sensor on observed entities."""
config = {
"binary_sensor": {
"name": "Test_Binary",
"platform": "bayesian",
"observations": [
{
"platform": "state",
"entity_id": "sensor.test_monitored",
"to_state": "off",
"prob_given_true": 0.9,
"prob_given_false": 0.4,
},
{
"platform": "template",
"value_template": "{{is_state('sensor.test_monitored1','on') and is_state('sensor.test_monitored','off')}}",
"prob_given_true": 0.9,
},
],
"prior": 0.2,
"probability_threshold": 0.32,
}
}

assert setup_component(self.hass, "binary_sensor", config)

self.hass.states.set("sensor.test_monitored", "on")
self.hass.block_till_done()
self.hass.states.set("sensor.test_monitored1", "off")
self.hass.block_till_done()

state = self.hass.states.get("binary_sensor.test_binary")
assert [] == state.attributes.get("occurred_observation_entities")

self.hass.states.set("sensor.test_monitored", "off")
self.hass.block_till_done()

state = self.hass.states.get("binary_sensor.test_binary")
assert ["sensor.test_monitored"] == state.attributes.get(
"occurred_observation_entities"
)

self.hass.states.set("sensor.test_monitored1", "on")
self.hass.block_till_done()

state = self.hass.states.get("binary_sensor.test_binary")
assert ["sensor.test_monitored", "sensor.test_monitored1"] == sorted(
state.attributes.get("occurred_observation_entities")
)

for key, attrs in state.attributes.items():
json.dumps(attrs)