Add logbook platforms (#37078)

* Add logbook platforms

* Fix logbook describe test
This commit is contained in:
Paulus Schoutsen 2020-06-24 18:14:50 -07:00
parent 508afd3b90
commit 5cbf77221a
19 changed files with 215 additions and 116 deletions

View file

@ -4,7 +4,6 @@ import logging
import voluptuous as vol
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_NAME
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv, entityfilter
from . import flash_briefings, intent, smart_home_http
@ -23,7 +22,6 @@ from .const import (
CONF_TITLE,
CONF_UID,
DOMAIN,
EVENT_ALEXA_SMART_HOME,
)
_LOGGER = logging.getLogger(__name__)
@ -82,28 +80,6 @@ CONFIG_SCHEMA = vol.Schema(
async def async_setup(hass, config):
"""Activate the Alexa component."""
@callback
def async_describe_logbook_event(event):
"""Describe a logbook event."""
data = event.data
entity_id = data["request"].get("entity_id")
if entity_id:
state = hass.states.get(entity_id)
name = state.name if state else entity_id
message = f"send command {data['request']['namespace']}/{data['request']['name']} for {name}"
else:
message = (
f"send command {data['request']['namespace']}/{data['request']['name']}"
)
return {"name": "Amazon Alexa", "message": message, "entity_id": entity_id}
hass.components.logbook.async_describe_event(
DOMAIN, EVENT_ALEXA_SMART_HOME, async_describe_logbook_event
)
if DOMAIN not in config:
return True

View file

@ -0,0 +1,28 @@
"""Describe logbook events."""
from homeassistant.core import callback
from .const import DOMAIN, EVENT_ALEXA_SMART_HOME
@callback
def async_describe_events(hass, async_describe_event):
"""Describe logbook events."""
@callback
def async_describe_logbook_event(event):
"""Describe a logbook event."""
data = event.data
entity_id = data["request"].get("entity_id")
if entity_id:
state = hass.states.get(entity_id)
name = state.name if state else entity_id
message = f"send command {data['request']['namespace']}/{data['request']['name']} for {name}"
else:
message = (
f"send command {data['request']['namespace']}/{data['request']['name']}"
)
return {"name": "Amazon Alexa", "message": message, "entity_id": entity_id}
async_describe_event(DOMAIN, EVENT_ALEXA_SMART_HOME, async_describe_logbook_event)

View file

@ -2,7 +2,14 @@
"domain": "alexa",
"name": "Amazon Alexa",
"documentation": "https://www.home-assistant.io/integrations/alexa",
"dependencies": ["http"],
"after_dependencies": ["logbook", "camera"],
"codeowners": ["@home-assistant/cloud", "@ochlocracy"]
"dependencies": [
"http"
],
"after_dependencies": [
"camera"
],
"codeowners": [
"@home-assistant/cloud",
"@ochlocracy"
]
}

View file

@ -222,19 +222,6 @@ async def async_setup(hass, config):
hass, DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=vol.Schema({})
)
@callback
def async_describe_logbook_event(event):
"""Describe a logbook event."""
return {
"name": event.data.get(ATTR_NAME),
"message": "has been triggered",
"entity_id": event.data.get(ATTR_ENTITY_ID),
}
hass.components.logbook.async_describe_event(
DOMAIN, EVENT_AUTOMATION_TRIGGERED, async_describe_logbook_event
)
return True

View file

@ -0,0 +1,23 @@
"""Describe logbook events."""
from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME
from homeassistant.core import callback
from . import DOMAIN, EVENT_AUTOMATION_TRIGGERED
@callback
def async_describe_events(hass, async_describe_event): # type: ignore
"""Describe logbook events."""
@callback
def async_describe_logbook_event(event): # type: ignore
"""Describe a logbook event."""
return {
"name": event.data.get(ATTR_NAME),
"message": "has been triggered",
"entity_id": event.data.get(ATTR_ENTITY_ID),
}
async_describe_event(
DOMAIN, EVENT_AUTOMATION_TRIGGERED, async_describe_logbook_event
)

View file

@ -2,7 +2,12 @@
"domain": "automation",
"name": "Automation",
"documentation": "https://www.home-assistant.io/integrations/automation",
"after_dependencies": ["device_automation", "logbook", "webhook"],
"codeowners": ["@home-assistant/core"],
"after_dependencies": [
"device_automation",
"webhook"
],
"codeowners": [
"@home-assistant/core"
],
"quality_scale": "internal"
}

