forked from home-assistant/core
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4750656f1a | ||
|
|
011cc624b6 | ||
|
|
2d9a964953 | ||
|
|
87133a0e77 | ||
|
|
fe8dec27a3 | ||
|
|
b5323cd894 | ||
|
|
23316a8344 | ||
|
|
86ecd7555a | ||
|
|
02f55b039c | ||
|
|
ebaf7f8c00 |
74
homeassistant/components/automation/geo_location.py
Normal file
74
homeassistant/components/automation/geo_location.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""
|
||||
Offer geo location automation rules.
|
||||
|
||||
For more details about this automation trigger, please refer to the
|
||||
documentation at
|
||||
https://home-assistant.io/docs/automation/trigger/#geo-location-trigger
|
||||
"""
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.geo_location import DOMAIN
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import (
|
||||
CONF_EVENT, CONF_PLATFORM, CONF_SOURCE, CONF_ZONE, EVENT_STATE_CHANGED)
|
||||
from homeassistant.helpers import (
|
||||
condition, config_validation as cv)
|
||||
from homeassistant.helpers.config_validation import entity_domain
|
||||
|
||||
EVENT_ENTER = 'enter'
|
||||
EVENT_LEAVE = 'leave'
|
||||
DEFAULT_EVENT = EVENT_ENTER
|
||||
|
||||
TRIGGER_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_PLATFORM): 'geo_location',
|
||||
vol.Required(CONF_SOURCE): cv.string,
|
||||
vol.Required(CONF_ZONE): entity_domain('zone'),
|
||||
vol.Required(CONF_EVENT, default=DEFAULT_EVENT):
|
||||
vol.Any(EVENT_ENTER, EVENT_LEAVE),
|
||||
})
|
||||
|
||||
|
||||
def source_match(state, source):
|
||||
"""Check if the state matches the provided source."""
|
||||
return state and state.attributes.get('source') == source
|
||||
|
||||
|
||||
async def async_trigger(hass, config, action):
|
||||
"""Listen for state changes based on configuration."""
|
||||
source = config.get(CONF_SOURCE).lower()
|
||||
zone_entity_id = config.get(CONF_ZONE)
|
||||
trigger_event = config.get(CONF_EVENT)
|
||||
|
||||
@callback
|
||||
def state_change_listener(event):
|
||||
"""Handle specific state changes."""
|
||||
# Skip if the event is not a geo_location entity.
|
||||
if not event.data.get('entity_id').startswith(DOMAIN):
|
||||
return
|
||||
# Skip if the event's source does not match the trigger's source.
|
||||
from_state = event.data.get('old_state')
|
||||
to_state = event.data.get('new_state')
|
||||
if not source_match(from_state, source) \
|
||||
and not source_match(to_state, source):
|
||||
return
|
||||
|
||||
zone_state = hass.states.get(zone_entity_id)
|
||||
from_match = condition.zone(hass, zone_state, from_state)
|
||||
to_match = condition.zone(hass, zone_state, to_state)
|
||||
|
||||
# pylint: disable=too-many-boolean-expressions
|
||||
if trigger_event == EVENT_ENTER and not from_match and to_match or \
|
||||
trigger_event == EVENT_LEAVE and from_match and not to_match:
|
||||
hass.async_run_job(action({
|
||||
'trigger': {
|
||||
'platform': 'geo_location',
|
||||
'source': source,
|
||||
'entity_id': event.data.get('entity_id'),
|
||||
'from_state': from_state,
|
||||
'to_state': to_state,
|
||||
'zone': zone_state,
|
||||
'event': trigger_event,
|
||||
},
|
||||
}, context=event.context))
|
||||
|
||||
return hass.bus.async_listen(EVENT_STATE_CHANGED, state_change_listener)
|
||||
@@ -24,7 +24,7 @@ from homeassistant.core import callback
|
||||
from homeassistant.helpers.translation import async_get_translations
|
||||
from homeassistant.loader import bind_hass
|
||||
|
||||
REQUIREMENTS = ['home-assistant-frontend==20181021.0']
|
||||
REQUIREMENTS = ['home-assistant-frontend==20181023.0']
|
||||
|
||||
DOMAIN = 'frontend'
|
||||
DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log',
|
||||
|
||||
5
homeassistant/components/ifttt/.translations/tr.json
Normal file
5
homeassistant/components/ifttt/.translations/tr.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"config": {
|
||||
"title": "IFTT"
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ from homeassistant.components.light import (
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.util.color as color_util
|
||||
|
||||
REQUIREMENTS = ['flux_led==0.21']
|
||||
REQUIREMENTS = ['flux_led==0.22']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ from homeassistant.util.color import (
|
||||
color_temperature_mired_to_kelvin, color_hs_to_RGB)
|
||||
from homeassistant.helpers.restore_state import async_get_last_state
|
||||
|
||||
REQUIREMENTS = ['limitlessled==1.1.2']
|
||||
REQUIREMENTS = ['limitlessled==1.1.3']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
import logging
|
||||
import uuid
|
||||
import os
|
||||
from os import O_WRONLY, O_CREAT, O_TRUNC
|
||||
from os import O_CREAT, O_TRUNC, O_WRONLY
|
||||
from collections import OrderedDict
|
||||
from typing import Union, List, Dict
|
||||
from typing import Dict, List, Union
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import websocket_api
|
||||
@@ -14,21 +15,45 @@ _LOGGER = logging.getLogger(__name__)
|
||||
DOMAIN = 'lovelace'
|
||||
REQUIREMENTS = ['ruamel.yaml==0.15.72']
|
||||
|
||||
LOVELACE_CONFIG_FILE = 'ui-lovelace.yaml'
|
||||
JSON_TYPE = Union[List, Dict, str] # pylint: disable=invalid-name
|
||||
|
||||
OLD_WS_TYPE_GET_LOVELACE_UI = 'frontend/lovelace_config'
|
||||
WS_TYPE_GET_LOVELACE_UI = 'lovelace/config'
|
||||
WS_TYPE_GET_CARD = 'lovelace/config/card/get'
|
||||
WS_TYPE_SET_CARD = 'lovelace/config/card/set'
|
||||
|
||||
SCHEMA_GET_LOVELACE_UI = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
|
||||
vol.Required('type'): vol.Any(WS_TYPE_GET_LOVELACE_UI,
|
||||
OLD_WS_TYPE_GET_LOVELACE_UI),
|
||||
})
|
||||
|
||||
JSON_TYPE = Union[List, Dict, str] # pylint: disable=invalid-name
|
||||
SCHEMA_GET_CARD = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
|
||||
vol.Required('type'): WS_TYPE_GET_CARD,
|
||||
vol.Required('card_id'): str,
|
||||
vol.Optional('format', default='yaml'): str,
|
||||
})
|
||||
|
||||
SCHEMA_SET_CARD = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
|
||||
vol.Required('type'): WS_TYPE_SET_CARD,
|
||||
vol.Required('card_id'): str,
|
||||
vol.Required('card_config'): vol.Any(str, Dict),
|
||||
vol.Optional('format', default='yaml'): str,
|
||||
})
|
||||
|
||||
|
||||
class WriteError(HomeAssistantError):
|
||||
"""Error writing the data."""
|
||||
|
||||
|
||||
class CardNotFoundError(HomeAssistantError):
|
||||
"""Card not found in data."""
|
||||
|
||||
|
||||
class UnsupportedYamlError(HomeAssistantError):
|
||||
"""Unsupported YAML."""
|
||||
|
||||
|
||||
def save_yaml(fname: str, data: JSON_TYPE):
|
||||
"""Save a YAML file."""
|
||||
from ruamel.yaml import YAML
|
||||
@@ -45,7 +70,7 @@ def save_yaml(fname: str, data: JSON_TYPE):
|
||||
_LOGGER.error(str(exc))
|
||||
raise HomeAssistantError(exc)
|
||||
except OSError as exc:
|
||||
_LOGGER.exception('Saving YAML file failed: %s', fname)
|
||||
_LOGGER.exception('Saving YAML file %s failed: %s', fname, exc)
|
||||
raise WriteError(exc)
|
||||
finally:
|
||||
if os.path.exists(tmp_fname):
|
||||
@@ -57,18 +82,29 @@ def save_yaml(fname: str, data: JSON_TYPE):
|
||||
_LOGGER.error("YAML replacement cleanup failed: %s", exc)
|
||||
|
||||
|
||||
def _yaml_unsupported(loader, node):
|
||||
raise UnsupportedYamlError(
|
||||
'Unsupported YAML, you can not use {} in ui-lovelace.yaml'
|
||||
.format(node.tag))
|
||||
|
||||
|
||||
def load_yaml(fname: str) -> JSON_TYPE:
|
||||
"""Load a YAML file."""
|
||||
from ruamel.yaml import YAML
|
||||
from ruamel.yaml.constructor import RoundTripConstructor
|
||||
from ruamel.yaml.error import YAMLError
|
||||
|
||||
RoundTripConstructor.add_constructor(None, _yaml_unsupported)
|
||||
|
||||
yaml = YAML(typ='rt')
|
||||
|
||||
try:
|
||||
with open(fname, encoding='utf-8') as conf_file:
|
||||
# If configuration file is empty YAML returns None
|
||||
# We convert that to an empty dict
|
||||
return yaml.load(conf_file) or OrderedDict()
|
||||
except YAMLError as exc:
|
||||
_LOGGER.error("YAML error: %s", exc)
|
||||
_LOGGER.error("YAML error in %s: %s", fname, exc)
|
||||
raise HomeAssistantError(exc)
|
||||
except UnicodeDecodeError as exc:
|
||||
_LOGGER.error("Unable to read file %s: %s", fname, exc)
|
||||
@@ -76,21 +112,86 @@ def load_yaml(fname: str) -> JSON_TYPE:
|
||||
|
||||
|
||||
def load_config(fname: str) -> JSON_TYPE:
|
||||
"""Load a YAML file and adds id to card if not present."""
|
||||
"""Load a YAML file and adds id to views and cards if not present."""
|
||||
config = load_yaml(fname)
|
||||
# Check if all cards have an ID or else add one
|
||||
# Check if all views and cards have an id or else add one
|
||||
updated = False
|
||||
index = 0
|
||||
for view in config.get('views', []):
|
||||
if 'id' not in view:
|
||||
updated = True
|
||||
view.insert(0, 'id', index,
|
||||
comment="Automatically created id")
|
||||
for card in view.get('cards', []):
|
||||
if 'id' not in card:
|
||||
updated = True
|
||||
card['id'] = uuid.uuid4().hex
|
||||
card.move_to_end('id', last=False)
|
||||
card.insert(0, 'id', uuid.uuid4().hex,
|
||||
comment="Automatically created id")
|
||||
index += 1
|
||||
if updated:
|
||||
save_yaml(fname, config)
|
||||
return config
|
||||
|
||||
|
||||
def object_to_yaml(data: JSON_TYPE) -> str:
|
||||
"""Create yaml string from object."""
|
||||
from ruamel.yaml import YAML
|
||||
from ruamel.yaml.error import YAMLError
|
||||
from ruamel.yaml.compat import StringIO
|
||||
yaml = YAML(typ='rt')
|
||||
yaml.indent(sequence=4, offset=2)
|
||||
stream = StringIO()
|
||||
try:
|
||||
yaml.dump(data, stream)
|
||||
return stream.getvalue()
|
||||
except YAMLError as exc:
|
||||
_LOGGER.error("YAML error: %s", exc)
|
||||
raise HomeAssistantError(exc)
|
||||
|
||||
|
||||
def yaml_to_object(data: str) -> JSON_TYPE:
|
||||
"""Create object from yaml string."""
|
||||
from ruamel.yaml import YAML
|
||||
from ruamel.yaml.error import YAMLError
|
||||
yaml = YAML(typ='rt')
|
||||
try:
|
||||
return yaml.load(data)
|
||||
except YAMLError as exc:
|
||||
_LOGGER.error("YAML error: %s", exc)
|
||||
raise HomeAssistantError(exc)
|
||||
|
||||
|
||||
def get_card(fname: str, card_id: str, data_format: str) -> JSON_TYPE:
|
||||
"""Load a specific card config for id."""
|
||||
config = load_yaml(fname)
|
||||
for view in config.get('views', []):
|
||||
for card in view.get('cards', []):
|
||||
if card.get('id') == card_id:
|
||||
if data_format == 'yaml':
|
||||
return object_to_yaml(card)
|
||||
return card
|
||||
|
||||
raise CardNotFoundError(
|
||||
"Card with ID: {} was not found in {}.".format(card_id, fname))
|
||||
|
||||
|
||||
def set_card(fname: str, card_id: str, card_config: str, data_format: str)\
|
||||
-> bool:
|
||||
"""Save a specific card config for id."""
|
||||
config = load_yaml(fname)
|
||||
for view in config.get('views', []):
|
||||
for card in view.get('cards', []):
|
||||
if card.get('id') == card_id:
|
||||
if data_format == 'yaml':
|
||||
card_config = yaml_to_object(card_config)
|
||||
card.update(card_config)
|
||||
save_yaml(fname, config)
|
||||
return True
|
||||
|
||||
raise CardNotFoundError(
|
||||
"Card with ID: {} was not found in {}.".format(card_id, fname))
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the Lovelace commands."""
|
||||
# Backwards compat. Added in 0.80. Remove after 0.85
|
||||
@@ -102,6 +203,14 @@ async def async_setup(hass, config):
|
||||
WS_TYPE_GET_LOVELACE_UI, websocket_lovelace_config,
|
||||
SCHEMA_GET_LOVELACE_UI)
|
||||
|
||||
hass.components.websocket_api.async_register_command(
|
||||
WS_TYPE_GET_CARD, websocket_lovelace_get_card,
|
||||
SCHEMA_GET_CARD)
|
||||
|
||||
hass.components.websocket_api.async_register_command(
|
||||
WS_TYPE_SET_CARD, websocket_lovelace_set_card,
|
||||
SCHEMA_SET_CARD)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -111,13 +220,15 @@ async def websocket_lovelace_config(hass, connection, msg):
|
||||
error = None
|
||||
try:
|
||||
config = await hass.async_add_executor_job(
|
||||
load_config, hass.config.path('ui-lovelace.yaml'))
|
||||
load_config, hass.config.path(LOVELACE_CONFIG_FILE))
|
||||
message = websocket_api.result_message(
|
||||
msg['id'], config
|
||||
)
|
||||
except FileNotFoundError:
|
||||
error = ('file_not_found',
|
||||
'Could not find ui-lovelace.yaml in your config dir.')
|
||||
except UnsupportedYamlError as err:
|
||||
error = 'unsupported_error', str(err)
|
||||
except HomeAssistantError as err:
|
||||
error = 'load_error', str(err)
|
||||
|
||||
@@ -125,3 +236,59 @@ async def websocket_lovelace_config(hass, connection, msg):
|
||||
message = websocket_api.error_message(msg['id'], *error)
|
||||
|
||||
connection.send_message(message)
|
||||
|
||||
|
||||
@websocket_api.async_response
|
||||
async def websocket_lovelace_get_card(hass, connection, msg):
|
||||
"""Send lovelace card config over websocket config."""
|
||||
error = None
|
||||
try:
|
||||
card = await hass.async_add_executor_job(
|
||||
get_card, hass.config.path(LOVELACE_CONFIG_FILE), msg['card_id'],
|
||||
msg.get('format', 'yaml'))
|
||||
message = websocket_api.result_message(
|
||||
msg['id'], card
|
||||
)
|
||||
except FileNotFoundError:
|
||||
error = ('file_not_found',
|
||||
'Could not find ui-lovelace.yaml in your config dir.')
|
||||
except UnsupportedYamlError as err:
|
||||
error = 'unsupported_error', str(err)
|
||||
except CardNotFoundError:
|
||||
error = ('card_not_found',
|
||||
'Could not find card in ui-lovelace.yaml.')
|
||||
except HomeAssistantError as err:
|
||||
error = 'load_error', str(err)
|
||||
|
||||
if error is not None:
|
||||
message = websocket_api.error_message(msg['id'], *error)
|
||||
|
||||
connection.send_message(message)
|
||||
|
||||
|
||||
@websocket_api.async_response
|
||||
async def websocket_lovelace_set_card(hass, connection, msg):
|
||||
"""Receive lovelace card config over websocket and save."""
|
||||
error = None
|
||||
try:
|
||||
result = await hass.async_add_executor_job(
|
||||
set_card, hass.config.path(LOVELACE_CONFIG_FILE),
|
||||
msg['card_id'], msg['card_config'], msg.get('format', 'yaml'))
|
||||
message = websocket_api.result_message(
|
||||
msg['id'], result
|
||||
)
|
||||
except FileNotFoundError:
|
||||
error = ('file_not_found',
|
||||
'Could not find ui-lovelace.yaml in your config dir.')
|
||||
except UnsupportedYamlError as err:
|
||||
error = 'unsupported_error', str(err)
|
||||
except CardNotFoundError:
|
||||
error = ('card_not_found',
|
||||
'Could not find card in ui-lovelace.yaml.')
|
||||
except HomeAssistantError as err:
|
||||
error = 'save_error', str(err)
|
||||
|
||||
if error is not None:
|
||||
message = websocket_api.error_message(msg['id'], *error)
|
||||
|
||||
connection.send_message(message)
|
||||
|
||||
18
homeassistant/components/mailgun/.translations/en.json
Normal file
18
homeassistant/components/mailgun/.translations/en.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive Mailgun messages.",
|
||||
"one_instance_allowed": "Only a single instance is necessary."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "To send events to Home Assistant, you will need to setup [Webhooks with Mailgun]({mailgun_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/x-www-form-urlencoded\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "Are you sure you want to set up Mailgun?",
|
||||
"title": "Set up the Mailgun Webhook"
|
||||
}
|
||||
},
|
||||
"title": "Mailgun"
|
||||
}
|
||||
}
|
||||
18
homeassistant/components/mailgun/.translations/lb.json
Normal file
18
homeassistant/components/mailgun/.translations/lb.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"not_internet_accessible": "\u00c4r Home Assistant Instanz muss iwwert Internet accessibel si fir Mailgun Noriichten z'empf\u00e4nken.",
|
||||
"one_instance_allowed": "N\u00ebmmen eng eenzeg Instanz ass n\u00e9ideg."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Fir Evenementer un Home Assistant ze sch\u00e9cken, mussen [Webhooks mat Mailgun]({mailgun_url}) ageriicht ginn.\n\nF\u00ebllt folgend Informatiounen aus:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/x-www-form-urlencoded\n\nLiest [Dokumentatioun]({docs_url}) w\u00e9i een Automatiounen ariicht welch eingehend Donn\u00e9\u00eb trait\u00e9ieren."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "S\u00e9cher fir Mailgun anzeriichten?",
|
||||
"title": "Mailgun Webhook ariichten"
|
||||
}
|
||||
},
|
||||
"title": "Mailgun"
|
||||
}
|
||||
}
|
||||
11
homeassistant/components/mqtt/.translations/tr.json
Normal file
11
homeassistant/components/mqtt/.translations/tr.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"data": {
|
||||
"discovery": "Ke\u015ffetmeyi etkinle\u015ftir"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
BASE_API_URL = 'https://rest.clicksend.com/v3'
|
||||
|
||||
DEFAULT_SENDER = 'hass'
|
||||
|
||||
HEADERS = {CONTENT_TYPE: CONTENT_TYPE_JSON}
|
||||
|
||||
|
||||
@@ -29,7 +31,7 @@ def validate_sender(config):
|
||||
"""Set the optional sender name if sender name is not provided."""
|
||||
if CONF_SENDER in config:
|
||||
return config
|
||||
config[CONF_SENDER] = config[CONF_RECIPIENT]
|
||||
config[CONF_SENDER] = DEFAULT_SENDER
|
||||
return config
|
||||
|
||||
|
||||
@@ -61,7 +63,7 @@ class ClicksendNotificationService(BaseNotificationService):
|
||||
self.username = config.get(CONF_USERNAME)
|
||||
self.api_key = config.get(CONF_API_KEY)
|
||||
self.recipients = config.get(CONF_RECIPIENT)
|
||||
self.sender = config.get(CONF_SENDER, CONF_RECIPIENT)
|
||||
self.sender = config.get(CONF_SENDER)
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
"""Send a message to a user."""
|
||||
|
||||
19
homeassistant/components/simplisafe/.translations/nl.json
Normal file
19
homeassistant/components/simplisafe/.translations/nl.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"identifier_exists": "Account bestaat al",
|
||||
"invalid_credentials": "Ongeldige gebruikersgegevens"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"code": "Code (voor Home Assistant)",
|
||||
"password": "Wachtwoord",
|
||||
"username": "E-mailadres"
|
||||
},
|
||||
"title": "Vul uw gegevens in"
|
||||
}
|
||||
},
|
||||
"title": "SimpliSafe"
|
||||
}
|
||||
}
|
||||
12
homeassistant/components/simplisafe/.translations/tr.json
Normal file
12
homeassistant/components/simplisafe/.translations/tr.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Parola",
|
||||
"username": "E-posta adresi"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
homeassistant/components/smhi/.translations/tr.json
Normal file
16
homeassistant/components/smhi/.translations/tr.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"name_exists": "Bu ad zaten var",
|
||||
"wrong_location": "Konum sadece \u0130sve\u00e7"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"latitude": "Enlem",
|
||||
"longitude": "Boylam"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
homeassistant/components/unifi/.translations/nl.json
Normal file
23
homeassistant/components/unifi/.translations/nl.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"user_privilege": "Gebruiker moet beheerder zijn"
|
||||
},
|
||||
"error": {
|
||||
"faulty_credentials": "Foutieve gebruikersgegevens",
|
||||
"service_unavailable": "Geen service beschikbaar"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host",
|
||||
"password": "Wachtwoord",
|
||||
"port": "Poort",
|
||||
"username": "Gebruikersnaam"
|
||||
},
|
||||
"title": "Stel de UniFi-controller in"
|
||||
}
|
||||
},
|
||||
"title": "UniFi-controller"
|
||||
}
|
||||
}
|
||||
12
homeassistant/components/unifi/.translations/tr.json
Normal file
12
homeassistant/components/unifi/.translations/tr.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Parola",
|
||||
"username": "Kullan\u0131c\u0131 ad\u0131"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
homeassistant/components/upnp/.translations/tr.json
Normal file
11
homeassistant/components/upnp/.translations/tr.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"enable_sensors": "Trafik sens\u00f6rleri ekleyin"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
homeassistant/components/zwave/.translations/tr.json
Normal file
11
homeassistant/components/zwave/.translations/tr.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"network_key": "A\u011f Anajtar\u0131 (otomatik \u00fcretilmesi i\u00e7in bo\u015f b\u0131rak\u0131n\u0131z)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"""Constants used by Home Assistant components."""
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 81
|
||||
PATCH_VERSION = '0.dev0'
|
||||
PATCH_VERSION = '0b1'
|
||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||
REQUIRED_PYTHON_VER = (3, 5, 3)
|
||||
@@ -129,6 +129,7 @@ CONF_SENSOR_TYPE = 'sensor_type'
|
||||
CONF_SENSORS = 'sensors'
|
||||
CONF_SHOW_ON_MAP = 'show_on_map'
|
||||
CONF_SLAVE = 'slave'
|
||||
CONF_SOURCE = 'source'
|
||||
CONF_SSL = 'ssl'
|
||||
CONF_STATE = 'state'
|
||||
CONF_STATE_TEMPLATE = 'state_template'
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Union, List, Dict
|
||||
|
||||
import json
|
||||
import os
|
||||
from os import O_WRONLY, O_CREAT, O_TRUNC
|
||||
import tempfile
|
||||
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
@@ -46,13 +46,17 @@ def save_json(filename: str, data: Union[List, Dict],
|
||||
|
||||
Returns True on success.
|
||||
"""
|
||||
tmp_filename = filename + "__TEMP__"
|
||||
tmp_filename = ""
|
||||
tmp_path = os.path.split(filename)[0]
|
||||
try:
|
||||
json_data = json.dumps(data, sort_keys=True, indent=4)
|
||||
mode = 0o600 if private else 0o644
|
||||
with open(os.open(tmp_filename, O_WRONLY | O_CREAT | O_TRUNC, mode),
|
||||
'w', encoding='utf-8') as fdesc:
|
||||
# Modern versions of Python tempfile create this file with mode 0o600
|
||||
with tempfile.NamedTemporaryFile(mode="w", encoding='utf-8',
|
||||
dir=tmp_path, delete=False) as fdesc:
|
||||
fdesc.write(json_data)
|
||||
tmp_filename = fdesc.name
|
||||
if not private:
|
||||
os.chmod(tmp_filename, 0o644)
|
||||
os.replace(tmp_filename, filename)
|
||||
except TypeError as error:
|
||||
_LOGGER.exception('Failed to serialize to JSON: %s',
|
||||
|
||||
@@ -376,7 +376,7 @@ fitbit==0.3.0
|
||||
fixerio==1.0.0a0
|
||||
|
||||
# homeassistant.components.light.flux_led
|
||||
flux_led==0.21
|
||||
flux_led==0.22
|
||||
|
||||
# homeassistant.components.sensor.foobot
|
||||
foobot_async==0.3.1
|
||||
@@ -466,7 +466,7 @@ hole==0.3.0
|
||||
holidays==0.9.8
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20181021.0
|
||||
home-assistant-frontend==20181023.0
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
# homekit==0.10
|
||||
@@ -559,7 +559,7 @@ liffylights==0.9.4
|
||||
lightify==1.0.6.1
|
||||
|
||||
# homeassistant.components.light.limitlessled
|
||||
limitlessled==1.1.2
|
||||
limitlessled==1.1.3
|
||||
|
||||
# homeassistant.components.linode
|
||||
linode-api==4.1.9b1
|
||||
|
||||
@@ -97,7 +97,7 @@ hdate==0.6.5
|
||||
holidays==0.9.8
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20181021.0
|
||||
home-assistant-frontend==20181023.0
|
||||
|
||||
# homeassistant.components.homematicip_cloud
|
||||
homematicip==0.9.8
|
||||
|
||||
271
tests/components/automation/test_geo_location.py
Normal file
271
tests/components/automation/test_geo_location.py
Normal file
@@ -0,0 +1,271 @@
|
||||
"""The tests for the geo location trigger."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.components import automation, zone
|
||||
from homeassistant.core import callback, Context
|
||||
from homeassistant.setup import setup_component
|
||||
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
from tests.components.automation import common
|
||||
|
||||
|
||||
class TestAutomationGeoLocation(unittest.TestCase):
|
||||
"""Test the geo location trigger."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
mock_component(self.hass, 'group')
|
||||
assert setup_component(self.hass, zone.DOMAIN, {
|
||||
'zone': {
|
||||
'name': 'test',
|
||||
'latitude': 32.880837,
|
||||
'longitude': -117.237561,
|
||||
'radius': 250,
|
||||
}
|
||||
})
|
||||
|
||||
self.calls = []
|
||||
|
||||
@callback
|
||||
def record_call(service):
|
||||
"""Record calls."""
|
||||
self.calls.append(service)
|
||||
|
||||
self.hass.services.register('test', 'automation', record_call)
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_if_fires_on_zone_enter(self):
|
||||
"""Test for firing on zone enter."""
|
||||
context = Context()
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.881011,
|
||||
'longitude': -117.234758,
|
||||
'source': 'test_source'
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert setup_component(self.hass, automation.DOMAIN, {
|
||||
automation.DOMAIN: {
|
||||
'trigger': {
|
||||
'platform': 'geo_location',
|
||||
'source': 'test_source',
|
||||
'zone': 'zone.test',
|
||||
'event': 'enter',
|
||||
},
|
||||
'action': {
|
||||
'service': 'test.automation',
|
||||
'data_template': {
|
||||
'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join((
|
||||
'platform', 'entity_id',
|
||||
'from_state.state', 'to_state.state',
|
||||
'zone.name'))
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.880586,
|
||||
'longitude': -117.237564
|
||||
}, context=context)
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.assertEqual(1, len(self.calls))
|
||||
assert self.calls[0].context is context
|
||||
self.assertEqual(
|
||||
'geo_location - geo_location.entity - hello - hello - test',
|
||||
self.calls[0].data['some'])
|
||||
|
||||
# Set out of zone again so we can trigger call
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.881011,
|
||||
'longitude': -117.234758
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
common.turn_off(self.hass)
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.880586,
|
||||
'longitude': -117.237564
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.assertEqual(1, len(self.calls))
|
||||
|
||||
def test_if_not_fires_for_enter_on_zone_leave(self):
|
||||
"""Test for not firing on zone leave."""
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.880586,
|
||||
'longitude': -117.237564,
|
||||
'source': 'test_source'
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert setup_component(self.hass, automation.DOMAIN, {
|
||||
automation.DOMAIN: {
|
||||
'trigger': {
|
||||
'platform': 'geo_location',
|
||||
'source': 'test_source',
|
||||
'zone': 'zone.test',
|
||||
'event': 'enter',
|
||||
},
|
||||
'action': {
|
||||
'service': 'test.automation',
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.881011,
|
||||
'longitude': -117.234758
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.assertEqual(0, len(self.calls))
|
||||
|
||||
def test_if_fires_on_zone_leave(self):
|
||||
"""Test for firing on zone leave."""
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.880586,
|
||||
'longitude': -117.237564,
|
||||
'source': 'test_source'
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert setup_component(self.hass, automation.DOMAIN, {
|
||||
automation.DOMAIN: {
|
||||
'trigger': {
|
||||
'platform': 'geo_location',
|
||||
'source': 'test_source',
|
||||
'zone': 'zone.test',
|
||||
'event': 'leave',
|
||||
},
|
||||
'action': {
|
||||
'service': 'test.automation',
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.881011,
|
||||
'longitude': -117.234758,
|
||||
'source': 'test_source'
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.assertEqual(1, len(self.calls))
|
||||
|
||||
def test_if_not_fires_for_leave_on_zone_enter(self):
|
||||
"""Test for not firing on zone enter."""
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.881011,
|
||||
'longitude': -117.234758,
|
||||
'source': 'test_source'
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert setup_component(self.hass, automation.DOMAIN, {
|
||||
automation.DOMAIN: {
|
||||
'trigger': {
|
||||
'platform': 'geo_location',
|
||||
'source': 'test_source',
|
||||
'zone': 'zone.test',
|
||||
'event': 'leave',
|
||||
},
|
||||
'action': {
|
||||
'service': 'test.automation',
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.880586,
|
||||
'longitude': -117.237564
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.assertEqual(0, len(self.calls))
|
||||
|
||||
def test_if_fires_on_zone_appear(self):
|
||||
"""Test for firing if entity appears in zone."""
|
||||
assert setup_component(self.hass, automation.DOMAIN, {
|
||||
automation.DOMAIN: {
|
||||
'trigger': {
|
||||
'platform': 'geo_location',
|
||||
'source': 'test_source',
|
||||
'zone': 'zone.test',
|
||||
'event': 'enter',
|
||||
},
|
||||
'action': {
|
||||
'service': 'test.automation',
|
||||
'data_template': {
|
||||
'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join((
|
||||
'platform', 'entity_id',
|
||||
'from_state.state', 'to_state.state',
|
||||
'zone.name'))
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# Entity appears in zone without previously existing outside the zone.
|
||||
context = Context()
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.880586,
|
||||
'longitude': -117.237564,
|
||||
'source': 'test_source'
|
||||
}, context=context)
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.assertEqual(1, len(self.calls))
|
||||
assert self.calls[0].context is context
|
||||
self.assertEqual(
|
||||
'geo_location - geo_location.entity - - hello - test',
|
||||
self.calls[0].data['some'])
|
||||
|
||||
def test_if_fires_on_zone_disappear(self):
|
||||
"""Test for firing if entity disappears from zone."""
|
||||
self.hass.states.set('geo_location.entity', 'hello', {
|
||||
'latitude': 32.880586,
|
||||
'longitude': -117.237564,
|
||||
'source': 'test_source'
|
||||
})
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert setup_component(self.hass, automation.DOMAIN, {
|
||||
automation.DOMAIN: {
|
||||
'trigger': {
|
||||
'platform': 'geo_location',
|
||||
'source': 'test_source',
|
||||
'zone': 'zone.test',
|
||||
'event': 'leave',
|
||||
},
|
||||
'action': {
|
||||
'service': 'test.automation',
|
||||
'data_template': {
|
||||
'some': '{{ trigger.%s }}' % '}} - {{ trigger.'.join((
|
||||
'platform', 'entity_id',
|
||||
'from_state.state', 'to_state.state',
|
||||
'zone.name'))
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# Entity disappears from zone without new coordinates outside the zone.
|
||||
self.hass.states.async_remove('geo_location.entity')
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.assertEqual(1, len(self.calls))
|
||||
self.assertEqual(
|
||||
'geo_location - geo_location.entity - hello - - test',
|
||||
self.calls[0].data['some'])
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Test the Emulated Hue component."""
|
||||
import json
|
||||
|
||||
from unittest.mock import patch, Mock, mock_open, MagicMock
|
||||
from unittest.mock import patch, Mock, MagicMock
|
||||
|
||||
from homeassistant.components.emulated_hue import Config
|
||||
|
||||
@@ -14,30 +12,30 @@ def test_config_google_home_entity_id_to_number():
|
||||
'type': 'google_home'
|
||||
})
|
||||
|
||||
mop = mock_open(read_data=json.dumps({'1': 'light.test2'}))
|
||||
handle = mop()
|
||||
with patch('homeassistant.components.emulated_hue.load_json',
|
||||
return_value={'1': 'light.test2'}) as json_loader:
|
||||
with patch('homeassistant.components.emulated_hue'
|
||||
'.save_json') as json_saver:
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '2'
|
||||
|
||||
with patch('homeassistant.util.json.open', mop, create=True):
|
||||
with patch('homeassistant.util.json.os.open', return_value=0):
|
||||
with patch('homeassistant.util.json.os.replace'):
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '2'
|
||||
assert handle.write.call_count == 1
|
||||
assert json.loads(handle.write.mock_calls[0][1][0]) == {
|
||||
'1': 'light.test2',
|
||||
'2': 'light.test',
|
||||
}
|
||||
assert json_saver.mock_calls[0][1][1] == {
|
||||
'1': 'light.test2', '2': 'light.test'
|
||||
}
|
||||
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '2'
|
||||
assert handle.write.call_count == 1
|
||||
assert json_saver.call_count == 1
|
||||
assert json_loader.call_count == 1
|
||||
|
||||
number = conf.entity_id_to_number('light.test2')
|
||||
assert number == '1'
|
||||
assert handle.write.call_count == 1
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '2'
|
||||
assert json_saver.call_count == 1
|
||||
|
||||
entity_id = conf.number_to_entity_id('1')
|
||||
assert entity_id == 'light.test2'
|
||||
number = conf.entity_id_to_number('light.test2')
|
||||
assert number == '1'
|
||||
assert json_saver.call_count == 1
|
||||
|
||||
entity_id = conf.number_to_entity_id('1')
|
||||
assert entity_id == 'light.test2'
|
||||
|
||||
|
||||
def test_config_google_home_entity_id_to_number_altered():
|
||||
@@ -48,30 +46,30 @@ def test_config_google_home_entity_id_to_number_altered():
|
||||
'type': 'google_home'
|
||||
})
|
||||
|
||||
mop = mock_open(read_data=json.dumps({'21': 'light.test2'}))
|
||||
handle = mop()
|
||||
with patch('homeassistant.components.emulated_hue.load_json',
|
||||
return_value={'21': 'light.test2'}) as json_loader:
|
||||
with patch('homeassistant.components.emulated_hue'
|
||||
'.save_json') as json_saver:
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '22'
|
||||
assert json_saver.call_count == 1
|
||||
assert json_loader.call_count == 1
|
||||
|
||||
with patch('homeassistant.util.json.open', mop, create=True):
|
||||
with patch('homeassistant.util.json.os.open', return_value=0):
|
||||
with patch('homeassistant.util.json.os.replace'):
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '22'
|
||||
assert handle.write.call_count == 1
|
||||
assert json.loads(handle.write.mock_calls[0][1][0]) == {
|
||||
'21': 'light.test2',
|
||||
'22': 'light.test',
|
||||
}
|
||||
assert json_saver.mock_calls[0][1][1] == {
|
||||
'21': 'light.test2',
|
||||
'22': 'light.test',
|
||||
}
|
||||
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '22'
|
||||
assert handle.write.call_count == 1
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '22'
|
||||
assert json_saver.call_count == 1
|
||||
|
||||
number = conf.entity_id_to_number('light.test2')
|
||||
assert number == '21'
|
||||
assert handle.write.call_count == 1
|
||||
number = conf.entity_id_to_number('light.test2')
|
||||
assert number == '21'
|
||||
assert json_saver.call_count == 1
|
||||
|
||||
entity_id = conf.number_to_entity_id('21')
|
||||
assert entity_id == 'light.test2'
|
||||
entity_id = conf.number_to_entity_id('21')
|
||||
assert entity_id == 'light.test2'
|
||||
|
||||
|
||||
def test_config_google_home_entity_id_to_number_empty():
|
||||
@@ -82,29 +80,29 @@ def test_config_google_home_entity_id_to_number_empty():
|
||||
'type': 'google_home'
|
||||
})
|
||||
|
||||
mop = mock_open(read_data='')
|
||||
handle = mop()
|
||||
with patch('homeassistant.components.emulated_hue.load_json',
|
||||
return_value={}) as json_loader:
|
||||
with patch('homeassistant.components.emulated_hue'
|
||||
'.save_json') as json_saver:
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '1'
|
||||
assert json_saver.call_count == 1
|
||||
assert json_loader.call_count == 1
|
||||
|
||||
with patch('homeassistant.util.json.open', mop, create=True):
|
||||
with patch('homeassistant.util.json.os.open', return_value=0):
|
||||
with patch('homeassistant.util.json.os.replace'):
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '1'
|
||||
assert handle.write.call_count == 1
|
||||
assert json.loads(handle.write.mock_calls[0][1][0]) == {
|
||||
'1': 'light.test',
|
||||
}
|
||||
assert json_saver.mock_calls[0][1][1] == {
|
||||
'1': 'light.test',
|
||||
}
|
||||
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '1'
|
||||
assert handle.write.call_count == 1
|
||||
number = conf.entity_id_to_number('light.test')
|
||||
assert number == '1'
|
||||
assert json_saver.call_count == 1
|
||||
|
||||
number = conf.entity_id_to_number('light.test2')
|
||||
assert number == '2'
|
||||
assert handle.write.call_count == 2
|
||||
number = conf.entity_id_to_number('light.test2')
|
||||
assert number == '2'
|
||||
assert json_saver.call_count == 2
|
||||
|
||||
entity_id = conf.number_to_entity_id('2')
|
||||
assert entity_id == 'light.test2'
|
||||
entity_id = conf.number_to_entity_id('2')
|
||||
assert entity_id == 'light.test2'
|
||||
|
||||
|
||||
def test_config_alexa_entity_id_to_number():
|
||||
|
||||
@@ -9,7 +9,8 @@ from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.websocket_api.const import TYPE_RESULT
|
||||
from homeassistant.components.lovelace import (load_yaml,
|
||||
save_yaml, load_config)
|
||||
save_yaml, load_config,
|
||||
UnsupportedYamlError)
|
||||
|
||||
TEST_YAML_A = """\
|
||||
title: My Awesome Home
|
||||
@@ -55,6 +56,8 @@ views:
|
||||
# Title of the view. Will be used as the tooltip for tab icon
|
||||
title: Second view
|
||||
cards:
|
||||
- id: test
|
||||
type: entities
|
||||
# Entities card will take a list of entities and show their state.
|
||||
- type: entities
|
||||
# Title of the entities card
|
||||
@@ -79,6 +82,7 @@ TEST_YAML_B = """\
|
||||
title: Home
|
||||
views:
|
||||
- title: Dashboard
|
||||
id: dashboard
|
||||
icon: mdi:home
|
||||
cards:
|
||||
- id: testid
|
||||
@@ -102,6 +106,15 @@ views:
|
||||
type: vertical-stack
|
||||
"""
|
||||
|
||||
# Test unsupported YAML
|
||||
TEST_UNSUP_YAML = """\
|
||||
title: Home
|
||||
views:
|
||||
- title: Dashboard
|
||||
icon: mdi:home
|
||||
cards: !include cards.yaml
|
||||
"""
|
||||
|
||||
|
||||
class TestYAML(unittest.TestCase):
|
||||
"""Test lovelace.yaml save and load."""
|
||||
@@ -147,9 +160,11 @@ class TestYAML(unittest.TestCase):
|
||||
"""Test if id is added."""
|
||||
fname = self._path_for("test6")
|
||||
with patch('homeassistant.components.lovelace.load_yaml',
|
||||
return_value=self.yaml.load(TEST_YAML_A)):
|
||||
return_value=self.yaml.load(TEST_YAML_A)), \
|
||||
patch('homeassistant.components.lovelace.save_yaml'):
|
||||
data = load_config(fname)
|
||||
assert 'id' in data['views'][0]['cards'][0]
|
||||
assert 'id' in data['views'][1]
|
||||
|
||||
def test_id_not_changed(self):
|
||||
"""Test if id is not changed if already exists."""
|
||||
@@ -256,7 +271,7 @@ async def test_lovelace_ui_not_found(hass, hass_ws_client):
|
||||
|
||||
|
||||
async def test_lovelace_ui_load_err(hass, hass_ws_client):
|
||||
"""Test lovelace_ui command cannot find file."""
|
||||
"""Test lovelace_ui command load error."""
|
||||
await async_setup_component(hass, 'lovelace')
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
@@ -272,3 +287,156 @@ async def test_lovelace_ui_load_err(hass, hass_ws_client):
|
||||
assert msg['type'] == TYPE_RESULT
|
||||
assert msg['success'] is False
|
||||
assert msg['error']['code'] == 'load_error'
|
||||
|
||||
|
||||
async def test_lovelace_ui_load_json_err(hass, hass_ws_client):
|
||||
"""Test lovelace_ui command load error."""
|
||||
await async_setup_component(hass, 'lovelace')
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
with patch('homeassistant.components.lovelace.load_config',
|
||||
side_effect=UnsupportedYamlError):
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'lovelace/config',
|
||||
})
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg['id'] == 5
|
||||
assert msg['type'] == TYPE_RESULT
|
||||
assert msg['success'] is False
|
||||
assert msg['error']['code'] == 'unsupported_error'
|
||||
|
||||
|
||||
async def test_lovelace_get_card(hass, hass_ws_client):
|
||||
"""Test get_card command."""
|
||||
await async_setup_component(hass, 'lovelace')
|
||||
client = await hass_ws_client(hass)
|
||||
yaml = YAML(typ='rt')
|
||||
|
||||
with patch('homeassistant.components.lovelace.load_yaml',
|
||||
return_value=yaml.load(TEST_YAML_A)):
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'lovelace/config/card/get',
|
||||
'card_id': 'test',
|
||||
})
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg['id'] == 5
|
||||
assert msg['type'] == TYPE_RESULT
|
||||
assert msg['success']
|
||||
assert msg['result'] == 'id: test\ntype: entities\n'
|
||||
|
||||
|
||||
async def test_lovelace_get_card_not_found(hass, hass_ws_client):
|
||||
"""Test get_card command cannot find card."""
|
||||
await async_setup_component(hass, 'lovelace')
|
||||
client = await hass_ws_client(hass)
|
||||
yaml = YAML(typ='rt')
|
||||
|
||||
with patch('homeassistant.components.lovelace.load_yaml',
|
||||
return_value=yaml.load(TEST_YAML_A)):
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'lovelace/config/card/get',
|
||||
'card_id': 'not_found',
|
||||
})
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg['id'] == 5
|
||||
assert msg['type'] == TYPE_RESULT
|
||||
assert msg['success'] is False
|
||||
assert msg['error']['code'] == 'card_not_found'
|
||||
|
||||
|
||||
async def test_lovelace_get_card_bad_yaml(hass, hass_ws_client):
|
||||
"""Test get_card command bad yaml."""
|
||||
await async_setup_component(hass, 'lovelace')
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
with patch('homeassistant.components.lovelace.load_yaml',
|
||||
side_effect=HomeAssistantError):
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'lovelace/config/card/get',
|
||||
'card_id': 'testid',
|
||||
})
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg['id'] == 5
|
||||
assert msg['type'] == TYPE_RESULT
|
||||
assert msg['success'] is False
|
||||
assert msg['error']['code'] == 'load_error'
|
||||
|
||||
|
||||
async def test_lovelace_set_card(hass, hass_ws_client):
|
||||
"""Test set_card command."""
|
||||
await async_setup_component(hass, 'lovelace')
|
||||
client = await hass_ws_client(hass)
|
||||
yaml = YAML(typ='rt')
|
||||
|
||||
with patch('homeassistant.components.lovelace.load_yaml',
|
||||
return_value=yaml.load(TEST_YAML_A)), \
|
||||
patch('homeassistant.components.lovelace.save_yaml') \
|
||||
as save_yaml_mock:
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'lovelace/config/card/set',
|
||||
'card_id': 'test',
|
||||
'card_config': 'id: test\ntype: glance\n',
|
||||
})
|
||||
msg = await client.receive_json()
|
||||
|
||||
result = save_yaml_mock.call_args_list[0][0][1]
|
||||
assert result.mlget(['views', 1, 'cards', 0, 'type'],
|
||||
list_ok=True) == 'glance'
|
||||
assert msg['id'] == 5
|
||||
assert msg['type'] == TYPE_RESULT
|
||||
assert msg['success']
|
||||
|
||||
|
||||
async def test_lovelace_set_card_not_found(hass, hass_ws_client):
|
||||
"""Test set_card command cannot find card."""
|
||||
await async_setup_component(hass, 'lovelace')
|
||||
client = await hass_ws_client(hass)
|
||||
yaml = YAML(typ='rt')
|
||||
|
||||
with patch('homeassistant.components.lovelace.load_yaml',
|
||||
return_value=yaml.load(TEST_YAML_A)):
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'lovelace/config/card/set',
|
||||
'card_id': 'not_found',
|
||||
'card_config': 'id: test\ntype: glance\n',
|
||||
})
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg['id'] == 5
|
||||
assert msg['type'] == TYPE_RESULT
|
||||
assert msg['success'] is False
|
||||
assert msg['error']['code'] == 'card_not_found'
|
||||
|
||||
|
||||
async def test_lovelace_set_card_bad_yaml(hass, hass_ws_client):
|
||||
"""Test set_card command bad yaml."""
|
||||
await async_setup_component(hass, 'lovelace')
|
||||
client = await hass_ws_client(hass)
|
||||
yaml = YAML(typ='rt')
|
||||
|
||||
with patch('homeassistant.components.lovelace.load_yaml',
|
||||
return_value=yaml.load(TEST_YAML_A)), \
|
||||
patch('homeassistant.components.lovelace.yaml_to_object',
|
||||
side_effect=HomeAssistantError):
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'lovelace/config/card/set',
|
||||
'card_id': 'test',
|
||||
'card_config': 'id: test\ntype: glance\n',
|
||||
})
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg['id'] == 5
|
||||
assert msg['type'] == TYPE_RESULT
|
||||
assert msg['success'] is False
|
||||
assert msg['error']['code'] == 'save_error'
|
||||
|
||||
Reference in New Issue
Block a user