From 64cc4a47ec31580178f455ed28d14aa3e459e8e0 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sat, 3 Sep 2016 15:59:20 -0700 Subject: [PATCH] 0.27.2 (#3151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Host should be optional for apcupsd component (#3072) * Zwave climate Bugfix: if some setpoints have different units, we should fetch the o… (#3078) * Bugfix: if some setpoints have different units, we should fetch the one that are active. * Move order of population for first time detection * Default to config if None unit_of_measurement * unit fix (#3083) * humidity slider (#3088) * If device was off target temp was null. Default to Heating setpoint (#3091) * Fix for BLE device tracker (#3019) * Bug fix tracked devices * Added scan_duration configuration parameter * fix homematic climate implementation (#3114) * Allow 'None' MAC to be loaded from known_devices (#3102) * Climate and cover bugfix (#3097) * Avoid None comparison for zwave cover. * Just rely on unit from config for unit_of_measurement * Explicit return None * Mqtt (#11) * Explicit return None * Missing service and wrong service name defined * Mqtt state was inverted, and never triggering * Fixed Homematic cover (#3116) * Add missing docstrings (fix PEP257 issues) (#3098) * Add missing docstrings (fix PEP257 issues) * Finish sentence * Merge pull request #3130 from turbokongen/zwave_fixes Bugfix. climate and covermqt * Back out insteon hub and fan changes (#3062) * Bump version * Special frontend build for 0.27.2 --- homeassistant/components/apcupsd.py | 2 +- homeassistant/components/climate/ecobee.py | 2 +- homeassistant/components/climate/homematic.py | 2 +- homeassistant/components/climate/zwave.py | 43 +++++----- homeassistant/components/cover/__init__.py | 1 + homeassistant/components/cover/homematic.py | 10 +-- homeassistant/components/cover/mqtt.py | 12 +-- homeassistant/components/cover/zwave.py | 2 + .../components/device_tracker/__init__.py | 3 +- .../device_tracker/bluetooth_le_tracker.py | 21 +++-- homeassistant/components/fan/insteon_hub.py | 66 --------------- homeassistant/components/frontend/version.py | 2 +- .../components/frontend/www_static/core.js.gz | Bin 32161 -> 32161 bytes .../frontend/www_static/frontend.html | 4 +- .../frontend/www_static/frontend.html.gz | Bin 124422 -> 124432 bytes .../www_static/home-assistant-polymer | 2 +- .../frontend/www_static/mdi.html.gz | Bin 174430 -> 174430 bytes .../panels/ha-panel-dev-event.html.gz | Bin 2639 -> 2639 bytes .../panels/ha-panel-dev-info.html.gz | Bin 1308 -> 1308 bytes .../panels/ha-panel-dev-service.html.gz | Bin 2824 -> 2824 bytes .../panels/ha-panel-dev-state.html.gz | Bin 2772 -> 2772 bytes .../panels/ha-panel-dev-template.html.gz | Bin 7290 -> 7290 bytes .../panels/ha-panel-history.html.gz | Bin 6842 -> 6842 bytes .../www_static/panels/ha-panel-iframe.html.gz | Bin 403 -> 403 bytes .../panels/ha-panel-logbook.html.gz | Bin 7344 -> 7344 bytes .../www_static/panels/ha-panel-map.html.gz | Bin 43920 -> 43920 bytes .../frontend/www_static/service_worker.js | 2 +- .../frontend/www_static/service_worker.js.gz | Bin 2285 -> 2282 bytes .../www_static/webcomponents-lite.min.js.gz | Bin 12355 -> 12355 bytes homeassistant/components/insteon_hub.py | 80 +++--------------- homeassistant/components/light/insteon_hub.py | 74 ++++++++-------- homeassistant/const.py | 4 +- requirements_all.txt | 2 +- tests/components/fan/test_insteon_hub.py | 73 ---------------- tests/components/test_emulated_hue.py | 31 ++++++- 35 files changed, 141 insertions(+), 297 deletions(-) delete mode 100644 homeassistant/components/fan/insteon_hub.py delete mode 100644 tests/components/fan/test_insteon_hub.py diff --git a/homeassistant/components/apcupsd.py b/homeassistant/components/apcupsd.py index 867208305b0..72db3e06dee 100644 --- a/homeassistant/components/apcupsd.py +++ b/homeassistant/components/apcupsd.py @@ -32,7 +32,7 @@ VALUE_ONLINE = 'ONLINE' CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_HOST, default=DEFAULT_HOST): cv.string, + vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, }), }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/components/climate/ecobee.py b/homeassistant/components/climate/ecobee.py index da4b29dfe92..2417a8562ce 100644 --- a/homeassistant/components/climate/ecobee.py +++ b/homeassistant/components/climate/ecobee.py @@ -181,7 +181,7 @@ class Thermostat(ClimateDevice): else: operation = status return { - "humidity": self.thermostat['runtime']['actualHumidity'], + "actual_humidity": self.thermostat['runtime']['actualHumidity'], "fan": self.fan, "mode": self.mode, "operation": operation, diff --git a/homeassistant/components/climate/homematic.py b/homeassistant/components/climate/homematic.py index be81bb9326e..e51ad5e67a5 100644 --- a/homeassistant/components/climate/homematic.py +++ b/homeassistant/components/climate/homematic.py @@ -101,7 +101,7 @@ class HMThermostat(homematic.HMDevice, ClimateDevice): for mode, state in HM_STATE_MAP.items(): if state == operation_mode: code = getattr(self._hmdevice, mode, 0) - self._hmdevice.STATE = code + self._hmdevice.MODE = code @property def min_temp(self): diff --git a/homeassistant/components/climate/zwave.py b/homeassistant/components/climate/zwave.py index c8425ab4e8c..530e3ea028f 100755 --- a/homeassistant/components/climate/zwave.py +++ b/homeassistant/components/climate/zwave.py @@ -12,7 +12,7 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.zwave import ( ATTR_NODE_ID, ATTR_VALUE_ID, ZWaveDeviceEntity) from homeassistant.components import zwave -from homeassistant.const import (TEMP_FAHRENHEIT, TEMP_CELSIUS) +from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT _LOGGER = logging.getLogger(__name__) @@ -59,11 +59,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.debug("No discovery_info=%s or no NETWORK=%s", discovery_info, zwave.NETWORK) return - + temp_unit = hass.config.units.temperature_unit node = zwave.NETWORK.nodes[discovery_info[ATTR_NODE_ID]] value = node.values[discovery_info[ATTR_VALUE_ID]] value.set_change_verified(False) - add_devices([ZWaveClimate(value)]) + add_devices([ZWaveClimate(value, temp_unit)]) _LOGGER.debug("discovery_info=%s and zwave.NETWORK=%s", discovery_info, zwave.NETWORK) @@ -73,7 +73,7 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): """Represents a ZWave Climate device.""" # pylint: disable=too-many-public-methods, too-many-instance-attributes - def __init__(self, value): + def __init__(self, value, temp_unit): """Initialize the zwave climate device.""" from openzwave.network import ZWaveNetwork from pydispatch import dispatcher @@ -87,7 +87,8 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): self._fan_list = None self._current_swing_mode = None self._swing_list = None - self._unit = None + self._unit = temp_unit + _LOGGER.debug("temp_unit is %s", self._unit) self._zxt_120 = None self.update_properties() # register listener @@ -115,18 +116,6 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): def update_properties(self): """Callback on data change for the registered node/value pair.""" - # Set point - for value in self._node.get_values( - class_id=COMMAND_CLASS_THERMOSTAT_SETPOINT).values(): - self._unit = value.units - if self.current_operation is not None: - if SET_TEMP_TO_INDEX.get(self._current_operation) \ - != value.index: - continue - if self._zxt_120: - continue - self._target_temperature = int(value.data) - # Operation Mode for value in self._node.get_values( class_id=COMMAND_CLASS_THERMOSTAT_MODE).values(): @@ -140,6 +129,7 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): class_id=COMMAND_CLASS_SENSOR_MULTILEVEL).values(): if value.label == 'Temperature': self._current_temperature = int(value.data) + self._unit = value.units # Fan Mode for value in self._node.get_values( class_id=COMMAND_CLASS_THERMOSTAT_FAN_MODE).values(): @@ -158,6 +148,17 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): _LOGGER.debug("self._swing_list=%s", self._swing_list) _LOGGER.debug("self._current_swing_mode=%s", self._current_swing_mode) + # Set point + for value in self._node.get_values( + class_id=COMMAND_CLASS_THERMOSTAT_SETPOINT).values(): + if self.current_operation is not None and \ + self.current_operation != 'Off': + if SET_TEMP_TO_INDEX.get(self._current_operation) \ + != value.index: + continue + if self._zxt_120: + continue + self._target_temperature = int(value.data) @property def should_poll(self): @@ -187,14 +188,12 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): @property def unit_of_measurement(self): """Return the unit of measurement.""" - unit = self._unit - if unit == 'C': + if self._unit == 'C': return TEMP_CELSIUS - elif unit == 'F': + elif self._unit == 'F': return TEMP_FAHRENHEIT else: - _LOGGER.exception("unit_of_measurement=%s is not valid", - unit) + return self._unit @property def current_temperature(self): diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index 3f08c7ff229..876a8b46cfa 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -61,6 +61,7 @@ SERVICE_TO_METHOD = { SERVICE_STOP_COVER: {'method': 'stop_cover'}, SERVICE_OPEN_COVER_TILT: {'method': 'open_cover_tilt'}, SERVICE_CLOSE_COVER_TILT: {'method': 'close_cover_tilt'}, + SERVICE_STOP_COVER_TILT: {'method': 'stop_cover_tilt'}, SERVICE_SET_COVER_TILT_POSITION: { 'method': 'set_cover_tilt_position', 'schema': COVER_SET_COVER_TILT_POSITION_SCHEMA}, diff --git a/homeassistant/components/cover/homematic.py b/homeassistant/components/cover/homematic.py index cab6b51e645..fd68ac3d265 100644 --- a/homeassistant/components/cover/homematic.py +++ b/homeassistant/components/cover/homematic.py @@ -11,7 +11,7 @@ properly configured. import logging from homeassistant.const import STATE_UNKNOWN from homeassistant.components.cover import CoverDevice,\ - ATTR_CURRENT_POSITION + ATTR_POSITION import homeassistant.components.homematic as homematic _LOGGER = logging.getLogger(__name__) @@ -41,16 +41,16 @@ class HMCover(homematic.HMDevice, CoverDevice): None is unknown, 0 is closed, 100 is fully open. """ if self.available: - return int((1 - self._hm_get_state()) * 100) + return int(self._hm_get_state() * 100) return None def set_cover_position(self, **kwargs): """Move the cover to a specific position.""" if self.available: - if ATTR_CURRENT_POSITION in kwargs: - position = float(kwargs[ATTR_CURRENT_POSITION]) + if ATTR_POSITION in kwargs: + position = float(kwargs[ATTR_POSITION]) position = min(100, max(0, position)) - level = (100 - position) / 100.0 + level = position / 100.0 self._hmdevice.set_level(level, self._channel) @property diff --git a/homeassistant/components/cover/mqtt.py b/homeassistant/components/cover/mqtt.py index dd6b10e244d..b47bcf124e1 100644 --- a/homeassistant/components/cover/mqtt.py +++ b/homeassistant/components/cover/mqtt.py @@ -97,12 +97,16 @@ class MqttCover(CoverDevice): hass, value_template, payload) if payload == self._state_open: self._state = False + _LOGGER.warning("state=%s", int(self._state)) self.update_ha_state() elif payload == self._state_closed: self._state = True self.update_ha_state() elif payload.isnumeric() and 0 <= int(payload) <= 100: - self._state = int(payload) + if int(payload) > 0: + self._state = False + else: + self._state = True self._position = int(payload) self.update_ha_state() else: @@ -129,11 +133,7 @@ class MqttCover(CoverDevice): @property def is_closed(self): """Return if the cover is closed.""" - if self.current_cover_position is not None: - if self.current_cover_position > 0: - return False - else: - return True + return self._state @property def current_cover_position(self): diff --git a/homeassistant/components/cover/zwave.py b/homeassistant/components/cover/zwave.py index 83d55001fe2..d7ebfb834e8 100644 --- a/homeassistant/components/cover/zwave.py +++ b/homeassistant/components/cover/zwave.py @@ -96,6 +96,8 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice): @property def is_closed(self): """Return if the cover is closed.""" + if self.current_cover_position is None: + return None if self.current_cover_position > 0: return False else: diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index b260eccd7d1..a4f65ab4ea4 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -388,7 +388,8 @@ def load_config(path: str, hass: HomeAssistantType, consider_home: timedelta): try: return [ Device(hass, consider_home, device.get('track', False), - str(dev_id).lower(), str(device.get('mac')).upper(), + str(dev_id).lower(), None if device.get('mac') is None + else str(device.get('mac')).upper(), device.get('name'), device.get('picture'), device.get('gravatar'), device.get(CONF_AWAY_HIDE, DEFAULT_AWAY_HIDE)) diff --git a/homeassistant/components/device_tracker/bluetooth_le_tracker.py b/homeassistant/components/device_tracker/bluetooth_le_tracker.py index 6576f46bad7..ce8a535ff57 100644 --- a/homeassistant/components/device_tracker/bluetooth_le_tracker.py +++ b/homeassistant/components/device_tracker/bluetooth_le_tracker.py @@ -2,16 +2,19 @@ import logging from datetime import timedelta +import voluptuous as vol from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.components.device_tracker import ( YAML_DEVICES, CONF_TRACK_NEW, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL, + PLATFORM_SCHEMA, load_config, ) import homeassistant.util as util import homeassistant.util.dt as dt_util +import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) @@ -19,6 +22,11 @@ REQUIREMENTS = ['gattlib==0.20150805'] BLE_PREFIX = 'BLE_' MIN_SEEN_NEW = 5 +CONF_SCAN_DURATION = "scan_duration" + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_SCAN_DURATION, default=10): cv.positive_int +}) def setup_scanner(hass, config, see): @@ -51,12 +59,13 @@ def setup_scanner(hass, config, see): """Discover Bluetooth LE devices.""" _LOGGER.debug("Discovering Bluetooth LE devices") service = DiscoveryService() - devices = service.discover(10) + devices = service.discover(duration) _LOGGER.debug("Bluetooth LE devices discovered = %s", devices) return devices yaml_path = hass.config.path(YAML_DEVICES) + duration = config.get(CONF_SCAN_DURATION) devs_to_track = [] devs_donot_track = [] @@ -65,11 +74,13 @@ def setup_scanner(hass, config, see): # to 0 for device in load_config(yaml_path, hass, 0): # check if device is a valid bluetooth device - if device.mac and device.mac[:3].upper() == BLE_PREFIX: + if device.mac and device.mac[:4].upper() == BLE_PREFIX: if device.track: - devs_to_track.append(device.mac[3:]) + _LOGGER.debug("Adding %s to BLE tracker", device.mac) + devs_to_track.append(device.mac[4:]) else: - devs_donot_track.append(device.mac[3:]) + _LOGGER.debug("Adding %s to BLE do not track", device.mac) + devs_donot_track.append(device.mac[4:]) # if track new devices is true discover new devices # on every scan. @@ -96,7 +107,7 @@ def setup_scanner(hass, config, see): if track_new: for address in devs: if address not in devs_to_track and \ - address not in devs_donot_track: + address not in devs_donot_track: _LOGGER.info("Discovered Bluetooth LE device %s", address) see_device(address, devs[address], new_device=True) diff --git a/homeassistant/components/fan/insteon_hub.py b/homeassistant/components/fan/insteon_hub.py deleted file mode 100644 index 4d65ee1f02b..00000000000 --- a/homeassistant/components/fan/insteon_hub.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Support for Insteon FanLinc. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/fan.insteon/ -""" - -import logging - -from homeassistant.components.fan import (FanEntity, SUPPORT_SET_SPEED, - SPEED_OFF, SPEED_LOW, SPEED_MED, - SPEED_HIGH) -from homeassistant.components.insteon_hub import (InsteonDevice, INSTEON, - filter_devices) -from homeassistant.const import STATE_UNKNOWN - -_LOGGER = logging.getLogger(__name__) - -DEVICE_CATEGORIES = [ - { - 'DevCat': 1, - 'SubCat': [46] - } -] - -DEPENDENCIES = ['insteon_hub'] - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the Insteon Hub fan platform.""" - devs = [] - for device in filter_devices(INSTEON.devices, DEVICE_CATEGORIES): - devs.append(InsteonFanDevice(device)) - add_devices(devs) - - -class InsteonFanDevice(InsteonDevice, FanEntity): - """Represet an insteon fan device.""" - - def __init__(self, node: object) -> None: - """Initialize the device.""" - super(InsteonFanDevice, self).__init__(node) - self.speed = STATE_UNKNOWN # Insteon hub can't get state via REST - - def turn_on(self, speed: str=None): - """Turn the fan on.""" - self.set_speed(speed if speed else SPEED_MED) - - def turn_off(self): - """Turn the fan off.""" - self.set_speed(SPEED_OFF) - - def set_speed(self, speed: str) -> None: - """Set the fan speed.""" - if self._send_command('fan', payload={'speed', speed}): - self.speed = speed - - @property - def supported_features(self) -> int: - """Get the supported features for device.""" - return SUPPORT_SET_SPEED - - @property - def speed_list(self) -> list: - """Get the available speeds for the fan.""" - return [SPEED_OFF, SPEED_LOW, SPEED_MED, SPEED_HIGH] diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 5fce36f45b1..b1bd204e1ce 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -2,7 +2,7 @@ FINGERPRINTS = { "core.js": "1fd10c1fcdf56a61f60cf861d5a0368c", - "frontend.html": "88c97d278de3320278da6c32fe9e7d61", + "frontend.html": "610cc799225ede933a9894b64bb35717", "mdi.html": "710b84acc99b32514f52291aba9cd8e8", "panels/ha-panel-dev-event.html": "3cc881ae8026c0fba5aa67d334a3ab2b", "panels/ha-panel-dev-info.html": "34e2df1af32e60fffcafe7e008a92169", diff --git a/homeassistant/components/frontend/www_static/core.js.gz b/homeassistant/components/frontend/www_static/core.js.gz index 847c937ec81bc9f7e4f5cb609fabfb184c8b4125..5accff5179510910698628a9e96fb9b1b509cb29 100644 GIT binary patch delta 18 acmZ4Zn{nZ9Mt1pb4vyG>(;L}m)dB!V;0Hqh delta 18 acmZ4Zn{nZ9Mt1pb4vy6!hc>d$ss#W^?FWni diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 128248ce62e..367b8d15a5a 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,5 +1,5 @@ \ No newline at end of file +var r=t.propertyDataFromStyles(n._styles,this),i=!this.__notStyleScopeCacheable;i&&(r.key.customStyle=this.customStyle,e=n._styleCache.retrieve(this.is,r.key,this._styles));var a=Boolean(e);a?this._styleProperties=e._styleProperties:this._computeStyleProperties(r.properties),this._computeOwnStyleProperties(),a||(e=o.retrieve(this.is,this._ownStyleProperties,this._styles));var l=Boolean(e)&&!a,h=this._applyStyleProperties(e);a||(h=h&&s?h.cloneNode(!0):h,e={style:h,_scopeSelector:this._scopeSelector,_styleProperties:this._styleProperties},i&&(r.key.customStyle={},this.mixin(r.key.customStyle,this.customStyle),n._styleCache.store(this.is,e,r.key,this._styles)),l||o.store(this.is,Object.create(e),this._ownStyleProperties,this._styles))},_computeStyleProperties:function(e){var n=this._findStyleHost();n._styleProperties||n._computeStyleProperties();var r=Object.create(n._styleProperties),s=t.hostAndRootPropertiesForScope(this);this.mixin(r,s.hostProps),e=e||t.propertyDataFromStyles(n._styles,this).properties,this.mixin(r,e),this.mixin(r,s.rootProps),t.mixinCustomStyle(r,this.customStyle),t.reify(r),this._styleProperties=r},_computeOwnStyleProperties:function(){for(var e,t={},n=0;n0&&l.push(t);return[{removed:a,added:l}]}},Polymer.Collection.get=function(e){return Polymer._collections.get(e)||new Polymer.Collection(e)},Polymer.Collection.applySplices=function(e,t){var n=Polymer._collections.get(e);return n?n._applySplices(t):null},Polymer({is:"dom-repeat",extends:"template",_template:null,properties:{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},sort:{type:Function,observer:"_sortChanged"},filter:{type:Function,observer:"_filterChanged"},observe:{type:String,observer:"_observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:!0,readOnly:!0},initialCount:{type:Number,observer:"_initializeChunking"},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"_computeFrameTime(targetFramerate)"}},behaviors:[Polymer.Templatizer],observers:["_itemsChanged(items.*)"],created:function(){this._instances=[],this._pool=[],this._limit=1/0;var e=this;this._boundRenderChunk=function(){e._renderChunk()}},detached:function(){this.__isDetached=!0;for(var e=0;e=0;t--){var n=this._instances[t];n.isPlaceholder&&t=this._limit&&(n=this._downgradeInstance(t,n.__key__)),e[n.__key__]=t,n.isPlaceholder||n.__setProperty(this.indexAs,t,!0)}this._pool.length=0,this._setRenderedItemCount(this._instances.length),this.fire("dom-change"),this._tryRenderChunk()},_applyFullRefresh:function(){var e,t=this.collection;if(this._sortFn)e=t?t.getKeys():[];else{e=[];var n=this.items;if(n)for(var r=0;r=r;a--)this._detachAndRemoveInstance(a)},_numericSort:function(e,t){return e-t},_applySplicesUserSort:function(e){for(var t,n,r=this.collection,s={},i=0;i=0;i--){var h=a[i];void 0!==h&&this._detachAndRemoveInstance(h)}var c=this;if(l.length){this._filterFn&&(l=l.filter(function(e){return c._filterFn(r.getItem(e))})),l.sort(function(e,t){return c._sortFn(r.getItem(e),r.getItem(t))});var u=0;for(i=0;i>1,a=this._instances[o].__key__,l=this._sortFn(n.getItem(a),r);if(l<0)e=o+1;else{if(!(l>0)){i=o;break}s=o-1}}return i<0&&(i=s+1),this._insertPlaceholder(i,t),i},_applySplicesArrayOrder:function(e){for(var t,n=0;n=0?(e=this.as+"."+e.substring(n+1),i._notifyPath(e,t,!0)):i.__setProperty(this.as,t,!0))}},itemForElement:function(e){var t=this.modelForElement(e);return t&&t[this.as]},keyForElement:function(e){var t=this.modelForElement(e);return t&&t.__key__},indexForElement:function(e){var t=this.modelForElement(e);return t&&t[this.indexAs]}}),Polymer({is:"array-selector",_template:null,properties:{items:{type:Array,observer:"clearSelection"},multi:{type:Boolean,value:!1,observer:"clearSelection"},selected:{type:Object,notify:!0},selectedItem:{type:Object,notify:!0},toggle:{type:Boolean,value:!1}},clearSelection:function(){if(Array.isArray(this.selected))for(var e=0;e \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/frontend.html.gz b/homeassistant/components/frontend/www_static/frontend.html.gz index bde1fe17a8dddad0788b4040de413876f4a6add9..5f719af40144d372731ad7dcb72d0108f03d2991 100644 GIT binary patch delta 23316 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ν*A_O3Ss*@dN$_SbW)mLdNUAN`Zb>cq0L{o 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 delta 16 XcmX>va$ba8zMF$%b;zNO>>gYIFarfs diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-dev-info.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-dev-info.html.gz index f2414ac39071ea1d135aff6c01e67cda414cc83a..d42283913c0504b2eedd0f0cd823b07c0565a86b 100644 GIT binary patch delta 16 XcmbQkHHV8`zMF$1HsJI|c2QOUCV2!{ delta 16 XcmbQkHHV8`zMF$%b;zNO?4qmyD24=~ diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-dev-service.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-dev-service.html.gz index 95cdd62d016a4626ae1fcbfd302c49869f387696..8ff19b321b3cfa7c38aad5053b5e731044d364d3 100644 GIT binary patch delta 16 XcmeAW>kwm?@8;l$4LH4#osk;=B~Ju8 delta 16 XcmeAW>kwm?@8;lG9dc+RJ0mv$CtL)B diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-dev-state.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-dev-state.html.gz index 679cc353108e3b243417b5ab49e273dece2f4a24..b7118f9fc63191f9c7ede6bd39e2cf9a3c4fcadb 100644 GIT binary patch delta 16 Xcmca2dPS67zMF$1HsJI|_Oo07FR}%j delta 16 Xcmca2dPS67zMF$%b;zNO>}R^+PCC!PfK diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html.gz index 124f8f5ace25a780a04951c8ebd77373627f2493..c68590101cf4e099e0e75c4708fd8d779cfd8e40 100644 GIT binary patch delta 16 XcmdmBxxtcMzMF$1HsJI|_LVXKFEj;- delta 16 XcmdmBxxtcMzMF$%b;zNO>?>seF+l~= diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-map.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-map.html.gz index 97afe1f0e84702a80767e0e51d6a721f3b758e46..30adcaafce323bc1ea1305b36f5a9c488b0fa43e 100644 GIT binary patch delta 18 acmbPmooT{#CU*I54vyG>(;L}4R|5b+dczt_A=@i3dRd diff --git a/homeassistant/components/frontend/www_static/service_worker.js b/homeassistant/components/frontend/www_static/service_worker.js index d7c78d557d4..120314a120f 100644 --- a/homeassistant/components/frontend/www_static/service_worker.js +++ b/homeassistant/components/frontend/www_static/service_worker.js @@ -1 +1 @@ -"use strict";function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}function notificationEventCallback(e,t){firePushCallback({action:t.action,data:t.notification.data,tag:t.notification.tag,type:e},t.notification.data.jwt)}function firePushCallback(e,t){delete e.data.jwt,0===Object.keys(e.data).length&&e.data.constructor===Object&&delete e.data,fetch("/api/notify.html5/callback",{method:"POST",headers:new Headers({"Content-Type":"application/json",Authorization:"Bearer "+t}),body:JSON.stringify(e)})}var precacheConfig=[["/","cf23e37da78b0dfa560d4a1895b39f76"],["/frontend/panels/dev-event-3cc881ae8026c0fba5aa67d334a3ab2b.html","e22ed0d2d10777c87eb9620d81f525b4"],["/frontend/panels/dev-info-34e2df1af32e60fffcafe7e008a92169.html","7e939dc762dc0c0ec769db4ea76a4b09"],["/frontend/panels/dev-service-bb5c587ada694e0fd42ceaaedd6fe6aa.html","782c4860c5e8ab274231ba9dfd528f29"],["/frontend/panels/dev-state-4608326978256644c42b13940c028e0a.html","26758b741ac1b7c8e9cfcb24762d8774"],["/frontend/panels/dev-template-0a099d4589636ed3038a3e9f020468a7.html","99114026cf9193263c74cc25f9f6a469"],["/frontend/panels/map-af7d04aff7dd5479c5a0016bc8d4dd7d.html","6031df1b4d23d5b321208449b2d293f8"],["/static/core-1fd10c1fcdf56a61f60cf861d5a0368c.js","800ebb1bbb48274790f2ee1a2e53a24c"],["/static/frontend-88c97d278de3320278da6c32fe9e7d61.html","be147f0848a4730291bac9cdb76e2d65"],["/static/mdi-710b84acc99b32514f52291aba9cd8e8.html","149c8eaf6bb78a9b642c7bcedab86900"],["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"],["static/webcomponents-lite.min.js","b0f32ad3c7749c40d486603f31c9d8b1"]],cacheName="sw-precache-v2--"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var a=new URL(e);return"/"===a.pathname.slice(-1)&&(a.pathname+=t),a.toString()},createCacheKey=function(e,t,a,n){var c=new URL(e);return n&&c.toString().match(n)||(c.search+=(c.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(a)),c.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var a=new URL(t).pathname;return e.some(function(e){return a.match(e)})},stripIgnoredUrlParameters=function(e,t){var a=new URL(e);return a.search=a.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(e){return t.every(function(t){return!t.test(e[0])})}).map(function(e){return e.join("=")}).join("&"),a.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],a=e[1],n=new URL(t,self.location),c=createCacheKey(n,hashParamName,a,!1);return[n.toString(),c]}));self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(e){return setOfCachedUrls(e).then(function(t){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(a){if(!t.has(a))return e.add(new Request(a,{credentials:"same-origin"}))}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var t=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(e){return e.keys().then(function(a){return Promise.all(a.map(function(a){if(!t.has(a.url))return e["delete"](a)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(e){if("GET"===e.request.method){var t,a=stripIgnoredUrlParameters(e.request.url,ignoreUrlParametersMatching);t=urlsToCacheKeys.has(a);var n="index.html";!t&&n&&(a=addDirectoryIndex(a,n),t=urlsToCacheKeys.has(a));var c="/";!t&&c&&"navigate"===e.request.mode&&isPathWhitelisted(["^((?!(static|api|local|service_worker.js|manifest.json)).)*$"],e.request.url)&&(a=new URL(c,self.location).toString(),t=urlsToCacheKeys.has(a)),t&&e.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(a))})["catch"](function(t){return console.warn('Couldn\'t serve response for "%s" from cache: %O',e.request.url,t),fetch(e.request)}))}}),self.addEventListener("push",function(e){var t;e.data&&(t=e.data.json(),e.waitUntil(self.registration.showNotification(t.title,t).then(function(e){firePushCallback({type:"received",tag:t.tag,data:t.data},t.data.jwt)})))}),self.addEventListener("notificationclick",function(e){var t;notificationEventCallback("clicked",e),e.notification.close(),e.notification.data&&e.notification.data.url&&(t=e.notification.data.url,t&&e.waitUntil(clients.matchAll({type:"window"}).then(function(e){var a,n;for(a=0;an$ zYnnnDXQEe?G(}wvS}^bRD=z1tzBFa)f_Kw^SvA!Fg70wMx?W)BP`R50dlMFX`S^!b zQ{H)Z$Cp-cHTVPuM{wHKmZ~;|E+n`A{CWwMdBw|8aQWQ@-*`8=XyC)Doo`2Pc(?A< z1pSMzxZ(KUuAIP*`G(IPj^mJTu9t8Mcm5+wf^S!5$Fu|8oka>tFo65rtnkC>boyR= z18I8n`cGa^LNzn<;cz`w))jhUB~9II7Yv7c;(QH8&Ru8Bm&Ldd;9MVXA{+6KqJ zS%8_>>eTu0{?lj2p95FWw5Jx`fAsI}jq|Fm3}QL@jGJ{%9ll(a>y3`TwRPqAFIOm} zDgN7yojN}QZ=e}CC+5!cMXjz+|M}_tyTC?HHA5?Ur(N=9uxvou$UzOdm`%^loU!9O zX->6BWkN}o<$~xuV~VIyQW1t>o~O>aj}vv%nWe@{UP0N86tGMQ%}SV}{c6P?gR#K-j%m99ro3{+{tbwnWz zwbqhr$N){`jFOaX)MUUSreu~5p8qzQ#=tdDM8mUA?l+ZAbVy5seWeiIz$yDNl(;#C4t$ zg&LwXm%+Cd<>p}sLJ%QDoTDo;7HSGWIE5tQG?sgEHyaqGC|PEVQJR3lz==4^SuE06 zh$zX(78jmKlqPLX2w-tWVlE}-N|mr z5lmu+4&^!(BEvvN%~WPWg5n}iSr~SLH&&z3?YOB$ZR+vI?lJngE>&QRMo}uF3{z1= zahjr8Xd+8uN;&6>h%k+QB%=7J;$mx4Tw zh}@M{*t$K|{L;RR2&2E_Z?LRpC?_V2g%LUleV?a11BwL<;|Ews2+5M8asvE=>qAWU zL@1u2R9i(S{U8mvl68PTUqs9W*d2kU%F zb|5_=+wx$^&AdYCL5oZX?udB9p?g1kGBuvh15!}FPx!cVK{+G)(XCSeEn+U7}t0w&eVDA z3->%U=jh53Q3mw@pF+E~QGs<^;`PQp3n%-)8E=cpWZT-c^#UGi>G*nY zU3cKyZejVlbEew!<{$GEO6w7sZeG^DB3{s9|6*K3$~)eFuIEf0+l1<(M9q&)Gy%38 z&Gr2!v-$K4D;Kobpq_>2Uf0SV)%C3|s*S4t9Zh=>KXcyBI}G|Xx!B1Q$H!*5{akN? ztL@Nui`w?vs{;cpbY^M|^f^ArxnJFT&hL_}toud}<72uX!fv&16GrhfvO)i>+A-dj z=Xahr>EvPFb$$688&nly>J-?=Vms>iyOu$4#S8PLGDYcjJ7@#U@5XJ~Z{BXdyuL?#rg(*8$S-!hL{daLLOR;4$Wb2YHtT=w^h$X3*B1xJTE*_!0iI zvJt@j8-z~bt_xnar%sFZkFZ^y6_ta)bUz0tot^z4Z0+}A`4tiAgTXHFkHBfqF_-A3 zeLQ8e}PyD;caNoyQ2S&kMb_kIoT;wdunCT9B35q%_eHME*yW*{&1ZQ0}* z9_}j3G1CL)axH!M$AtCLjW@BW3kH`wc;X&bd&z#>}_r)`}=lWYb zOy1h6P~NUjCl^6@7i1 z(AwPacXRrY#%W$(z1z9EaFMnOQ(9_0#P9I_q`UucFdU({1m&!+N$kzY`XlQzvXedHP#TY8{fN=k!9V2dobLi_Q;@h&l=80zx83bQ!m$b zr;DwZSmf5=Twxy9SI%R1SzYhqR}+jCOp0*A|F&tUCj8{Y!%|csFLUH&s}QplRXVzs zs}_sHpxV^PD(J>sZ-Of}X4qu-+Ns`5JN)AQId>p_`|fzs5jeZ^!<{$zALgYi(K!?V E0KGtIBme*a literal 2285 zcmV4B=~S^4oo}I-CLxvLW8v5-3mXP&*v}1 z2b5+&Z}{W|71j&09FMnCWmA(UHqtche!+NrB+l1pCKb zrxluIqb{A7FJ8ZK{3U|Iw!5_G{(X3NKRM5v+7OnLH?&#j(t-85+HQ3Ep=)Z#|9(S4 z+Va2s*roFWLW}LlxiI&hFB)}s`H$BxehqBo)C;m=aM}&DqjihYMhnTnzqA`o2kD3BiH995{eVqunLGS5&HDGyc7bixx6pVw2?x|u{Va-|vQh+`UR zttDv8Fbs1jI7^G2nhcAmP%=xolA#Qd-U=mRge-+vgvEI^9kw^6#EB4zO!5pAq(zLO zRxy_dfJ&trQ-HmuoXa>*Lz!Ss8_i-KF#(0rD&e{2U(#g2;3Q7NJmP6Va*{NSV;OV7 zq9P`%d5+;;4^Ois7g@|eGC{y$A+;1dwp+}z>lwtmzFE6BD;X0en!(S;`T(lcA4vN8dksW1jj50%7OArlBgmI|4x zSgB0yG^Am~$c-XaJW>>4obfP^<3e!7i%92#1`ALrr?P2r!ZbyUWLhenq>wUAi0eFM ziZn!NE`tvp$<4zMg`Gxhyipvs_^m@zB0NDkHA3 zz)YoVXSF~UXPOMnAITHj8Cy&Knll2R# zaSr%rjkX%!4rAf$;ihxjr>sJkXg^NuE?N%DdNM{txi4dR@R-_sPO9(E7 zc^ol$D6O=0d#d5azD!ubf1}@ET`x#Z%!(q6$Vr@&L69M*0!HzJMaUS-lCyFG`a|nO zO7}$Asu@RUw}pa9fn3P6p!_+4s9eDUJ0gNsy zUP4O}4N=%HKs-p)GryYtdt+Abr`J9x^;aTfOP_Z?)e3)`KlB)umn}KL+|uCNS3eV9 z&4#vv=!w`Cf;E_BP11vom=N6w^TuQMVfbQhJRbtnyzVK__3nMyB9*Xd2eF&r>9AKxDpq+4_okK}Y?IaS18!eE+$fGk0tgs>_NrKQ++= z)N-_U51-8L(=)1E*inOe6<&LND|=Sgho-D|s)lzm?NR(Jp%MgQs_Arn4KI{U5lWIqbocXv3GFv}eBuIIo=HP~%+FwBLi3tV&Bm^n`Nt6}0*j z(lJzIi1W7}-#C4BYX=tw!zpQtBPGk(;^aQ8p0&!gzdA_}we#->&=JGk4(n z&vcmlY^y@`d3!o}f7`Slu_d$QoeY9{u8wcN09VHxvw(vQI$EY8fSOp z8+!_Fu_N|U?@F`$Wh<>;Fi-yXw347NR{?}z({Js+~`O}Bg^xo5+fpU|a> zzP(N8Y;O36IsHWAv}|sFJ-E7XiMC2pS!zAS@AUqpzyEM39I?DX?9IsbCdEGN z^~l4Gi1($0JiJArM7CEHr&~RL%j5KGtRZMOesm`ztENNuWOzXBi9ze0Eu2q&8^Z9Q zUT*77A6ws3k=ufEOL^SfI#1nY^}UN<&nQ+XDZ&~2W!F&6;NrrgQdAQ!1M#v|NZCp% z9o@)HN5x@O?`mWn^kc3!qZK^24fHu(kZ diff --git a/homeassistant/components/insteon_hub.py b/homeassistant/components/insteon_hub.py index 3ad107886b8..306acab5361 100644 --- a/homeassistant/components/insteon_hub.py +++ b/homeassistant/components/insteon_hub.py @@ -8,93 +8,37 @@ import logging from homeassistant.const import CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME from homeassistant.helpers import validate_config, discovery -from homeassistant.helpers.entity import Entity - -DOMAIN = 'insteon_hub' # type: str -REQUIREMENTS = ['insteon_hub==0.5.0'] # type: list -INSTEON = None # type: Insteon -DEVCAT = 'DevCat' # type: str -SUBCAT = 'SubCat' # type: str -DEVICE_CLASSES = ['light', 'fan'] # type: list +DOMAIN = "insteon_hub" +REQUIREMENTS = ['insteon_hub==0.4.5'] +INSTEON = None _LOGGER = logging.getLogger(__name__) -def _is_successful(response: dict) -> bool: - """Check http response for successful status.""" - return 'status' in response and response['status'] == 'succeeded' +def setup(hass, config): + """Setup Insteon Hub component. - -def filter_devices(devices: list, categories: list) -> list: - """Filter insteon device list by category/subcategory.""" - categories = (categories - if isinstance(categories, list) - else [categories]) - matching_devices = [] - for device in devices: - if any( - device.DevCat == c[DEVCAT] and - (SUBCAT not in c or device.SubCat in c[SUBCAT]) - for c in categories): - matching_devices.append(device) - return matching_devices - - -def setup(hass, config: dict) -> bool: - """Setup Insteon Hub component.""" + This will automatically import associated lights. + """ if not validate_config( config, {DOMAIN: [CONF_USERNAME, CONF_PASSWORD, CONF_API_KEY]}, _LOGGER): return False - from insteon import Insteon + import insteon username = config[DOMAIN][CONF_USERNAME] password = config[DOMAIN][CONF_PASSWORD] api_key = config[DOMAIN][CONF_API_KEY] global INSTEON - INSTEON = Insteon(username, password, api_key) + INSTEON = insteon.Insteon(username, password, api_key) if INSTEON is None: - _LOGGER.error('Could not connect to Insteon service.') + _LOGGER.error("Could not connect to Insteon service.") return - for device_class in DEVICE_CLASSES: - discovery.load_platform(hass, device_class, DOMAIN, {}, config) + discovery.load_platform(hass, 'light', DOMAIN, {}, config) + return True - - -class InsteonDevice(Entity): - """Represents an insteon device.""" - - def __init__(self: Entity, node: object) -> None: - """Initialize the insteon device.""" - self._node = node - - def update(self: Entity) -> None: - """Update state of the device.""" - pass - - @property - def name(self: Entity) -> str: - """Name of the insteon device.""" - return self._node.DeviceName - - @property - def unique_id(self: Entity) -> str: - """Unique identifier for the device.""" - return self._node.DeviceID - - @property - def supported_features(self: Entity) -> int: - """Supported feature flags.""" - return 0 - - def _send_command(self: Entity, command: str, level: int=None, - payload: dict=None) -> bool: - """Send command to insteon device.""" - resp = self._node.send_command(command, payload=payload, level=level, - wait=True) - return _is_successful(resp) diff --git a/homeassistant/components/light/insteon_hub.py b/homeassistant/components/light/insteon_hub.py index 29254735ced..70beadb6c1d 100644 --- a/homeassistant/components/light/insteon_hub.py +++ b/homeassistant/components/light/insteon_hub.py @@ -4,76 +4,74 @@ Support for Insteon Hub lights. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/insteon_hub/ """ -from homeassistant.components.insteon_hub import (INSTEON, InsteonDevice) +from homeassistant.components.insteon_hub import INSTEON from homeassistant.components.light import (ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) SUPPORT_INSTEON_HUB = SUPPORT_BRIGHTNESS -DEPENDENCIES = ['insteon_hub'] - def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Insteon Hub light platform.""" devs = [] for device in INSTEON.devices: if device.DeviceCategory == "Switched Lighting Control": - devs.append(InsteonLightDevice(device)) + devs.append(InsteonToggleDevice(device)) if device.DeviceCategory == "Dimmable Lighting Control": - devs.append(InsteonDimmableDevice(device)) + devs.append(InsteonToggleDevice(device)) add_devices(devs) -class InsteonLightDevice(InsteonDevice, Light): - """A representation of a light device.""" +class InsteonToggleDevice(Light): + """An abstract Class for an Insteon node.""" - def __init__(self, node: object) -> None: + def __init__(self, node): """Initialize the device.""" - super(InsteonLightDevice, self).__init__(node) + self.node = node self._value = 0 - def update(self) -> None: - """Update state of the device.""" - resp = self._node.send_command('get_status', wait=True) + @property + def name(self): + """Return the the name of the node.""" + return self.node.DeviceName + + @property + def unique_id(self): + """Return the ID of this insteon node.""" + return self.node.DeviceID + + @property + def brightness(self): + """Return the brightness of this light between 0..255.""" + return self._value / 100 * 255 + + def update(self): + """Update state of the sensor.""" + resp = self.node.send_command('get_status', wait=True) try: self._value = resp['response']['level'] except KeyError: pass @property - def is_on(self) -> None: + def is_on(self): """Return the boolean response if the node is on.""" return self._value != 0 - def turn_on(self, **kwargs) -> None: - """Turn device on.""" - if self._send_command('on'): - self._value = 100 - - def turn_off(self, **kwargs) -> None: - """Turn device off.""" - if self._send_command('off'): - self._value = 0 - - -class InsteonDimmableDevice(InsteonLightDevice): - """A representation for a dimmable device.""" - @property - def brightness(self) -> int: - """Return the brightness of this light between 0..255.""" - return round(self._value / 100 * 255, 0) # type: int - - @property - def supported_features(self) -> int: + def supported_features(self): """Flag supported features.""" return SUPPORT_INSTEON_HUB - def turn_on(self, **kwargs) -> None: + def turn_on(self, **kwargs): """Turn device on.""" - level = 100 # type: int if ATTR_BRIGHTNESS in kwargs: - level = round(kwargs[ATTR_BRIGHTNESS] / 255 * 100, 0) # type: int + self._value = kwargs[ATTR_BRIGHTNESS] / 255 * 100 + self.node.send_command('on', self._value) + else: + self._value = 100 + self.node.send_command('on') - if self._send_command('on', level=level): - self._value = level + def turn_off(self, **kwargs): + """Turn device off.""" + self.node.send_command('off') diff --git a/homeassistant/const.py b/homeassistant/const.py index a720c989907..f6f0cd7855d 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """Constants used by Home Assistant components.""" -__version__ = '0.27.1' +__version__ = '0.27.2' REQUIRED_PYTHON_VER = (3, 4) PLATFORM_FORMAT = '{}.{}' @@ -244,7 +244,7 @@ SERVICE_OPEN_COVER = 'open_cover' SERVICE_OPEN_COVER_TILT = 'open_cover_tilt' SERVICE_SET_COVER_POSITION = 'set_cover_position' SERVICE_SET_COVER_TILT_POSITION = 'set_cover_tilt_position' -SERVICE_STOP_COVER = 'stop' +SERVICE_STOP_COVER = 'stop_cover' SERVICE_STOP_COVER_TILT = 'stop_cover_tilt' SERVICE_MOVE_UP = 'move_up' diff --git a/requirements_all.txt b/requirements_all.txt index 934dec82bed..56d445daf54 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -204,7 +204,7 @@ https://github.com/wokar/pylgnetcast/archive/v0.2.0.zip#pylgnetcast==0.2.0 influxdb==3.0.0 # homeassistant.components.insteon_hub -insteon_hub==0.5.0 +insteon_hub==0.4.5 # homeassistant.components.media_player.kodi jsonrpc-requests==0.3 diff --git a/tests/components/fan/test_insteon_hub.py b/tests/components/fan/test_insteon_hub.py deleted file mode 100644 index dfdb4b7a9f0..00000000000 --- a/tests/components/fan/test_insteon_hub.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Tests for the insteon hub fan platform.""" -import unittest - -from homeassistant.const import (STATE_OFF, STATE_ON) -from homeassistant.components.fan import (SPEED_LOW, SPEED_MED, SPEED_HIGH, - ATTR_SPEED) -from homeassistant.components.fan.insteon_hub import (InsteonFanDevice, - SUPPORT_SET_SPEED) - - -class Node(object): - """Fake insteon node.""" - - def __init__(self, name, id, dev_cat, sub_cat): - """Initialize fake insteon node.""" - self.DeviceName = name - self.DeviceID = id - self.DevCat = dev_cat - self.SubCat = sub_cat - self.response = None - - def send_command(self, command, payload, level, wait): - """Send fake command.""" - return self.response - - -class TestInsteonHubFanDevice(unittest.TestCase): - """Test around insteon hub fan device methods.""" - - _NODE = Node('device', '12345', '1', '46') - - def setUp(self): - """Initialize test data.""" - self._DEVICE = InsteonFanDevice(self._NODE) - - def tearDown(self): - """Tear down test data.""" - self._DEVICE = None - - def test_properties(self): - """Test basic properties.""" - self.assertEqual(self._NODE.DeviceName, self._DEVICE.name) - self.assertEqual(self._NODE.DeviceID, self._DEVICE.unique_id) - self.assertEqual(SUPPORT_SET_SPEED, self._DEVICE.supported_features) - - for speed in [STATE_OFF, SPEED_LOW, SPEED_MED, SPEED_HIGH]: - self.assertIn(speed, self._DEVICE.speed_list) - - def test_turn_on(self): - """Test the turning on device.""" - self._NODE.response = { - 'status': 'succeeded' - } - self.assertEqual(STATE_OFF, self._DEVICE.state) - self._DEVICE.turn_on() - - self.assertEqual(STATE_ON, self._DEVICE.state) - - self._DEVICE.turn_on(SPEED_MED) - - self.assertEqual(STATE_ON, self._DEVICE.state) - self.assertEqual(SPEED_MED, self._DEVICE.state_attributes[ATTR_SPEED]) - - def test_turn_off(self): - """Test turning off device.""" - self._NODE.response = { - 'status': 'succeeded' - } - self.assertEqual(STATE_OFF, self._DEVICE.state) - self._DEVICE.turn_on() - self.assertEqual(STATE_ON, self._DEVICE.state) - self._DEVICE.turn_off() - self.assertEqual(STATE_OFF, self._DEVICE.state) diff --git a/tests/components/test_emulated_hue.py b/tests/components/test_emulated_hue.py index c9efa6e9fda..9433aacc20b 100755 --- a/tests/components/test_emulated_hue.py +++ b/tests/components/test_emulated_hue.py @@ -1,3 +1,4 @@ +"""The tests for the emulated Hue component.""" import time import json import threading @@ -11,8 +12,7 @@ import homeassistant.components as core_components from homeassistant.components import emulated_hue, http, light, mqtt from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.components.emulated_hue import ( - HUE_API_STATE_ON, HUE_API_STATE_BRI -) + HUE_API_STATE_ON, HUE_API_STATE_BRI) from tests.common import get_test_instance_port, get_test_home_assistant @@ -27,6 +27,7 @@ mqtt_broker = None def setUpModule(): + """Setup things to be run when tests are started.""" global mqtt_broker mqtt_broker = MQTTBroker('127.0.0.1', MQTT_BROKER_PORT) @@ -34,12 +35,14 @@ def setUpModule(): def tearDownModule(): + """Stop everything that was started.""" global mqtt_broker mqtt_broker.stop() def setup_hass_instance(emulated_hue_config): + """Setup the Home Assistant instance to test.""" hass = get_test_home_assistant() # We need to do this to get access to homeassistant/turn_(on,off) @@ -55,15 +58,19 @@ def setup_hass_instance(emulated_hue_config): def start_hass_instance(hass): + """Start the Home Assistant instance to test.""" hass.start() time.sleep(0.05) class TestEmulatedHue(unittest.TestCase): + """Test the emulated Hue component.""" + hass = None @classmethod def setUpClass(cls): + """Setup the class.""" cls.hass = setup_hass_instance({ emulated_hue.DOMAIN: { emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT @@ -73,9 +80,11 @@ class TestEmulatedHue(unittest.TestCase): @classmethod def tearDownClass(cls): + """Stop the class.""" cls.hass.stop() def test_description_xml(self): + """Test the description.""" import xml.etree.ElementTree as ET result = requests.get( @@ -91,6 +100,7 @@ class TestEmulatedHue(unittest.TestCase): self.fail('description.xml is not valid XML!') def test_create_username(self): + """Test the creation of an username.""" request_json = {'devicetype': 'my_device'} result = requests.post( @@ -107,6 +117,7 @@ class TestEmulatedHue(unittest.TestCase): self.assertTrue('username' in success_json['success']) def test_valid_username_request(self): + """Test request with a valid username.""" request_json = {'invalid_key': 'my_device'} result = requests.post( @@ -117,8 +128,11 @@ class TestEmulatedHue(unittest.TestCase): class TestEmulatedHueExposedByDefault(unittest.TestCase): + """Test class for emulated hue component.""" + @classmethod def setUpClass(cls): + """Setup the class.""" cls.hass = setup_hass_instance({ emulated_hue.DOMAIN: { emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT, @@ -177,9 +191,11 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): @classmethod def tearDownClass(cls): + """Stop the class.""" cls.hass.stop() def test_discover_lights(self): + """Test the discovery of lights.""" result = requests.get( BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5) @@ -194,6 +210,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): self.assertTrue('light.kitchen_light' not in result_json) def test_get_light_state(self): + """Test the getting of light state.""" # Turn office light on and set to 127 brightness self.hass.services.call( light.DOMAIN, const.SERVICE_TURN_ON, @@ -229,6 +246,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): self.assertEqual(kitchen_result.status_code, 404) def test_put_light_state(self): + """Test the seeting of light states.""" self.perform_put_test_on_office_light() # Turn the bedroom light on first @@ -264,6 +282,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): self.assertEqual(kitchen_result.status_code, 404) def test_put_with_form_urlencoded_content_type(self): + """Test the form with urlencoded content.""" # Needed for Alexa self.perform_put_test_on_office_light( 'application/x-www-form-urlencoded') @@ -278,6 +297,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): self.assertEqual(result.status_code, 400) def test_entity_not_found(self): + """Test for entity which are not found.""" result = requests.get( BRIDGE_URL_BASE.format( '/api/username/lights/{}'.format("not.existant_entity")), @@ -293,6 +313,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): self.assertEqual(result.status_code, 404) def test_allowed_methods(self): + """Test the allowed methods.""" result = requests.get( BRIDGE_URL_BASE.format( '/api/username/lights/{}/state'.format("light.office_light"))) @@ -313,6 +334,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): self.assertEqual(result.status_code, 405) def test_proper_put_state_request(self): + """Test the request to set the state.""" # Test proper on value parsing result = requests.put( BRIDGE_URL_BASE.format( @@ -334,6 +356,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): def perform_put_test_on_office_light(self, content_type='application/json'): + """Test the setting of a light.""" # Turn the office light off first self.hass.services.call( light.DOMAIN, const.SERVICE_TURN_OFF, @@ -361,6 +384,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): self.assertEqual(office_light.attributes[light.ATTR_BRIGHTNESS], 56) def perform_get_light_state(self, entity_id, expected_status): + """Test the gettting of a light state.""" result = requests.get( BRIDGE_URL_BASE.format( '/api/username/lights/{}'.format(entity_id)), timeout=5) @@ -377,6 +401,7 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): def perform_put_light_state(self, entity_id, is_on, brightness=None, content_type='application/json'): + """Test the setting of a light state.""" url = BRIDGE_URL_BASE.format( '/api/username/lights/{}/state'.format(entity_id)) @@ -432,6 +457,7 @@ class MQTTBroker(object): self._thread.join() def _run_loop(self): + """Run the loop.""" asyncio.set_event_loop(self._loop) self._loop.run_until_complete(self._broker_coroutine()) @@ -442,4 +468,5 @@ class MQTTBroker(object): @asyncio.coroutine def _broker_coroutine(self): + """The Broker coroutine.""" yield from self._broker.start()