core/homeassistant/components/demo/__init__.py

335 lines
9.2 KiB
Python
Raw Normal View History

"""Set up the demo environment that mimics interaction with devices."""
import asyncio
import datetime
from random import random
2014-11-02 17:41:41 +00:00
from homeassistant import bootstrap, config_entries
from homeassistant.components import persistent_notification
from homeassistant.components.recorder.statistics import (
async_add_external_statistics,
get_last_statistics,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_ENTITY_ID,
EVENT_HOMEASSISTANT_START,
SOUND_PRESSURE_DB,
)
import homeassistant.core as ha
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
import homeassistant.util.dt as dt_util
2014-11-02 17:41:41 +00:00
2019-07-31 19:25:30 +00:00
DOMAIN = "demo"
COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM = [
2019-07-31 19:25:30 +00:00
"air_quality",
"alarm_control_panel",
"binary_sensor",
"button",
2019-07-31 19:25:30 +00:00
"camera",
"climate",
"cover",
"fan",
"humidifier",
2019-07-31 19:25:30 +00:00
"light",
"lock",
"media_player",
"number",
"select",
2019-07-31 19:25:30 +00:00
"sensor",
Add siren platform (#48309) * Add siren platform * add more supported flags and an ability to set siren duration * tone can be int or string * fix typing * fix typehinting * fix typehints * implement a proposed approach based on discussion * Address comments * fix tests * Small fix * Update homeassistant/components/demo/siren.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/demo/siren.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/demo/siren.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/demo/siren.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/demo/siren.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * typing * use class attributes * fix naming * remove device from service description * Filter out params from turn on service * fix tests * fix bugs and tests * add test * Combine is_on test with turn on/off/toggle service tests * Update homeassistant/components/siren/__init__.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * fix filtering of turn_on attributes * none check * remove services and attributes for volume level, default duration, and default tone * Update homeassistant/components/siren/__init__.py Co-authored-by: Franck Nijhof <frenck@frenck.nl> * Update homeassistant/components/siren/__init__.py Co-authored-by: Franck Nijhof <frenck@frenck.nl> * Update homeassistant/components/siren/__init__.py Co-authored-by: Franck Nijhof <frenck@frenck.nl> * import final * Update homeassistant/components/siren/__init__.py Co-authored-by: Franck Nijhof <frenck@frenck.nl> * Fix typing and used TypedDict for service parameters * remove is_on function * remove class name redundancy * remove extra service descriptions * switch to positive_int * fix schema for tone Co-authored-by: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2021-07-11 20:51:11 +00:00
"siren",
2019-07-31 19:25:30 +00:00
"switch",
"vacuum",
"water_heater",
]
COMPONENTS_WITH_DEMO_PLATFORM = [
2019-07-31 19:25:30 +00:00
"tts",
"stt",
2019-07-31 19:25:30 +00:00
"mailbox",
"notify",
"image_processing",
"calendar",
"device_tracker",
2015-12-05 09:12:38 +00:00
]
2014-11-02 17:41:41 +00:00
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the demo environment."""
if DOMAIN not in config:
return True
2014-11-02 17:41:41 +00:00
if not hass.config_entries.async_entries(DOMAIN):
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={}
)
)
2014-11-29 06:49:29 +00:00
# Set up demo platforms
for platform in COMPONENTS_WITH_DEMO_PLATFORM:
2019-07-31 19:25:30 +00:00
hass.async_create_task(
hass.helpers.discovery.async_load_platform(platform, DOMAIN, {}, config)
2019-07-31 19:25:30 +00:00
)
2014-11-02 17:41:41 +00:00
config.setdefault(ha.DOMAIN, {})
config.setdefault(DOMAIN, {})
# Set up sun
2015-04-26 00:44:05 +00:00
if not hass.config.latitude:
hass.config.latitude = 32.87336
2015-04-26 00:44:05 +00:00
if not hass.config.longitude:
hass.config.longitude = 117.22743
2015-04-26 00:44:05 +00:00
2019-07-31 19:25:30 +00:00
tasks = [bootstrap.async_setup_component(hass, "sun", config)]
2014-11-02 17:41:41 +00:00
# Set up input select
2019-07-31 19:25:30 +00:00
tasks.append(
bootstrap.async_setup_component(
hass,
"input_select",
{
"input_select": {
"living_room_preset": {
"options": ["Visitors", "Visitors with kids", "Home Alone"]
},
"who_cooks": {
"icon": "mdi:panda",
"initial": "Anne Therese",
"name": "Cook today",
"options": ["Paulus", "Anne Therese"],
},
}
},
)
)
# Set up input boolean
2019-07-31 19:25:30 +00:00
tasks.append(
bootstrap.async_setup_component(
hass,
"input_boolean",
{
"input_boolean": {
"notify": {
"icon": "mdi:car",
"initial": False,
"name": "Notify Anne Therese is home",
}
}
},
)
)
# Set up input button
tasks.append(
bootstrap.async_setup_component(
hass,
"input_button",
{
"input_button": {
"bell": {
"icon": "mdi:bell-ring-outline",
"name": "Ring bell",
}
}
},
)
)
# Set up input number
2019-07-31 19:25:30 +00:00
tasks.append(
bootstrap.async_setup_component(
hass,
"input_number",
{
"input_number": {
"noise_allowance": {
"icon": "mdi:bell-ring",
"min": 0,
"max": 10,
"name": "Allowed Noise",
"unit_of_measurement": SOUND_PRESSURE_DB,
2019-07-31 19:25:30 +00:00
}
}
},
)
)
results = await asyncio.gather(*tasks)
if any(not result for result in results):
return False
2014-11-02 17:41:41 +00:00
# Set up example persistent notification
persistent_notification.async_create(
hass,
"This is an example of a persistent notification.",
title="Example Notification",
2019-07-31 19:25:30 +00:00
)
async def demo_start_listener(_event):
"""Finish set up."""
await finish_setup(hass, config)
hass.bus.async_listen(EVENT_HOMEASSISTANT_START, demo_start_listener)
return True
def _generate_mean_statistics(start, end, init_value, max_diff):
statistics = []
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
def _generate_sum_statistics(start, end, init_value, max_diff):
statistics = []
now = start
sum_ = init_value
while now < end:
sum_ = sum_ + random() * max_diff
statistics.append(
{
"start": now,
"sum": sum_,
}
)
now = now + datetime.timedelta(hours=1)
return statistics
async def _insert_statistics(hass):
"""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)
# Fake yesterday's temperatures
metadata = {
"source": DOMAIN,
"statistic_id": f"{DOMAIN}:temperature_outdoor",
"unit_of_measurement": "°C",
"has_mean": True,
"has_sum": False,
}
statistics = _generate_mean_statistics(
yesterday_midnight, yesterday_midnight + datetime.timedelta(days=1), 15, 1
)
async_add_external_statistics(hass, metadata, statistics)
# Fake yesterday's energy consumption
metadata = {
"source": DOMAIN,
"statistic_id": f"{DOMAIN}:energy_consumption",
"unit_of_measurement": "kWh",
"has_mean": False,
"has_sum": True,
}
statistic_id = f"{DOMAIN}:energy_consumption"
sum_ = 0
last_stats = await hass.async_add_executor_job(
get_last_statistics, hass, 1, statistic_id, True
)
if "domain:energy_consumption" in last_stats:
sum_ = last_stats["domain.electricity_total"]["sum"] or 0
statistics = _generate_sum_statistics(
yesterday_midnight, yesterday_midnight + datetime.timedelta(days=1), sum_, 1
)
async_add_external_statistics(hass, metadata, statistics)
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Set the config entry up."""
# Set up demo platforms with config entry
for platform in COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
if "recorder" in hass.config.components:
await _insert_statistics(hass)
return True
async def finish_setup(hass, config):
"""Finish set up once demo platforms are set up."""
switches = None
lights = None
while not switches and not lights:
# Not all platforms might be loaded.
if switches is not None:
await asyncio.sleep(0)
switches = sorted(hass.states.async_entity_ids("switch"))
lights = sorted(hass.states.async_entity_ids("light"))
2016-01-26 05:27:36 +00:00
# Set up scripts
await bootstrap.async_setup_component(
2019-07-31 19:25:30 +00:00
hass,
"script",
{
"script": {
"demo": {
"alias": f"Toggle {lights[0].split('.')[1]}",
2019-07-31 19:25:30 +00:00
"sequence": [
{
"service": "light.turn_off",
"data": {ATTR_ENTITY_ID: lights[0]},
},
{"delay": {"seconds": 5}},
{
"service": "light.turn_on",
"data": {ATTR_ENTITY_ID: lights[0]},
},
{"delay": {"seconds": 5}},
{
"service": "light.turn_off",
"data": {ATTR_ENTITY_ID: lights[0]},
},
],
}
}
},
)
# Set up scenes
await bootstrap.async_setup_component(
2019-07-31 19:25:30 +00:00
hass,
"scene",
{
"scene": [
{
"name": "Romantic lights",
"entities": {
lights[0]: True,
lights[1]: {
"state": "on",
"xy_color": [0.33, 0.66],
"brightness": 200,
},
},
},
{
"name": "Switch on and off",
"entities": {switches[0]: True, switches[1]: False},
},
]
},
)