Fix logbook filtering by entity id (#36973)

* Fix logbook filtering by entity_id

* remove debug
pull/37160/head
J. Nick Koston 2020-06-21 14:34:47 -05:00 committed by GitHub
parent a6536bb622
commit 1de97e3a35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 115 additions and 23 deletions

View File

@ -82,13 +82,13 @@ CONFIG_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA,
)
ALL_EVENT_TYPES = [
EVENT_STATE_CHANGED,
EVENT_LOGBOOK_ENTRY,
HOMEASSISTANT_EVENTS = [
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
]
ALL_EVENT_TYPES = [EVENT_STATE_CHANGED, EVENT_LOGBOOK_ENTRY, *HOMEASSISTANT_EVENTS]
LOG_MESSAGE_SCHEMA = vol.Schema(
{
vol.Required(ATTR_NAME): cv.string,
@ -124,7 +124,9 @@ def async_describe_event(hass, domain, event_name, describe_callback):
async def async_setup(hass, config):
"""Listen for download events to download files."""
"""Logbook setup."""
hass.data.setdefault(DOMAIN, {})
@callback
def log_message(service):
@ -374,9 +376,14 @@ def _generate_filter_from_config(config):
)
def _all_entities_filter(_):
"""Filter that accepts all entities."""
return True
def _get_events(hass, config, start_day, end_day, entity_id=None):
"""Get events for a period of time."""
entities_filter = _generate_filter_from_config(config)
entity_attr_cache = EntityAttributeCache(hass)
def yield_events(query):
@ -389,9 +396,12 @@ def _get_events(hass, config, start_day, end_day, entity_id=None):
with session_scope(hass=hass) as session:
if entity_id is not None:
entity_ids = [entity_id.lower()]
entities_filter = generate_filter([], entity_ids, [], [])
elif config.get(CONF_EXCLUDE) or config.get(CONF_INCLUDE):
entities_filter = _generate_filter_from_config(config)
entity_ids = _get_related_entity_ids(session, entities_filter)
else:
entities_filter = _all_entities_filter
entity_ids = None
old_state = aliased(States, name="old_state")
@ -456,7 +466,6 @@ def _get_events(hass, config, start_day, end_day, entity_id=None):
def _keep_event(hass, event, entities_filter, entity_attr_cache):
if event.event_type == EVENT_STATE_CHANGED:
entity_id = event.entity_id
if entity_id is None:
@ -476,26 +485,25 @@ def _keep_event(hass, event, entities_filter, entity_attr_cache):
):
# Don't show continuous sensor value changes in the logbook
return False
elif event.event_type == EVENT_LOGBOOK_ENTRY:
event_data = event.data
domain = event_data.get(ATTR_DOMAIN)
entity_id = None
elif event.event_type in hass.data.get(DOMAIN, {}) and not event.data.get(
"entity_id"
):
elif event.event_type in HOMEASSISTANT_EVENTS:
entity_id = f"{HA_DOMAIN}."
elif event.event_type in hass.data[DOMAIN] and ATTR_ENTITY_ID not in event.data:
# If the entity_id isn't described, use the domain that describes
# the event for filtering.
domain = hass.data[DOMAIN][event.event_type][0]
entity_id = None
if domain is None:
return False
entity_id = f"{domain}."
else:
event_data = event.data
domain = event_data.get(ATTR_DOMAIN)
entity_id = event_data.get("entity_id")
entity_id = event_data.get(ATTR_ENTITY_ID)
if entity_id is None:
domain = event_data.get(ATTR_DOMAIN)
if domain is None:
return False
entity_id = f"{domain}."
if not entity_id and domain:
entity_id = f"{domain}."
return not entity_id or entities_filter(entity_id)
return entities_filter(entity_id)
def _entry_message_from_event(hass, entity_id, domain, event, entity_attr_cache):

View File

@ -12,9 +12,12 @@ import voluptuous as vol
from homeassistant.components import logbook, recorder, sun
from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME
from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED
from homeassistant.components.script import EVENT_SCRIPT_STARTED
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_HIDDEN,
ATTR_NAME,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
EVENT_STATE_CHANGED,
@ -354,7 +357,10 @@ class TestComponentLogbook(unittest.TestCase):
{
ha.DOMAIN: {},
logbook.DOMAIN: {
logbook.CONF_INCLUDE: {logbook.CONF_ENTITIES: [entity_id2]}
logbook.CONF_INCLUDE: {
logbook.CONF_DOMAINS: ["homeassistant"],
logbook.CONF_ENTITIES: [entity_id2],
}
},
}
)
@ -399,7 +405,9 @@ class TestComponentLogbook(unittest.TestCase):
{
ha.DOMAIN: {},
logbook.DOMAIN: {
logbook.CONF_INCLUDE: {logbook.CONF_DOMAINS: ["sensor", "alexa"]}
logbook.CONF_INCLUDE: {
logbook.CONF_DOMAINS: ["homeassistant", "sensor", "alexa"]
}
},
}
)
@ -445,7 +453,7 @@ class TestComponentLogbook(unittest.TestCase):
ha.DOMAIN: {},
logbook.DOMAIN: {
logbook.CONF_INCLUDE: {
logbook.CONF_DOMAINS: ["sensor"],
logbook.CONF_DOMAINS: ["sensor", "homeassistant"],
logbook.CONF_ENTITIES: ["switch.bla"],
},
logbook.CONF_EXCLUDE: {
@ -1543,6 +1551,82 @@ async def test_logbook_view_end_time_entity(hass, hass_client):
assert json[0]["entity_id"] == entity_id_test
async def test_logbook_entity_filter_with_automations(hass, hass_client):
"""Test the logbook view with end_time and entity with automations and scripts."""
await hass.async_add_executor_job(init_recorder_component, hass)
await async_setup_component(hass, "logbook", {})
await async_setup_component(hass, "automation", {})
await async_setup_component(hass, "script", {})
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
entity_id_test = "alarm_control_panel.area_001"
hass.states.async_set(entity_id_test, STATE_OFF)
hass.states.async_set(entity_id_test, STATE_ON)
entity_id_second = "alarm_control_panel.area_002"
hass.states.async_set(entity_id_second, STATE_OFF)
hass.states.async_set(entity_id_second, STATE_ON)
hass.bus.async_fire(
EVENT_AUTOMATION_TRIGGERED,
{ATTR_NAME: "Mock automation", ATTR_ENTITY_ID: "automation.mock_automation"},
)
hass.bus.async_fire(
EVENT_SCRIPT_STARTED,
{ATTR_NAME: "Mock script", ATTR_ENTITY_ID: "script.mock_script"},
)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_add_job(partial(trigger_db_commit, hass))
await hass.async_block_till_done()
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
client = await hass_client()
# Today time 00:00:00
start = dt_util.utcnow().date()
start_date = datetime(start.year, start.month, start.day)
# Test today entries with filter by end_time
end_time = start + timedelta(hours=24)
response = await client.get(
f"/api/logbook/{start_date.isoformat()}?end_time={end_time}"
)
assert response.status == 200
json_dict = await response.json()
assert len(json_dict) == 5
assert json_dict[0]["entity_id"] == entity_id_test
assert json_dict[1]["entity_id"] == entity_id_second
assert json_dict[2]["entity_id"] == "automation.mock_automation"
assert json_dict[3]["entity_id"] == "script.mock_script"
assert json_dict[4]["domain"] == "homeassistant"
# Test entries for 3 days with filter by entity_id
end_time = start + timedelta(hours=72)
response = await client.get(
f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=alarm_control_panel.area_001"
)
assert response.status == 200
json_dict = await response.json()
assert len(json_dict) == 1
assert json_dict[0]["entity_id"] == entity_id_test
# Tomorrow time 00:00:00
start = dt_util.utcnow()
start_date = datetime(start.year, start.month, start.day)
# Test entries from today to 3 days with filter by entity_id
end_time = start_date + timedelta(hours=72)
response = await client.get(
f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=alarm_control_panel.area_002"
)
assert response.status == 200
json_dict = await response.json()
assert len(json_dict) == 1
assert json_dict[0]["entity_id"] == entity_id_second
class MockLazyEventPartialState(ha.Event):
"""Minimal mock of a Lazy event."""