From b20424261cb96b54f308e0a7f426e9d7fc925337 Mon Sep 17 00:00:00 2001
From: Ryan Kraus <rmkraus@gmail.com>
Date: Wed, 15 Apr 2015 02:05:34 -0400
Subject: [PATCH] 1) Performed many pylint and flake8 fixes to clean up isy994
 integration and hidden entities addition. 2) Added necessary code to allow
 groups to also be hidden. 3) Made most of the weather data from the isy994
 component be hidden by default.

---
 homeassistant/__init__.py                 |  4 +-
 homeassistant/bootstrap.py                |  2 +-
 homeassistant/components/group.py         | 28 +++++++++++++-
 homeassistant/components/isy994.py        | 47 +++++++++++++++--------
 homeassistant/components/sensor/isy994.py | 28 +++++++++++---
 homeassistant/helpers/entity.py           |  6 +--
 6 files changed, 85 insertions(+), 30 deletions(-)

diff --git a/homeassistant/__init__.py b/homeassistant/__init__.py
index 80916011d55..1f8f88d3723 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 c0b8fe09af3..e31a4bb66c7 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 4c5e6adb2c6..badd5ac66fe 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 d364007d12c..33d11c8ebbe 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 aa98c594910..9b58a574527 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 3e559d39532..7d6ef65e1a7 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
 
-- 
GitLab