Merge pull request #22883 from home-assistant/rc

0.91.2
This commit is contained in:
Pascal Vizeli 2019-04-08 11:27:14 +02:00 committed by GitHub
commit 8f9c2000ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 221 additions and 83 deletions

View File

@ -88,8 +88,8 @@ CAMERA_SERVICE_PLAY_STREAM = CAMERA_SERVICE_SCHEMA.extend({
CAMERA_SERVICE_RECORD = CAMERA_SERVICE_SCHEMA.extend({
vol.Required(CONF_FILENAME): cv.template,
vol.Optional(CONF_DURATION, default=30): int,
vol.Optional(CONF_LOOKBACK, default=0): int,
vol.Optional(CONF_DURATION, default=30): vol.Coerce(int),
vol.Optional(CONF_LOOKBACK, default=0): vol.Coerce(int),
})
WS_TYPE_CAMERA_THUMBNAIL = 'camera_thumbnail'

View File

@ -19,6 +19,7 @@ _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['libpyfoscam==1.0']
CONF_IP = 'ip'
CONF_RTSP_PORT = 'rtsp_port'
DEFAULT_NAME = 'Foscam Camera'
DEFAULT_PORT = 88
@ -31,6 +32,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_RTSP_PORT): cv.port
})
@ -58,11 +60,12 @@ class FoscamCam(Camera):
self._foscam_session = FoscamCamera(
ip_address, port, self._username, self._password, verbose=False)
self._rtsp_port = None
result, response = self._foscam_session.get_port_info()
if result == 0:
self._rtsp_port = response.get('rtspPort') or \
response.get('mediaPort')
self._rtsp_port = device_info.get(CONF_RTSP_PORT)
if not self._rtsp_port:
result, response = self._foscam_session.get_port_info()
if result == 0:
self._rtsp_port = response.get('rtspPort') or \
response.get('mediaPort')
def camera_image(self):
"""Return a still image response from the camera."""

View File

@ -118,6 +118,7 @@ class HassIOIngress(HomeAssistantView):
return web.Response(
headers=headers,
status=result.status,
content_type=result.content_type,
body=body
)
@ -145,8 +146,7 @@ def _init_header(
# filter flags
for name, value in request.headers.items():
if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE,
hdrs.CONTENT_ENCODING):
if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_ENCODING):
continue
headers[name] = value

View File

@ -41,9 +41,10 @@ class KonnectedSwitch(ToggleEntity):
self._pause = self._data.get(CONF_PAUSE)
self._repeat = self._data.get(CONF_REPEAT)
self._state = self._boolean_state(self._data.get(ATTR_STATE))
self._unique_id = '{}-{}'.format(device_id, hash(frozenset(
{self._pin_num, self._momentary, self._pause, self._repeat})))
self._name = self._data.get(CONF_NAME)
self._unique_id = '{}-{}-{}-{}-{}'.format(
device_id, self._pin_num, self._momentary,
self._pause, self._repeat)
@property
def unique_id(self) -> str:

View File

@ -42,6 +42,11 @@ ATTR_OS_NAME = 'os_name'
ATTR_OS_VERSION = 'os_version'
ATTR_PUSH_TOKEN = 'push_token'
ATTR_PUSH_URL = 'push_url'
ATTR_PUSH_RATE_LIMITS = 'rateLimits'
ATTR_PUSH_RATE_LIMITS_ERRORS = 'errors'
ATTR_PUSH_RATE_LIMITS_MAXIMUM = 'maximum'
ATTR_PUSH_RATE_LIMITS_RESETS_AT = 'resetsAt'
ATTR_PUSH_RATE_LIMITS_SUCCESSFUL = 'successful'
ATTR_SUPPORTS_ENCRYPTION = 'supports_encryption'
ATTR_EVENT_DATA = 'event_data'
@ -67,6 +72,8 @@ ERR_SENSOR_DUPLICATE_UNIQUE_ID = 'duplicate_unique_id'
WEBHOOK_TYPE_CALL_SERVICE = 'call_service'
WEBHOOK_TYPE_FIRE_EVENT = 'fire_event'
WEBHOOK_TYPE_GET_CONFIG = 'get_config'
WEBHOOK_TYPE_GET_ZONES = 'get_zones'
WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor'
WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template'
WEBHOOK_TYPE_UPDATE_LOCATION = 'update_location'
@ -74,6 +81,7 @@ WEBHOOK_TYPE_UPDATE_REGISTRATION = 'update_registration'
WEBHOOK_TYPE_UPDATE_SENSOR_STATES = 'update_sensor_states'
WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT,
WEBHOOK_TYPE_GET_CONFIG, WEBHOOK_TYPE_GET_ZONES,
WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE,
WEBHOOK_TYPE_UPDATE_LOCATION,
WEBHOOK_TYPE_UPDATE_REGISTRATION,
@ -163,7 +171,7 @@ REGISTER_SENSOR_SCHEMA = vol.Schema({
vol.Required(ATTR_SENSOR_NAME): cv.string,
vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES),
vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string,
vol.Required(ATTR_SENSOR_UOM): cv.string,
vol.Optional(ATTR_SENSOR_UOM): cv.string,
vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float),
vol.Optional(ATTR_SENSOR_ICON, default='mdi:cellphone'): cv.icon,
})

