diff --git a/config/configuration.yaml.example b/config/configuration.yaml.example index c99f760f21f..5acca361a30 100644 --- a/config/configuration.yaml.example +++ b/config/configuration.yaml.example @@ -159,5 +159,5 @@ scene: light.tv_back_light: on light.ceiling: state: on - color: [0.33, 0.66] + xy_color: [0.33, 0.66] brightness: 200 diff --git a/homeassistant/components/isy994.py b/homeassistant/components/isy994.py index 5c6e151e3a7..dad3cd70534 100644 --- a/homeassistant/components/isy994.py +++ b/homeassistant/components/isy994.py @@ -29,6 +29,7 @@ DISCOVER_SENSORS = "isy994.sensors" ISY = None SENSOR_STRING = 'Sensor' HIDDEN_STRING = '{HIDE ME}' +CONF_TLS_VER = 'tls' # setup logger _LOGGER = logging.getLogger(__name__) @@ -43,7 +44,6 @@ def setup(hass, config): import PyISY except ImportError: _LOGGER.error("Error while importing dependency PyISY.") - return False # pylint: disable=global-statement @@ -75,10 +75,12 @@ def setup(hass, config): global HIDDEN_STRING SENSOR_STRING = str(config[DOMAIN].get('sensor_string', SENSOR_STRING)) HIDDEN_STRING = str(config[DOMAIN].get('hidden_string', HIDDEN_STRING)) + tls_version = config[DOMAIN].get(CONF_TLS_VER, None) # 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, + tls_ver=tls_version, log=_LOGGER) if not ISY.connected: return False diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index b8ad84dc914..58c3c3cd8f2 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -89,6 +89,10 @@ ATTR_FLASH = "flash" FLASH_SHORT = "short" FLASH_LONG = "long" +# Apply an effect to the light, can be EFFECT_COLORLOOP +ATTR_EFFECT = "effect" +EFFECT_COLORLOOP = "colorloop" + LIGHT_PROFILES_FILE = "light_profiles.csv" # Maps discovered services to their platforms @@ -115,7 +119,8 @@ def is_on(hass, entity_id=None): # pylint: disable=too-many-arguments def turn_on(hass, entity_id=None, transition=None, brightness=None, - rgb_color=None, xy_color=None, profile=None, flash=None): + rgb_color=None, xy_color=None, profile=None, flash=None, + effect=None): """ Turns all or specified light on. """ data = { key: value for key, value in [ @@ -126,6 +131,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None, (ATTR_RGB_COLOR, rgb_color), (ATTR_XY_COLOR, xy_color), (ATTR_FLASH, flash), + (ATTR_EFFECT, effect), ] if value is not None } @@ -254,6 +260,10 @@ def setup(hass, config): elif dat[ATTR_FLASH] == FLASH_LONG: params[ATTR_FLASH] = FLASH_LONG + if ATTR_EFFECT in dat: + if dat[ATTR_EFFECT] == EFFECT_COLORLOOP: + params[ATTR_EFFECT] = EFFECT_COLORLOOP + for light in target_lights: light.turn_on(**params) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index b4f9416b144..c908992eb82 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -9,7 +9,8 @@ import homeassistant.util as util from homeassistant.const import CONF_HOST, DEVICE_DEFAULT_NAME from homeassistant.components.light import ( Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION, - ATTR_FLASH, FLASH_LONG, FLASH_SHORT) + ATTR_FLASH, FLASH_LONG, FLASH_SHORT, ATTR_EFFECT, + EFFECT_COLORLOOP) REQUIREMENTS = ['phue>=0.8'] MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) @@ -192,6 +193,13 @@ class HueLight(Light): else: command['alert'] = 'none' + effect = kwargs.get(ATTR_EFFECT) + + if effect == EFFECT_COLORLOOP: + command['effect'] = 'colorloop' + else: + command['effect'] = 'none' + self.bridge.set_light(self.light_id, command) def turn_off(self, **kwargs): diff --git a/homeassistant/components/media_player/cast.py b/homeassistant/components/media_player/cast.py index bf51c540e82..17ae9e6cd33 100644 --- a/homeassistant/components/media_player/cast.py +++ b/homeassistant/components/media_player/cast.py @@ -6,6 +6,8 @@ Provides functionality to interact with Cast devices on the network. WARNING: This platform is currently not working due to a changed Cast API """ +import logging + try: import pychromecast except ImportError: @@ -23,10 +25,12 @@ from homeassistant.components.media_player import ( MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO) REQUIREMENTS = ['pychromecast>=0.6.9'] +CONF_IGNORE_CEC = 'ignore_cec' CAST_SPLASH = 'https://home-assistant.io/images/cast/splash.png' SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PREVIOUS_TRACK | \ SUPPORT_NEXT_TRACK | SUPPORT_YOUTUBE +KNOWN_HOSTS = [] # pylint: disable=unused-argument @@ -37,12 +41,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): import pychromecast as pychromecast_ pychromecast = pychromecast_ - if discovery_info: + logger = logging.getLogger(__name__) + + # import CEC IGNORE attributes + ignore_cec = config.get(CONF_IGNORE_CEC, []) + if isinstance(ignore_cec, list): + pychromecast.IGNORE_CEC += ignore_cec + else: + logger.error('Chromecast conig, %s must be a list.', CONF_IGNORE_CEC) + + hosts = [] + + if discovery_info and discovery_info[0] not in KNOWN_HOSTS: hosts = [discovery_info[0]] else: hosts = (host_port[0] for host_port - in pychromecast.discover_chromecasts()) + in pychromecast.discover_chromecasts() + if host_port[0] not in KNOWN_HOSTS) casts = [] @@ -51,6 +67,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): casts.append(CastDevice(host)) except pychromecast.ChromecastConnectionError: pass + else: + KNOWN_HOSTS.append(host) add_devices(casts) diff --git a/homeassistant/components/sensor/efergy.py b/homeassistant/components/sensor/efergy.py new file mode 100644 index 00000000000..d0391e5c37b --- /dev/null +++ b/homeassistant/components/sensor/efergy.py @@ -0,0 +1,138 @@ +""" +homeassistant.components.sensor.efergy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Monitors home energy use as measured by an efergy +engage hub using its (unofficial, undocumented) API. + +Configuration: + +To use the efergy sensor you will need to add something +like the following to your config/configuration.yaml + +sensor: + platform: efergy + app_token: APP_TOKEN + utc_offset: UTC_OFFSET + monitored_variables: + - type: instant_readings + - type: budget + - type: cost + period: day + currency: $ + +Variables: + +api_key +*Required +To get a new App Token, log in to your efergy account, go +to the Settings page, click on App tokens, and click "Add token". + +utc_offset +*Required for some variables +Some variables (currently only the daily_cost) require that the +negative number of minutes your timezone is ahead/behind UTC time. + +monitored_variables +*Required +An array specifying the variables to monitor. + +period +*Optional +Some variables take a period argument. Valid options are "day", +1"week", "month", and "year" + +currency +*Optional +This is used to display the cost/period as the unit when monitoring the +cost. It should correspond to the actual currency used in your dashboard. +""" +import logging +from requests import get + +from homeassistant.helpers.entity import Entity + +_LOGGER = logging.getLogger(__name__) +_RESOURCE = 'https://engage.efergy.com/mobile_proxy/' +SENSOR_TYPES = { + 'instant_readings': ['Energy Usage', 'kW'], + 'budget': ['Energy Budget', ''], + 'cost': ['Energy Cost', ''], +} + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the efergy sensor. """ + app_token = config.get("app_token") + if not app_token: + _LOGGER.error( + "Configuration Error" + "Please make sure you have configured your app token") + return None + utc_offset = str(config.get("utc_offset")) + dev = [] + for variable in config['monitored_variables']: + if 'period' not in variable: + variable['period'] = '' + if 'currency' not in variable: + variable['currency'] = '' + if variable['type'] not in SENSOR_TYPES: + _LOGGER.error('Sensor type: "%s" does not exist', variable) + else: + dev.append(EfergySensor(variable['type'], app_token, utc_offset, + variable['period'], variable['currency'])) + + add_devices(dev) + + +# pylint: disable=too-many-instance-attributes +class EfergySensor(Entity): + """ Implements an Efergy sensor. """ + + # pylint: disable=too-many-arguments + def __init__(self, sensor_type, app_token, utc_offset, period, currency): + self._name = SENSOR_TYPES[sensor_type][0] + self.type = sensor_type + self.app_token = app_token + self.utc_offset = utc_offset + self._state = None + self.period = period + self.currency = currency + if self.type == 'cost': + self._unit_of_measurement = self.currency + '/' + self.period + else: + self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] + + @property + def name(self): + """ Returns the name. """ + return self._name + + @property + def state(self): + """ Returns the state of the device. """ + return self._state + + @property + def unit_of_measurement(self): + """ Unit of measurement of this entity, if any. """ + return self._unit_of_measurement + + def update(self): + """ Gets the efergy monitor data from the web service """ + if self.type == 'instant_readings': + url_string = _RESOURCE + 'getInstant?token=' + self.app_token + response = get(url_string) + self._state = response.json()['reading'] / 1000 + elif self.type == 'budget': + url_string = _RESOURCE + 'getBudget?token=' + self.app_token + response = get(url_string) + self._state = response.json()['status'] + elif self.type == 'cost': + url_string = _RESOURCE + 'getCost?token=' + self.app_token \ + + '&offset=' + self.utc_offset + '&period=' \ + + self.period + response = get(url_string) + self._state = response.json()['sum'] + else: + self._state = 'Unknown' diff --git a/homeassistant/components/switch/tellstick.py b/homeassistant/components/switch/tellstick.py index 84f5d2b31d5..6f9070d28fb 100644 --- a/homeassistant/components/switch/tellstick.py +++ b/homeassistant/components/switch/tellstick.py @@ -3,6 +3,12 @@ homeassistant.components.switch.tellstick ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for Tellstick switches. + +Because the tellstick sends its actions via radio and from most +receivers it's impossible to know if the signal was received or not. +Therefore you can configure the switch to try to send each signal repeatedly +with the config parameter signal_repetitions (default is 1). +signal_repetitions: 3 """ import logging @@ -11,6 +17,8 @@ from homeassistant.const import ATTR_FRIENDLY_NAME from homeassistant.helpers.entity import ToggleEntity import tellcore.constants as tellcore_constants +SINGAL_REPETITIONS = 1 + # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): @@ -22,6 +30,8 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): "Failed to import tellcore") return + signal_repetitions = config.get('signal_repetitions', SINGAL_REPETITIONS) + core = telldus.TelldusCore() switches_and_lights = core.devices() @@ -29,7 +39,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): for switch in switches_and_lights: if not switch.methods(tellcore_constants.TELLSTICK_DIM): - switches.append(TellstickSwitchDevice(switch)) + switches.append(TellstickSwitchDevice(switch, signal_repetitions)) add_devices_callback(switches) @@ -39,9 +49,10 @@ class TellstickSwitchDevice(ToggleEntity): last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON | tellcore_constants.TELLSTICK_TURNOFF) - def __init__(self, tellstick): + def __init__(self, tellstick, signal_repetitions): self.tellstick = tellstick self.state_attr = {ATTR_FRIENDLY_NAME: tellstick.name} + self.signal_repetitions = signal_repetitions @property def name(self): @@ -63,8 +74,10 @@ class TellstickSwitchDevice(ToggleEntity): def turn_on(self, **kwargs): """ Turns the switch on. """ - self.tellstick.turn_on() + for _ in range(self.signal_repetitions): + self.tellstick.turn_on() def turn_off(self, **kwargs): """ Turns the switch off. """ - self.tellstick.turn_off() + for _ in range(self.signal_repetitions): + self.tellstick.turn_off() diff --git a/requirements.txt b/requirements.txt index 0ee24587cf2..83780721c2c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ phue>=0.8 ledcontroller>=1.0.7 # Chromecast bindings (media_player.cast) -pychromecast>=0.6.6 +pychromecast>=0.6.9 # Keyboard (keyboard) pyuserinput>=0.1.9 @@ -39,7 +39,7 @@ python-nest>=2.3.1 pydispatcher>=2.0.5 # ISY994 bindings (*.isy994) -PyISY>=1.0.2 +PyISY>=1.0.5 # PSutil (sensor.systemmonitor) psutil>=3.0.0 diff --git a/scripts/homeassistant.daemon b/scripts/homeassistant.daemon new file mode 100755 index 00000000000..4c504557dec --- /dev/null +++ b/scripts/homeassistant.daemon @@ -0,0 +1,88 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: homeassistant +# Required-Start: $local_fs $network $named $time $syslog +# Required-Stop: $local_fs $network $named $time $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Description: Home\ Assistant +### END INIT INFO + +# /etc/init.d Service Script for Home Assistant +# Created with: https://gist.github.com/naholyr/4275302#file-new-service-sh +# +# Installation: +# 1) Populate RUNAS and RUNDIR folders +# 2) Create Log -- sudo touch /var/log/homeassistant.log +# 3) Set Log Ownership -- sudo chown USER:GROUP /var/log/homeassistant.log +# 4) Create PID File -- sudo touch /var/run/homeassistant.pid +# 5) Set PID File Ownership -- sudo chown USER:GROUP /var/run/homeassistant.pid +# 6) Install init.d script -- cp homeassistant.daemon /etc/init.d/homeassistant +# 7) Setup HA for Auto Start -- sudo update-rc.d homeassistant defaults +# 8) Run HA -- sudo service homeassistant start +# +# After installation, HA should start automatically. If HA does not start, +# check the log file output for errors. (/var/log/homeassistant.log) +# +# For this script, it is assumed that you are using a local Virtual Environment +# per the install directions as http://home-assistant.io +# If you are not, the SCRIPT variable must be modified to point to the correct +# Python environment. + +SCRIPT="./bin/python -m homeassistant" +RUNAS= +RUNDIR= +PIDFILE=/var/run/homeassistant.pid +LOGFILE=/var/log/homeassistant.log + +start() { + if [ -f /var/run/$PIDNAME ] && kill -0 $(cat /var/run/$PIDNAME); then + echo 'Service already running' >&2 + return 1 + fi + echo 'Starting service…' >&2 + local CMD="cd $RUNDIR; $SCRIPT &> \"$LOGFILE\" & echo \$!" + su -c "$CMD" $RUNAS > "$PIDFILE" + echo 'Service started' >&2 +} + +stop() { + if [ ! -f "$PIDFILE" ] || ! kill -0 $(cat "$PIDFILE"); then + echo 'Service not running' >&2 + return 1 + fi + echo 'Stopping service…' >&2 + kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE" + echo 'Service stopped' >&2 +} + +uninstall() { + echo -n "Are you really sure you want to uninstall this service? That cannot be undone. [yes|No] " + local SURE + read SURE + if [ "$SURE" = "yes" ]; then + stop + rm -f "$PIDFILE" + echo "Notice: log file is not be removed: '$LOGFILE'" >&2 + update-rc.d -f homeassistant remove + rm -fv "$0" + fi +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + uninstall) + uninstall + ;; + retart) + stop + start + ;; + *) + echo "Usage: $0 {start|stop|restart|uninstall}" +esac