Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2e1b163
Version bump to 0.52
balloob Aug 25, 2017
1d615ea
Refactor mysensors callback and add validation (#9069)
MartinHjelmare Aug 25, 2017
50e5032
Merge pull request #9131 from home-assistant/release-0-52
balloob Aug 26, 2017
10e3c00
Version bump to 0.52.1
balloob Aug 28, 2017
8a89643
Close stream request once we end up with proxy (#9110)
foxel Aug 26, 2017
0ae1f85
Fix issue #9116 in pushbullet (#9128)
Danielhiversen Aug 26, 2017
422be25
bug fix pushbullet (#9139)
Danielhiversen Aug 26, 2017
e6892a4
Fix import for foscam (#9140)
sdague Aug 26, 2017
d986b8f
Bump aioautomatic to prevent leaking exceptions (#9148)
emlove Aug 26, 2017
308b822
Wrap state when iterating a domain in templates (#9157)
balloob Aug 27, 2017
3509ecf
Prevent iCloud exceptions in logfile (#9179)
mjj4791 Aug 28, 2017
0ccff6c
bump ecobee version to fix issue 9190 (#9191)
nkgilley Aug 28, 2017
bd71a33
Merge pull request #9196 from home-assistant/release-0-52-1
balloob Aug 28, 2017
5ec5552
Cleanup input_text (#9326)
balloob Sep 9, 2017
fc7ffba
Merge branch 'master' into release-0-53
balloob Sep 9, 2017
c44972c
Update frontend
balloob Sep 9, 2017
7036a78
Update frontend
balloob Sep 9, 2017
2defb85
Bump pyHik version to add IO support (#9341)
mezz64 Sep 9, 2017
d2d8769
Fix for potential issue with tesla initialization (#9307)
zabuldon Sep 7, 2017
c539b5c
Adds the AirVisual air quality sensor platform (#9320)
bachya Sep 8, 2017
74bfcde
Cleanup input_text (#9326)
balloob Sep 9, 2017
3065575
Bump pyHik version to add IO support (#9341)
mezz64 Sep 9, 2017
2d72cff
Merge pull request #9327 from home-assistant/release-0-53
balloob Sep 9, 2017
3f2eba0
Version bump to 0.53
balloob Sep 9, 2017
ba310d3
Version bump to 0.54.0.dev0
balloob Sep 9, 2017
313a9e3
Merge branch 'master' into dev
balloob Sep 9, 2017
160c7fc
Add HTTP Basic auth to RESTful Switch (#9162)
amelchio Sep 9, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion homeassistant/components/binary_sensor/hikvision.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
CONF_SSL, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START,
ATTR_LAST_TRIP_TIME, CONF_CUSTOMIZE)

REQUIREMENTS = ['pyhik==0.1.3']
REQUIREMENTS = ['pyhik==0.1.4']
_LOGGER = logging.getLogger(__name__)

CONF_IGNORED = 'ignored'
Expand Down Expand Up @@ -47,6 +47,7 @@
'PIR Alarm': 'motion',
'Face Detection': 'motion',
'Scene Change Detection': 'motion',
'I/O': None,
}

CUSTOMIZE_SCHEMA = vol.Schema({
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/frontend/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
FINGERPRINTS = {
"compatibility.js": "1686167ff210e001f063f5c606b2e74b",
"core.js": "2a7d01e45187c7d4635da05065b5e54e",
"frontend.html": "3ce24a1e0bc1c6620373f38a2d11b359",
"frontend.html": "c04709d3517dd3fd34b2f7d6bba6ec8e",
"mdi.html": "89074face5529f5fe6fbae49ecb3e88b",
"micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a",
"panels/ha-panel-config.html": "37803526cb203a8f1eaacd528fb2c7b3",
"panels/ha-panel-config.html": "0091008947ed61a6691c28093a6a6fcd",
"panels/ha-panel-dev-event.html": "d409e7ab537d9fe629126d122345279c",
"panels/ha-panel-dev-info.html": "b0e55eb657fd75f21aba2426ac0cedc0",
"panels/ha-panel-dev-mqtt.html": "94b222b013a98583842de3e72d5888c6",
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/frontend/www_static/frontend.html

Large diffs are not rendered by default.

Binary file modified homeassistant/components/frontend/www_static/frontend.html.gz
Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
/* eslint-disable indent, no-unused-vars, no-multiple-empty-lines, max-nested-callbacks, space-before-function-paren, quotes, comma-spacing */
'use strict';

var precacheConfig = [["/","f16ed1a09418a161f933bda67dc83fbe"],["/frontend/panels/dev-event-d409e7ab537d9fe629126d122345279c.html","936814991f2a5e23d61d29f0d40f81b8"],["/frontend/panels/dev-info-b0e55eb657fd75f21aba2426ac0cedc0.html","1fa953b0224470f70d4e87bbe4dff191"],["/frontend/panels/dev-mqtt-94b222b013a98583842de3e72d5888c6.html","dc3ddfac58397feda97317358f0aecbb"],["/frontend/panels/dev-service-422b2c181ee0713fa31d45a64e605baf.html","ae7d26b1c8c3309fd3c65944f89ea03f"],["/frontend/panels/dev-state-7948d3dba058f31517d880df8ed0e857.html","ff8156bb1a52490fcc07466556fce0e1"],["/frontend/panels/dev-template-928e7b81b9c113b70edc9f4a1d051827.html","312c8313800b44c83bcb8dc2df30c759"],["/frontend/panels/map-565db019147162080c21af962afc097f.html","a1a360042395682335e2f471dddad309"],["/static/compatibility-1686167ff210e001f063f5c606b2e74b.js","6ee7b5e2dd82b510c3bd92f7e215988e"],["/static/core-2a7d01e45187c7d4635da05065b5e54e.js","90a0a8a6a6dd0ca41b16f40e7d23924d"],["/static/frontend-3ce24a1e0bc1c6620373f38a2d11b359.html","4dcc9dcddbe093ebd5bc3ecc6dbaebb4"],["/static/mdi-e91f61a039ed0a9936e7ee5360da3870.html","5e587bc82719b740a4f0798722a83aee"],["static/fonts/roboto/Roboto-Bold.ttf","d329cc8b34667f114a95422aaad1b063"],["static/fonts/roboto/Roboto-Light.ttf","7b5fb88f12bec8143f00e21bc3222124"],["static/fonts/roboto/Roboto-Medium.ttf","fe13e4170719c2fc586501e777bde143"],["static/fonts/roboto/Roboto-Regular.ttf","ac3f799d5bbaf5196fab15ab8de8431c"],["static/icons/favicon-192x192.png","419903b8422586a7e28021bbe9011175"],["static/icons/favicon.ico","04235bda7843ec2fceb1cbe2bc696cf4"],["static/images/card_media_player_bg.png","a34281d1c1835d338a642e90930e61aa"]];
var precacheConfig = [["/","eceffe0debe81636e1eb8604e6eefbd6"],["/frontend/panels/dev-event-d409e7ab537d9fe629126d122345279c.html","936814991f2a5e23d61d29f0d40f81b8"],["/frontend/panels/dev-info-b0e55eb657fd75f21aba2426ac0cedc0.html","1fa953b0224470f70d4e87bbe4dff191"],["/frontend/panels/dev-mqtt-94b222b013a98583842de3e72d5888c6.html","dc3ddfac58397feda97317358f0aecbb"],["/frontend/panels/dev-service-422b2c181ee0713fa31d45a64e605baf.html","ae7d26b1c8c3309fd3c65944f89ea03f"],["/frontend/panels/dev-state-7948d3dba058f31517d880df8ed0e857.html","ff8156bb1a52490fcc07466556fce0e1"],["/frontend/panels/dev-template-928e7b81b9c113b70edc9f4a1d051827.html","312c8313800b44c83bcb8dc2df30c759"],["/frontend/panels/map-565db019147162080c21af962afc097f.html","a1a360042395682335e2f471dddad309"],["/static/compatibility-1686167ff210e001f063f5c606b2e74b.js","6ee7b5e2dd82b510c3bd92f7e215988e"],["/static/core-2a7d01e45187c7d4635da05065b5e54e.js","90a0a8a6a6dd0ca41b16f40e7d23924d"],["/static/frontend-c04709d3517dd3fd34b2f7d6bba6ec8e.html","e072f7bbe595bcb104d117a45592459d"],["/static/mdi-89074face5529f5fe6fbae49ecb3e88b.html","97754e463f9e56a95c813d4d8e792347"],["static/fonts/roboto/Roboto-Bold.ttf","d329cc8b34667f114a95422aaad1b063"],["static/fonts/roboto/Roboto-Light.ttf","7b5fb88f12bec8143f00e21bc3222124"],["static/fonts/roboto/Roboto-Medium.ttf","fe13e4170719c2fc586501e777bde143"],["static/fonts/roboto/Roboto-Regular.ttf","ac3f799d5bbaf5196fab15ab8de8431c"],["static/icons/favicon-192x192.png","419903b8422586a7e28021bbe9011175"],["static/icons/favicon.ico","04235bda7843ec2fceb1cbe2bc696cf4"],["static/images/card_media_player_bg.png","a34281d1c1835d338a642e90930e61aa"]];
var cacheName = 'sw-precache-v3--' + (self.registration ? self.registration.scope : '');


Expand Down
Binary file not shown.
37 changes: 13 additions & 24 deletions homeassistant/components/input_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,15 @@
CONF_INITIAL = 'initial'
CONF_MIN = 'min'
CONF_MAX = 'max'
CONF_DISABLED = 'disabled'

ATTR_VALUE = 'value'
ATTR_MIN = 'min'
ATTR_MAX = 'max'
ATTR_PATTERN = 'pattern'
ATTR_DISABLED = 'disabled'

SERVICE_SELECT_VALUE = 'select_value'
SERVICE_SET_VALUE = 'set_value'

SERVICE_SELECT_VALUE_SCHEMA = vol.Schema({
SERVICE_SET_VALUE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_VALUE): cv.string,
})
Expand Down Expand Up @@ -65,16 +63,15 @@ def _cv_input_text(cfg):
vol.Optional(CONF_ICON): cv.icon,
vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string,
vol.Optional(ATTR_PATTERN): cv.string,
vol.Optional(CONF_DISABLED, default=False): cv.boolean,
}, _cv_input_text)
})
}, required=True, extra=vol.ALLOW_EXTRA)


@bind_hass
def select_value(hass, entity_id, value):
def set_value(hass, entity_id, value):
"""Set input_text to value."""
hass.services.call(DOMAIN, SERVICE_SELECT_VALUE, {
hass.services.call(DOMAIN, SERVICE_SET_VALUE, {
ATTR_ENTITY_ID: entity_id,
ATTR_VALUE: value,
})
Expand All @@ -95,28 +92,27 @@ def async_setup(hass, config):
icon = cfg.get(CONF_ICON)
unit = cfg.get(ATTR_UNIT_OF_MEASUREMENT)
pattern = cfg.get(ATTR_PATTERN)
disabled = cfg.get(CONF_DISABLED)

entities.append(InputText(
object_id, name, initial, minimum, maximum, icon, unit,
pattern, disabled))
pattern))

if not entities:
return False

@asyncio.coroutine
def async_select_value_service(call):
def async_set_value_service(call):
"""Handle a calls to the input box services."""
target_inputs = component.async_extract_from_service(call)

tasks = [input_text.async_select_value(call.data[ATTR_VALUE])
tasks = [input_text.async_set_value(call.data[ATTR_VALUE])
for input_text in target_inputs]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)

hass.services.async_register(
DOMAIN, SERVICE_SELECT_VALUE, async_select_value_service,
schema=SERVICE_SELECT_VALUE_SCHEMA)
DOMAIN, SERVICE_SET_VALUE, async_set_value_service,
schema=SERVICE_SET_VALUE_SCHEMA)

yield from component.async_add_entities(entities)
return True
Expand All @@ -126,8 +122,8 @@ class InputText(Entity):
"""Represent a text box."""

def __init__(self, object_id, name, initial, minimum, maximum, icon,
unit, pattern, disabled):
"""Initialize a select input."""
unit, pattern):
"""Initialize a text input."""
self.entity_id = ENTITY_ID_FORMAT.format(object_id)
self._name = name
self._current_value = initial
Expand All @@ -136,7 +132,6 @@ def __init__(self, object_id, name, initial, minimum, maximum, icon,
self._icon = icon
self._unit = unit
self._pattern = pattern
self._disabled = disabled

@property
def should_poll(self):
Expand All @@ -145,7 +140,7 @@ def should_poll(self):

@property
def name(self):
"""Return the name of the select input box."""
"""Return the name of the text input entity."""
return self._name

@property
Expand All @@ -163,19 +158,13 @@ def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return self._unit

@property
def disabled(self):
"""Return the disabled flag."""
return self._disabled

@property
def state_attributes(self):
"""Return the state attributes."""
return {
ATTR_MIN: self._minimum,
ATTR_MAX: self._maximum,
ATTR_PATTERN: self._pattern,
ATTR_DISABLED: self._disabled,
}

@asyncio.coroutine
Expand All @@ -192,7 +181,7 @@ def async_added_to_hass(self):
self._current_value = value

@asyncio.coroutine
def async_select_value(self, value):
def async_set_value(self, value):
"""Select new value."""
if len(value) < self._minimum or len(value) > self._maximum:
_LOGGER.warning("Invalid value: %s (length range %s - %s)",
Expand Down
96 changes: 54 additions & 42 deletions homeassistant/components/switch/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import (
CONF_NAME, CONF_RESOURCE, CONF_TIMEOUT, CONF_METHOD)
CONF_NAME, CONF_RESOURCE, CONF_TIMEOUT, CONF_METHOD, CONF_USERNAME,
CONF_PASSWORD)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.template import Template
Expand Down Expand Up @@ -41,6 +42,8 @@
vol.All(vol.Lower, vol.In(SUPPORT_REST_METHODS)),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Inclusive(CONF_USERNAME, 'authentication'): cv.string,
vol.Inclusive(CONF_PASSWORD, 'authentication'): cv.string,
})


Expand All @@ -53,8 +56,13 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
is_on_template = config.get(CONF_IS_ON_TEMPLATE)
method = config.get(CONF_METHOD)
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
resource = config.get(CONF_RESOURCE)
websession = async_get_clientsession(hass)

auth = None
if username:
auth = aiohttp.BasicAuth(username, password=password)

if is_on_template is not None:
is_on_template.hass = hass
Expand All @@ -65,37 +73,32 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
timeout = config.get(CONF_TIMEOUT)

try:
with async_timeout.timeout(timeout, loop=hass.loop):
req = yield from websession.get(resource)
switch = RestSwitch(name, resource, method, auth, body_on, body_off,
is_on_template, timeout)

req = yield from switch.get_device_state(hass)
if req.status >= 400:
_LOGGER.error("Got non-ok response from resource: %s", req.status)
return False

else:
async_add_devices([switch])
except (TypeError, ValueError):
_LOGGER.error("Missing resource or schema in configuration. "
"Add http:// or https:// to your URL")
return False
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("No route to resource/endpoint: %s", resource)
return False

async_add_devices(
[RestSwitch(hass, name, resource, method, body_on, body_off,
is_on_template, timeout)])


class RestSwitch(SwitchDevice):
"""Representation of a switch that can be toggled using REST."""

def __init__(self, hass, name, resource, method, body_on, body_off,
def __init__(self, name, resource, method, auth, body_on, body_off,
is_on_template, timeout):
"""Initialize the REST switch."""
self._state = None
self.hass = hass
self._name = name
self._resource = resource
self._method = method
self._auth = auth
self._body_on = body_on
self._body_off = body_off
self._is_on_template = is_on_template
Expand All @@ -115,54 +118,61 @@ def is_on(self):
def async_turn_on(self, **kwargs):
"""Turn the device on."""
body_on_t = self._body_on.async_render()
websession = async_get_clientsession(self.hass)

try:
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
request = yield from getattr(websession, self._method)(
self._resource, data=bytes(body_on_t, 'utf-8'))
req = yield from self.set_device_state(body_on_t)

if req.status == 200:
self._state = True
else:
_LOGGER.error(
"Can't turn on %s. Is resource/endpoint offline?",
self._resource)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error while turn on %s", self._resource)
return

if request.status == 200:
self._state = True
else:
_LOGGER.error("Can't turn on %s. Is resource/endpoint offline?",
self._resource)

@asyncio.coroutine
def async_turn_off(self, **kwargs):
"""Turn the device off."""
body_off_t = self._body_off.async_render()
websession = async_get_clientsession(self.hass)

try:
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
request = yield from getattr(websession, self._method)(
self._resource, data=bytes(body_off_t, 'utf-8'))
req = yield from self.set_device_state(body_off_t)
if req.status == 200:
self._state = False
else:
_LOGGER.error(
"Can't turn off %s. Is resource/endpoint offline?",
self._resource)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error while turn off %s", self._resource)
return

if request.status == 200:
self._state = False
else:
_LOGGER.error("Can't turn off %s. Is resource/endpoint offline?",
self._resource)

@asyncio.coroutine
def async_update(self):
"""Get the latest data from REST API and update the state."""
def set_device_state(self, body):
"""Send a state update to the device."""
websession = async_get_clientsession(self.hass)

with async_timeout.timeout(self._timeout, loop=self.hass.loop):
req = yield from getattr(websession, self._method)(
self._resource, auth=self._auth, data=bytes(body, 'utf-8'))
return req

@asyncio.coroutine
def async_update(self):
"""Get the current state, catching errors."""
try:
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
request = yield from websession.get(self._resource)
text = yield from request.text()
yield from self.get_device_state(self.hass)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error while fetch data.")
return

@asyncio.coroutine
def get_device_state(self, hass):
"""Get the latest data from REST API and update the state."""
websession = async_get_clientsession(hass)

with async_timeout.timeout(self._timeout, loop=hass.loop):
req = yield from websession.get(self._resource, auth=self._auth)
text = yield from req.text()

if self._is_on_template is not None:
text = self._is_on_template.async_render_with_possible_json_value(
Expand All @@ -181,3 +191,5 @@ def async_update(self):
self._state = False
else:
self._state = None

return req
2 changes: 1 addition & 1 deletion homeassistant/const.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# coding: utf-8
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 53
MINOR_VERSION = 54
PATCH_VERSION = '0.dev0'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ pyfttt==0.3
pyharmony==1.0.16

# homeassistant.components.binary_sensor.hikvision
pyhik==0.1.3
pyhik==0.1.4

# homeassistant.components.homematic
pyhomematic==0.1.30
Expand Down
4 changes: 3 additions & 1 deletion tests/components/switch/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,13 @@ def setup_method(self):
self.name = 'foo'
self.method = 'post'
self.resource = 'http://localhost/'
self.auth = None
self.body_on = Template('on', self.hass)
self.body_off = Template('off', self.hass)
self.switch = rest.RestSwitch(
self.hass, self.name, self.resource, self.method, self.body_on,
self.name, self.resource, self.method, self.auth, self.body_on,
self.body_off, None, 10)
self.switch.hass = self.hass

def teardown_method(self):
"""Stop everything that was started."""
Expand Down
Loading