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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 H&&~h<8x*%G diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 670ba0292bf..659ec6552f7 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 670ba0292bfca2b65aeca70804c0856b6cabf10e +Subproject commit 659ec6552f761ff4779dd52ee35d26f7be5e111f diff --git a/homeassistant/components/frontend/www_static/mdi.html.gz b/homeassistant/components/frontend/www_static/mdi.html.gz index 3ab4238a3970e7a735be3af0c39e796a81174316..09cadf26e6b345ed26285253c32ad7169877be33 100644 GIT binary patch delta 24 gcmcb2iR<1aE_V5D4vzlBLyhdM?2KF4nZj2B0DAHWy8r+H delta 24 gcmcb2iR<1aE_V5D4vs9fy^ZXx?2KF4nZj2B0CpA#RsaA1 diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-dev-event.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-dev-event.html.gz index 686df6e41eee4b0db912d665b4d4f7ffde18b357..d32bfd90fb1c90ed6e087336399bf040751d0bd1 100644 GIT binary patch delta 16 XcmX>va$ba8zMF$1HsJI|b`LH9E%pTp 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,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 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()