View File

@ -6,6 +6,7 @@ from typing import Callable, Dict, Tuple
from aiohttp.web import json_response, Response
from homeassistant.core import Context
from homeassistant.helpers.json import JSONEncoder
from homeassistant.helpers.typing import HomeAssistantType
from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_NAME,
@ -133,9 +134,9 @@ def savable_state(hass: HomeAssistantType) -> Dict:
def webhook_response(data, *, registration: Dict, status: int = 200,
headers: Dict = None) -> Response:
"""Return a encrypted response if registration supports it."""
data = json.dumps(data)
data = json.dumps(data, cls=JSONEncoder)
if CONF_SECRET in registration:
if registration[ATTR_SUPPORTS_ENCRYPTION]:
keylen, encrypt = setup_encrypt()
key = registration[CONF_SECRET].encode("utf-8")

View File

@ -8,13 +8,18 @@ import async_timeout
from homeassistant.components.notify import (
ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT,
BaseNotificationService)
from homeassistant.components.mobile_app.const import (
ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_VERSION, ATTR_DEVICE_NAME,
ATTR_OS_VERSION, ATTR_PUSH_TOKEN, ATTR_PUSH_URL, DATA_CONFIG_ENTRIES,
DOMAIN)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.util.dt as dt_util
from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_VERSION,
ATTR_DEVICE_NAME, ATTR_OS_VERSION, ATTR_PUSH_RATE_LIMITS,
ATTR_PUSH_RATE_LIMITS_ERRORS,
ATTR_PUSH_RATE_LIMITS_MAXIMUM,
ATTR_PUSH_RATE_LIMITS_RESETS_AT,
ATTR_PUSH_RATE_LIMITS_SUCCESSFUL, ATTR_PUSH_TOKEN,
ATTR_PUSH_URL, DATA_CONFIG_ENTRIES, DOMAIN)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['mobile_app']
@ -38,16 +43,21 @@ def push_registrations(hass):
# pylint: disable=invalid-name
def log_rate_limits(hass, device_name, resp, level=logging.INFO):
"""Output rate limit log line at given level."""
rate_limits = resp['rateLimits']
resetsAt = dt_util.parse_datetime(rate_limits['resetsAt'])
resetsAtTime = resetsAt - datetime.now(timezone.utc)
if ATTR_PUSH_RATE_LIMITS not in resp:
return
rate_limits = resp[ATTR_PUSH_RATE_LIMITS]
resetsAt = rate_limits[ATTR_PUSH_RATE_LIMITS_RESETS_AT]
resetsAtTime = (dt_util.parse_datetime(resetsAt) -
datetime.now(timezone.utc))
rate_limit_msg = ("mobile_app push notification rate limits for %s: "
"%d sent, %d allowed, %d errors, "
"resets in %s")
_LOGGER.log(level, rate_limit_msg,
device_name,
rate_limits['successful'],
rate_limits['maximum'], rate_limits['errors'],
rate_limits[ATTR_PUSH_RATE_LIMITS_SUCCESSFUL],
rate_limits[ATTR_PUSH_RATE_LIMITS_MAXIMUM],
rate_limits[ATTR_PUSH_RATE_LIMITS_ERRORS],
str(resetsAtTime).split(".")[0])

View File

