diff --git a/homeassistant/__init__.py b/homeassistant/__init__.py index 5e54cbd5d0a..80916011d55 100644 --- a/homeassistant/__init__.py +++ b/homeassistant/__init__.py @@ -22,7 +22,7 @@ from homeassistant.const import ( SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED, EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL, EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED, - TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_FRIENDLY_NAME) + TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_FRIENDLY_NAME, ATTR_HIDDEN) import homeassistant.util as util DOMAIN = "homeassistant" @@ -621,6 +621,16 @@ class StateMachine(object): new_state = str(new_state) attributes = attributes or {} + # Last chance to enforce the visibility property. This is required for + # 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 + # attribute in its attributes dictionary. + if ATTR_HIDDEN not in attributes: + attributes[ATTR_HIDDEN] = False + with self._lock: old_state = self._states.get(entity_id) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 4a6aab53483..c0b8fe09af3 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -20,6 +20,7 @@ import homeassistant import homeassistant.loader as loader import homeassistant.components as core_components import homeassistant.components.group as group +from homeassistant.helpers.entity import Entity from homeassistant.const import ( EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE, CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, TEMP_CELCIUS, @@ -207,6 +208,8 @@ def process_ha_core_config(hass, config): if key in config: setattr(hass.config, attr, config[key]) + Entity._visibility.update(config.get('visibility', [{}])[0]) + if CONF_TEMPERATURE_UNIT in config: unit = config[CONF_TEMPERATURE_UNIT] diff --git a/homeassistant/components/isy994.py b/homeassistant/components/isy994.py index 7fa0524d32f..d364007d12c 100644 --- a/homeassistant/components/isy994.py +++ b/homeassistant/components/isy994.py @@ -60,8 +60,8 @@ def setup(hass, config): # pull and parse optional configuration global SENSOR_STRING global HIDDEN_STRING - SENSOR_STRING = config[DOMAIN].get('sensor_string', SENSOR_STRING) - HIDDEN_STRING = config[DOMAIN].get('hidden_string', HIDDEN_STRING) + SENSOR_STRING = str(config[DOMAIN].get('sensor_string', SENSOR_STRING)) + HIDDEN_STRING = str(config[DOMAIN].get('hidden_string', HIDDEN_STRING)) # connect to ISY controller global ISY @@ -95,6 +95,7 @@ class ISYDeviceABC(ToggleEntity): def __init__(self, node): # setup properties self.node = node + self.hidden = HIDDEN_STRING in self.raw_name # track changes self._changeHandler = self.node.status. \ @@ -135,13 +136,17 @@ class ISYDeviceABC(ToggleEntity): """ Returns the id of this isy sensor """ return self.node._id + @property + def raw_name(self): + try: + return str(self._name) + except AttributeError: + return str(self.node.name) + @property def name(self): """ Returns the name of the node if any. """ - try: - return self._name - except AttributeError: - return self.node.name + return self.raw_name.replace(HIDDEN_STRING, '').strip() def update(self): """ Update state of the sensor. """ diff --git a/homeassistant/const.py b/homeassistant/const.py index 467bb692399..a1aa0df40d1 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -86,6 +86,9 @@ ATTR_TRIPPED = "device_tripped" # time the device was tripped ATTR_LAST_TRIP_TIME = "last_tripped_time" +# For all entity's, this hold whether or not it should be hidden +ATTR_HIDDEN = "hidden" + # #### SERVICES #### SERVICE_HOMEASSISTANT_STOP = "stop" diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index a8ee712b0f7..d7599eaf971 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -8,16 +8,19 @@ Provides ABC for entities in HA. from homeassistant import NoEntitySpecifiedError from homeassistant.const import ( - ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, - DEVICE_DEFAULT_NAME, TEMP_CELCIUS, TEMP_FAHRENHEIT) + ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, ATTR_HIDDEN, STATE_ON, + STATE_OFF, DEVICE_DEFAULT_NAME, TEMP_CELCIUS, TEMP_FAHRENHEIT) class Entity(object): """ ABC for Home Assistant entities. """ # pylint: disable=no-self-use - hass = None - entity_id = None + # SAFE TO OVERWRITE + # The properties and methods here are safe to overwrite when inherting this + # class. These may be used to customize the behavior of the entity. + + _hidden = False # suggestion as to whether the entity should be hidden @property def should_poll(self): @@ -52,6 +55,10 @@ class Entity(object): """ Unit of measurement of this entity, if any. """ return None + def update(self): + """ Retrieve latest state. """ + pass + # DEPRECATION NOTICE: # Device is moving from getters to properties. # For now the new properties will call the old functions @@ -69,9 +76,14 @@ class Entity(object): """ Returns optional state attributes. """ return None - def update(self): - """ Retrieve latest state. """ - pass + # DO NOT OVERWRITE + # These properties and methods are either managed by Home Assistant or they + # are used to perform a very specific function. Overwriting these may + # produce undesirable effects in the entity's operation. + + hass = None + entity_id = None + _visibility = {} def update_ha_state(self, force_refresh=False): """ @@ -97,6 +109,9 @@ class Entity(object): if ATTR_UNIT_OF_MEASUREMENT not in attr and self.unit_of_measurement: attr[ATTR_UNIT_OF_MEASUREMENT] = self.unit_of_measurement + if ATTR_HIDDEN not in attr: + attr[ATTR_HIDDEN] = bool(self.hidden) + # Convert temperature if we detect one if attr.get(ATTR_UNIT_OF_MEASUREMENT) in (TEMP_CELCIUS, TEMP_FAHRENHEIT): @@ -115,6 +130,24 @@ class Entity(object): def __repr__(self): return "".format(self.name, self.state) + @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()] is 'hide' + else: + return self._hidden + + @hidden.setter + def hidden(self, val): + """ Sets the suggestion for visibility. """ + self._hidden = bool(val) + class ToggleEntity(Entity): """ ABC for entities that can be turned on and off. """