Add kitchen_sink integration (#85592)

This commit is contained in:
Erik Montnemery 2023-01-10 17:31:47 +01:00 committed by GitHub
parent 3d02b5af21
commit 7621c450c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 518 additions and 466 deletions

View file

@ -605,6 +605,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/keyboard_remote/ @bendavid @lanrat /homeassistant/components/keyboard_remote/ @bendavid @lanrat
/homeassistant/components/keymitt_ble/ @spycle /homeassistant/components/keymitt_ble/ @spycle
/tests/components/keymitt_ble/ @spycle /tests/components/keymitt_ble/ @spycle
/homeassistant/components/kitchen_sink/ @home-assistant/core
/tests/components/kitchen_sink/ @home-assistant/core
/homeassistant/components/kmtronic/ @dgomes /homeassistant/components/kmtronic/ @dgomes
/tests/components/kmtronic/ @dgomes /tests/components/kmtronic/ @dgomes
/homeassistant/components/knx/ @Julius2342 @farmio @marvin-w /homeassistant/components/knx/ @Julius2342 @farmio @marvin-w

View file

@ -2,33 +2,20 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import datetime
from random import random
from homeassistant import config_entries, setup from homeassistant import config_entries, setup
from homeassistant.components import persistent_notification from homeassistant.components import persistent_notification
from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.models import StatisticData, StatisticMetaData
from homeassistant.components.recorder.statistics import (
async_add_external_statistics,
get_last_statistics,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_START,
Platform, Platform,
UnitOfEnergy,
UnitOfSoundPressure, UnitOfSoundPressure,
UnitOfTemperature,
UnitOfVolume,
) )
import homeassistant.core as ha import homeassistant.core as ha
from homeassistant.core import Event, HomeAssistant from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
import homeassistant.util.dt as dt_util
DOMAIN = "demo" DOMAIN = "demo"
@ -186,192 +173,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.bus.async_listen(EVENT_HOMEASSISTANT_START, demo_start_listener) hass.bus.async_listen(EVENT_HOMEASSISTANT_START, demo_start_listener)
# Create issues
async_create_issue(
hass,
DOMAIN,
"transmogrifier_deprecated",
breaks_in_ha_version="2023.1.1",
is_fixable=False,
learn_more_url="https://en.wiktionary.org/wiki/transmogrifier",
severity=IssueSeverity.WARNING,
translation_key="transmogrifier_deprecated",
)
async_create_issue(
hass,
DOMAIN,
"out_of_blinker_fluid",
breaks_in_ha_version="2023.1.1",
is_fixable=True,
learn_more_url="https://www.youtube.com/watch?v=b9rntRxLlbU",
severity=IssueSeverity.CRITICAL,
translation_key="out_of_blinker_fluid",
)
async_create_issue(
hass,
DOMAIN,
"unfixable_problem",
is_fixable=False,
learn_more_url="https://www.youtube.com/watch?v=dQw4w9WgXcQ",
severity=IssueSeverity.WARNING,
translation_key="unfixable_problem",
)
async_create_issue(
hass,
DOMAIN,
"bad_psu",
is_fixable=True,
learn_more_url="https://www.youtube.com/watch?v=b9rntRxLlbU",
severity=IssueSeverity.CRITICAL,
translation_key="bad_psu",
)
async_create_issue(
hass,
DOMAIN,
"cold_tea",
is_fixable=True,
severity=IssueSeverity.WARNING,
translation_key="cold_tea",
)
return True return True
def _generate_mean_statistics(
start: datetime.datetime, end: datetime.datetime, init_value: float, max_diff: float
) -> list[StatisticData]:
statistics: list[StatisticData] = []
mean = init_value
now = start
while now < end:
mean = mean + random() * max_diff - max_diff / 2
statistics.append(
{
"start": now,
"mean": mean,
"min": mean - random() * max_diff,
"max": mean + random() * max_diff,
}
)
now = now + datetime.timedelta(hours=1)
return statistics
async def _insert_sum_statistics(
hass: HomeAssistant,
metadata: StatisticMetaData,
start: datetime.datetime,
end: datetime.datetime,
max_diff: float,
) -> None:
statistics: list[StatisticData] = []
now = start
sum_ = 0.0
statistic_id = metadata["statistic_id"]
last_stats = await get_instance(hass).async_add_executor_job(
get_last_statistics, hass, 1, statistic_id, False, {"sum"}
)
if statistic_id in last_stats:
sum_ = last_stats[statistic_id][0]["sum"] or 0
while now < end:
sum_ = sum_ + random() * max_diff
statistics.append(
{
"start": now,
"sum": sum_,
}
)
now = now + datetime.timedelta(hours=1)
async_add_external_statistics(hass, metadata, statistics)
async def _insert_statistics(hass: HomeAssistant) -> None:
"""Insert some fake statistics."""
now = dt_util.now()
yesterday = now - datetime.timedelta(days=1)
yesterday_midnight = yesterday.replace(hour=0, minute=0, second=0, microsecond=0)
today_midnight = yesterday_midnight + datetime.timedelta(days=1)
# Fake yesterday's temperatures
metadata: StatisticMetaData = {
"source": DOMAIN,
"name": "Outdoor temperature",
"statistic_id": f"{DOMAIN}:temperature_outdoor",
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"has_mean": True,
"has_sum": False,
}
statistics = _generate_mean_statistics(yesterday_midnight, today_midnight, 15, 1)
async_add_external_statistics(hass, metadata, statistics)
# Add external energy consumption in kWh, ~ 12 kWh / day
# This should be possible to pick for the energy dashboard
metadata = {
"source": DOMAIN,
"name": "Energy consumption 1",
"statistic_id": f"{DOMAIN}:energy_consumption_kwh",
"unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR,
"has_mean": False,
"has_sum": True,
}
await _insert_sum_statistics(hass, metadata, yesterday_midnight, today_midnight, 1)
# Add external energy consumption in MWh, ~ 12 kWh / day
# This should not be possible to pick for the energy dashboard
metadata = {
"source": DOMAIN,
"name": "Energy consumption 2",
"statistic_id": f"{DOMAIN}:energy_consumption_mwh",
"unit_of_measurement": UnitOfEnergy.MEGA_WATT_HOUR,
"has_mean": False,
"has_sum": True,
}
await _insert_sum_statistics(
hass, metadata, yesterday_midnight, today_midnight, 0.001
)
# Add external gas consumption in m³, ~6 m3/day
# This should be possible to pick for the energy dashboard
metadata = {
"source": DOMAIN,
"name": "Gas consumption 1",
"statistic_id": f"{DOMAIN}:gas_consumption_m3",
"unit_of_measurement": UnitOfVolume.CUBIC_METERS,
"has_mean": False,
"has_sum": True,
}
await _insert_sum_statistics(
hass, metadata, yesterday_midnight, today_midnight, 0.5
)
# Add external gas consumption in ft³, ~180 ft3/day
# This should not be possible to pick for the energy dashboard
metadata = {
"source": DOMAIN,
"name": "Gas consumption 2",
"statistic_id": f"{DOMAIN}:gas_consumption_ft3",
"unit_of_measurement": UnitOfVolume.CUBIC_FEET,
"has_mean": False,
"has_sum": True,
}
await _insert_sum_statistics(hass, metadata, yesterday_midnight, today_midnight, 15)
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Set the config entry up.""" """Set the config entry up."""
# Set up demo platforms with config entry # Set up demo platforms with config entry
await hass.config_entries.async_forward_entry_setups( await hass.config_entries.async_forward_entry_setups(
config_entry, COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM config_entry, COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM
) )
if "recorder" in hass.config.components:
await _insert_statistics(hass)
return True return True

View file

@ -2,7 +2,6 @@
"domain": "demo", "domain": "demo",
"name": "Demo", "name": "Demo",
"documentation": "https://www.home-assistant.io/integrations/demo", "documentation": "https://www.home-assistant.io/integrations/demo",
"after_dependencies": ["recorder"],
"dependencies": ["conversation", "group", "zone"], "dependencies": ["conversation", "group", "zone"],
"codeowners": ["@home-assistant/core"], "codeowners": ["@home-assistant/core"],
"quality_scale": "internal", "quality_scale": "internal",

View file

@ -0,0 +1,212 @@
"""The Kitchen Sink integration contains demonstrations of various odds and ends.
This sets up a demo environment of features which are obscure or which represent
incorrect behavior, and are thus not wanted in the demo integration.
"""
from __future__ import annotations
import datetime
from random import random
from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.models import StatisticData, StatisticMetaData
from homeassistant.components.recorder.statistics import (
async_add_external_statistics,
get_last_statistics,
)
from homeassistant.const import UnitOfEnergy, UnitOfTemperature, UnitOfVolume
from homeassistant.core import HomeAssistant
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType
import homeassistant.util.dt as dt_util
DOMAIN = "kitchen_sink"
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the demo environment."""
# Create issues
_create_issues(hass)
# Insert some external statistics
if "recorder" in hass.config.components:
await _insert_statistics(hass)
return True
def _create_issues(hass):
"""Create some issue registry issues."""
async_create_issue(
hass,
DOMAIN,
"transmogrifier_deprecated",
breaks_in_ha_version="2023.1.1",
is_fixable=False,
learn_more_url="https://en.wiktionary.org/wiki/transmogrifier",
severity=IssueSeverity.WARNING,
translation_key="transmogrifier_deprecated",
)
async_create_issue(
hass,
DOMAIN,
"out_of_blinker_fluid",
breaks_in_ha_version="2023.1.1",
is_fixable=True,
learn_more_url="https://www.youtube.com/watch?v=b9rntRxLlbU",
severity=IssueSeverity.CRITICAL,
translation_key="out_of_blinker_fluid",
)
async_create_issue(
hass,
DOMAIN,
"unfixable_problem",
is_fixable=False,
learn_more_url="https://www.youtube.com/watch?v=dQw4w9WgXcQ",
severity=IssueSeverity.WARNING,
translation_key="unfixable_problem",
)
async_create_issue(
hass,
DOMAIN,
"bad_psu",
is_fixable=True,
learn_more_url="https://www.youtube.com/watch?v=b9rntRxLlbU",
severity=IssueSeverity.CRITICAL,
translation_key="bad_psu",
)
async_create_issue(
hass,
DOMAIN,
"cold_tea",
is_fixable=True,
severity=IssueSeverity.WARNING,
translation_key="cold_tea",
)
def _generate_mean_statistics(
start: datetime.datetime, end: datetime.datetime, init_value: float, max_diff: float
) -> list[StatisticData]:
statistics: list[StatisticData] = []
mean = init_value
now = start
while now < end:
mean = mean + random() * max_diff - max_diff / 2
statistics.append(
{
"start": now,
"mean": mean,
"min": mean - random() * max_diff,
"max": mean + random() * max_diff,
}
)
now = now + datetime.timedelta(hours=1)
return statistics
async def _insert_sum_statistics(
hass: HomeAssistant,
metadata: StatisticMetaData,
start: datetime.datetime,
end: datetime.datetime,
max_diff: float,
) -> None:
statistics: list[StatisticData] = []
now = start
sum_ = 0.0
statistic_id = metadata["statistic_id"]
last_stats = await get_instance(hass).async_add_executor_job(
get_last_statistics, hass, 1, statistic_id, False, {"sum"}
)
if statistic_id in last_stats:
sum_ = last_stats[statistic_id][0]["sum"] or 0
while now < end:
sum_ = sum_ + random() * max_diff
statistics.append(
{
"start": now,
"sum": sum_,
}
)
now = now + datetime.timedelta(hours=1)
async_add_external_statistics(hass, metadata, statistics)
async def _insert_statistics(hass: HomeAssistant) -> None:
"""Insert some fake statistics."""
now = dt_util.now()
yesterday = now - datetime.timedelta(days=1)
yesterday_midnight = yesterday.replace(hour=0, minute=0, second=0, microsecond=0)
today_midnight = yesterday_midnight + datetime.timedelta(days=1)
# Fake yesterday's temperatures
metadata: StatisticMetaData = {
"source": DOMAIN,
"name": "Outdoor temperature",
"statistic_id": f"{DOMAIN}:temperature_outdoor",
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"has_mean": True,
"has_sum": False,
}
statistics = _generate_mean_statistics(yesterday_midnight, today_midnight, 15, 1)
async_add_external_statistics(hass, metadata, statistics)
# Add external energy consumption in kWh, ~ 12 kWh / day
# This should be possible to pick for the energy dashboard
metadata = {
"source": DOMAIN,
"name": "Energy consumption 1",
"statistic_id": f"{DOMAIN}:energy_consumption_kwh",
"unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR,
"has_mean": False,
"has_sum": True,
}
await _insert_sum_statistics(hass, metadata, yesterday_midnight, today_midnight, 1)
# Add external energy consumption in MWh, ~ 12 kWh / day
# This should not be possible to pick for the energy dashboard
metadata = {
"source": DOMAIN,
"name": "Energy consumption 2",
"statistic_id": f"{DOMAIN}:energy_consumption_mwh",
"unit_of_measurement": UnitOfEnergy.MEGA_WATT_HOUR,
"has_mean": False,
"has_sum": True,
}
await _insert_sum_statistics(
hass, metadata, yesterday_midnight, today_midnight, 0.001
)
# Add external gas consumption in m³, ~6 m3/day
# This should be possible to pick for the energy dashboard
metadata = {
"source": DOMAIN,
"name": "Gas consumption 1",
"statistic_id": f"{DOMAIN}:gas_consumption_m3",
"unit_of_measurement": UnitOfVolume.CUBIC_METERS,
"has_mean": False,
"has_sum": True,
}
await _insert_sum_statistics(
hass, metadata, yesterday_midnight, today_midnight, 0.5
)
# Add external gas consumption in ft³, ~180 ft3/day
# This should not be possible to pick for the energy dashboard
metadata = {
"source": DOMAIN,
"name": "Gas consumption 2",
"statistic_id": f"{DOMAIN}:gas_consumption_ft3",
"unit_of_measurement": UnitOfVolume.CUBIC_FEET,
"has_mean": False,
"has_sum": True,
}
await _insert_sum_statistics(hass, metadata, yesterday_midnight, today_midnight, 15)