@ -55,4 +55,4 @@ class MobileAppSensor(MobileAppEntity):
@property
def unit_of_measurement(self):
"""Return the unit of measurement this sensor expresses itself in."""
return self._config[ATTR_SENSOR_UOM]
return self._config.get(ATTR_SENSOR_UOM)

View File

@ -8,6 +8,8 @@ from homeassistant.components.device_tracker import (ATTR_ATTRIBUTES,
ATTR_DEV_ID,
DOMAIN as DT_DOMAIN,
SERVICE_SEE as DT_SEE)
from homeassistant.components.frontend import MANIFEST_JSON
from homeassistant.components.zone.const import DOMAIN as ZONE_DOMAIN
from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA,
CONF_WEBHOOK_ID, HTTP_BAD_REQUEST,
@ -33,8 +35,9 @@ from .const import (ATTR_ALTITUDE, ATTR_BATTERY, ATTR_COURSE, ATTR_DEVICE_ID,
DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED,
ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED,
SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA,
WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE,
WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_REGISTER_SENSOR,
WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE,
WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_CONFIG,
WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR,
WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION,
WEBHOOK_TYPE_UPDATE_REGISTRATION,
WEBHOOK_TYPE_UPDATE_SENSOR_STATES)
@ -87,16 +90,19 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str,
enc_data = req_data[ATTR_WEBHOOK_ENCRYPTED_DATA]
webhook_payload = _decrypt_payload(registration[CONF_SECRET], enc_data)
if webhook_type not in WEBHOOK_SCHEMAS:
if webhook_type not in WEBHOOK_TYPES:
_LOGGER.error('Received invalid webhook type: %s', webhook_type)
return empty_okay_response()
try:
data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload)
except vol.Invalid as ex:
err = vol.humanize.humanize_error(webhook_payload, ex)
_LOGGER.error('Received invalid webhook payload: %s', err)
return empty_okay_response(headers=headers)
data = webhook_payload
if webhook_type in WEBHOOK_SCHEMAS:
try:
data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload)
except vol.Invalid as ex:
err = vol.humanize.humanize_error(webhook_payload, ex)
_LOGGER.error('Received invalid webhook payload: %s', err)
return empty_okay_response(headers=headers)
context = registration_context(registration)
@ -139,18 +145,26 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str,
if webhook_type == WEBHOOK_TYPE_UPDATE_LOCATION:
see_payload = {
ATTR_DEV_ID: registration[ATTR_DEVICE_ID],
ATTR_LOCATION_NAME: data.get(ATTR_LOCATION_NAME),
ATTR_GPS: data.get(ATTR_GPS),
ATTR_GPS_ACCURACY: data.get(ATTR_GPS_ACCURACY),
ATTR_BATTERY: data.get(ATTR_BATTERY),
ATTR_ATTRIBUTES: {
ATTR_SPEED: data.get(ATTR_SPEED),
ATTR_ALTITUDE: data.get(ATTR_ALTITUDE),
ATTR_COURSE: data.get(ATTR_COURSE),
ATTR_VERTICAL_ACCURACY: data.get(ATTR_VERTICAL_ACCURACY),
}
ATTR_GPS: data[ATTR_GPS],
ATTR_GPS_ACCURACY: data[ATTR_GPS_ACCURACY],
}
for key in (ATTR_LOCATION_NAME, ATTR_BATTERY):
value = data.get(key)
if value is not None:
see_payload[key] = value
attrs = {}
for key in (ATTR_ALTITUDE, ATTR_COURSE,
ATTR_SPEED, ATTR_VERTICAL_ACCURACY):
value = data.get(key)
if value is not None:
attrs[key] = value
if attrs:
see_payload[ATTR_ATTRIBUTES] = attrs
try:
await hass.services.async_call(DT_DOMAIN,
DT_SEE, see_payload,
@ -214,7 +228,7 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str,
data[ATTR_SENSOR_TYPE])
async_dispatcher_send(hass, register_signal, data)
return webhook_response({"status": "registered"},
return webhook_response({'success': True},
registration=registration, status=HTTP_CREATED,
headers=headers)
@ -257,7 +271,29 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str,
async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, new_state)
resp[unique_id] = {"status": "okay"}
resp[unique_id] = {'success': True}
return webhook_response(resp, registration=registration,
headers=headers)
if webhook_type == WEBHOOK_TYPE_GET_ZONES:
zones = (hass.states.get(entity_id) for entity_id
in sorted(hass.states.async_entity_ids(ZONE_DOMAIN)))
return webhook_response(list(zones), registration=registration,
headers=headers)
if webhook_type == WEBHOOK_TYPE_GET_CONFIG:
hass_config = hass.config.as_dict()
return webhook_response({
'latitude': hass_config['latitude'],
'longitude': hass_config['longitude'],
'elevation': hass_config['elevation'],
'unit_system': hass_config['unit_system'],
'location_name': hass_config['location_name'],
'time_zone': hass_config['time_zone'],
'components': hass_config['components'],
'version': hass_config['version'],
'theme_color': MANIFEST_JSON['theme_color'],
}, registration=registration, headers=headers)

