Merge pull request #54117 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2021-08-05 23:18:48 -07:00 committed by GitHub
commit a07048aacf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 145 additions and 44 deletions

View File

@ -71,9 +71,6 @@ from homeassistant.loader import bind_hass
from homeassistant.util.dt import parse_datetime from homeassistant.util.dt import parse_datetime
from .config import AutomationConfig, async_validate_config_item from .config import AutomationConfig, async_validate_config_item
# Not used except by packages to check config structure
from .config import PLATFORM_SCHEMA # noqa: F401
from .const import ( from .const import (
CONF_ACTION, CONF_ACTION,
CONF_INITIAL_STATE, CONF_INITIAL_STATE,

View File

@ -37,6 +37,8 @@ from .helpers import async_get_blueprints
# mypy: allow-untyped-calls, allow-untyped-defs # mypy: allow-untyped-calls, allow-untyped-defs
# mypy: no-check-untyped-defs, no-warn-return-any # mypy: no-check-untyped-defs, no-warn-return-any
PACKAGE_MERGE_HINT = "list"
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA]) _CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
PLATFORM_SCHEMA = vol.All( PLATFORM_SCHEMA = vol.All(

View File

@ -81,12 +81,12 @@ def _retrieve_max_kb_s_received_state(status: FritzStatus, last_value: str) -> f
def _retrieve_gb_sent_state(status: FritzStatus, last_value: str) -> float: def _retrieve_gb_sent_state(status: FritzStatus, last_value: str) -> float:
"""Return upload total data.""" """Return upload total data."""
return round(status.bytes_sent * 8 / 1000 / 1000 / 1000, 1) # type: ignore[no-any-return] return round(status.bytes_sent / 1000 / 1000 / 1000, 1) # type: ignore[no-any-return]
def _retrieve_gb_received_state(status: FritzStatus, last_value: str) -> float: def _retrieve_gb_received_state(status: FritzStatus, last_value: str) -> float:
"""Return download total data.""" """Return download total data."""
return round(status.bytes_received * 8 / 1000 / 1000 / 1000, 1) # type: ignore[no-any-return] return round(status.bytes_received / 1000 / 1000 / 1000, 1) # type: ignore[no-any-return]
class SensorData(TypedDict, total=False): class SensorData(TypedDict, total=False):

View File

@ -13,7 +13,6 @@ from fritzconnection.core.exceptions import (
FritzSecurityError, FritzSecurityError,
FritzServiceError, FritzServiceError,
) )
import slugify as unicode_slug
import xmltodict import xmltodict
from homeassistant.components.network import async_get_source_ip from homeassistant.components.network import async_get_source_ip
@ -248,10 +247,18 @@ def wifi_entities_list(
) )
if network_info: if network_info:
ssid = network_info["NewSSID"] ssid = network_info["NewSSID"]
if unicode_slug.slugify(ssid, lowercase=False) in networks.values(): _LOGGER.debug("SSID from device: <%s>", ssid)
if (
slugify(
ssid,
)
in [slugify(v) for v in networks.values()]
):
_LOGGER.debug("SSID duplicated, adding suffix")
networks[i] = f'{ssid} {std_table[network_info["NewStandard"]]}' networks[i] = f'{ssid} {std_table[network_info["NewStandard"]]}'
else: else:
networks[i] = ssid networks[i] = ssid
_LOGGER.debug("SSID normalized: <%s>", networks[i])
return [ return [
FritzBoxWifiSwitch(fritzbox_tools, device_friendly_name, net, network_name) FritzBoxWifiSwitch(fritzbox_tools, device_friendly_name, net, network_name)

View File

@ -228,17 +228,17 @@ class HomeAccessory(Accessory):
self.config = config or {} self.config = config or {}
domain = split_entity_id(entity_id)[0].replace("_", " ") domain = split_entity_id(entity_id)[0].replace("_", " ")
if ATTR_MANUFACTURER in self.config: if self.config.get(ATTR_MANUFACTURER) is not None:
manufacturer = self.config[ATTR_MANUFACTURER] manufacturer = self.config[ATTR_MANUFACTURER]
elif ATTR_INTEGRATION in self.config: elif self.config.get(ATTR_INTEGRATION) is not None:
manufacturer = self.config[ATTR_INTEGRATION].replace("_", " ").title() manufacturer = self.config[ATTR_INTEGRATION].replace("_", " ").title()
else: else:
manufacturer = f"{MANUFACTURER} {domain}".title() manufacturer = f"{MANUFACTURER} {domain}".title()
if ATTR_MODEL in self.config: if self.config.get(ATTR_MODEL) is not None:
model = self.config[ATTR_MODEL] model = self.config[ATTR_MODEL]
else: else:
model = domain.title() model = domain.title()
if ATTR_SOFTWARE_VERSION in self.config: if self.config.get(ATTR_SOFTWARE_VERSION) is not None:
sw_version = format_sw_version(self.config[ATTR_SOFTWARE_VERSION]) sw_version = format_sw_version(self.config[ATTR_SOFTWARE_VERSION])
else: else:
sw_version = __version__ sw_version = __version__

View File

@ -57,6 +57,8 @@ VALVE_TYPE = {
ACTIVATE_ONLY_SWITCH_DOMAINS = {"scene", "script"} ACTIVATE_ONLY_SWITCH_DOMAINS = {"scene", "script"}
ACTIVATE_ONLY_RESET_SECONDS = 10
@TYPES.register("Outlet") @TYPES.register("Outlet")
class Outlet(HomeAccessory): class Outlet(HomeAccessory):
@ -141,7 +143,7 @@ class Switch(HomeAccessory):
self.async_call_service(self._domain, service, params) self.async_call_service(self._domain, service, params)
if self.activate_only: if self.activate_only:
async_call_later(self.hass, 1, self.reset_switch) async_call_later(self.hass, ACTIVATE_ONLY_RESET_SECONDS, self.reset_switch)
@callback @callback
def async_update_state(self, new_state): def async_update_state(self, new_state):

View File

@ -3,7 +3,7 @@
"name": "Litter-Robot", "name": "Litter-Robot",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/litterrobot", "documentation": "https://www.home-assistant.io/integrations/litterrobot",
"requirements": ["pylitterbot==2021.7.2"], "requirements": ["pylitterbot==2021.8.0"],
"codeowners": ["@natekspencer"], "codeowners": ["@natekspencer"],
"iot_class": "cloud_polling" "iot_class": "cloud_polling"
} }

View File

@ -18,6 +18,7 @@ from homeassistant.const import (
CONF_PASSWORD, CONF_PASSWORD,
CONF_TYPE, CONF_TYPE,
CONF_USERNAME, CONF_USERNAME,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
ENERGY_KILO_WATT_HOUR, ENERGY_KILO_WATT_HOUR,
@ -204,6 +205,8 @@ class SAJsensor(SensorEntity):
"""Return the device class the sensor belongs to.""" """Return the device class the sensor belongs to."""
if self.unit_of_measurement == POWER_WATT: if self.unit_of_measurement == POWER_WATT:
return DEVICE_CLASS_POWER return DEVICE_CLASS_POWER
if self.unit_of_measurement == ENERGY_KILO_WATT_HOUR:
return DEVICE_CLASS_ENERGY
if ( if (
self.unit_of_measurement == TEMP_CELSIUS self.unit_of_measurement == TEMP_CELSIUS
or self._sensor.unit == TEMP_FAHRENHEIT or self._sensor.unit == TEMP_FAHRENHEIT

View File

@ -39,6 +39,8 @@ from .const import (
) )
from .helpers import async_get_blueprints from .helpers import async_get_blueprints
PACKAGE_MERGE_HINT = "dict"
SCRIPT_ENTITY_SCHEMA = make_script_schema( SCRIPT_ENTITY_SCHEMA = make_script_schema(
{ {
vol.Optional(CONF_ALIAS): cv.string, vol.Optional(CONF_ALIAS): cv.string,

View File

@ -285,7 +285,6 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity):
self._unit: None | str | Callable[[dict], str] = unit self._unit: None | str | Callable[[dict], str] = unit
self._unique_id: str = f"{super().unique_id}-{self.attribute}" self._unique_id: str = f"{super().unique_id}-{self.attribute}"
self._name = get_entity_name(wrapper.device, block, self.description.name) self._name = get_entity_name(wrapper.device, block, self.description.name)
self._last_value: str | None = None
@property @property
def unique_id(self) -> str: def unique_id(self) -> str:

View File

@ -1,9 +1,12 @@
"""Sensor for Shelly.""" """Sensor for Shelly."""
from __future__ import annotations from __future__ import annotations
from datetime import datetime from datetime import timedelta
import logging
from typing import Final, cast from typing import Final, cast
import aioshelly
from homeassistant.components import sensor from homeassistant.components import sensor
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -23,6 +26,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.util import dt from homeassistant.util import dt
from . import ShellyDeviceWrapper
from .const import LAST_RESET_NEVER, LAST_RESET_UPTIME, SHAIR_MAX_WORK_HOURS from .const import LAST_RESET_NEVER, LAST_RESET_UPTIME, SHAIR_MAX_WORK_HOURS
from .entity import ( from .entity import (
BlockAttributeDescription, BlockAttributeDescription,
@ -35,6 +39,8 @@ from .entity import (
) )
from .utils import get_device_uptime, temperature_unit from .utils import get_device_uptime, temperature_unit
_LOGGER: Final = logging.getLogger(__name__)
SENSORS: Final = { SENSORS: Final = {
("device", "battery"): BlockAttributeDescription( ("device", "battery"): BlockAttributeDescription(
name="Battery", name="Battery",
@ -255,9 +261,39 @@ async def async_setup_entry(
class ShellySensor(ShellyBlockAttributeEntity, SensorEntity): class ShellySensor(ShellyBlockAttributeEntity, SensorEntity):
"""Represent a shelly sensor.""" """Represent a shelly sensor."""
def __init__(
self,
wrapper: ShellyDeviceWrapper,
block: aioshelly.Block,
attribute: str,
description: BlockAttributeDescription,
) -> None:
"""Initialize sensor."""
super().__init__(wrapper, block, attribute, description)
self._last_value: float | None = None
if description.last_reset == LAST_RESET_NEVER:
self._attr_last_reset = dt.utc_from_timestamp(0)
elif description.last_reset == LAST_RESET_UPTIME:
self._attr_last_reset = (
dt.utcnow() - timedelta(seconds=wrapper.device.status["uptime"])
).replace(second=0, microsecond=0)
@property @property
def state(self) -> StateType: def state(self) -> StateType:
"""Return value of sensor.""" """Return value of sensor."""
if (
self.description.last_reset == LAST_RESET_UPTIME
and self.attribute_value is not None
):
value = cast(float, self.attribute_value)
if self._last_value and self._last_value > value:
self._attr_last_reset = dt.utcnow().replace(second=0, microsecond=0)
_LOGGER.info("Energy reset detected for entity %s", self.name)
self._last_value = value
return self.attribute_value return self.attribute_value
@property @property
@ -265,20 +301,6 @@ class ShellySensor(ShellyBlockAttributeEntity, SensorEntity):
"""State class of sensor.""" """State class of sensor."""
return self.description.state_class return self.description.state_class
@property
def last_reset(self) -> datetime | None:
"""State class of sensor."""
if self.description.last_reset == LAST_RESET_UPTIME:
self._last_value = get_device_uptime(
self.wrapper.device.status, self._last_value
)
return dt.parse_datetime(self._last_value)
if self.description.last_reset == LAST_RESET_NEVER:
return dt.utc_from_timestamp(0)
return None
@property @property
def unit_of_measurement(self) -> str | None: def unit_of_measurement(self) -> str | None:
"""Return unit of sensor.""" """Return unit of sensor."""

View File

@ -13,6 +13,8 @@ from homeassistant.helpers.trigger import async_validate_trigger_config
from . import binary_sensor as binary_sensor_platform, sensor as sensor_platform from . import binary_sensor as binary_sensor_platform, sensor as sensor_platform
from .const import CONF_TRIGGER, DOMAIN from .const import CONF_TRIGGER, DOMAIN
PACKAGE_MERGE_HINT = "list"
CONFIG_SECTION_SCHEMA = vol.Schema( CONFIG_SECTION_SCHEMA = vol.Schema(
{ {
vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Optional(CONF_UNIQUE_ID): cv.string,

View File

@ -2,7 +2,7 @@
"domain": "zeroconf", "domain": "zeroconf",
"name": "Zero-configuration networking (zeroconf)", "name": "Zero-configuration networking (zeroconf)",
"documentation": "https://www.home-assistant.io/integrations/zeroconf", "documentation": "https://www.home-assistant.io/integrations/zeroconf",
"requirements": ["zeroconf==0.33.2"], "requirements": ["zeroconf==0.33.3"],
"dependencies": ["network", "api"], "dependencies": ["network", "api"],
"codeowners": ["@bdraco"], "codeowners": ["@bdraco"],
"quality_scale": "internal", "quality_scale": "internal",

View File

@ -9,11 +9,11 @@
"pyserial-asyncio==0.5", "pyserial-asyncio==0.5",
"zha-quirks==0.0.59", "zha-quirks==0.0.59",
"zigpy-cc==0.5.2", "zigpy-cc==0.5.2",
"zigpy-deconz==0.12.0", "zigpy-deconz==0.12.1",
"zigpy==0.36.1", "zigpy==0.36.1",
"zigpy-xbee==0.13.0", "zigpy-xbee==0.13.0",
"zigpy-zigate==0.7.3", "zigpy-zigate==0.7.3",
"zigpy-znp==0.5.2" "zigpy-znp==0.5.3"
], ],
"codeowners": ["@dmulcahey", "@adminiuga"], "codeowners": ["@dmulcahey", "@adminiuga"],
"zeroconf": [ "zeroconf": [

View File

@ -723,7 +723,22 @@ async def merge_packages_config(
_log_pkg_error(pack_name, comp_name, config, str(ex)) _log_pkg_error(pack_name, comp_name, config, str(ex))
continue continue
merge_list = hasattr(component, "PLATFORM_SCHEMA") try:
config_platform: ModuleType | None = integration.get_platform("config")
# Test if config platform has a config validator
if not hasattr(config_platform, "async_validate_config"):
config_platform = None
except ImportError:
config_platform = None
merge_list = False
# If integration has a custom config validator, it needs to provide a hint.
if config_platform is not None:
merge_list = config_platform.PACKAGE_MERGE_HINT == "list" # type: ignore[attr-defined]
if not merge_list:
merge_list = hasattr(component, "PLATFORM_SCHEMA")
if not merge_list and hasattr(component, "CONFIG_SCHEMA"): if not merge_list and hasattr(component, "CONFIG_SCHEMA"):
merge_list = _identify_config_schema(component) == "list" merge_list = _identify_config_schema(component) == "list"

View File

@ -5,7 +5,7 @@ from typing import Final
MAJOR_VERSION: Final = 2021 MAJOR_VERSION: Final = 2021
MINOR_VERSION: Final = 8 MINOR_VERSION: Final = 8
PATCH_VERSION: Final = "1" PATCH_VERSION: Final = "2"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0) REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)

View File

@ -33,7 +33,7 @@ sqlalchemy==1.4.17
voluptuous-serialize==2.4.0 voluptuous-serialize==2.4.0
voluptuous==0.12.1 voluptuous==0.12.1
yarl==1.6.3 yarl==1.6.3
zeroconf==0.33.2 zeroconf==0.33.3
pycryptodome>=3.6.6 pycryptodome>=3.6.6

View File

@ -1562,7 +1562,7 @@ pylibrespot-java==0.1.0
pylitejet==0.3.0 pylitejet==0.3.0
# homeassistant.components.litterrobot # homeassistant.components.litterrobot
pylitterbot==2021.7.2 pylitterbot==2021.8.0
# homeassistant.components.loopenergy # homeassistant.components.loopenergy
pyloopenergy==0.2.1 pyloopenergy==0.2.1
@ -2439,7 +2439,7 @@ zeep[async]==4.0.0
zengge==0.2 zengge==0.2
# homeassistant.components.zeroconf # homeassistant.components.zeroconf
zeroconf==0.33.2 zeroconf==0.33.3
# homeassistant.components.zha # homeassistant.components.zha
zha-quirks==0.0.59 zha-quirks==0.0.59
@ -2454,7 +2454,7 @@ ziggo-mediabox-xl==1.1.0
zigpy-cc==0.5.2 zigpy-cc==0.5.2
# homeassistant.components.zha # homeassistant.components.zha
zigpy-deconz==0.12.0 zigpy-deconz==0.12.1
# homeassistant.components.zha # homeassistant.components.zha
zigpy-xbee==0.13.0 zigpy-xbee==0.13.0
@ -2463,7 +2463,7 @@ zigpy-xbee==0.13.0
zigpy-zigate==0.7.3 zigpy-zigate==0.7.3
# homeassistant.components.zha # homeassistant.components.zha
zigpy-znp==0.5.2 zigpy-znp==0.5.3
# homeassistant.components.zha # homeassistant.components.zha
zigpy==0.36.1 zigpy==0.36.1

View File

@ -884,7 +884,7 @@ pylibrespot-java==0.1.0
pylitejet==0.3.0 pylitejet==0.3.0
# homeassistant.components.litterrobot # homeassistant.components.litterrobot
pylitterbot==2021.7.2 pylitterbot==2021.8.0
# homeassistant.components.lutron_caseta # homeassistant.components.lutron_caseta
pylutron-caseta==0.11.0 pylutron-caseta==0.11.0
@ -1341,7 +1341,7 @@ youless-api==0.10
zeep[async]==4.0.0 zeep[async]==4.0.0
# homeassistant.components.zeroconf # homeassistant.components.zeroconf
zeroconf==0.33.2 zeroconf==0.33.3
# homeassistant.components.zha # homeassistant.components.zha
zha-quirks==0.0.59 zha-quirks==0.0.59
@ -1350,7 +1350,7 @@ zha-quirks==0.0.59
zigpy-cc==0.5.2 zigpy-cc==0.5.2
# homeassistant.components.zha # homeassistant.components.zha
zigpy-deconz==0.12.0 zigpy-deconz==0.12.1
# homeassistant.components.zha # homeassistant.components.zha
zigpy-xbee==0.13.0 zigpy-xbee==0.13.0
@ -1359,7 +1359,7 @@ zigpy-xbee==0.13.0
zigpy-zigate==0.7.3 zigpy-zigate==0.7.3
# homeassistant.components.zha # homeassistant.components.zha
zigpy-znp==0.5.2 zigpy-znp==0.5.3
# homeassistant.components.zha # homeassistant.components.zha
zigpy==0.36.1 zigpy==0.36.1

View File

@ -41,6 +41,7 @@ from homeassistant.const import (
STATE_ON, STATE_ON,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
__version__, __version__,
__version__ as hass_version,
) )
from homeassistant.helpers.event import TRACK_STATE_CHANGE_CALLBACKS from homeassistant.helpers.event import TRACK_STATE_CHANGE_CALLBACKS
@ -130,6 +131,7 @@ async def test_home_accessory(hass, hk_driver):
serv.get_characteristic(CHAR_SERIAL_NUMBER).value serv.get_characteristic(CHAR_SERIAL_NUMBER).value
== "light.accessory_that_exceeds_the_maximum_maximum_maximum_maximum" == "light.accessory_that_exceeds_the_maximum_maximum_maximum_maximum"
) )
assert serv.get_characteristic(CHAR_FIRMWARE_REVISION).value == "0.4.3"
hass.states.async_set(entity_id, "on") hass.states.async_set(entity_id, "on")
await hass.async_block_till_done() await hass.async_block_till_done()
@ -157,6 +159,31 @@ async def test_home_accessory(hass, hk_driver):
assert serv.get_characteristic(CHAR_MODEL).value == "Test Model" assert serv.get_characteristic(CHAR_MODEL).value == "Test Model"
async def test_accessory_with_missing_basic_service_info(hass, hk_driver):
"""Test HomeAccessory class."""
entity_id = "sensor.accessory"
hass.states.async_set(entity_id, "on")
acc = HomeAccessory(
hass,
hk_driver,
"Home Accessory",
entity_id,
3,
{
ATTR_MODEL: None,
ATTR_MANUFACTURER: None,
ATTR_SOFTWARE_VERSION: None,
ATTR_INTEGRATION: None,
},
)
serv = acc.get_service(SERV_ACCESSORY_INFO)
assert serv.get_characteristic(CHAR_NAME).value == "Home Accessory"
assert serv.get_characteristic(CHAR_MANUFACTURER).value == "Home Assistant Sensor"
assert serv.get_characteristic(CHAR_MODEL).value == "Sensor"
assert serv.get_characteristic(CHAR_SERIAL_NUMBER).value == entity_id
assert serv.get_characteristic(CHAR_FIRMWARE_REVISION).value == hass_version
async def test_battery_service(hass, hk_driver, caplog): async def test_battery_service(hass, hk_driver, caplog):
"""Test battery service.""" """Test battery service."""
entity_id = "homekit.accessory" entity_id = "homekit.accessory"

View File

@ -329,7 +329,13 @@ async def test_reset_switch(hass, hk_driver, events):
future = dt_util.utcnow() + timedelta(seconds=1) future = dt_util.utcnow() + timedelta(seconds=1)
async_fire_time_changed(hass, future) async_fire_time_changed(hass, future)
await hass.async_block_till_done() await hass.async_block_till_done()
assert acc.char_on.value is True
future = dt_util.utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
assert acc.char_on.value is False assert acc.char_on.value is False
assert len(events) == 1 assert len(events) == 1
assert not call_turn_off assert not call_turn_off
@ -367,7 +373,13 @@ async def test_script_switch(hass, hk_driver, events):
future = dt_util.utcnow() + timedelta(seconds=1) future = dt_util.utcnow() + timedelta(seconds=1)
async_fire_time_changed(hass, future) async_fire_time_changed(hass, future)
await hass.async_block_till_done() await hass.async_block_till_done()
assert acc.char_on.value is True
future = dt_util.utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
assert acc.char_on.value is False assert acc.char_on.value is False
assert len(events) == 1 assert len(events) == 1
assert not call_turn_off assert not call_turn_off

View File

@ -650,19 +650,30 @@ async def test_merge(merge_log_err, hass):
"pack_list": {"light": {"platform": "test"}}, "pack_list": {"light": {"platform": "test"}},
"pack_list2": {"light": [{"platform": "test"}]}, "pack_list2": {"light": [{"platform": "test"}]},
"pack_none": {"wake_on_lan": None}, "pack_none": {"wake_on_lan": None},
"pack_special": {
"automation": [{"some": "yay"}],
"script": {"a_script": "yay"},
"template": [{"some": "yay"}],
},
} }
config = { config = {
config_util.CONF_CORE: {config_util.CONF_PACKAGES: packages}, config_util.CONF_CORE: {config_util.CONF_PACKAGES: packages},
"input_boolean": {"ib2": None}, "input_boolean": {"ib2": None},
"light": {"platform": "test"}, "light": {"platform": "test"},
"automation": [],
"script": {},
"template": [],
} }
await config_util.merge_packages_config(hass, config, packages) await config_util.merge_packages_config(hass, config, packages)
assert merge_log_err.call_count == 0 assert merge_log_err.call_count == 0
assert len(config) == 5 assert len(config) == 8
assert len(config["input_boolean"]) == 2 assert len(config["input_boolean"]) == 2
assert len(config["input_select"]) == 1 assert len(config["input_select"]) == 1
assert len(config["light"]) == 3 assert len(config["light"]) == 3
assert len(config["automation"]) == 1
assert len(config["script"]) == 1
assert len(config["template"]) == 1
assert isinstance(config["wake_on_lan"], OrderedDict) assert isinstance(config["wake_on_lan"], OrderedDict)