View file

@ -21,7 +21,6 @@ from homeassistant.const import (
ATTR_BATTERY_CHARGING,
ATTR_BATTERY_LEVEL,
ATTR_ENTITY_ID,
ATTR_SERVICE,
CONF_IP_ADDRESS,
CONF_NAME,
CONF_PORT,
@ -41,12 +40,10 @@ from .accessories import get_accessory
from .aidmanager import AccessoryAidStorage
from .const import (
AID_STORAGE,
ATTR_DISPLAY_NAME,
ATTR_INTERGRATION,
ATTR_MANUFACTURER,
ATTR_MODEL,
ATTR_SOFTWARE_VERSION,
ATTR_VALUE,
BRIDGE_NAME,
BRIDGE_SERIAL_NUMBER,
CONF_ADVERTISE_IP,
@ -64,7 +61,6 @@ from .const import (
DEFAULT_PORT,
DEFAULT_SAFE_MODE,
DOMAIN,
EVENT_HOMEKIT_CHANGED,
HOMEKIT,
HOMEKIT_PAIRING_QR,
HOMEKIT_PAIRING_QR_SECRET,
@ -325,26 +321,6 @@ def _async_register_events_and_services(hass: HomeAssistant):
schema=RESET_ACCESSORY_SERVICE_SCHEMA,
)
@callback
def async_describe_logbook_event(event):
"""Describe a logbook event."""
data = event.data
entity_id = data.get(ATTR_ENTITY_ID)
value = data.get(ATTR_VALUE)
value_msg = f" to {value}" if value else ""
message = f"send command {data[ATTR_SERVICE]}{value_msg} for {data[ATTR_DISPLAY_NAME]}"
return {
"name": "HomeKit",
"message": message,
"entity_id": entity_id,
}
hass.components.logbook.async_describe_event(
DOMAIN, EVENT_HOMEKIT_CHANGED, async_describe_logbook_event
)
async def async_handle_homekit_service_start(service):
"""Handle start HomeKit service call."""
for entry_id in hass.data[DOMAIN]:

View file

@ -0,0 +1,28 @@
"""Describe logbook events."""
from homeassistant.const import ATTR_ENTITY_ID, ATTR_SERVICE
from homeassistant.core import callback
from .const import ATTR_DISPLAY_NAME, ATTR_VALUE, DOMAIN, EVENT_HOMEKIT_CHANGED
@callback
def async_describe_events(hass, async_describe_event):
"""Describe logbook events."""
@callback
def async_describe_logbook_event(event):
"""Describe a logbook event."""
data = event.data
entity_id = data.get(ATTR_ENTITY_ID)
value = data.get(ATTR_VALUE)
value_msg = f" to {value}" if value else ""
message = f"send command {data[ATTR_SERVICE]}{value_msg} for {data[ATTR_DISPLAY_NAME]}"
return {
"name": "HomeKit",
"message": message,
"entity_id": entity_id,
}
async_describe_event(DOMAIN, EVENT_HOMEKIT_CHANGED, async_describe_logbook_event)

View file

@ -2,9 +2,23 @@
"domain": "homekit",
"name": "HomeKit",
"documentation": "https://www.home-assistant.io/integrations/homekit",
"requirements": ["HAP-python==2.9.1","fnvhash==0.1.0","PyQRCode==1.2.1","base36==0.1.1","PyTurboJPEG==1.4.0"],
"dependencies": ["http", "camera", "ffmpeg"],
"after_dependencies": ["logbook", "zeroconf"],
"codeowners": ["@bdraco"],
"requirements": [
"HAP-python==2.9.1",
"fnvhash==0.1.0",
"PyQRCode==1.2.1",
"base36==0.1.1",
"PyTurboJPEG==1.4.0"
],
"dependencies": [
"http",
"camera",
"ffmpeg"
],
"after_dependencies": [
"zeroconf"
],
"codeowners": [
"@bdraco"
],
"config_flow": true
}

View file

@ -47,6 +47,9 @@ from homeassistant.helpers.entityfilter import (
convert_include_exclude_filter,
generate_filter,
)
from homeassistant.helpers.integration_platform import (
async_process_integration_platforms,
)
from homeassistant.loader import bind_hass
import homeassistant.util.dt as dt_util
@ -102,15 +105,9 @@ def async_log_entry(hass, name, message, domain=None, entity_id=None):
hass.bus.async_fire(EVENT_LOGBOOK_ENTRY, data)
@bind_hass
def async_describe_event(hass, domain, event_name, describe_callback):
"""Teach logbook how to describe a new event."""
hass.data.setdefault(DOMAIN, {})[event_name] = (domain, describe_callback)
async def async_setup(hass, config):
"""Logbook setup."""
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN] = {}
@callback
def log_message(service):
@ -131,9 +128,23 @@ async def async_setup(hass, config):
)
hass.services.async_register(DOMAIN, "log", log_message, schema=LOG_MESSAGE_SCHEMA)
await async_process_integration_platforms(hass, DOMAIN, _process_logbook_platform)
return True
async def _process_logbook_platform(hass, domain, platform):
"""Process a logbook platform."""
@callback
def _async_describe_event(domain, event_name, describe_callback):
"""Teach logbook how to describe a new event."""
hass.data[DOMAIN][event_name] = (domain, describe_callback)
platform.async_describe_events(hass, _async_describe_event)
class LogbookView(HomeAssistantView):
"""Handle logbook view requests."""