View File

@ -53,12 +53,13 @@ def setup(hass, config):
"""Set up the Mopar component."""
import motorparts
conf = config[DOMAIN]
cookie = hass.config.path(COOKIE_FILE)
try:
session = motorparts.get_session(
config[CONF_USERNAME],
config[CONF_PASSWORD],
config[CONF_PIN],
conf[CONF_USERNAME],
conf[CONF_PASSWORD],
conf[CONF_PIN],
cookie_path=cookie
)
except motorparts.MoparError:
@ -69,7 +70,7 @@ def setup(hass, config):
data.update(now=None)
track_time_interval(
hass, data.update, config[CONF_SCAN_INTERVAL]
hass, data.update, conf[CONF_SCAN_INTERVAL]
)
def handle_horn(call):

View File

@ -121,3 +121,8 @@ class TadoDataStore:
"""Wrap for setZoneOverlay(..)."""
self.tado.setZoneOverlay(zone_id, mode, temperature, duration)
self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg
def set_zone_off(self, zone_id, mode):
"""Set a zone to off."""
self.tado.setZoneOverlay(zone_id, mode, None, None, 'HEATING', 'OFF')
self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg

View File

@ -363,7 +363,7 @@ class TadoClimate(ClimateDevice):
if self._current_operation == CONST_MODE_OFF:
_LOGGER.info("Switching mytado.com to OFF for zone %s",
self.zone_name)
self._store.set_zone_overlay(self.zone_id, CONST_OVERLAY_MANUAL)
self._store.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL)
self._overlay_mode = self._current_operation
return

View File

