Add websocket API to get list of recorded entities (#92640)

* Add API to get list of recorded entities

* update for latest codebase

* ruff

* Update homeassistant/components/recorder/websocket_api.py

* Update homeassistant/components/recorder/websocket_api.py

* Update homeassistant/components/recorder/websocket_api.py

* add suggested test
pull/117476/head
J. Nick Koston 2024-05-15 15:41:56 +09:00 committed by GitHub
parent d29084d6fc
commit 3f053eddbd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 114 additions and 3 deletions

View File

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import datetime as dt
from typing import Any, Literal, cast
from typing import TYPE_CHECKING, Any, Literal, cast
import voluptuous as vol
@ -44,7 +44,11 @@ from .statistics import (
statistics_during_period,
validate_statistics,
)
from .util import PERIOD_SCHEMA, get_instance, resolve_period
from .util import PERIOD_SCHEMA, get_instance, resolve_period, session_scope
if TYPE_CHECKING:
from .core import Recorder
UNIT_SCHEMA = vol.Schema(
{
@ -81,6 +85,7 @@ def async_setup(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, ws_info)
websocket_api.async_register_command(hass, ws_update_statistics_metadata)
websocket_api.async_register_command(hass, ws_validate_statistics)
websocket_api.async_register_command(hass, ws_get_recorded_entities)
def _ws_get_statistic_during_period(
@ -513,3 +518,40 @@ def ws_info(
"thread_running": is_running,
}
connection.send_result(msg["id"], recorder_info)
def _get_recorded_entities(
hass: HomeAssistant, msg_id: int, instance: Recorder
) -> bytes:
"""Get the list of entities being recorded."""
with session_scope(hass=hass, read_only=True) as session:
return json_bytes(
messages.result_message(
msg_id,
{
"entity_ids": list(
instance.states_meta_manager.get_metadata_id_to_entity_id(
session
).values()
)
},
)
)
@websocket_api.websocket_command(
{
vol.Required("type"): "recorder/recorded_entities",
}
)
@websocket_api.async_response
async def ws_get_recorded_entities(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
) -> None:
"""Get the list of entities being recorded."""
instance = get_instance(hass)
return connection.send_message(
await instance.async_add_executor_job(
_get_recorded_entities, hass, msg["id"], instance
)
)

View File

@ -23,6 +23,7 @@ from homeassistant.components.recorder.statistics import (
from homeassistant.components.recorder.util import session_scope
from homeassistant.components.recorder.websocket_api import UNIT_SCHEMA
from homeassistant.components.sensor import UNIT_CONVERTERS
from homeassistant.const import CONF_DOMAINS, CONF_EXCLUDE
from homeassistant.core import HomeAssistant
from homeassistant.helpers import recorder as recorder_helper
from homeassistant.setup import async_setup_component
@ -38,7 +39,7 @@ from .common import (
)
from tests.common import async_fire_time_changed
from tests.typing import WebSocketGenerator
from tests.typing import RecorderInstanceGenerator, WebSocketGenerator
DISTANCE_SENSOR_FT_ATTRIBUTES = {
"device_class": "distance",
@ -132,6 +133,13 @@ VOLUME_SENSOR_M3_ATTRIBUTES_TOTAL = {
}
@pytest.fixture
async def mock_recorder_before_hass(
async_setup_recorder_instance: RecorderInstanceGenerator,
) -> None:
"""Set up recorder."""
def test_converters_align_with_sensor() -> None:
"""Ensure UNIT_SCHEMA is aligned with sensor UNIT_CONVERTERS."""
for converter in UNIT_CONVERTERS.values():
@ -3177,3 +3185,64 @@ async def test_adjust_sum_statistics_errors(
stats = statistics_during_period(hass, zero, period="hour")
assert stats != previous_stats
previous_stats = stats
async def test_recorder_recorded_entities_no_filter(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
async_setup_recorder_instance: RecorderInstanceGenerator,
) -> None:
"""Test getting the list of recorded entities without a filter."""
await async_setup_recorder_instance(hass, {recorder.CONF_COMMIT_INTERVAL: 0})
client = await hass_ws_client()
await client.send_json({"id": 1, "type": "recorder/recorded_entities"})
response = await client.receive_json()
assert response["result"] == {"entity_ids": []}
assert response["id"] == 1
assert response["success"]
assert response["type"] == "result"
hass.states.async_set("sensor.test", 10)
await async_wait_recording_done(hass)
await client.send_json({"id": 2, "type": "recorder/recorded_entities"})
response = await client.receive_json()
assert response["result"] == {"entity_ids": ["sensor.test"]}
assert response["id"] == 2
assert response["success"]
assert response["type"] == "result"
async def test_recorder_recorded_entities_with_filter(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
async_setup_recorder_instance: RecorderInstanceGenerator,
) -> None:
"""Test getting the list of recorded entities with a filter."""
await async_setup_recorder_instance(
hass,
{
recorder.CONF_COMMIT_INTERVAL: 0,
CONF_EXCLUDE: {CONF_DOMAINS: ["sensor"]},
},
)
client = await hass_ws_client()
await client.send_json({"id": 1, "type": "recorder/recorded_entities"})
response = await client.receive_json()
assert response["result"] == {"entity_ids": []}
assert response["id"] == 1
assert response["success"]
assert response["type"] == "result"
hass.states.async_set("switch.test", 10)
hass.states.async_set("sensor.test", 10)
await async_wait_recording_done(hass)
await client.send_json({"id": 2, "type": "recorder/recorded_entities"})
response = await client.receive_json()
assert response["result"] == {"entity_ids": ["switch.test"]}
assert response["id"] == 2
assert response["success"]
assert response["type"] == "result"