Apply new customize format to Zwave (#5603)
parent
405b2fdfa0
commit
1fb372ffdb
|
@ -17,6 +17,7 @@ from homeassistant.const import STATE_OFF, STATE_ON
|
|||
from homeassistant.util.color import HASS_COLOR_MAX, HASS_COLOR_MIN, \
|
||||
color_temperature_mired_to_kelvin, color_temperature_to_rgb, \
|
||||
color_rgb_to_rgbw, color_rgbw_to_rgb
|
||||
from homeassistant.helpers import customize
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -54,13 +55,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
return
|
||||
node = zwave.NETWORK.nodes[discovery_info[zwave.const.ATTR_NODE_ID]]
|
||||
value = node.values[discovery_info[zwave.const.ATTR_VALUE_ID]]
|
||||
customize = hass.data['zwave_customize']
|
||||
name = '{}.{}'.format(DOMAIN, zwave.object_id(value))
|
||||
node_config = customize.get(name, {})
|
||||
node_config = customize.get_overrides(hass, zwave.DOMAIN, name)
|
||||
refresh = node_config.get(zwave.CONF_REFRESH_VALUE)
|
||||
delay = node_config.get(zwave.CONF_REFRESH_DELAY)
|
||||
_LOGGER.debug('customize=%s name=%s node_config=%s CONF_REFRESH_VALUE=%s'
|
||||
' CONF_REFRESH_DELAY=%s', customize, name, node_config,
|
||||
_LOGGER.debug('name=%s node_config=%s CONF_REFRESH_VALUE=%s'
|
||||
' CONF_REFRESH_DELAY=%s', name, node_config,
|
||||
refresh, delay)
|
||||
if value.command_class != zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL:
|
||||
return
|
||||
|
|
|
@ -11,10 +11,10 @@ from pprint import pprint
|
|||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.helpers import discovery
|
||||
from homeassistant.helpers import discovery, customize
|
||||
from homeassistant.const import (
|
||||
ATTR_BATTERY_LEVEL, ATTR_LOCATION, ATTR_ENTITY_ID, CONF_CUSTOMIZE,
|
||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, CONF_ENTITY_ID)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import track_time_change
|
||||
from homeassistant.util import convert, slugify
|
||||
|
@ -150,9 +150,9 @@ CHANGE_ASSOCIATION_SCHEMA = vol.Schema({
|
|||
vol.Optional(const.ATTR_INSTANCE, default=0x00): vol.Coerce(int)
|
||||
})
|
||||
|
||||
CUSTOMIZE_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_POLLING_INTENSITY):
|
||||
vol.All(cv.positive_int),
|
||||
_ZWAVE_CUSTOMIZE_SCHEMA_ENTRY = vol.Schema({
|
||||
vol.Required(CONF_ENTITY_ID): cv.match_all,
|
||||
vol.Optional(CONF_POLLING_INTENSITY): cv.positive_int,
|
||||
vol.Optional(CONF_IGNORED, default=DEFAULT_CONF_IGNORED): cv.boolean,
|
||||
vol.Optional(CONF_REFRESH_VALUE, default=DEFAULT_CONF_REFRESH_VALUE):
|
||||
cv.boolean,
|
||||
|
@ -164,8 +164,9 @@ CONFIG_SCHEMA = vol.Schema({
|
|||
DOMAIN: vol.Schema({
|
||||
vol.Optional(CONF_AUTOHEAL, default=DEFAULT_CONF_AUTOHEAL): cv.boolean,
|
||||
vol.Optional(CONF_CONFIG_PATH): cv.string,
|
||||
vol.Optional(CONF_CUSTOMIZE, default={}):
|
||||
vol.Schema({cv.string: CUSTOMIZE_SCHEMA}),
|
||||
vol.Optional(CONF_CUSTOMIZE, default=[]):
|
||||
vol.All(customize.CUSTOMIZE_SCHEMA,
|
||||
[_ZWAVE_CUSTOMIZE_SCHEMA_ENTRY]),
|
||||
vol.Optional(CONF_DEBUG, default=DEFAULT_DEBUG): cv.boolean,
|
||||
vol.Optional(CONF_POLLING_INTERVAL, default=DEFAULT_POLLING_INTERVAL):
|
||||
cv.positive_int,
|
||||
|
@ -268,8 +269,7 @@ def setup(hass, config):
|
|||
|
||||
# Load configuration
|
||||
use_debug = config[DOMAIN].get(CONF_DEBUG)
|
||||
hass.data['zwave_customize'] = config[DOMAIN].get(CONF_CUSTOMIZE)
|
||||
customize = hass.data['zwave_customize']
|
||||
customize.set_customize(hass, DOMAIN, config[DOMAIN].get(CONF_CUSTOMIZE))
|
||||
autoheal = config[DOMAIN].get(CONF_AUTOHEAL)
|
||||
|
||||
# Setup options
|
||||
|
@ -349,7 +349,7 @@ def setup(hass, config):
|
|||
value.genre)
|
||||
name = "{}.{}".format(component, object_id(value))
|
||||
|
||||
node_config = customize.get(name, {})
|
||||
node_config = customize.get_overrides(hass, DOMAIN, name)
|
||||
|
||||
if node_config.get(CONF_IGNORED):
|
||||
_LOGGER.info("Ignoring device %s", name)
|
||||
|
|
|
@ -13,7 +13,7 @@ from homeassistant.const import (
|
|||
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_PACKAGES, CONF_UNIT_SYSTEM,
|
||||
CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC,
|
||||
CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS,
|
||||
CONF_ENTITY_ID, __version__)
|
||||
__version__)
|
||||
from homeassistant.core import DOMAIN as CONF_CORE
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.loader import get_component
|
||||
|
@ -21,7 +21,7 @@ from homeassistant.util.yaml import load_yaml
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util import dt as date_util, location as loc_util
|
||||
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
|
||||
from homeassistant.helpers.customize import set_customize
|
||||
from homeassistant.helpers import customize
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -86,27 +86,6 @@ tts:
|
|||
"""
|
||||
|
||||
|
||||
CUSTOMIZE_SCHEMA_ENTRY = vol.Schema({
|
||||
vol.Required(CONF_ENTITY_ID): vol.All(
|
||||
cv.ensure_list_csv, vol.Length(min=1), [cv.string], [vol.Lower])
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
def _convert_old_config(inp: Any) -> List:
|
||||
if not isinstance(inp, dict):
|
||||
return cv.ensure_list(inp)
|
||||
if CONF_ENTITY_ID in inp:
|
||||
return [inp] # sigle entry
|
||||
res = []
|
||||
|
||||
inp = vol.Schema({cv.match_all: dict})(inp)
|
||||
for key, val in inp.items():
|
||||
val = dict(val)
|
||||
val[CONF_ENTITY_ID] = key
|
||||
res.append(val)
|
||||
return res
|
||||
|
||||
|
||||
PACKAGES_CONFIG_SCHEMA = vol.Schema({
|
||||
cv.slug: vol.Schema( # Package names are slugs
|
||||
{cv.slug: vol.Any(dict, list)}) # Only slugs for component names
|
||||
|
@ -120,8 +99,7 @@ CORE_CONFIG_SCHEMA = vol.Schema({
|
|||
vol.Optional(CONF_TEMPERATURE_UNIT): cv.temperature_unit,
|
||||
CONF_UNIT_SYSTEM: cv.unit_system,
|
||||
CONF_TIME_ZONE: cv.time_zone,
|
||||
vol.Optional(CONF_CUSTOMIZE, default=[]): vol.All(
|
||||
_convert_old_config, [CUSTOMIZE_SCHEMA_ENTRY]),
|
||||
vol.Optional(CONF_CUSTOMIZE, default=[]): customize.CUSTOMIZE_SCHEMA,
|
||||
vol.Optional(CONF_PACKAGES, default={}): PACKAGES_CONFIG_SCHEMA,
|
||||
})
|
||||
|
||||
|
@ -280,6 +258,7 @@ def async_process_ha_core_config(hass, config):
|
|||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
print(CORE_CONFIG_SCHEMA)
|
||||
config = CORE_CONFIG_SCHEMA(config)
|
||||
hac = hass.config
|
||||
|
||||
|
@ -306,9 +285,9 @@ def async_process_ha_core_config(hass, config):
|
|||
if CONF_TIME_ZONE in config:
|
||||
set_time_zone(config.get(CONF_TIME_ZONE))
|
||||
|
||||
customize = merge_packages_customize(
|
||||
merged_customize = merge_packages_customize(
|
||||
config[CONF_CUSTOMIZE], config[CONF_PACKAGES])
|
||||
set_customize(hass, customize)
|
||||
customize.set_customize(hass, CONF_CORE, merged_customize)
|
||||
|
||||
if CONF_UNIT_SYSTEM in config:
|
||||
if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
|
||||
|
@ -463,15 +442,14 @@ def merge_packages_config(config, packages):
|
|||
return config
|
||||
|
||||
|
||||
def merge_packages_customize(customize, packages):
|
||||
def merge_packages_customize(core_customize, packages):
|
||||
"""Merge customize from packages."""
|
||||
schema = vol.Schema({
|
||||
vol.Optional(CONF_CORE): vol.Schema({
|
||||
CONF_CUSTOMIZE: vol.All(
|
||||
_convert_old_config, [CUSTOMIZE_SCHEMA_ENTRY])})
|
||||
CONF_CUSTOMIZE: customize.CUSTOMIZE_SCHEMA}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
cust = list(customize)
|
||||
cust = list(core_customize)
|
||||
for pkg in packages.values():
|
||||
conf = schema(pkg)
|
||||
cust.extend(conf.get(CONF_CORE, {}).get(CONF_CUSTOMIZE, []))
|
||||
|
|
|
@ -1,25 +1,51 @@
|
|||
"""A helper module for customization."""
|
||||
import collections
|
||||
from typing import Dict, List
|
||||
from typing import Any, Dict, List
|
||||
import fnmatch
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, split_entity_id
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_OVERWRITE_KEY = 'overwrite'
|
||||
_OVERWRITE_CACHE_KEY = 'overwrite_cache'
|
||||
_OVERWRITE_KEY_FORMAT = '{}.overwrite'
|
||||
_OVERWRITE_CACHE_KEY_FORMAT = '{}.overwrite_cache'
|
||||
|
||||
_CUSTOMIZE_SCHEMA_ENTRY = vol.Schema({
|
||||
vol.Required(CONF_ENTITY_ID): vol.All(
|
||||
cv.ensure_list_csv, vol.Length(min=1), [vol.Schema(str)], [vol.Lower])
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
def set_customize(hass: HomeAssistant, customize: List[Dict]) -> None:
|
||||
def _convert_old_config(inp: Any) -> List:
|
||||
if not isinstance(inp, dict):
|
||||
return cv.ensure_list(inp)
|
||||
if CONF_ENTITY_ID in inp:
|
||||
return [inp] # sigle entry
|
||||
res = []
|
||||
|
||||
inp = vol.Schema({cv.match_all: dict})(inp)
|
||||
for key, val in inp.items():
|
||||
val = dict(val)
|
||||
val[CONF_ENTITY_ID] = key
|
||||
res.append(val)
|
||||
return res
|
||||
|
||||
|
||||
CUSTOMIZE_SCHEMA = vol.All(_convert_old_config, [_CUSTOMIZE_SCHEMA_ENTRY])
|
||||
|
||||
|
||||
def set_customize(
|
||||
hass: HomeAssistant, domain: str, customize: List[Dict]) -> None:
|
||||
"""Overwrite all current customize settings.
|
||||
|
||||
Async friendly.
|
||||
"""
|
||||
hass.data[_OVERWRITE_KEY] = customize
|
||||
hass.data[_OVERWRITE_CACHE_KEY] = {}
|
||||
hass.data[_OVERWRITE_KEY_FORMAT.format(domain)] = customize
|
||||
hass.data[_OVERWRITE_CACHE_KEY_FORMAT.format(domain)] = {}
|
||||
|
||||
|
||||
def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict:
|
||||
def get_overrides(hass: HomeAssistant, domain: str, entity_id: str) -> Dict:
|
||||
"""Return a dictionary of overrides related to entity_id.
|
||||
|
||||
Whole-domain overrides are of lowest priorities,
|
||||
|
@ -28,10 +54,11 @@ def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict:
|
|||
|
||||
The lookups are cached.
|
||||
"""
|
||||
if _OVERWRITE_CACHE_KEY in hass.data and \
|
||||
entity_id in hass.data[_OVERWRITE_CACHE_KEY]:
|
||||
return hass.data[_OVERWRITE_CACHE_KEY][entity_id]
|
||||
if _OVERWRITE_KEY not in hass.data:
|
||||
cache_key = _OVERWRITE_CACHE_KEY_FORMAT.format(domain)
|
||||
if cache_key in hass.data and entity_id in hass.data[cache_key]:
|
||||
return hass.data[cache_key][entity_id]
|
||||
overwrite_key = _OVERWRITE_KEY_FORMAT.format(domain)
|
||||
if overwrite_key not in hass.data:
|
||||
return {}
|
||||
domain_result = {} # type: Dict[str, Any]
|
||||
glob_result = {} # type: Dict[str, Any]
|
||||
|
@ -57,7 +84,7 @@ def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict:
|
|||
else:
|
||||
target[key] = source[key]
|
||||
|
||||
for rule in hass.data[_OVERWRITE_KEY]:
|
||||
for rule in hass.data[overwrite_key]:
|
||||
if CONF_ENTITY_ID in rule:
|
||||
entities = rule[CONF_ENTITY_ID]
|
||||
if domain in entities:
|
||||
|
@ -74,7 +101,7 @@ def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict:
|
|||
deep_update(result, clean_entry(domain_result))
|
||||
deep_update(result, clean_entry(glob_result))
|
||||
deep_update(result, clean_entry(exact_result))
|
||||
if _OVERWRITE_CACHE_KEY not in hass.data:
|
||||
hass.data[_OVERWRITE_CACHE_KEY] = {}
|
||||
hass.data[_OVERWRITE_CACHE_KEY][entity_id] = result
|
||||
if cache_key not in hass.data:
|
||||
hass.data[cache_key] = {}
|
||||
hass.data[cache_key][entity_id] = result
|
||||
return result
|
||||
|
|
|
@ -11,7 +11,7 @@ from homeassistant.const import (
|
|||
ATTR_UNIT_OF_MEASUREMENT, DEVICE_DEFAULT_NAME, STATE_OFF, STATE_ON,
|
||||
STATE_UNAVAILABLE, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT,
|
||||
ATTR_ENTITY_PICTURE)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, DOMAIN as CORE_DOMAIN
|
||||
from homeassistant.exceptions import NoEntitySpecifiedError
|
||||
from homeassistant.util import ensure_unique_string, slugify
|
||||
from homeassistant.util.async import (
|
||||
|
@ -242,7 +242,7 @@ class Entity(object):
|
|||
end - start)
|
||||
|
||||
# Overwrite properties that have been set in the config file.
|
||||
attr.update(get_overrides(self.hass, self.entity_id))
|
||||
attr.update(get_overrides(self.hass, CORE_DOMAIN, self.entity_id))
|
||||
|
||||
# Remove hidden property if false so it won't show up.
|
||||
if not attr.get(ATTR_HIDDEN, True):
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
"""The tests for the zwave component."""
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.components import zwave
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
|
||||
class TestComponentZwave(unittest.TestCase):
|
||||
"""Test the Zwave component."""
|
||||
|
||||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def _validate_config(self, validator, config):
|
||||
libopenzwave = MagicMock()
|
||||
libopenzwave.__file__ = 'test'
|
||||
with patch.dict('sys.modules', {
|
||||
'libopenzwave': libopenzwave,
|
||||
'openzwave.option': MagicMock(),
|
||||
'openzwave.network': MagicMock(),
|
||||
'openzwave.group': MagicMock(),
|
||||
}):
|
||||
validator(setup_component(self.hass, zwave.DOMAIN, {
|
||||
zwave.DOMAIN: config,
|
||||
}))
|
||||
|
||||
def test_empty_config(self):
|
||||
"""Test empty config."""
|
||||
self._validate_config(self.assertTrue, {})
|
||||
|
||||
def test_empty_customize(self):
|
||||
"""Test empty customize."""
|
||||
self._validate_config(self.assertTrue, {'customize': {}})
|
||||
self._validate_config(self.assertTrue, {'customize': []})
|
||||
|
||||
def test_empty_customize_content(self):
|
||||
"""Test empty customize."""
|
||||
self._validate_config(
|
||||
self.assertTrue, {'customize': {'test.test': {}}})
|
||||
|
||||
def test_full_customize_dict(self):
|
||||
"""Test full customize as dict."""
|
||||
self._validate_config(self.assertTrue, {'customize': {'test.test': {
|
||||
zwave.CONF_POLLING_INTENSITY: 10,
|
||||
zwave.CONF_IGNORED: 1,
|
||||
zwave.CONF_REFRESH_VALUE: 1,
|
||||
zwave.CONF_REFRESH_DELAY: 10}}})
|
||||
|
||||
def test_full_customize_list(self):
|
||||
"""Test full customize as list."""
|
||||
self._validate_config(self.assertTrue, {'customize': [{
|
||||
'entity_id': 'test.test',
|
||||
zwave.CONF_POLLING_INTENSITY: 10,
|
||||
zwave.CONF_IGNORED: 1,
|
||||
zwave.CONF_REFRESH_VALUE: 1,
|
||||
zwave.CONF_REFRESH_DELAY: 10}]})
|
||||
|
||||
def test_bad_customize(self):
|
||||
"""Test customize with extra keys."""
|
||||
self._validate_config(
|
||||
self.assertFalse, {'customize': {'test.test': {'extra_key': 10}}})
|
|
@ -1,5 +1,7 @@
|
|||
"""Test the customize helper."""
|
||||
import homeassistant.helpers.customize as customize
|
||||
from voluptuous import MultipleInvalid
|
||||
import pytest
|
||||
|
||||
|
||||
class MockHass(object):
|
||||
|
@ -17,8 +19,9 @@ class TestHelpersCustomize(object):
|
|||
self.hass = MockHass()
|
||||
|
||||
def _get_overrides(self, overrides):
|
||||
customize.set_customize(self.hass, overrides)
|
||||
return customize.get_overrides(self.hass, self.entity_id)
|
||||
test_domain = 'test.domain'
|
||||
customize.set_customize(self.hass, test_domain, overrides)
|
||||
return customize.get_overrides(self.hass, test_domain, self.entity_id)
|
||||
|
||||
def test_override_single_value(self):
|
||||
"""Test entity customization through configuration."""
|
||||
|
@ -75,7 +78,7 @@ class TestHelpersCustomize(object):
|
|||
'key3': 'valueDomain'}
|
||||
|
||||
def test_override_deep_dict(self):
|
||||
"""Test we can overwrite hidden property to True."""
|
||||
"""Test we can deep-overwrite a dict."""
|
||||
result = self._get_overrides(
|
||||
[{'entity_id': [self.entity_id],
|
||||
'test': {'key1': 'value1', 'key2': 'value2'}},
|
||||
|
@ -85,3 +88,32 @@ class TestHelpersCustomize(object):
|
|||
'key1': 'value1',
|
||||
'key2': 'value22',
|
||||
'key3': 'value3'}
|
||||
|
||||
def test_schema_bad_schema(self):
|
||||
"""Test bad customize schemas."""
|
||||
for value in (
|
||||
{'test.test': 10},
|
||||
{'test.test': ['hello']},
|
||||
{'entity_id': {'a': 'b'}},
|
||||
{'entity_id': 10},
|
||||
[{'test.test': 'value'}],
|
||||
):
|
||||
with pytest.raises(
|
||||
MultipleInvalid,
|
||||
message="{} should have raised MultipleInvalid".format(
|
||||
value)):
|
||||
customize.CUSTOMIZE_SCHEMA(value)
|
||||
|
||||
def test_get_customize_schema_allow_extra(self):
|
||||
"""Test schema with ALLOW_EXTRA."""
|
||||
for value in (
|
||||
{'test.test': {'hidden': True}},
|
||||
{'test.test': {'key': ['value1', 'value2']}},
|
||||
[{'entity_id': 'id1', 'key': 'value'}],
|
||||
):
|
||||
customize.CUSTOMIZE_SCHEMA(value)
|
||||
|
||||
def test_get_customize_schema_csv(self):
|
||||
"""Test schema with comma separated entity IDs."""
|
||||
assert [{'entity_id': ['id1', 'id2', 'id3']}] == \
|
||||
customize.CUSTOMIZE_SCHEMA([{'entity_id': 'id1,ID2 , id3'}])
|
||||
|
|
|
@ -90,6 +90,7 @@ class TestHelpersEntity(object):
|
|||
"""Test we can overwrite hidden property to True."""
|
||||
set_customize(
|
||||
self.hass,
|
||||
entity.CORE_DOMAIN,
|
||||
[{'entity_id': [self.entity.entity_id], ATTR_HIDDEN: True}])
|
||||
self.entity.update_ha_state()
|
||||
|
||||
|
|
|
@ -234,25 +234,6 @@ class TestConfig(unittest.TestCase):
|
|||
|
||||
assert state.attributes['hidden']
|
||||
|
||||
def test_entity_customization_comma_separated(self):
|
||||
"""Test entity customization through configuration."""
|
||||
config = {CONF_LATITUDE: 50,
|
||||
CONF_LONGITUDE: 50,
|
||||
CONF_NAME: 'Test',
|
||||
CONF_CUSTOMIZE: [
|
||||
{'entity_id': 'test.not_test,test,test.not_t*',
|
||||
'key1': 'value1'},
|
||||
{'entity_id': 'test.test,not_test,test.not_t*',
|
||||
'key2': 'value2'},
|
||||
{'entity_id': 'test.not_test,not_test,test.t*',
|
||||
'key3': 'value3'}]}
|
||||
|
||||
state = self._compute_state(config)
|
||||
|
||||
assert state.attributes['key1'] == 'value1'
|
||||
assert state.attributes['key2'] == 'value2'
|
||||
assert state.attributes['key3'] == 'value3'
|
||||
|
||||
@mock.patch('homeassistant.config.shutil')
|
||||
@mock.patch('homeassistant.config.os')
|
||||
def test_remove_lib_on_upgrade(self, mock_os, mock_shutil):
|
||||
|
|
Loading…
Reference in New Issue