View file

@ -0,0 +1,9 @@
{
"after_dependencies": ["recorder"],
"codeowners": ["@home-assistant/core"],
"documentation": "https://www.home-assistant.io/integrations/kitchen_sink",
"domain": "kitchen_sink",
"iot_class": "calculated",
"name": "Everything but the Kitchen Sink",
"quality_scale": "internal"
}

View file

@ -2673,6 +2673,12 @@
"config_flow": false, "config_flow": false,
"iot_class": "local_push" "iot_class": "local_push"
}, },
"kitchen_sink": {
"name": "Everything but the Kitchen Sink",
"integration_type": "hub",
"config_flow": false,
"iot_class": "calculated"
},
"kiwi": { "kiwi": {
"name": "KIWI", "name": "KIWI",
"integration_type": "hub", "integration_type": "hub",

View file

@ -1,25 +1,12 @@
"""The tests for the Demo component.""" """The tests for the Demo component."""
import datetime
from http import HTTPStatus
import json import json
from unittest.mock import ANY, patch from unittest.mock import patch
import pytest import pytest
from homeassistant.components.demo import DOMAIN from homeassistant.components.demo import DOMAIN
from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.statistics import (
async_add_external_statistics,
get_last_statistics,
list_statistic_ids,
)
from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN
from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.json import JSONEncoder
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
from tests.components.recorder.common import async_wait_recording_done
@pytest.fixture @pytest.fixture
@ -50,264 +37,3 @@ async def test_setting_up_demo(mock_history, hass):
"Unable to convert all demo entities to JSON. " "Unable to convert all demo entities to JSON. "
"Wrong data in state machine!" "Wrong data in state machine!"
) )
async def test_demo_statistics(recorder_mock, mock_history, hass):
"""Test that the demo components makes some statistics available."""
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
await hass.async_start()
await async_wait_recording_done(hass)
statistic_ids = await get_instance(hass).async_add_executor_job(
list_statistic_ids, hass
)
assert {
"display_unit_of_measurement": "°C",
"has_mean": True,
"has_sum": False,
"name": "Outdoor temperature",
"source": "demo",
"statistic_id": "demo:temperature_outdoor",
"statistics_unit_of_measurement": "°C",
"unit_class": "temperature",
} in statistic_ids
assert {
"display_unit_of_measurement": "kWh",
"has_mean": False,
"has_sum": True,
"name": "Energy consumption 1",
"source": "demo",
"statistic_id": "demo:energy_consumption_kwh",
"statistics_unit_of_measurement": "kWh",
"unit_class": "energy",
} in statistic_ids
async def test_demo_statistics_growth(recorder_mock, mock_history, hass):
"""Test that the demo sum statistics adds to the previous state."""
hass.config.units = US_CUSTOMARY_SYSTEM
now = dt_util.now()
last_week = now - datetime.timedelta(days=7)
last_week_midnight = last_week.replace(hour=0, minute=0, second=0, microsecond=0)
statistic_id = f"{DOMAIN}:energy_consumption_kwh"
metadata = {
"source": DOMAIN,
"name": "Energy consumption 1",
"statistic_id": statistic_id,
"unit_of_measurement": "",
"has_mean": False,
"has_sum": True,
}
statistics = [
{
"start": last_week_midnight,
"sum": 2**20,
}
]
async_add_external_statistics(hass, metadata, statistics)
await async_wait_recording_done(hass)
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
await hass.async_start()
await async_wait_recording_done(hass)
statistics = await get_instance(hass).async_add_executor_job(
get_last_statistics, hass, 1, statistic_id, False, {"sum"}
)
assert statistics[statistic_id][0]["sum"] > 2**20
assert statistics[statistic_id][0]["sum"] <= (2**20 + 24)
async def test_issues_created(mock_history, hass, hass_client, hass_ws_client):
"""Test issues are created and can be fixed."""
assert await async_setup_component(hass, REPAIRS_DOMAIN, {REPAIRS_DOMAIN: {}})
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
await hass.async_start()
ws_client = await hass_ws_client(hass)
client = await hass_client()
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
msg = await ws_client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
{
"breaks_in_ha_version": "2023.1.1",
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"ignored": False,
"is_fixable": False,
"issue_id": "transmogrifier_deprecated",
"issue_domain": None,
"learn_more_url": "https://en.wiktionary.org/wiki/transmogrifier",
"severity": "warning",
"translation_key": "transmogrifier_deprecated",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": "2023.1.1",
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"ignored": False,
"is_fixable": True,
"issue_id": "out_of_blinker_fluid",
"issue_domain": None,
"learn_more_url": "https://www.youtube.com/watch?v=b9rntRxLlbU",
"severity": "critical",
"translation_key": "out_of_blinker_fluid",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"ignored": False,
"is_fixable": False,
"issue_id": "unfixable_problem",
"issue_domain": None,
"learn_more_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"severity": "warning",
"translation_key": "unfixable_problem",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"ignored": False,
"is_fixable": True,
"issue_domain": None,
"issue_id": "bad_psu",
"learn_more_url": "https://www.youtube.com/watch?v=b9rntRxLlbU",
"severity": "critical",
"translation_key": "bad_psu",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"is_fixable": True,
"issue_domain": None,
"issue_id": "cold_tea",
"learn_more_url": None,
"severity": "warning",
"translation_key": "cold_tea",
"translation_placeholders": None,
"ignored": False,
},
]
}
url = "/api/repairs/issues/fix"
resp = await client.post(
url, json={"handler": "demo", "issue_id": "out_of_blinker_fluid"}
)
assert resp.status == HTTPStatus.OK
data = await resp.json()
flow_id = data["flow_id"]
assert data == {
"data_schema": [],
"description_placeholders": None,
"errors": None,
"flow_id": ANY,
"handler": "demo",
"last_step": None,
"step_id": "confirm",
"type": "form",
}
url = f"/api/repairs/issues/fix/{flow_id}"
resp = await client.post(url)
assert resp.status == HTTPStatus.OK
data = await resp.json()
flow_id = data["flow_id"]
assert data == {
"description": None,
"description_placeholders": None,
"flow_id": flow_id,
"handler": "demo",
"type": "create_entry",
"version": 1,
}
await ws_client.send_json({"id": 4, "type": "repairs/list_issues"})
msg = await ws_client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
{
"breaks_in_ha_version": "2023.1.1",
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"ignored": False,
"is_fixable": False,
"issue_id": "transmogrifier_deprecated",
"issue_domain": None,
"learn_more_url": "https://en.wiktionary.org/wiki/transmogrifier",
"severity": "warning",
"translation_key": "transmogrifier_deprecated",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"ignored": False,
"is_fixable": False,
"issue_id": "unfixable_problem",
"issue_domain": None,
"learn_more_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"severity": "warning",
"translation_key": "unfixable_problem",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"ignored": False,
"is_fixable": True,
"issue_domain": None,
"issue_id": "bad_psu",
"learn_more_url": "https://www.youtube.com/watch?v=b9rntRxLlbU",
"severity": "critical",
"translation_key": "bad_psu",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": "demo",
"is_fixable": True,
"issue_domain": None,
"issue_id": "cold_tea",
"learn_more_url": None,
"severity": "warning",
"translation_key": "cold_tea",
"translation_placeholders": None,
"ignored": False,
},
]
}

