diff --git a/.coveragerc b/.coveragerc index d361cf2ddad..80ca261f32d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -542,6 +542,7 @@ omit = homeassistant/components/notify/rest.py homeassistant/components/notify/rocketchat.py homeassistant/components/notify/sendgrid.py + homeassistant/components/notify/simplepush.py homeassistant/components/notify/slack.py homeassistant/components/notify/smtp.py homeassistant/components/notify/stride.py diff --git a/homeassistant/components/climate/nest.py b/homeassistant/components/climate/nest.py index 0a5344fdf98..28e8020ab90 100644 --- a/homeassistant/components/climate/nest.py +++ b/homeassistant/components/climate/nest.py @@ -134,7 +134,9 @@ class NestThermostat(ClimateDevice): @property def target_temperature(self): """Return the temperature we try to reach.""" - if self._mode != NEST_MODE_HEAT_COOL and not self.is_away_mode_on: + if self._mode != NEST_MODE_HEAT_COOL and \ + self._mode != STATE_ECO and \ + not self.is_away_mode_on: return self._target_temperature return None diff --git a/homeassistant/components/cover/mqtt.py b/homeassistant/components/cover/mqtt.py index 0f31d3a9fe0..235ff5799cc 100644 --- a/homeassistant/components/cover/mqtt.py +++ b/homeassistant/components/cover/mqtt.py @@ -235,6 +235,11 @@ class MqttCover(MqttAvailability, CoverDevice): """No polling needed.""" return False + @property + def assumed_state(self): + """Return true if we do optimistic updates.""" + return self._optimistic + @property def name(self): """Return the name of the cover.""" diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 2bd7283e38e..5ebf6e8762f 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -25,7 +25,7 @@ from homeassistant.core import callback from homeassistant.helpers.translation import async_get_translations from homeassistant.loader import bind_hass -REQUIREMENTS = ['home-assistant-frontend==20180526.4'] +REQUIREMENTS = ['home-assistant-frontend==20180531.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log'] diff --git a/homeassistant/components/hue/__init__.py b/homeassistant/components/hue/__init__.py index 0aed854d4e4..251d8cba095 100644 --- a/homeassistant/components/hue/__init__.py +++ b/homeassistant/components/hue/__init__.py @@ -17,7 +17,7 @@ from .bridge import HueBridge # Loading the config flow file will register the flow from .config_flow import configured_hosts -REQUIREMENTS = ['aiohue==1.3.0'] +REQUIREMENTS = ['aiohue==1.5.0'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/hue/bridge.py b/homeassistant/components/hue/bridge.py index 5ff5e2dbf6f..d7a8dc7f730 100644 --- a/homeassistant/components/hue/bridge.py +++ b/homeassistant/components/hue/bridge.py @@ -124,9 +124,21 @@ class HueBridge(object): (group for group in self.api.groups.values() if group.name == group_name), None) - scene_id = next( - (scene.id for scene in self.api.scenes.values() - if scene.name == scene_name), None) + # The same scene name can exist in multiple groups. + # In this case, activate first scene that contains the + # the exact same light IDs as the group + scenes = [] + for scene in self.api.scenes.values(): + if scene.name == scene_name: + scenes.append(scene) + if len(scenes) == 1: + scene_id = scenes[0].id + else: + group_lights = sorted(group.lights) + for scene in scenes: + if group_lights == scene.lights: + scene_id = scene.id + break # If we can't find it, fetch latest info. if not updated and (group is None or scene_id is None): diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index 06e5f3befe4..0f536e1edfb 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -427,15 +427,18 @@ class SonosDevice(MediaPlayerDevice): self.update_volume() self._favorites = [] - for fav in self.soco.music_library.get_sonos_favorites(): - # SoCo 0.14 raises a generic Exception on invalid xml in favorites. - # Filter those out now so our list is safe to use. - try: - if fav.reference.get_uri(): - self._favorites.append(fav) - # pylint: disable=broad-except - except Exception: - _LOGGER.debug("Ignoring invalid favorite '%s'", fav.title) + # SoCo 0.14 raises a generic Exception on invalid xml in favorites. + # Filter those out now so our list is safe to use. + # pylint: disable=broad-except + try: + for fav in self.soco.music_library.get_sonos_favorites(): + try: + if fav.reference.get_uri(): + self._favorites.append(fav) + except Exception: + _LOGGER.debug("Ignoring invalid favorite '%s'", fav.title) + except Exception: + _LOGGER.debug("Ignoring invalid favorite list") def _radio_artwork(self, url): """Return the private URL with artwork for a radio stream.""" diff --git a/homeassistant/components/notify/simplepush.py b/homeassistant/components/notify/simplepush.py new file mode 100644 index 00000000000..9d5c58fc5b1 --- /dev/null +++ b/homeassistant/components/notify/simplepush.py @@ -0,0 +1,59 @@ +""" +Simplepush notification service. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/notify.simplepush/ +""" +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.notify import ( + ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) +from homeassistant.const import CONF_PASSWORD + +REQUIREMENTS = ['simplepush==1.1.4'] + +_LOGGER = logging.getLogger(__name__) + +ATTR_ENCRYPTED = 'encrypted' + +CONF_DEVICE_KEY = 'device_key' +CONF_EVENT = 'event' +CONF_SALT = 'salt' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_DEVICE_KEY): cv.string, + vol.Optional(CONF_EVENT): cv.string, + vol.Inclusive(CONF_PASSWORD, ATTR_ENCRYPTED): cv.string, + vol.Inclusive(CONF_SALT, ATTR_ENCRYPTED): cv.string, +}) + + +def get_service(hass, config, discovery_info=None): + """Get the Simplepush notification service.""" + return SimplePushNotificationService(config) + + +class SimplePushNotificationService(BaseNotificationService): + """Implementation of the notification service for Simplepush.""" + + def __init__(self, config): + """Initialize the Simplepush notification service.""" + self._device_key = config.get(CONF_DEVICE_KEY) + self._event = config.get(CONF_EVENT) + self._password = config.get(CONF_PASSWORD) + self._salt = config.get(CONF_SALT) + + def send_message(self, message='', **kwargs): + """Send a message to a Simplepush user.""" + from simplepush import send, send_encrypted + + title = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT) + + if self._password: + send_encrypted(self._device_key, self._password, self._salt, title, + message, event=self._event) + else: + send(self._device_key, title, message, event=self._event) diff --git a/homeassistant/const.py b/homeassistant/const.py index 84088c4511c..bb60a42fff9 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 70 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) diff --git a/requirements_all.txt b/requirements_all.txt index ae16651d8e9..3bed3fa9fdf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -86,7 +86,7 @@ aiodns==1.1.1 aiohttp_cors==0.7.0 # homeassistant.components.hue -aiohue==1.3.0 +aiohue==1.5.0 # homeassistant.components.sensor.imap aioimaplib==0.7.13 @@ -389,7 +389,7 @@ hipnotify==1.0.8 holidays==0.9.5 # homeassistant.components.frontend -home-assistant-frontend==20180526.4 +home-assistant-frontend==20180531.0 # homeassistant.components.homekit_controller # homekit==0.6 @@ -1186,6 +1186,9 @@ sharp_aquos_rc==0.3.2 # homeassistant.components.sensor.shodan shodan==1.7.7 +# homeassistant.components.notify.simplepush +simplepush==1.1.4 + # homeassistant.components.alarm_control_panel.simplisafe simplisafe-python==1.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ce458995d2a..1e04d3fdb03 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -35,7 +35,7 @@ aioautomatic==0.6.5 aiohttp_cors==0.7.0 # homeassistant.components.hue -aiohue==1.3.0 +aiohue==1.5.0 # homeassistant.components.notify.apns apns2==0.3.0 @@ -81,7 +81,7 @@ hbmqtt==0.9.2 holidays==0.9.5 # homeassistant.components.frontend -home-assistant-frontend==20180526.4 +home-assistant-frontend==20180531.0 # homeassistant.components.influxdb # homeassistant.components.sensor.influxdb diff --git a/tests/components/cover/test_mqtt.py b/tests/components/cover/test_mqtt.py index 23a7b32fc28..aea6398e3ae 100644 --- a/tests/components/cover/test_mqtt.py +++ b/tests/components/cover/test_mqtt.py @@ -2,8 +2,8 @@ import unittest from homeassistant.setup import setup_component -from homeassistant.const import STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN,\ - STATE_UNAVAILABLE +from homeassistant.const import STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN, \ + STATE_UNAVAILABLE, ATTR_ASSUMED_STATE import homeassistant.components.cover as cover from homeassistant.components.cover.mqtt import MqttCover @@ -40,6 +40,7 @@ class TestCoverMQTT(unittest.TestCase): state = self.hass.states.get('cover.test') self.assertEqual(STATE_UNKNOWN, state.state) + self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE)) fire_mqtt_message(self.hass, 'state-topic', '0') self.hass.block_till_done() @@ -112,6 +113,7 @@ class TestCoverMQTT(unittest.TestCase): state = self.hass.states.get('cover.test') self.assertEqual(STATE_UNKNOWN, state.state) + self.assertTrue(state.attributes.get(ATTR_ASSUMED_STATE)) cover.open_cover(self.hass, 'cover.test') self.hass.block_till_done()