@ -134,7 +134,7 @@ def setup(hass, config):
discovery.listen(hass, SERVICE_YEELIGHT, device_discovered)
def update(event):
for device in yeelight_data.values():
for device in list(yeelight_data.values()):
device.update()
track_time_interval(
@ -185,8 +185,8 @@ class YeelightDevice:
@property
def bulb(self):
"""Return bulb device."""
import yeelight
if self._bulb_device is None:
import yeelight
try:
self._bulb_device = yeelight.Bulb(self._ipaddr,
model=self._model)
@ -241,33 +241,27 @@ class YeelightDevice:
def turn_on(self, duration=DEFAULT_TRANSITION, light_type=None):
"""Turn on device."""
import yeelight
if not light_type:
light_type = yeelight.enums.LightType.Main
from yeelight import BulbException
try:
self.bulb.turn_on(duration=duration, light_type=light_type)
except yeelight.BulbException as ex:
except BulbException as ex:
_LOGGER.error("Unable to turn the bulb on: %s", ex)
return
def turn_off(self, duration=DEFAULT_TRANSITION, light_type=None):
"""Turn off device."""
import yeelight
if not light_type:
light_type = yeelight.enums.LightType.Main
from yeelight import BulbException
try:
self.bulb.turn_off(duration=duration, light_type=light_type)
except yeelight.BulbException as ex:
except BulbException as ex:
_LOGGER.error("Unable to turn the bulb off: %s", ex)
return
def update(self):
"""Read new properties from the device."""
import yeelight
from yeelight import BulbException
if not self.bulb:
return
@ -275,7 +269,7 @@ class YeelightDevice:
try:
self.bulb.get_properties(UPDATE_REQUEST_PROPERTIES)
self._available = True
except yeelight.BulbException as ex:
except BulbException as ex:
if self._available: # just inform once
_LOGGER.error("Unable to update bulb status: %s", ex)
self._available = False

View File

@ -4,7 +4,7 @@ import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.components.yeelight import DATA_YEELIGHT, DATA_UPDATED
from . import DATA_YEELIGHT, DATA_UPDATED
DEPENDENCIES = ['yeelight']

View File

@ -15,7 +15,7 @@ from homeassistant.components.light import (
SUPPORT_COLOR, SUPPORT_TRANSITION, SUPPORT_COLOR_TEMP, SUPPORT_FLASH,
SUPPORT_EFFECT, Light)
import homeassistant.util.color as color_util
from homeassistant.components.yeelight import (
from . import (
CONF_TRANSITION, DATA_YEELIGHT, CONF_MODE_MUSIC,
CONF_SAVE_ON_CHANGE, CONF_CUSTOM_EFFECTS, DATA_UPDATED,
YEELIGHT_SERVICE_SCHEMA, DOMAIN, ATTR_TRANSITIONS,
@ -189,6 +189,8 @@ class YeelightLight(Light):
def __init__(self, device, custom_effects=None):
"""Initialize the Yeelight light."""
from yeelight.enums import LightType
self.config = device.config
self._device = device
@ -202,6 +204,8 @@ class YeelightLight(Light):
self._min_mireds = None
self._max_mireds = None
self._light_type = LightType.Main
if custom_effects:
self._custom_effects = custom_effects
else:
@ -281,8 +285,7 @@ class YeelightLight(Light):
@property
def light_type(self):
"""Return light type."""
import yeelight
return yeelight.enums.LightType.Main
return self._light_type
def _get_hs_from_properties(self):
rgb = self._get_property('rgb')
@ -589,21 +592,19 @@ class YeelightAmbientLight(YeelightLight):
def __init__(self, *args, **kwargs):
"""Initialize the Yeelight Ambient light."""
from yeelight.enums import LightType
super().__init__(*args, **kwargs)
self._min_mireds = kelvin_to_mired(6500)
self._max_mireds = kelvin_to_mired(1700)
self._light_type = LightType.Ambient
@property
def name(self) -> str:
"""Return the name of the device if any."""
return "{} ambilight".format(self.device.name)
@property
def light_type(self):
"""Return light type."""
import yeelight
return yeelight.enums.LightType.Ambient
@property
def _is_nightlight_enabled(self):
return False

View File

@ -172,6 +172,7 @@ class Light(ZhaEntity, light.Light):
duration = transition * 10 if transition else DEFAULT_DURATION
brightness = kwargs.get(light.ATTR_BRIGHTNESS)
t_log = {}
if (brightness is not None or transition) and \
self._supported_features & light.SUPPORT_BRIGHTNESS:
if brightness is not None:
@ -182,7 +183,9 @@ class Light(ZhaEntity, light.Light):
level,
duration
)
t_log['move_to_level_with_on_off'] = success
if not success:
self.debug("turned on: %s", t_log)
return
self._state = bool(level)
if level:
@ -190,7 +193,9 @@ class Light(ZhaEntity, light.Light):
if brightness is None or brightness:
success = await self._on_off_channel.on()
t_log['on_off'] = success
if not success:
self.debug("turned on: %s", t_log)
return
self._state = True
@ -199,7 +204,9 @@ class Light(ZhaEntity, light.Light):
temperature = kwargs[light.ATTR_COLOR_TEMP]
success = await self._color_channel.move_to_color_temp(
temperature, duration)
t_log['move_to_color_temp'] = success
if not success:
self.debug("turned on: %s", t_log)
return
self._color_temp = temperature
@ -212,10 +219,13 @@ class Light(ZhaEntity, light.Light):
int(xy_color[1] * 65535),
duration,
)
t_log['move_to_color'] = success
if not success:
self.debug("turned on: %s", t_log)
return
self._hs_color = hs_color
self.debug("turned on: %s", t_log)
self.async_schedule_update_ha_state()
async def async_turn_off(self, **kwargs):
@ -229,7 +239,7 @@ class Light(ZhaEntity, light.Light):
)
else:
success = await self._on_off_channel.off()
_LOGGER.debug("%s was turned off: %s", self.entity_id, success)
self.debug("turned off: %s", success)
if not success:
return
self._state = False
@ -238,13 +248,21 @@ class Light(ZhaEntity, light.Light):
async def async_update(self):
"""Attempt to retrieve on off state from the light."""
await super().async_update()
await self.async_get_state()
async def async_get_state(self, from_cache=True):
"""Attempt to retrieve on off state from the light."""
if self._on_off_channel:
self._state = await self._on_off_channel.get_attribute_value(
'on_off')
'on_off', from_cache=from_cache)
if self._level_channel:
self._brightness = await self._level_channel.get_attribute_value(
'current_level')
'current_level', from_cache=from_cache)
async def refresh(self, time):
"""Call async_update at an interval."""
await self.async_update()
"""Call async_get_state at an interval."""
await self.async_get_state(from_cache=False)
def debug(self, msg, *args):
"""Log debug message."""
_LOGGER.debug('%s: ' + msg, self.entity_id, *args)

