From 5ec555280384ec4fcdd4257f09826d2797f10450 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 8 Sep 2017 21:19:49 -0700 Subject: [PATCH 1/5] Cleanup input_text (#9326) --- homeassistant/components/input_text.py | 37 +++++++++----------------- tests/components/test_input_text.py | 10 +++---- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/input_text.py b/homeassistant/components/input_text.py index d17837b0ced8fb..583181fe453ec9 100755 --- a/homeassistant/components/input_text.py +++ b/homeassistant/components/input_text.py @@ -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, }) @@ -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, }) @@ -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 @@ -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 @@ -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): @@ -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 @@ -163,11 +158,6 @@ 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.""" @@ -175,7 +165,6 @@ def state_attributes(self): ATTR_MIN: self._minimum, ATTR_MAX: self._maximum, ATTR_PATTERN: self._pattern, - ATTR_DISABLED: self._disabled, } @asyncio.coroutine @@ -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)", diff --git a/tests/components/test_input_text.py b/tests/components/test_input_text.py index 81b1f58aa878bd..be22e1122eaba4 100755 --- a/tests/components/test_input_text.py +++ b/tests/components/test_input_text.py @@ -5,7 +5,7 @@ from homeassistant.core import CoreState, State from homeassistant.setup import setup_component, async_setup_component -from homeassistant.components.input_text import (DOMAIN, select_value) +from homeassistant.components.input_text import (DOMAIN, set_value) from tests.common import get_test_home_assistant, mock_restore_cache @@ -38,8 +38,8 @@ def test_config(self): self.assertFalse( setup_component(self.hass, DOMAIN, {DOMAIN: cfg})) - def test_select_value(self): - """Test select_value method.""" + def test_set_value(self): + """Test set_value method.""" self.assertTrue(setup_component(self.hass, DOMAIN, {DOMAIN: { 'test_1': { 'initial': 'test', @@ -52,13 +52,13 @@ def test_select_value(self): state = self.hass.states.get(entity_id) self.assertEqual('test', str(state.state)) - select_value(self.hass, entity_id, 'testing') + set_value(self.hass, entity_id, 'testing') self.hass.block_till_done() state = self.hass.states.get(entity_id) self.assertEqual('testing', str(state.state)) - select_value(self.hass, entity_id, 'testing too long') + set_value(self.hass, entity_id, 'testing too long') self.hass.block_till_done() state = self.hass.states.get(entity_id) From c44972c2c9866d899c33261bb207f41c41b605c0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 8 Sep 2017 23:08:38 -0700 Subject: [PATCH 2/5] Update frontend --- homeassistant/components/frontend/version.py | 4 ++-- .../frontend/www_static/frontend.html | 8 ++++---- .../frontend/www_static/frontend.html.gz | Bin 167216 -> 167890 bytes .../www_static/home-assistant-polymer | 2 +- .../www_static/panels/ha-panel-config.html | 2 +- .../www_static/panels/ha-panel-config.html.gz | Bin 32839 -> 32428 bytes .../frontend/www_static/service_worker.js | 2 +- .../frontend/www_static/service_worker.js.gz | Bin 5139 -> 5136 bytes 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 54d9ffda6c5193..21215e14d23787 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -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", diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 02063e4df3ee18..d6a15a0d610c4e 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -8,7 +8,7 @@ .flex-1{-ms-flex:1 1 0.000000001px;-webkit-flex:1;flex:1;-webkit-flex-basis:0.000000001px;flex-basis:0.000000001px;}.flex-2{-ms-flex:2;-webkit-flex:2;flex:2;}.flex-3{-ms-flex:3;-webkit-flex:3;flex:3;}.flex-4{-ms-flex:4;-webkit-flex:4;flex:4;}.flex-5{-ms-flex:5;-webkit-flex:5;flex:5;}.flex-6{-ms-flex:6;-webkit-flex:6;flex:6;}.flex-7{-ms-flex:7;-webkit-flex:7;flex:7;}.flex-8{-ms-flex:8;-webkit-flex:8;flex:8;}.flex-9{-ms-flex:9;-webkit-flex:9;flex:9;}.flex-10{-ms-flex:10;-webkit-flex:10;flex:10;}.flex-11{-ms-flex:11;-webkit-flex:11;flex:11;}.flex-12{-ms-flex:12;-webkit-flex:12;flex:12;} \ No newline at end of file + ha-script-editor{height:100%;} \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-config.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-config.html.gz index 335c067e4b5e797fe65c72dd74463b0875e0733b..08a7f5002cd0f9d15d61a5e673e4c0d16885f183 100644 GIT binary patch delta 5781 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 delta 837 zcmV-L1G@Z>D3d6#fdqd;dm@F+EpKGcxIM8$&vkvlCsPs3+|ZYYAA10V*%L~e!zE8- zoWn$SeWjzm7+Id2a)0bjg%`?+VsTg&?S|a7#c0eYBRR3gJ`W8gE~mno z_;xT0T-WkK;Rcg_1z~?3_?GR?EXyB_@T(vAGZ8o)f;M3 zN?b5e=+9=M?f5c4_HjeYf`uS7g4}jS-^2BX6wzu?A`5?|?aGloMYMW>6C(5{V;FQg zo%%uo_xF%p$@wbgS&5AYZaDQkSOBp;w!KNneS6Fi2XZ!Y?VyP*Lfw%MLzJrcrEh!A zJ^mjolR25x_B_k=k*6Fm#NmK53ljR$vuxX*j`!jj;18^)7#SnqPeE0ZSQ4qmmyRDy zyh#wYR1_;MXv-f4JQJT5P!0ctxPOq@Cx2dY!`+cHvxSYk2KRR1f|HR0Ry@~|@FTMX P2m}lWfbbiX12q5uxuS Date: Sat, 9 Sep 2017 03:06:06 -0400 Subject: [PATCH 3/5] Bump pyHik version to add IO support (#9341) --- homeassistant/components/binary_sensor/hikvision.py | 3 ++- requirements_all.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/binary_sensor/hikvision.py b/homeassistant/components/binary_sensor/hikvision.py index 7f2127fcad5f14..df488cc0ed6004 100644 --- a/homeassistant/components/binary_sensor/hikvision.py +++ b/homeassistant/components/binary_sensor/hikvision.py @@ -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' @@ -47,6 +47,7 @@ 'PIR Alarm': 'motion', 'Face Detection': 'motion', 'Scene Change Detection': 'motion', + 'I/O': None, } CUSTOMIZE_SCHEMA = vol.Schema({ diff --git a/requirements_all.txt b/requirements_all.txt index 80401ed3733f32..703bbd6b1848a8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 From ba310d3bd1ba9401bf49ac55717c70626074c151 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 9 Sep 2017 00:52:47 -0700 Subject: [PATCH 4/5] Version bump to 0.54.0.dev0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 88ab58201f865c..1a92f0d68c908c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -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) From 160c7fc68509d87e66637f00279f88caf3f65b33 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sat, 9 Sep 2017 19:20:48 +0200 Subject: [PATCH 5/5] Add HTTP Basic auth to RESTful Switch (#9162) * Add HTTP Basic auth to RESTful Switch * Remove redundant hass passing * Initialize to current state The state used to be None until the first periodic poll. This commit refactors async_update so it can be used during setup as well, allowing the state to start out with the correct value. * Refactor turn_on/turn_off device communication * Remove lint * Fix Travis errors --- homeassistant/components/switch/rest.py | 96 ++++++++++++++----------- tests/components/switch/test_rest.py | 4 +- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/switch/rest.py b/homeassistant/components/switch/rest.py index 31d4f0f3e06172..c0f755094256a0 100644 --- a/homeassistant/components/switch/rest.py +++ b/homeassistant/components/switch/rest.py @@ -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 @@ -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, }) @@ -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 @@ -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 @@ -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( @@ -181,3 +191,5 @@ def async_update(self): self._state = False else: self._state = None + + return req diff --git a/tests/components/switch/test_rest.py b/tests/components/switch/test_rest.py index 97911fccbfd6a0..1b8215660bd0b3 100644 --- a/tests/components/switch/test_rest.py +++ b/tests/components/switch/test_rest.py @@ -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."""