View file

@ -188,19 +188,6 @@ async def async_setup(hass, config):
DOMAIN, SERVICE_TOGGLE, toggle_service, schema=SCRIPT_TURN_ONOFF_SCHEMA
)
@callback
def async_describe_logbook_event(event):
"""Describe the logbook event."""
return {
"name": event.data.get(ATTR_NAME),
"message": "started",
"entity_id": event.data.get(ATTR_ENTITY_ID),
}
hass.components.logbook.async_describe_event(
DOMAIN, EVENT_SCRIPT_STARTED, async_describe_logbook_event
)
return True

View file

@ -0,0 +1,21 @@
"""Describe logbook events."""
from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME
from homeassistant.core import callback
from . import DOMAIN, EVENT_SCRIPT_STARTED
@callback
def async_describe_events(hass, async_describe_event):
"""Describe logbook events."""
@callback
def async_describe_logbook_event(event):
"""Describe the logbook event."""
return {
"name": event.data.get(ATTR_NAME),
"message": "started",
"entity_id": event.data.get(ATTR_ENTITY_ID),
}
async_describe_event(DOMAIN, EVENT_SCRIPT_STARTED, async_describe_logbook_event)

View file

@ -2,7 +2,8 @@
"domain": "script",
"name": "Scripts",
"documentation": "https://www.home-assistant.io/integrations/script",
"after_dependencies": ["logbook"],
"codeowners": ["@home-assistant/core"],
"codeowners": [
"@home-assistant/core"
],
"quality_scale": "internal"
}

View file