View File

@ -14,7 +14,7 @@ from .const import CONF_PASSIVE, DOMAIN, HOME_ZONE
@callback
def configured_zones(hass):
"""Return a set of the configured hosts."""
"""Return a set of the configured zones."""
return set((slugify(entry.data[CONF_NAME])) for
entry in hass.config_entries.async_entries(DOMAIN))

View File

@ -2,7 +2,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 91
PATCH_VERSION = '1'
PATCH_VERSION = '2'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 5, 3)

View File

@ -35,7 +35,7 @@ async def test_sensor(hass, create_registrations, webhook_client): # noqa: F401
assert reg_resp.status == 201
json = await reg_resp.json()
assert json == {'status': 'registered'}
assert json == {'success': True}
entity = hass.states.get('sensor.battery_state')
assert entity is not None
@ -122,7 +122,7 @@ async def test_sensor_id_no_dupes(hass, create_registrations, # noqa: F401, F81
assert reg_resp.status == 201
reg_json = await reg_resp.json()
assert reg_json == {'status': 'registered'}
assert reg_json == {'success': True}
dupe_resp = await webhook_client.post(webhook_url, json=payload)

View File

@ -4,8 +4,10 @@ import logging
import pytest
from homeassistant.components.mobile_app.const import CONF_SECRET
from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN
from homeassistant.const import CONF_WEBHOOK_ID
from homeassistant.core import callback
from homeassistant.setup import async_setup_component
from tests.common import async_mock_service
@ -100,6 +102,64 @@ async def test_webhook_update_registration(webhook_client, hass_client): # noqa
assert CONF_SECRET not in update_json
async def test_webhook_handle_get_zones(hass, create_registrations, # noqa: F401, F811, E501
webhook_client): # noqa: F811
"""Test that we can get zones properly."""
await async_setup_component(hass, ZONE_DOMAIN, {
ZONE_DOMAIN: {
'name': 'test',
'latitude': 32.880837,
'longitude': -117.237561,
'radius': 250,
}
})
resp = await webhook_client.post(
'/api/webhook/{}'.format(create_registrations[1]['webhook_id']),
json={'type': 'get_zones'}
)
assert resp.status == 200
json = await resp.json()
assert len(json) == 1
assert json[0]['entity_id'] == 'zone.home'
async def test_webhook_handle_get_config(hass, create_registrations, # noqa: F401, F811, E501
webhook_client): # noqa: F811
"""Test that we can get config properly."""
resp = await webhook_client.post(
'/api/webhook/{}'.format(create_registrations[1]['webhook_id']),
json={'type': 'get_config'}
)
assert resp.status == 200
json = await resp.json()
if 'components' in json:
json['components'] = set(json['components'])
if 'whitelist_external_dirs' in json:
json['whitelist_external_dirs'] = \
set(json['whitelist_external_dirs'])
hass_config = hass.config.as_dict()
expected_dict = {
'latitude': hass_config['latitude'],
'longitude': hass_config['longitude'],
'elevation': hass_config['elevation'],
'unit_system': hass_config['unit_system'],
'location_name': hass_config['location_name'],
'time_zone': hass_config['time_zone'],
'components': hass_config['components'],
'version': hass_config['version'],
'theme_color': '#03A9F4', # Default frontend theme color
}
assert expected_dict == json
async def test_webhook_returns_error_incorrect_json(webhook_client, # noqa: F401, F811, E501
create_registrations, # noqa: F401, F811, E501
caplog): # noqa: E501 F811