forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow easier customization of whole domain, entity lists, globs. (hom…
- Loading branch information
1 parent
ab19577
commit addc2c4
Showing
8 changed files
with
285 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
"""A helper module for customization.""" | ||
import collections | ||
from typing import Dict, List | ||
import fnmatch | ||
|
||
from homeassistant.const import CONF_ENTITY_ID | ||
from homeassistant.core import HomeAssistant, split_entity_id | ||
|
||
_OVERWRITE_KEY = 'overwrite' | ||
_OVERWRITE_CACHE_KEY = 'overwrite_cache' | ||
|
||
|
||
def set_customize(hass: HomeAssistant, customize: List[Dict]) -> None: | ||
"""Overwrite all current customize settings. | ||
Async friendly. | ||
""" | ||
hass.data[_OVERWRITE_KEY] = customize | ||
hass.data[_OVERWRITE_CACHE_KEY] = {} | ||
|
||
|
||
def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict: | ||
"""Return a dictionary of overrides related to entity_id. | ||
Whole-domain overrides are of lowest priorities, | ||
then glob on entity ID, and finally exact entity_id | ||
matches are of highest priority. | ||
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: | ||
return {} | ||
domain_result = {} # type: Dict[str, Any] | ||
glob_result = {} # type: Dict[str, Any] | ||
exact_result = {} # type: Dict[str, Any] | ||
domain = split_entity_id(entity_id)[0] | ||
|
||
def clean_entry(entry: Dict) -> Dict: | ||
"""Clean up entity-matching keys.""" | ||
entry.pop(CONF_ENTITY_ID, None) | ||
return entry | ||
|
||
def deep_update(target: Dict, source: Dict) -> None: | ||
"""Deep update a dictionary.""" | ||
for key, value in source.items(): | ||
if isinstance(value, collections.Mapping): | ||
updated_value = target.get(key, {}) | ||
# If the new value is map, but the old value is not - | ||
# overwrite the old value. | ||
if not isinstance(updated_value, collections.Mapping): | ||
updated_value = {} | ||
deep_update(updated_value, value) | ||
target[key] = updated_value | ||
else: | ||
target[key] = source[key] | ||
|
||
for rule in hass.data[_OVERWRITE_KEY]: | ||
if CONF_ENTITY_ID in rule: | ||
entities = rule[CONF_ENTITY_ID] | ||
if domain in entities: | ||
deep_update(domain_result, rule) | ||
if entity_id in entities: | ||
deep_update(exact_result, rule) | ||
for entity_id_glob in entities: | ||
if entity_id_glob == entity_id: | ||
continue | ||
if fnmatch.fnmatchcase(entity_id, entity_id_glob): | ||
deep_update(glob_result, rule) | ||
break | ||
result = {} | ||
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 | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
"""Test the customize helper.""" | ||
import homeassistant.helpers.customize as customize | ||
|
||
|
||
class MockHass(object): | ||
"""Mock object for HassAssistant.""" | ||
|
||
data = {} | ||
|
||
|
||
class TestHelpersCustomize(object): | ||
"""Test homeassistant.helpers.customize module.""" | ||
|
||
def setup_method(self, method): | ||
"""Setup things to be run when tests are started.""" | ||
self.entity_id = 'test.test' | ||
self.hass = MockHass() | ||
|
||
def _get_overrides(self, overrides): | ||
customize.set_customize(self.hass, overrides) | ||
return customize.get_overrides(self.hass, self.entity_id) | ||
|
||
def test_override_single_value(self): | ||
"""Test entity customization through configuration.""" | ||
result = self._get_overrides([ | ||
{'entity_id': [self.entity_id], 'key': 'value'}]) | ||
|
||
assert result == {'key': 'value'} | ||
|
||
def test_override_multiple_values(self): | ||
"""Test entity customization through configuration.""" | ||
result = self._get_overrides([ | ||
{'entity_id': [self.entity_id], 'key1': 'value1'}, | ||
{'entity_id': [self.entity_id], 'key2': 'value2'}]) | ||
|
||
assert result == {'key1': 'value1', 'key2': 'value2'} | ||
|
||
def test_override_same_value(self): | ||
"""Test entity customization through configuration.""" | ||
result = self._get_overrides([ | ||
{'entity_id': [self.entity_id], 'key': 'value1'}, | ||
{'entity_id': [self.entity_id], 'key': 'value2'}]) | ||
|
||
assert result == {'key': 'value2'} | ||
|
||
def test_override_by_domain(self): | ||
"""Test entity customization through configuration.""" | ||
result = self._get_overrides([ | ||
{'entity_id': ['test'], 'key': 'value'}]) | ||
|
||
assert result == {'key': 'value'} | ||
|
||
def test_override_by_glob(self): | ||
"""Test entity customization through configuration.""" | ||
result = self._get_overrides([ | ||
{'entity_id': ['test.?e*'], 'key': 'value'}]) | ||
|
||
assert result == {'key': 'value'} | ||
|
||
def test_override_exact_over_glob_over_domain(self): | ||
"""Test entity customization through configuration.""" | ||
result = self._get_overrides([ | ||
{'entity_id': ['test.test'], 'key1': 'valueExact'}, | ||
{'entity_id': ['test.tes?'], | ||
'key1': 'valueGlob', | ||
'key2': 'valueGlob'}, | ||
{'entity_id': ['test'], | ||
'key1': 'valueDomain', | ||
'key2': 'valueDomain', | ||
'key3': 'valueDomain'}]) | ||
|
||
assert result == { | ||
'key1': 'valueExact', | ||
'key2': 'valueGlob', | ||
'key3': 'valueDomain'} | ||
|
||
def test_override_deep_dict(self): | ||
"""Test we can overwrite hidden property to True.""" | ||
result = self._get_overrides( | ||
[{'entity_id': [self.entity_id], | ||
'test': {'key1': 'value1', 'key2': 'value2'}}, | ||
{'entity_id': [self.entity_id], | ||
'test': {'key3': 'value3', 'key2': 'value22'}}]) | ||
assert result['test'] == { | ||
'key1': 'value1', | ||
'key2': 'value22', | ||
'key3': 'value3'} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.