View file

@ -0,0 +1 @@
"""Tests for the Everything but the Kitchen Sink integration."""

View file

@ -0,0 +1,287 @@
"""The tests for the Everything but the Kitchen Sink integration."""
import datetime
from http import HTTPStatus
from unittest.mock import ANY
import pytest
from homeassistant.components.kitchen_sink import DOMAIN
from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.statistics import (
async_add_external_statistics,
get_last_statistics,
list_statistic_ids,
)
from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
from tests.components.recorder.common import async_wait_recording_done
@pytest.fixture
def mock_history(hass):
"""Mock history component loaded."""
hass.config.components.add("history")
async def test_demo_statistics(recorder_mock, mock_history, hass):
"""Test that the kitchen sink component makes some statistics available."""
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
await hass.async_start()
await async_wait_recording_done(hass)
statistic_ids = await get_instance(hass).async_add_executor_job(
list_statistic_ids, hass
)
assert {
"display_unit_of_measurement": "°C",
"has_mean": True,
"has_sum": False,
"name": "Outdoor temperature",
"source": DOMAIN,
"statistic_id": f"{DOMAIN}:temperature_outdoor",
"statistics_unit_of_measurement": "°C",
"unit_class": "temperature",
} in statistic_ids
assert {
"display_unit_of_measurement": "kWh",
"has_mean": False,
"has_sum": True,
"name": "Energy consumption 1",
"source": DOMAIN,
"statistic_id": f"{DOMAIN}:energy_consumption_kwh",
"statistics_unit_of_measurement": "kWh",
"unit_class": "energy",
} in statistic_ids
async def test_demo_statistics_growth(recorder_mock, mock_history, hass):
"""Test that the kitchen sink sum statistics adds to the previous state."""
hass.config.units = US_CUSTOMARY_SYSTEM
now = dt_util.now()
last_week = now - datetime.timedelta(days=7)
last_week_midnight = last_week.replace(hour=0, minute=0, second=0, microsecond=0)
statistic_id = f"{DOMAIN}:energy_consumption_kwh"
metadata = {
"source": DOMAIN,
"name": "Energy consumption 1",
"statistic_id": statistic_id,
"unit_of_measurement": "",
"has_mean": False,
"has_sum": True,
}
statistics = [
{
"start": last_week_midnight,
"sum": 2**20,
}
]
async_add_external_statistics(hass, metadata, statistics)
await async_wait_recording_done(hass)
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
await hass.async_start()
await async_wait_recording_done(hass)
statistics = await get_instance(hass).async_add_executor_job(
get_last_statistics, hass, 1, statistic_id, False, {"sum"}
)
assert statistics[statistic_id][0]["sum"] > 2**20
assert statistics[statistic_id][0]["sum"] <= (2**20 + 24)
async def test_issues_created(mock_history, hass, hass_client, hass_ws_client):
"""Test issues are created and can be fixed."""
assert await async_setup_component(hass, REPAIRS_DOMAIN, {REPAIRS_DOMAIN: {}})
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
await hass.async_start()
ws_client = await hass_ws_client(hass)
client = await hass_client()
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
msg = await ws_client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
{
"breaks_in_ha_version": "2023.1.1",
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"ignored": False,
"is_fixable": False,
"issue_id": "transmogrifier_deprecated",
"issue_domain": None,
"learn_more_url": "https://en.wiktionary.org/wiki/transmogrifier",
"severity": "warning",
"translation_key": "transmogrifier_deprecated",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": "2023.1.1",
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"ignored": False,
"is_fixable": True,
"issue_id": "out_of_blinker_fluid",
"issue_domain": None,
"learn_more_url": "https://www.youtube.com/watch?v=b9rntRxLlbU",
"severity": "critical",
"translation_key": "out_of_blinker_fluid",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"ignored": False,
"is_fixable": False,
"issue_id": "unfixable_problem",
"issue_domain": None,
"learn_more_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"severity": "warning",
"translation_key": "unfixable_problem",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"ignored": False,
"is_fixable": True,
"issue_domain": None,
"issue_id": "bad_psu",
"learn_more_url": "https://www.youtube.com/watch?v=b9rntRxLlbU",
"severity": "critical",
"translation_key": "bad_psu",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"is_fixable": True,
"issue_domain": None,
"issue_id": "cold_tea",
"learn_more_url": None,
"severity": "warning",
"translation_key": "cold_tea",
"translation_placeholders": None,
"ignored": False,
},
]
}
url = "/api/repairs/issues/fix"
resp = await client.post(
url, json={"handler": DOMAIN, "issue_id": "out_of_blinker_fluid"}
)
assert resp.status == HTTPStatus.OK
data = await resp.json()
flow_id = data["flow_id"]
assert data == {
"data_schema": [],
"description_placeholders": None,
"errors": None,
"flow_id": ANY,
"handler": DOMAIN,
"last_step": None,
"step_id": "confirm",
"type": "form",
}
url = f"/api/repairs/issues/fix/{flow_id}"
resp = await client.post(url)
assert resp.status == HTTPStatus.OK
data = await resp.json()
flow_id = data["flow_id"]
assert data == {
"description": None,
"description_placeholders": None,
"flow_id": flow_id,
"handler": DOMAIN,
"type": "create_entry",
"version": 1,
}
await ws_client.send_json({"id": 4, "type": "repairs/list_issues"})
msg = await ws_client.receive_json()
assert msg["success"]
assert msg["result"] == {
"issues": [
{
"breaks_in_ha_version": "2023.1.1",
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"ignored": False,
"is_fixable": False,
"issue_id": "transmogrifier_deprecated",
"issue_domain": None,
"learn_more_url": "https://en.wiktionary.org/wiki/transmogrifier",
"severity": "warning",
"translation_key": "transmogrifier_deprecated",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"ignored": False,
"is_fixable": False,
"issue_id": "unfixable_problem",
"issue_domain": None,
"learn_more_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"severity": "warning",
"translation_key": "unfixable_problem",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"ignored": False,
"is_fixable": True,
"issue_domain": None,
"issue_id": "bad_psu",
"learn_more_url": "https://www.youtube.com/watch?v=b9rntRxLlbU",
"severity": "critical",
"translation_key": "bad_psu",
"translation_placeholders": None,
},
{
"breaks_in_ha_version": None,
"created": ANY,
"dismissed_version": None,
"domain": DOMAIN,
"is_fixable": True,
"issue_domain": None,
"issue_id": "cold_tea",
"learn_more_url": None,
"severity": "warning",
"translation_key": "cold_tea",
"translation_placeholders": None,
"ignored": False,
},
]
}