@ -4,7 +4,7 @@ import logging
from typing import Any, Awaitable, Callable
from homeassistant.core import Event, HomeAssistant
from homeassistant.loader import IntegrationNotFound, async_get_integration, bind_hass
from homeassistant.loader import async_get_integration, bind_hass
from homeassistant.setup import ATTR_COMPONENT, EVENT_COMPONENT_LOADED
_LOGGER = logging.getLogger(__name__)
@ -21,10 +21,20 @@ async def async_process_integration_platforms(
async def _process(component_name: str) -> None:
"""Process the intents of a component."""
if "." in component_name:
return
integration = await async_get_integration(hass, component_name)
try:
integration = await async_get_integration(hass, component_name)
platform = integration.get_platform(platform_name)
except (IntegrationNotFound, ImportError):
except ImportError as err:
if f"{component_name}.{platform_name}" not in str(err):
_LOGGER.exception(
"Unexpected error importing %s/%s.py",
component_name,
platform_name,
)
return
try:

View file

@ -8,7 +8,9 @@ from tests.components.logbook.test_init import MockLazyEventPartialState
async def test_humanify_alexa_event(hass):
"""Test humanifying Alexa event."""
hass.config.components.add("recorder")
await async_setup_component(hass, "alexa", {})
await async_setup_component(hass, "logbook", {})
hass.states.async_set("light.kitchen", "on", {"friendly_name": "Kitchen Light"})
entity_attr_cache = logbook.EntityAttributeCache(hass)

View file

@ -1039,7 +1039,9 @@ async def test_extraction_functions(hass):
async def test_logbook_humanify_automation_triggered_event(hass):
"""Test humanifying Automation Trigger event."""
hass.config.components.add("recorder")
await async_setup_component(hass, automation.DOMAIN, {})
await async_setup_component(hass, "logbook", {})
entity_attr_cache = logbook.EntityAttributeCache(hass)
event1, event2 = list(

View file

@ -15,8 +15,10 @@ from tests.components.logbook.test_init import MockLazyEventPartialState
async def test_humanify_homekit_changed_event(hass, hk_driver):
"""Test humanifying HomeKit changed event."""
hass.config.components.add("recorder")
with patch("homeassistant.components.homekit.HomeKit"):
assert await async_setup_component(hass, "homekit", {"homekit": {}})
assert await async_setup_component(hass, "logbook", {})
entity_attr_cache = logbook.EntityAttributeCache(hass)
event1, event2 = list(

View file

@ -36,8 +36,8 @@ from homeassistant.helpers.json import JSONEncoder
from homeassistant.setup import async_setup_component, setup_component
import homeassistant.util.dt as dt_util
from tests.async_mock import patch
from tests.common import get_test_home_assistant, init_recorder_component
from tests.async_mock import Mock, patch
from tests.common import get_test_home_assistant, init_recorder_component, mock_platform
from tests.components.recorder.common import trigger_db_commit
_LOGGER = logging.getLogger(__name__)
@ -1563,6 +1563,22 @@ async def test_logbook_view_period_entity(hass, hass_client):
async def test_logbook_describe_event(hass, hass_client):
"""Test teaching logbook about a new event."""
await hass.async_add_executor_job(init_recorder_component, hass)
def _describe(event):
"""Describe an event."""
return {"name": "Test Name", "message": "tested a message"}
hass.config.components.add("fake_integration")
mock_platform(
hass,
"fake_integration.logbook",
Mock(
async_describe_events=lambda hass, async_describe_event: async_describe_event(
"test_domain", "some_event", _describe
)
),
)
assert await async_setup_component(hass, "logbook", {})
with patch(
"homeassistant.util.dt.utcnow",
@ -1574,12 +1590,6 @@ async def test_logbook_describe_event(hass, hass_client):
hass.data[recorder.DATA_INSTANCE].block_till_done
)
def _describe(event):
"""Describe an event."""
return {"name": "Test Name", "message": "tested a message"}
hass.components.logbook.async_describe_event("test_domain", "some_event", _describe)
client = await hass_client()
response = await client.get("/api/logbook")
results = await response.json()
@ -1597,6 +1607,26 @@ async def test_exclude_described_event(hass, hass_client):
entity_id2 = "automation.included_rule"
entity_id3 = "sensor.excluded_domain"
def _describe(event):
"""Describe an event."""
return {
"name": "Test Name",
"message": "tested a message",
"entity_id": event.data.get(ATTR_ENTITY_ID),
}
def async_describe_events(hass, async_describe_event):
"""Mock to describe events."""
async_describe_event("automation", "some_automation_event", _describe)
async_describe_event("sensor", "some_event", _describe)
hass.config.components.add("fake_integration")
mock_platform(
hass,
"fake_integration.logbook",
Mock(async_describe_events=async_describe_events),
)
await hass.async_add_executor_job(init_recorder_component, hass)
assert await async_setup_component(
hass,
@ -1631,19 +1661,6 @@ async def test_exclude_described_event(hass, hass_client):
hass.data[recorder.DATA_INSTANCE].block_till_done
)
def _describe(event):
"""Describe an event."""
return {
"name": "Test Name",
"message": "tested a message",
"entity_id": event.data.get(ATTR_ENTITY_ID),
}
hass.components.logbook.async_describe_event(
"automation", "some_automation_event", _describe
)
hass.components.logbook.async_describe_event("sensor", "some_event", _describe)
client = await hass_client()
response = await client.get("/api/logbook")
results = await response.json()

View file

@ -472,7 +472,9 @@ async def test_config(hass):
async def test_logbook_humanify_script_started_event(hass):
"""Test humanifying script started event."""
hass.config.components.add("recorder")
await async_setup_component(hass, DOMAIN, {})
await async_setup_component(hass, "logbook", {})
entity_attr_cache = logbook.EntityAttributeCache(hass)
event1, event2 = list(