diff --git a/homeassistant/__init__.py b/homeassistant/__init__.py index 80916011d55e84bc31d6647eff80f71edf12b22d..1f8f88d3723b8e7c052bf5aed64623d59931cd90 100644 --- a/homeassistant/__init__.py +++ b/homeassistant/__init__.py @@ -625,8 +625,8 @@ class StateMachine(object): # components that don't use the Entity base class for their entities. # The sun component is an example of this. The Entity class cannot be # imported cleanly, so assume the state is shown. This means that for - # visibility to be supported, the state must originate from a class that - # uses the base class Entity or it must manually put the hidden + # visibility to be supported, the state must originate from a class + # that uses the base class Entity or it must manually put the hidden # attribute in its attributes dictionary. if ATTR_HIDDEN not in attributes: attributes[ATTR_HIDDEN] = False diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index c0b8fe09af3f911ed49ec5798451f16a438c01cf..e31a4bb66c7e797de7dbfa888f085da6896fc696 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -208,7 +208,7 @@ def process_ha_core_config(hass, config): if key in config: setattr(hass.config, attr, config[key]) - Entity._visibility.update(config.get('visibility', [{}])[0]) + Entity.visibility.update(config.get('visibility', [{}])[0]) if CONF_TEMPERATURE_UNIT in config: unit = config[CONF_TEMPERATURE_UNIT] diff --git a/homeassistant/components/group.py b/homeassistant/components/group.py index 4c5e6adb2c685567388debd78ca853a16be58dd4..badd5ac66fed4a54e7fbf7a691d13bdaf34a7448 100644 --- a/homeassistant/components/group.py +++ b/homeassistant/components/group.py @@ -7,10 +7,11 @@ Provides functionality to group devices that can be turned on or off. import homeassistant as ha from homeassistant.helpers import generate_entity_id +from homeassistant.helpers.entity import Entity import homeassistant.util as util from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF, - STATE_HOME, STATE_NOT_HOME, STATE_UNKNOWN) + STATE_HOME, STATE_NOT_HOME, STATE_UNKNOWN, ATTR_HIDDEN) DOMAIN = "group" DEPENDENCIES = [] @@ -112,6 +113,10 @@ def setup(hass, config): class Group(object): """ Tracks a group of entity ids. """ + + visibility = Entity.visibility + _hidden = False + def __init__(self, hass, name, entity_ids=None, user_defined=True): self.hass = hass self.name = name @@ -138,7 +143,8 @@ class Group(object): return { ATTR_ENTITY_ID: self.tracking, ATTR_AUTO: not self.user_defined, - ATTR_FRIENDLY_NAME: self.name + ATTR_FRIENDLY_NAME: self.name, + ATTR_HIDDEN: self.hidden } def update_tracked_entity_ids(self, entity_ids): @@ -213,6 +219,24 @@ class Group(object): self.hass.states.set( self.entity_id, group_off, self.state_attr) + @property + def hidden(self): + """ + Returns the official decision of whether the entity should be hidden. + Any value set by the user in the configuration file will overwrite + whatever the component sets for visibility. + """ + if self.entity_id is not None and \ + self.entity_id.lower() in self.visibility: + return self.visibility[self.entity_id.lower()] == 'hide' + else: + return self._hidden + + @hidden.setter + def hidden(self, val): + """ Sets the suggestion for visibility. """ + self._hidden = bool(val) + def setup_group(hass, name, entity_ids, user_defined=True): """ Sets up a group state that is the combined state of diff --git a/homeassistant/components/isy994.py b/homeassistant/components/isy994.py index d364007d12cdb26919377ec1bd16ddae21d3ffd8..33d11c8ebbe657bb4e678489364fbbbb22e1e528 100644 --- a/homeassistant/components/isy994.py +++ b/homeassistant/components/isy994.py @@ -29,15 +29,19 @@ SENSOR_STRING = 'Sensor' HIDDEN_STRING = '{HIDE ME}' # setup logger -logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) +_LOGGER = logging.getLogger(__name__) def setup(hass, config): + """ + Setup isy994 component. + This will automatically import associated lights, switches, and sensors. + """ + # pylint: disable=global-statement # check for required values in configuration file if not validate_config(config, {DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]}, - logger): + _LOGGER): return False # pull and parse standard configuration @@ -52,7 +56,7 @@ def setup(hass, config): addr = addr.replace('https://', '') https = True else: - logger.error('isy994 host value in configuration file is invalid.') + _LOGGER.error('isy994 host value in configuration file is invalid.') return False port = host.port addr = addr.replace(':{}'.format(port), '') @@ -65,7 +69,7 @@ def setup(hass, config): # connect to ISY controller global ISY - ISY = PyISY.ISY(addr, port, user, password, use_https=https, log=logger) + ISY = PyISY.ISY(addr, port, user, password, use_https=https, log=_LOGGER) if not ISY.connected: return False @@ -91,6 +95,7 @@ class ISYDeviceABC(ToggleEntity): _states = [] _dtype = None _domain = None + _name = None def __init__(self, node): # setup properties @@ -98,34 +103,39 @@ class ISYDeviceABC(ToggleEntity): self.hidden = HIDDEN_STRING in self.raw_name # track changes - self._changeHandler = self.node.status. \ - subscribe('changed', self.onUpdate) + self._change_handler = self.node.status. \ + subscribe('changed', self.on_update) def __del__(self): """ cleanup subscriptions because it is the right thing to do. """ - self._changeHandler.unsubscribe() + self._change_handler.unsubscribe() @property def domain(self): + """ Returns the domain of the entity. """ return self._domain @property def dtype(self): + """ Returns the data type of the entity (binary or analog). """ if self._dtype in ['analog', 'binary']: return self._dtype - return 'binary' if self._units is None else 'analog' + return 'binary' if self.unit_of_measurement is None else 'analog' @property def should_poll(self): + """ Tells Home Assistant not to poll this entity. """ return False @property def value(self): """ returns the unclean value from the controller """ + # pylint: disable=protected-access return self.node.status._val @property def state_attributes(self): + """ Returns the state attributes for the node. """ attr = {ATTR_FRIENDLY_NAME: self.name} for name, prop in self._attrs.items(): attr[name] = getattr(self, prop) @@ -134,18 +144,18 @@ class ISYDeviceABC(ToggleEntity): @property def unique_id(self): """ Returns the id of this isy sensor """ + # pylint: disable=protected-access return self.node._id @property def raw_name(self): - try: - return str(self._name) - except AttributeError: - return str(self.node.name) + """ Returns the unclean node name. """ + return str(self._name) \ + if self._name is not None else str(self.node.name) @property def name(self): - """ Returns the name of the node if any. """ + """ Returns the cleaned name of the node. """ return self.raw_name.replace(HIDDEN_STRING, '').strip() def update(self): @@ -153,16 +163,18 @@ class ISYDeviceABC(ToggleEntity): # ISY objects are automatically updated by the ISY's event stream pass - def onUpdate(self, e): + def on_update(self, event): """ Handles the update received event. """ self.update_ha_state() @property def is_on(self): + """ Returns boolean response if the node is on. """ return bool(self.value) @property def is_open(self): + """ Returns boolean respons if the node is open. On = Open. """ return self.is_on @property @@ -178,17 +190,18 @@ class ISYDeviceABC(ToggleEntity): attrs = [kwargs.get(name) for name in self._onattrs] self.node.on(*attrs) else: - logger.error('ISY cannot turn on sensors.') + _LOGGER.error('ISY cannot turn on sensors.') def turn_off(self, **kwargs): """ turns the device off """ if self.domain is not 'sensor': self.node.off() else: - logger.error('ISY cannot turn off sensors.') + _LOGGER.error('ISY cannot turn off sensors.') @property def unit_of_measurement(self): + """ Returns the defined units of measurement or None. """ try: return self.node.units except AttributeError: diff --git a/homeassistant/components/sensor/isy994.py b/homeassistant/components/sensor/isy994.py index aa98c5949101cbe0f387e380fc507fb5b13336a4..9b58a574527ee7849ce750dc2a71d6c67a5afbf7 100644 --- a/homeassistant/components/sensor/isy994.py +++ b/homeassistant/components/sensor/isy994.py @@ -3,13 +3,29 @@ import logging # homeassistant imports -from homeassistant.components.isy994 import ISY, ISYDeviceABC, SENSOR_STRING +from homeassistant.components.isy994 import (ISY, ISYDeviceABC, SENSOR_STRING, + HIDDEN_STRING) from homeassistant.const import (STATE_OPEN, STATE_CLOSED, STATE_HOME, STATE_NOT_HOME, STATE_ON, STATE_OFF) +DEFAULT_HIDDEN_WEATHER = ['Temperature_High', 'Temperature_Low', 'Feels_Like', + 'Temperature_Average', 'Pressure', 'Dew_Point', + 'Gust_Speed', 'Evapotranspiration', + 'Irrigation_Requirement', 'Water_Deficit_Yesterday', + 'Elevation', 'Average_Temperature_Tomorrow', + 'High_Temperature_Tomorrow', + 'Low_Temperature_Tomorrow', 'Humidity_Tomorrow', + 'Wind_Speed_Tomorrow', 'Gust_Speed_Tomorrow', + 'Rain_Tomorrow', 'Snow_Tomorrow', + 'Forecast_Average_Temperature', + 'Forecast_High_Temperature', + 'Forecast_Low_Temperature', 'Forecast_Humidity', + 'Forecast_Rain', 'Forecast_Snow'] + def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the isy994 platform. """ + # pylint: disable=protected-access logger = logging.getLogger(__name__) devs = [] # verify connection @@ -21,7 +37,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if ISY.climate is not None: for prop in ISY.climate._id2name: if prop is not None: - node = WeatherPseudoNode('ISY.weather.' + prop, prop, + prefix = HIDDEN_STRING if prop in DEFAULT_HIDDEN_WEATHER else '' + node = WeatherPseudoNode('ISY.weather.' + prop, prefix + prop, getattr(ISY.climate, prop), getattr(ISY.climate, prop + '_units')) devs.append(ISYSensorDevice(node)) @@ -42,7 +59,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # folder does not exist pass else: - for dtype, name, node_id in folder.children: + for _, _, node_id in folder.children: node = folder[node_id].leaf devs.append(ISYSensorDevice(node, states)) @@ -51,6 +68,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class WeatherPseudoNode(object): """ This class allows weather variable to act as regular nodes. """ + # pylint: disable=too-few-public-methods def __init__(self, device_id, name, status, units=None): self._id = device_id @@ -64,6 +82,6 @@ class ISYSensorDevice(ISYDeviceABC): _domain = 'sensor' - def __init__(self, node, states=[]): + def __init__(self, node, states=None): super().__init__(node) - self._states = states + self._states = states or [] diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 3e559d3953249322766f53a32b61117ed83b85ed..7d6ef65e1a74fb6081759b1fe2c8e665a00bb74a 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -83,7 +83,7 @@ class Entity(object): hass = None entity_id = None - _visibility = {} + visibility = {} def update_ha_state(self, force_refresh=False): """ @@ -138,8 +138,8 @@ class Entity(object): whatever the component sets for visibility. """ if self.entity_id is not None and \ - self.entity_id.lower() in self._visibility: - return self._visibility[self.entity_id.lower()] == 'hide' + self.entity_id.lower() in self.visibility: + return self.visibility[self.entity_id.lower()] == 'hide' else: return self._hidden