Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	homeassistant/components/light/zwave.py
#	homeassistant/components/switch/zwave.py
This commit is contained in:
Stefan Jonasson 2016-01-26 20:31:42 +01:00
commit 5b6371ecda
33 changed files with 1306 additions and 245 deletions

View File

@ -78,6 +78,7 @@ omit =
homeassistant/components/light/blinksticklight.py homeassistant/components/light/blinksticklight.py
homeassistant/components/light/hue.py homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py homeassistant/components/light/hyperion.py
homeassistant/components/light/lifx.py
homeassistant/components/light/limitlessled.py homeassistant/components/light/limitlessled.py
homeassistant/components/media_player/cast.py homeassistant/components/media_player/cast.py
homeassistant/components/media_player/denon.py homeassistant/components/media_player/denon.py

View File

@ -29,9 +29,13 @@ import time
import logging import logging
from homeassistant.const import STATE_HOME, STATE_NOT_HOME, STATE_ON, STATE_OFF from homeassistant.const import STATE_HOME, STATE_NOT_HOME, STATE_ON, STATE_OFF
import homeassistant.loader as loader
from homeassistant.helpers import validate_config from homeassistant.helpers import validate_config
from homeassistant.helpers.event_decorators import \
track_state_change, track_time_change
from homeassistant.helpers.service import service
import homeassistant.components as core import homeassistant.components as core
from homeassistant.components import device_tracker
from homeassistant.components import light
# The domain of your component. Should be equal to the name of your component # The domain of your component. Should be equal to the name of your component
DOMAIN = "example" DOMAIN = "example"
@ -39,11 +43,14 @@ DOMAIN = "example"
# List of component names (string) your component depends upon # List of component names (string) your component depends upon
# We depend on group because group will be loaded after all the components that # We depend on group because group will be loaded after all the components that
# initialize devices have been setup. # initialize devices have been setup.
DEPENDENCIES = ['group'] DEPENDENCIES = ['group', 'device_tracker', 'light']
# Configuration key for the entity id we are targetting # Configuration key for the entity id we are targetting
CONF_TARGET = 'target' CONF_TARGET = 'target'
# Variable for storing configuration parameters
TARGET_ID = None
# Name of the service that we expose # Name of the service that we expose
SERVICE_FLASH = 'flash' SERVICE_FLASH = 'flash'
@ -53,84 +60,89 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config): def setup(hass, config):
""" Setup example component. """ """ Setup example component. """
global TARGET_ID
# Validate that all required config options are given # Validate that all required config options are given
if not validate_config(config, {DOMAIN: [CONF_TARGET]}, _LOGGER): if not validate_config(config, {DOMAIN: [CONF_TARGET]}, _LOGGER):
return False return False
target_id = config[DOMAIN][CONF_TARGET] TARGET_ID = config[DOMAIN][CONF_TARGET]
# Validate that the target entity id exists # Validate that the target entity id exists
if hass.states.get(target_id) is None: if hass.states.get(TARGET_ID) is None:
_LOGGER.error("Target entity id %s does not exist", target_id) _LOGGER.error("Target entity id %s does not exist",
TARGET_ID)
# Tell the bootstrapper that we failed to initialize # Tell the bootstrapper that we failed to initialize and clear the
# stored target id so our functions don't run.
TARGET_ID = None
return False return False
# We will use the component helper methods to check the states. # Tell the bootstrapper that we initialized successfully
device_tracker = loader.get_component('device_tracker')
light = loader.get_component('light')
def track_devices(entity_id, old_state, new_state):
""" Called when the group.all devices change state. """
# If anyone comes home and the core is not on, turn it on.
if new_state.state == STATE_HOME and not core.is_on(hass, target_id):
core.turn_on(hass, target_id)
# If all people leave the house and the core is on, turn it off
elif new_state.state == STATE_NOT_HOME and core.is_on(hass, target_id):
core.turn_off(hass, target_id)
# Register our track_devices method to receive state changes of the
# all tracked devices group.
hass.states.track_change(
device_tracker.ENTITY_ID_ALL_DEVICES, track_devices)
def wake_up(now):
""" Turn it on in the morning if there are people home and
it is not already on. """
if device_tracker.is_on(hass) and not core.is_on(hass, target_id):
_LOGGER.info('People home at 7AM, turning it on')
core.turn_on(hass, target_id)
# Register our wake_up service to be called at 7AM in the morning
hass.track_time_change(wake_up, hour=7, minute=0, second=0)
def all_lights_off(entity_id, old_state, new_state):
""" If all lights turn off, turn off. """
if core.is_on(hass, target_id):
_LOGGER.info('All lights have been turned off, turning it off')
core.turn_off(hass, target_id)
# Register our all_lights_off method to be called when all lights turn off
hass.states.track_change(
light.ENTITY_ID_ALL_LIGHTS, all_lights_off, STATE_ON, STATE_OFF)
def flash_service(call):
""" Service that will turn the target off for 10 seconds
if on and vice versa. """
if core.is_on(hass, target_id):
core.turn_off(hass, target_id)
time.sleep(10)
core.turn_on(hass, target_id)
else:
core.turn_on(hass, target_id)
time.sleep(10)
core.turn_off(hass, target_id)
# Register our service with HASS.
hass.services.register(DOMAIN, SERVICE_FLASH, flash_service)
# Tells the bootstrapper that the component was successfully initialized
return True return True
@track_state_change(device_tracker.ENTITY_ID_ALL_DEVICES)
def track_devices(hass, entity_id, old_state, new_state):
""" Called when the group.all devices change state. """
# If the target id is not set, return
if not TARGET_ID:
return
# If anyone comes home and the entity is not on, turn it on.
if new_state.state == STATE_HOME and not core.is_on(hass, TARGET_ID):
core.turn_on(hass, TARGET_ID)
# If all people leave the house and the entity is on, turn it off
elif new_state.state == STATE_NOT_HOME and core.is_on(hass, TARGET_ID):
core.turn_off(hass, TARGET_ID)
@track_time_change(hour=7, minute=0, second=0)
def wake_up(hass, now):
"""
Turn it on in the morning (7 AM) if there are people home and
it is not already on.
"""
if not TARGET_ID:
return
if device_tracker.is_on(hass) and not core.is_on(hass, TARGET_ID):
_LOGGER.info('People home at 7AM, turning it on')
core.turn_on(hass, TARGET_ID)
@track_state_change(light.ENTITY_ID_ALL_LIGHTS, STATE_ON, STATE_OFF)
def all_lights_off(hass, entity_id, old_state, new_state):
""" If all lights turn off, turn off. """
if not TARGET_ID:
return
if core.is_on(hass, TARGET_ID):
_LOGGER.info('All lights have been turned off, turning it off')
core.turn_off(hass, TARGET_ID)
@service(DOMAIN, SERVICE_FLASH)
def flash_service(hass, call):
"""
Service that will turn the target off for 10 seconds if on and vice versa.
"""
if not TARGET_ID:
return
if core.is_on(hass, TARGET_ID):
core.turn_off(hass, TARGET_ID)
time.sleep(10)
core.turn_on(hass, TARGET_ID)
else:
core.turn_on(hass, TARGET_ID)
time.sleep(10)
core.turn_off(hass, TARGET_ID)

View File

@ -24,6 +24,7 @@ import homeassistant.config as config_util
import homeassistant.loader as loader import homeassistant.loader as loader
import homeassistant.components as core_components import homeassistant.components as core_components
import homeassistant.components.group as group import homeassistant.components.group as group
from homeassistant.helpers import event_decorators, service
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.const import ( from homeassistant.const import (
__version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE, __version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
@ -199,6 +200,10 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
_LOGGER.info('Home Assistant core initialized') _LOGGER.info('Home Assistant core initialized')
# give event decorators access to HASS
event_decorators.HASS = hass
service.HASS = hass
# Setup the components # Setup the components
for domain in loader.load_order_components(components): for domain in loader.load_order_components(components):
_setup_component(hass, domain, config) _setup_component(hass, domain, config)

View File

@ -10,7 +10,7 @@ import logging
from datetime import timedelta from datetime import timedelta
from homeassistant.components import sun from homeassistant.components import sun
from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.helpers.event import track_sunrise, track_sunset
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
DEPENDENCIES = ['sun'] DEPENDENCIES = ['sun']
@ -47,9 +47,9 @@ def trigger(hass, config, action):
# Do something to call action # Do something to call action
if event == EVENT_SUNRISE: if event == EVENT_SUNRISE:
trigger_sunrise(hass, action, offset) track_sunrise(hass, action, offset)
else: else:
trigger_sunset(hass, action, offset) track_sunset(hass, action, offset)
return True return True
@ -125,44 +125,6 @@ def if_action(hass, config):
return time_if return time_if
def trigger_sunrise(hass, action, offset):
""" Trigger action at next sun rise. """
def next_rise():
""" Returns next sunrise. """
next_time = sun.next_rising_utc(hass) + offset
while next_time < dt_util.utcnow():
next_time = next_time + timedelta(days=1)
return next_time
def sunrise_automation_listener(now):
""" Called when it's time for action. """
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
action()
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
def trigger_sunset(hass, action, offset):
""" Trigger action at next sun set. """
def next_set():
""" Returns next sunrise. """
next_time = sun.next_setting_utc(hass) + offset
while next_time < dt_util.utcnow():
next_time = next_time + timedelta(days=1)
return next_time
def sunset_automation_listener(now):
""" Called when it's time for action. """
track_point_in_utc_time(hass, sunset_automation_listener, next_set())
action()
track_point_in_utc_time(hass, sunset_automation_listener, next_set())
def _parse_offset(raw_offset): def _parse_offset(raw_offset):
if raw_offset is None: if raw_offset is None:
return timedelta(0) return timedelta(0)

View File

@ -62,10 +62,16 @@ def setup(hass, config):
lights = sorted(hass.states.entity_ids('light')) lights = sorted(hass.states.entity_ids('light'))
switches = sorted(hass.states.entity_ids('switch')) switches = sorted(hass.states.entity_ids('switch'))
media_players = sorted(hass.states.entity_ids('media_player')) media_players = sorted(hass.states.entity_ids('media_player'))
group.setup_group(hass, 'living room', [lights[2], lights[1], switches[0], group.Group(hass, 'living room', [
media_players[1]]) lights[2], lights[1], switches[0], media_players[1],
group.setup_group(hass, 'bedroom', [lights[0], switches[1], 'scene.romantic_lights'])
media_players[0]]) group.Group(hass, 'bedroom', [lights[0], switches[1],
media_players[0]])
group.Group(hass, 'Rooms', [
'group.living_room', 'group.bedroom',
'scene.romantic_lights', 'rollershutter.kitchen_window',
'rollershutter.living_room_window',
], view=True)
# Setup scripts # Setup scripts
bootstrap.setup_component( bootstrap.setup_component(

View File

@ -229,7 +229,7 @@ class DeviceTracker(object):
""" Initializes group for all tracked devices. """ """ Initializes group for all tracked devices. """
entity_ids = (dev.entity_id for dev in self.devices.values() entity_ids = (dev.entity_id for dev in self.devices.values()
if dev.track) if dev.track)
self.group = group.setup_group( self.group = group.Group(
self.hass, GROUP_NAME_ALL_DEVICES, entity_ids, False) self.hass, GROUP_NAME_ALL_DEVICES, entity_ids, False)
def update_stale(self, now): def update_stale(self, now):

View File

@ -19,7 +19,7 @@ from homeassistant.components.device_tracker import DOMAIN
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pynetgear==0.3.1'] REQUIREMENTS = ['pynetgear==0.3.2']
def get_scanner(hass, config): def get_scanner(hass, config):

View File

@ -21,7 +21,9 @@ _LOGGER = logging.getLogger(__name__)
FRONTEND_URLS = [ FRONTEND_URLS = [
URL_ROOT, '/logbook', '/history', '/map', '/devService', '/devState', URL_ROOT, '/logbook', '/history', '/map', '/devService', '/devState',
'/devEvent', '/devInfo', '/devTemplate', '/states'] '/devEvent', '/devInfo', '/devTemplate',
re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)'),
]
_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE) _FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)

View File

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """ """ DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "1003c31441ec44b3db84b49980f736a7" VERSION = "d3490eb2c77bfe127e09c8c1ad148580"

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit 2ecd6a818443780dc5d0d981996d165218b2b094 Subproject commit 8524390ae5b6a28f9bf3472cd9643765de7a3425

View File

@ -13,13 +13,18 @@ from homeassistant.helpers.entity import (
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, ATTR_ENTITY_ID, STATE_ON, STATE_OFF,
STATE_HOME, STATE_NOT_HOME, STATE_OPEN, STATE_CLOSED, STATE_HOME, STATE_NOT_HOME, STATE_OPEN, STATE_CLOSED,
STATE_UNKNOWN) STATE_UNKNOWN, CONF_NAME, CONF_ICON)
DOMAIN = "group" DOMAIN = 'group'
ENTITY_ID_FORMAT = DOMAIN + ".{}" ENTITY_ID_FORMAT = DOMAIN + '.{}'
ATTR_AUTO = "auto" CONF_ENTITIES = 'entities'
CONF_VIEW = 'view'
ATTR_AUTO = 'auto'
ATTR_ORDER = 'order'
ATTR_VIEW = 'view'
# List of ON/OFF state tuples for groupable states # List of ON/OFF state tuples for groupable states
_GROUP_TYPES = [(STATE_ON, STATE_OFF), (STATE_HOME, STATE_NOT_HOME), _GROUP_TYPES = [(STATE_ON, STATE_OFF), (STATE_HOME, STATE_NOT_HOME),
@ -103,10 +108,20 @@ def get_entity_ids(hass, entity_id, domain_filter=None):
def setup(hass, config): def setup(hass, config):
""" Sets up all groups found definded in the configuration. """ """ Sets up all groups found definded in the configuration. """
for name, entity_ids in config.get(DOMAIN, {}).items(): for object_id, conf in config.get(DOMAIN, {}).items():
if not isinstance(conf, dict):
conf = {CONF_ENTITIES: conf}
name = conf.get(CONF_NAME, object_id)
entity_ids = conf.get(CONF_ENTITIES)
icon = conf.get(CONF_ICON)
view = conf.get(CONF_VIEW)
if isinstance(entity_ids, str): if isinstance(entity_ids, str):
entity_ids = [ent.strip() for ent in entity_ids.split(",")] entity_ids = [ent.strip() for ent in entity_ids.split(",")]
setup_group(hass, name, entity_ids)
Group(hass, name, entity_ids, icon=icon, view=view,
object_id=object_id)
return True return True
@ -114,14 +129,19 @@ def setup(hass, config):
class Group(Entity): class Group(Entity):
""" Tracks a group of entity ids. """ """ Tracks a group of entity ids. """
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes, too-many-arguments
def __init__(self, hass, name, entity_ids=None, user_defined=True): def __init__(self, hass, name, entity_ids=None, user_defined=True,
icon=None, view=False, object_id=None):
self.hass = hass self.hass = hass
self._name = name self._name = name
self._state = STATE_UNKNOWN self._state = STATE_UNKNOWN
self.user_defined = user_defined self._order = len(hass.states.entity_ids(DOMAIN))
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, name, hass=hass) self._user_defined = user_defined
self._icon = icon
self._view = view
self.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, object_id or name, hass=hass)
self.tracking = [] self.tracking = []
self.group_on = None self.group_on = None
self.group_off = None self.group_off = None
@ -143,12 +163,25 @@ class Group(Entity):
def state(self): def state(self):
return self._state return self._state
@property
def icon(self):
return self._icon
@property
def hidden(self):
return not self._user_defined or self._view
@property @property
def state_attributes(self): def state_attributes(self):
return { data = {
ATTR_ENTITY_ID: self.tracking, ATTR_ENTITY_ID: self.tracking,
ATTR_AUTO: not self.user_defined, ATTR_ORDER: self._order,
} }
if not self._user_defined:
data[ATTR_AUTO] = True
if self._view:
data[ATTR_VIEW] = True
return data
def update_tracked_entity_ids(self, entity_ids): def update_tracked_entity_ids(self, entity_ids):
""" Update the tracked entity IDs. """ """ Update the tracked entity IDs. """
@ -219,10 +252,3 @@ class Group(Entity):
for ent_id in self.tracking for ent_id in self.tracking
if tr_state.entity_id != ent_id): if tr_state.entity_id != ent_id):
self._state = group_off self._state = group_off
def setup_group(hass, name, entity_ids, user_defined=True):
""" Sets up a group state that is the combined state of
several states. Supports ON/OFF and DEVICE_HOME/DEVICE_NOT_HOME. """
return Group(hass, name, entity_ids, user_defined)

View File

@ -0,0 +1,271 @@
"""
homeassistant.components.light.lifx
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LIFX platform that implements lights
Configuration:
light:
# platform name
platform: lifx
# optional server address
# only needed if using more than one network interface
# (omit if you are unsure)
server: 192.168.1.3
# optional broadcast address, set to reach all LIFX bulbs
# (omit if you are unsure)
broadcast: 192.168.1.255
"""
# pylint: disable=missing-docstring
import logging
import colorsys
from homeassistant.helpers.event import track_time_change
from homeassistant.components.light import \
(Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_COLOR_TEMP, ATTR_TRANSITION)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['liffylights==0.9.0']
DEPENDENCIES = []
CONF_SERVER = "server" # server address configuration item
CONF_BROADCAST = "broadcast" # broadcast address configuration item
SHORT_MAX = 65535 # short int maximum
BYTE_MAX = 255 # byte maximum
TEMP_MIN = 2500 # lifx minimum temperature
TEMP_MAX = 9000 # lifx maximum temperature
TEMP_MIN_HASS = 154 # home assistant minimum temperature
TEMP_MAX_HASS = 500 # home assistant maximum temperature
class LIFX():
def __init__(self, add_devices_callback,
server_addr=None, broadcast_addr=None):
import liffylights
self._devices = []
self._add_devices_callback = add_devices_callback
self._liffylights = liffylights.LiffyLights(
self.on_device,
self.on_power,
self.on_color,
server_addr,
broadcast_addr)
def find_bulb(self, ipaddr):
bulb = None
for device in self._devices:
if device.ipaddr == ipaddr:
bulb = device
break
return bulb
# pylint: disable=too-many-arguments
def on_device(self, ipaddr, name, power, hue, sat, bri, kel):
bulb = self.find_bulb(ipaddr)
if bulb is None:
bulb = LIFXLight(self._liffylights, ipaddr, name,
power, hue, sat, bri, kel)
self._devices.append(bulb)
self._add_devices_callback([bulb])
# pylint: disable=too-many-arguments
def on_color(self, ipaddr, hue, sat, bri, kel):
bulb = self.find_bulb(ipaddr)
if bulb is not None:
bulb.set_color(hue, sat, bri, kel)
bulb.update_ha_state()
def on_power(self, ipaddr, power):
bulb = self.find_bulb(ipaddr)
if bulb is not None:
bulb.set_power(power)
bulb.update_ha_state()
# pylint: disable=unused-argument
def poll(self, now):
self.probe()
def probe(self, address=None):
self._liffylights.probe(address)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Set up platform. """
server_addr = config.get(CONF_SERVER, None)
broadcast_addr = config.get(CONF_BROADCAST, None)
lifx_library = LIFX(add_devices_callback, server_addr, broadcast_addr)
# register our poll service
track_time_change(hass, lifx_library.poll, second=10)
lifx_library.probe()
def convert_rgb_to_hsv(rgb):
""" Convert HASS RGB values to HSV values. """
red, green, blue = [_ / BYTE_MAX for _ in rgb]
hue, saturation, brightness = colorsys.rgb_to_hsv(red, green, blue)
return [int(hue * SHORT_MAX),
int(saturation * SHORT_MAX),
int(brightness * SHORT_MAX)]
# pylint: disable=too-many-instance-attributes
class LIFXLight(Light):
""" Provides LIFX light. """
# pylint: disable=too-many-arguments
def __init__(self, liffy, ipaddr, name, power, hue,
saturation, brightness, kelvin):
_LOGGER.debug("LIFXLight: %s %s",
ipaddr, name)
self._liffylights = liffy
self._ip = ipaddr
self.set_name(name)
self.set_power(power)
self.set_color(hue, saturation, brightness, kelvin)
@property
def should_poll(self):
""" No polling needed for LIFX light. """
return False
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
def ipaddr(self):
""" Returns the ip of the device. """
return self._ip
@property
def rgb_color(self):
""" Returns RGB value. """
_LOGGER.debug("rgb_color: [%d %d %d]",
self._rgb[0], self._rgb[1], self._rgb[2])
return self._rgb
@property
def brightness(self):
""" Returns brightness of this light between 0..255. """
brightness = int(self._bri / (BYTE_MAX + 1))
_LOGGER.debug("brightness: %d",
brightness)
return brightness
@property
def color_temp(self):
""" Returns color temperature. """
temperature = int(TEMP_MIN_HASS + (TEMP_MAX_HASS - TEMP_MIN_HASS) *
(self._kel - TEMP_MIN) / (TEMP_MAX - TEMP_MIN))
_LOGGER.debug("color_temp: %d",
temperature)
return temperature
@property
def is_on(self):
""" True if device is on. """
_LOGGER.debug("is_on: %d",
self._power)
return self._power != 0
def turn_on(self, **kwargs):
""" Turn the device on. """
if ATTR_TRANSITION in kwargs:
fade = kwargs[ATTR_TRANSITION] * 1000
else:
fade = 0
if ATTR_RGB_COLOR in kwargs:
hue, saturation, brightness = \
convert_rgb_to_hsv(kwargs[ATTR_RGB_COLOR])
else:
hue = self._hue
saturation = self._sat
brightness = self._bri
if ATTR_BRIGHTNESS in kwargs:
brightness = kwargs[ATTR_BRIGHTNESS] * (BYTE_MAX + 1)
else:
brightness = self._bri
if ATTR_COLOR_TEMP in kwargs:
kelvin = int(((TEMP_MAX - TEMP_MIN) *
(kwargs[ATTR_COLOR_TEMP] - TEMP_MIN_HASS) /
(TEMP_MAX_HASS - TEMP_MIN_HASS)) + TEMP_MIN)
else:
kelvin = self._kel
_LOGGER.debug("turn_on: %s (%d) %d %d %d %d %d",
self._ip, self._power,
hue, saturation, brightness, kelvin, fade)
if self._power == 0:
self._liffylights.set_power(self._ip, 65535, fade)
self._liffylights.set_color(self._ip, hue, saturation,
brightness, kelvin, fade)
def turn_off(self, **kwargs):
""" Turn the device off. """
if ATTR_TRANSITION in kwargs:
fade = kwargs[ATTR_TRANSITION] * 1000
else:
fade = 0
_LOGGER.debug("turn_off: %s %d",
self._ip, fade)
self._liffylights.set_power(self._ip, 0, fade)
def set_name(self, name):
""" Set name. """
self._name = name
def set_power(self, power):
""" Set power state value. """
_LOGGER.debug("set_power: %d",
power)
self._power = (power != 0)
def set_color(self, hue, sat, bri, kel):
""" Set color state values. """
self._hue = hue
self._sat = sat
self._bri = bri
self._kel = kel
red, green, blue = colorsys.hsv_to_rgb(hue / SHORT_MAX,
sat / SHORT_MAX,
bri / SHORT_MAX)
red = int(red * BYTE_MAX)
green = int(green * BYTE_MAX)
blue = int(blue * BYTE_MAX)
_LOGGER.debug("set_color: %d %d %d %d [%d %d %d]",
hue, sat, bri, kel, red, green, blue)
self._rgb = [red, green, blue]

View File

@ -149,8 +149,8 @@ class RfxtrxLight(Light):
self._brightness = ((brightness + 4) * 100 // 255 - 1) self._brightness = ((brightness + 4) * 100 // 255 - 1)
if hasattr(self, '_event') and self._event: if hasattr(self, '_event') and self._event:
self._event.device.send_on(rfxtrx.RFXOBJECT.transport, self._event.device.send_dim(rfxtrx.RFXOBJECT.transport,
self._brightness) self._brightness)
self._brightness = (self._brightness * 255 // 100) self._brightness = (self._brightness * 255 // 100)
self._state = True self._state = True

View File

@ -12,6 +12,7 @@ from threading import Timer
from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.components.light import (Light, ATTR_BRIGHTNESS) from homeassistant.components.light import (Light, ATTR_BRIGHTNESS)
from homeassistant.util import slugify
import homeassistant.components.zwave as zwave import homeassistant.components.zwave as zwave
@ -91,13 +92,33 @@ class ZwaveDimmer(Light):
""" No polling needed for a light. """ """ No polling needed for a light. """
return False return False
@property
def unique_id(self):
""" Returns a unique id. """
return "ZWAVE-{}-{}".format(self._node.node_id, self._value.object_id)
@property @property
def name(self): def name(self):
""" Returns the name of the device if any. """ """ Returns the name of the device. """
name = self._node.name or "{} {}".format( name = self._node.name or "{} {}".format(
self._node.manufacturer_name, self._node.product_name) self._node.manufacturer_name, self._node.product_name)
return "{} {} {}".format(name, self._node.node_id, self._value.label) return "{} {}".format(name, self._value.label)
@property
def entity_id(self):
""" Returns the entity_id of the device if any.
The entity_id contains node_id and value instance id
to not collide with other entity_ids"""
entity_id = "light.{}_{}".format(slugify(self.name),
self._node.node_id)
# Add the instance id if there is more than one instance for the value
if self._value.instance > 1:
return "{}_{}".format(entity_id, self._value.instance)
return entity_id
@property @property
def brightness(self): def brightness(self):

View File

@ -202,9 +202,12 @@ class SqueezeBoxDevice(MediaPlayerDevice):
""" Image url of current playing media. """ """ Image url of current playing media. """
if 'artwork_url' in self._status: if 'artwork_url' in self._status:
media_url = self._status['artwork_url'] media_url = self._status['artwork_url']
else: elif 'id' in self._status:
media_url = ('/music/{track_id}/cover.jpg').format( media_url = ('/music/{track_id}/cover.jpg').format(
track_id=self._status["id"]) track_id=self._status['id'])
else:
media_url = ('/music/current/cover.jpg?player={player}').format(
player=self._id)
base_url = 'http://{server}:{port}/'.format( base_url = 'http://{server}:{port}/'.format(
server=self._lms.host, server=self._lms.host,

View File

@ -14,6 +14,7 @@ from homeassistant.helpers.event import track_point_in_time
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.components.zwave as zwave import homeassistant.components.zwave as zwave
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.util import slugify
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF, ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF,
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION)
@ -109,6 +110,21 @@ class ZWaveSensor(Entity):
return "{} {} {}".format(name, self._node.node_id, self._value.label) return "{} {} {}".format(name, self._node.node_id, self._value.label)
@property
def entity_id(self):
""" Returns the entity_id of the device if any.
The entity_id contains node_id and value instance id
to not collide with other entity_ids"""
entity_id = "sensor.{}_{}".format(slugify(self.name),
self._node.node_id)
# Add the instance id if there is more than one instance for the value
if self._value.instance > 1:
return "{}_{}".format(entity_id, self._value.instance)
return entity_id
@property @property
def state(self): def state(self):
""" Returns the state of the sensor. """ """ Returns the state of the sensor. """

View File

@ -11,7 +11,7 @@ import logging
from homeassistant.components.switch import SwitchDevice from homeassistant.components.switch import SwitchDevice
DEFAULT_NAME = "Orvibo S20 Switch" DEFAULT_NAME = "Orvibo S20 Switch"
REQUIREMENTS = ['orvibo==1.1.0'] REQUIREMENTS = ['orvibo==1.1.1']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -9,6 +9,7 @@ Zwave platform that handles simple binary switches.
import homeassistant.components.zwave as zwave import homeassistant.components.zwave as zwave
from homeassistant.components.switch import SwitchDevice from homeassistant.components.switch import SwitchDevice
from homeassistant.util import slugify
# pylint: disable=unused-argument # pylint: disable=unused-argument
@ -55,13 +56,33 @@ class ZwaveSwitch(SwitchDevice):
""" No polling needed for a demo switch. """ """ No polling needed for a demo switch. """
return False return False
@property
def unique_id(self):
""" Returns a unique id. """
return "ZWAVE-{}-{}".format(self._node.node_id, self._value.object_id)
@property @property
def name(self): def name(self):
""" Returns the name of the device if any. """ """ Returns the name of the device. """
name = self._node.name or "{} {}".format( name = self._node.name or "{} {}".format(
self._node.manufacturer_name, self._node.product_name) self._node.manufacturer_name, self._node.product_name)
return "{} {} {}".format(name, self._node.node_id, self._value.label) return "{} {}".format(name, self._value.label)
@property
def entity_id(self):
""" Returns the entity_id of the device if any.
The entity_id contains node_id and value instance id
to not collide with other entity_ids"""
entity_id = "switch.{}_{}".format(slugify(self.name),
self._node.node_id)
# Add the instance id if there is more than one instance for the value
if self._value.instance > 1:
return "{}_{}".format(entity_id, self._value.instance)
return entity_id
@property @property
def is_on(self): def is_on(self):

View File

@ -27,6 +27,7 @@ SCAN_INTERVAL = 60
SERVICE_SET_AWAY_MODE = "set_away_mode" SERVICE_SET_AWAY_MODE = "set_away_mode"
SERVICE_SET_TEMPERATURE = "set_temperature" SERVICE_SET_TEMPERATURE = "set_temperature"
SERVICE_SET_FAN_MODE = "set_fan_mode"
STATE_HEAT = "heat" STATE_HEAT = "heat"
STATE_COOL = "cool" STATE_COOL = "cool"
@ -34,6 +35,7 @@ STATE_IDLE = "idle"
ATTR_CURRENT_TEMPERATURE = "current_temperature" ATTR_CURRENT_TEMPERATURE = "current_temperature"
ATTR_AWAY_MODE = "away_mode" ATTR_AWAY_MODE = "away_mode"
ATTR_FAN = "fan"
ATTR_MAX_TEMP = "max_temp" ATTR_MAX_TEMP = "max_temp"
ATTR_MIN_TEMP = "min_temp" ATTR_MIN_TEMP = "min_temp"
ATTR_TEMPERATURE_LOW = "target_temp_low" ATTR_TEMPERATURE_LOW = "target_temp_low"
@ -69,59 +71,100 @@ def set_temperature(hass, temperature, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_TEMPERATURE, data) hass.services.call(DOMAIN, SERVICE_SET_TEMPERATURE, data)
def set_fan_mode(hass, fan_mode, entity_id=None):
""" Turn all or specified thermostat fan mode on. """
data = {
ATTR_FAN: fan_mode
}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_SET_FAN_MODE, data)
# pylint: disable=too-many-branches
def setup(hass, config): def setup(hass, config):
""" Setup thermostats. """ """ Setup thermostats. """
component = EntityComponent(_LOGGER, DOMAIN, hass, component = EntityComponent(_LOGGER, DOMAIN, hass,
SCAN_INTERVAL, DISCOVERY_PLATFORMS) SCAN_INTERVAL, DISCOVERY_PLATFORMS)
component.setup(config) component.setup(config)
def thermostat_service(service):
""" Handles calls to the services. """
# Convert the entity ids to valid light ids
target_thermostats = component.extract_from_service(service)
if service.service == SERVICE_SET_AWAY_MODE:
away_mode = service.data.get(ATTR_AWAY_MODE)
if away_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_AWAY_MODE, ATTR_AWAY_MODE)
elif away_mode:
for thermostat in target_thermostats:
thermostat.turn_away_mode_on()
else:
for thermostat in target_thermostats:
thermostat.turn_away_mode_off()
elif service.service == SERVICE_SET_TEMPERATURE:
temperature = util.convert(
service.data.get(ATTR_TEMPERATURE), float)
if temperature is None:
return
for thermostat in target_thermostats:
thermostat.set_temperature(convert(
temperature, hass.config.temperature_unit,
thermostat.unit_of_measurement))
for thermostat in target_thermostats:
thermostat.update_ha_state(True)
descriptions = load_yaml_config_file( descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml')) os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register( def away_mode_set_service(service):
DOMAIN, SERVICE_SET_AWAY_MODE, thermostat_service, """ Set away mode on target thermostats """
descriptions.get(SERVICE_SET_AWAY_MODE))
target_thermostats = component.extract_from_service(service)
away_mode = service.data.get(ATTR_AWAY_MODE)
if away_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_AWAY_MODE, ATTR_AWAY_MODE)
return
for thermostat in target_thermostats:
if away_mode:
thermostat.turn_away_mode_on()
else:
thermostat.turn_away_mode_off()
thermostat.update_ha_state(True)
hass.services.register( hass.services.register(
DOMAIN, SERVICE_SET_TEMPERATURE, thermostat_service, DOMAIN, SERVICE_SET_AWAY_MODE, away_mode_set_service,
descriptions.get(SERVICE_SET_AWAY_MODE))
def temperature_set_service(service):
""" Set temperature on the target thermostats """
target_thermostats = component.extract_from_service(service)
temperature = util.convert(
service.data.get(ATTR_TEMPERATURE), float)
if temperature is None:
return
for thermostat in target_thermostats:
thermostat.set_temperature(convert(
temperature, hass.config.temperature_unit,
thermostat.unit_of_measurement))
thermostat.update_ha_state(True)
hass.services.register(
DOMAIN, SERVICE_SET_TEMPERATURE, temperature_set_service,
descriptions.get(SERVICE_SET_TEMPERATURE)) descriptions.get(SERVICE_SET_TEMPERATURE))
def fan_mode_set_service(service):
""" Set fan mode on target thermostats """
target_thermostats = component.extract_from_service(service)
fan_mode = service.data.get(ATTR_FAN)
if fan_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_FAN_MODE, ATTR_FAN)
return
for thermostat in target_thermostats:
if fan_mode:
thermostat.turn_fan_on()
else:
thermostat.turn_fan_off()
thermostat.update_ha_state(True)
hass.services.register(
DOMAIN, SERVICE_SET_FAN_MODE, fan_mode_set_service,
descriptions.get(SERVICE_SET_FAN_MODE))
return True return True
@ -164,6 +207,10 @@ class ThermostatDevice(Entity):
if is_away is not None: if is_away is not None:
data[ATTR_AWAY_MODE] = STATE_ON if is_away else STATE_OFF data[ATTR_AWAY_MODE] = STATE_ON if is_away else STATE_OFF
is_fan_on = self.is_fan_on
if is_fan_on is not None:
data[ATTR_FAN] = STATE_ON if is_fan_on else STATE_OFF
device_attr = self.device_state_attributes device_attr = self.device_state_attributes
if device_attr is not None: if device_attr is not None:
@ -209,6 +256,14 @@ class ThermostatDevice(Entity):
""" """
return None return None
@property
def is_fan_on(self):
"""
Returns if the fan is on
Return None if not available.
"""
return None
def set_temperate(self, temperature): def set_temperate(self, temperature):
""" Set new target temperature. """ """ Set new target temperature. """
pass pass
@ -221,6 +276,14 @@ class ThermostatDevice(Entity):
""" Turns away mode off. """ """ Turns away mode off. """
pass pass
def turn_fan_on(self):
""" Turns fan on. """
pass
def turn_fan_off(self):
""" Turns fan off. """
pass
@property @property
def min_temp(self): def min_temp(self):
""" Return minimum temperature. """ """ Return minimum temperature. """

View File

@ -66,7 +66,6 @@ class NestThermostat(ThermostatDevice):
return { return {
"humidity": self.device.humidity, "humidity": self.device.humidity,
"target_humidity": self.device.target_humidity, "target_humidity": self.device.target_humidity,
"fan": self.device.fan,
"mode": self.device.mode "mode": self.device.mode
} }
@ -143,6 +142,19 @@ class NestThermostat(ThermostatDevice):
""" Turns away off. """ """ Turns away off. """
self.structure.away = False self.structure.away = False
@property
def is_fan_on(self):
""" Returns whether the fan is on """
return self.device.fan
def turn_fan_on(self):
""" Turns fan on """
self.device.fan = True
def turn_fan_off(self):
""" Turns fan off """
self.device.fan = False
@property @property
def min_temp(self): def min_temp(self):
""" Identifies min_temp in Nest API or defaults if not available. """ """ Identifies min_temp in Nest API or defaults if not available. """

View File

@ -22,3 +22,15 @@ set_temperature:
temperature: temperature:
description: New target temperature for thermostat description: New target temperature for thermostat
example: 25 example: 25
set_fan_mode:
description: Turn fan on/off for a thermostat
fields:
entity_id:
description: Name(s) of entities to change
example: 'thermostat.nest'
fan:
description: New value of fan mode
example: true

View File

@ -28,7 +28,7 @@ DISCOVER_SWITCHES = 'verisure.switches'
DISCOVER_ALARMS = 'verisure.alarm_control_panel' DISCOVER_ALARMS = 'verisure.alarm_control_panel'
DEPENDENCIES = ['alarm_control_panel'] DEPENDENCIES = ['alarm_control_panel']
REQUIREMENTS = ['vsure==0.4.5'] REQUIREMENTS = ['vsure==0.4.8']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -10,6 +10,7 @@ MATCH_ALL = '*'
DEVICE_DEFAULT_NAME = "Unnamed Device" DEVICE_DEFAULT_NAME = "Unnamed Device"
# #### CONFIG #### # #### CONFIG ####
CONF_ICON = "icon"
CONF_LATITUDE = "latitude" CONF_LATITUDE = "latitude"
CONF_LONGITUDE = "longitude" CONF_LONGITUDE = "longitude"
CONF_TEMPERATURE_UNIT = "temperature_unit" CONF_TEMPERATURE_UNIT = "temperature_unit"

View File

@ -1,6 +1,7 @@
""" """
Helpers for listening to events Helpers for listening to events
""" """
from datetime import timedelta
import functools as ft import functools as ft
from ..util import dt as dt_util from ..util import dt as dt_util
@ -95,6 +96,54 @@ def track_point_in_utc_time(hass, action, point_in_time):
return point_in_time_listener return point_in_time_listener
def track_sunrise(hass, action, offset=None):
"""
Adds a listener that will fire a specified offset from sunrise daily.
"""
from homeassistant.components import sun
offset = offset or timedelta()
def next_rise():
""" Returns next sunrise. """
next_time = sun.next_rising_utc(hass) + offset
while next_time < dt_util.utcnow():
next_time = next_time + timedelta(days=1)
return next_time
def sunrise_automation_listener(now):
""" Called when it's time for action. """
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
action()
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
def track_sunset(hass, action, offset=None):
"""
Adds a listener that will fire a specified offset from sunset daily.
"""
from homeassistant.components import sun
offset = offset or timedelta()
def next_set():
""" Returns next sunrise. """
next_time = sun.next_setting_utc(hass) + offset
while next_time < dt_util.utcnow():
next_time = next_time + timedelta(days=1)
return next_time
def sunset_automation_listener(now):
""" Called when it's time for action. """
track_point_in_utc_time(hass, sunset_automation_listener, next_set())
action()
track_point_in_utc_time(hass, sunset_automation_listener, next_set())
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
def track_utc_time_change(hass, action, year=None, month=None, day=None, def track_utc_time_change(hass, action, year=None, month=None, day=None,
hour=None, minute=None, second=None, local=False): hour=None, minute=None, second=None, local=False):

View File

@ -0,0 +1,76 @@
""" Event Decorators for custom components """
import functools
from homeassistant.helpers import event
HASS = None
def track_state_change(entity_ids, from_state=None, to_state=None):
""" Decorator factory to track state changes for entity id """
def track_state_change_decorator(action):
""" Decorator to track state changes """
event.track_state_change(HASS, entity_ids,
functools.partial(action, HASS),
from_state, to_state)
return action
return track_state_change_decorator
def track_sunrise(offset=None):
""" Decorator factory to track sunrise events """
def track_sunrise_decorator(action):
""" Decorator to track sunrise events """
event.track_sunrise(HASS,
functools.partial(action, HASS),
offset)
return action
return track_sunrise_decorator
def track_sunset(offset=None):
""" Decorator factory to track sunset events """
def track_sunset_decorator(action):
""" Decorator to track sunset events """
event.track_sunset(HASS,
functools.partial(action, HASS),
offset)
return action
return track_sunset_decorator
# pylint: disable=too-many-arguments
def track_time_change(year=None, month=None, day=None, hour=None, minute=None,
second=None):
""" Decorator factory to track time changes """
def track_time_change_decorator(action):
""" Decorator to track time changes """
event.track_time_change(HASS,
functools.partial(action, HASS),
year, month, day, hour, minute, second)
return action
return track_time_change_decorator
# pylint: disable=too-many-arguments
def track_utc_time_change(year=None, month=None, day=None, hour=None,
minute=None, second=None):
""" Decorator factory to track time changes """
def track_utc_time_change_decorator(action):
""" Decorator to track time changes """
event.track_utc_time_change(HASS,
functools.partial(action, HASS),
year, month, day, hour, minute, second)
return action
return track_utc_time_change_decorator

View File

@ -1,10 +1,13 @@
"""Service calling related helpers.""" """Service calling related helpers."""
import functools
import logging import logging
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.helpers.entity import split_entity_id from homeassistant.helpers.entity import split_entity_id
from homeassistant.loader import get_component from homeassistant.loader import get_component
HASS = None
CONF_SERVICE = 'service' CONF_SERVICE = 'service'
CONF_SERVICE_ENTITY_ID = 'entity_id' CONF_SERVICE_ENTITY_ID = 'entity_id'
CONF_SERVICE_DATA = 'data' CONF_SERVICE_DATA = 'data'
@ -12,6 +15,18 @@ CONF_SERVICE_DATA = 'data'
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def service(domain, service_name):
""" Decorator factory to register a service """
def register_service_decorator(action):
""" Decorator to register a service """
HASS.services.register(domain, service_name,
functools.partial(action, HASS))
return action
return register_service_decorator
def call_from_config(hass, config, blocking=False): def call_from_config(hass, config, blocking=False):
"""Call a service based on a config hash.""" """Call a service based on a config hash."""
if not isinstance(config, dict) or CONF_SERVICE not in config: if not isinstance(config, dict) or CONF_SERVICE not in config:
@ -19,7 +34,7 @@ def call_from_config(hass, config, blocking=False):
return return
try: try:
domain, service = split_entity_id(config[CONF_SERVICE]) domain, service_name = split_entity_id(config[CONF_SERVICE])
except ValueError: except ValueError:
_LOGGER.error('Invalid service specified: %s', config[CONF_SERVICE]) _LOGGER.error('Invalid service specified: %s', config[CONF_SERVICE])
return return
@ -41,21 +56,21 @@ def call_from_config(hass, config, blocking=False):
elif entity_id is not None: elif entity_id is not None:
service_data[ATTR_ENTITY_ID] = entity_id service_data[ATTR_ENTITY_ID] = entity_id
hass.services.call(domain, service, service_data, blocking) hass.services.call(domain, service_name, service_data, blocking)
def extract_entity_ids(hass, service): def extract_entity_ids(hass, service_call):
""" """
Helper method to extract a list of entity ids from a service call. Helper method to extract a list of entity ids from a service call.
Will convert group entity ids to the entity ids it represents. Will convert group entity ids to the entity ids it represents.
""" """
if not (service.data and ATTR_ENTITY_ID in service.data): if not (service_call.data and ATTR_ENTITY_ID in service_call.data):
return [] return []
group = get_component('group') group = get_component('group')
# Entity ID attr can be a list or a string # Entity ID attr can be a list or a string
service_ent_id = service.data[ATTR_ENTITY_ID] service_ent_id = service_call.data[ATTR_ENTITY_ID]
if isinstance(service_ent_id, str): if isinstance(service_ent_id, str):
return group.expand_entity_ids(hass, [service_ent_id]) return group.expand_entity_ids(hass, [service_ent_id])

View File

@ -19,7 +19,7 @@ fuzzywuzzy==0.8.0
pyicloud==0.7.2 pyicloud==0.7.2
# homeassistant.components.device_tracker.netgear # homeassistant.components.device_tracker.netgear
pynetgear==0.3.1 pynetgear==0.3.2
# homeassistant.components.device_tracker.nmap_tracker # homeassistant.components.device_tracker.nmap_tracker
python-nmap==0.4.3 python-nmap==0.4.3
@ -54,6 +54,9 @@ blinkstick==1.1.7
# homeassistant.components.light.hue # homeassistant.components.light.hue
phue==0.8 phue==0.8
# homeassistant.components.light.lifx
liffylights==0.9.0
# homeassistant.components.light.limitlessled # homeassistant.components.light.limitlessled
limitlessled==1.0.0 limitlessled==1.0.0
@ -190,7 +193,7 @@ https://github.com/rkabadi/pyedimax/archive/365301ce3ff26129a7910c501ead09ea625f
hikvision==0.4 hikvision==0.4
# homeassistant.components.switch.orvibo # homeassistant.components.switch.orvibo
orvibo==1.1.0 orvibo==1.1.1
# homeassistant.components.switch.wemo # homeassistant.components.switch.wemo
pywemo==0.3.8 pywemo==0.3.8
@ -211,7 +214,7 @@ proliphix==0.1.0
radiotherm==1.2 radiotherm==1.2
# homeassistant.components.verisure # homeassistant.components.verisure
vsure==0.4.5 vsure==0.4.8
# homeassistant.components.zwave # homeassistant.components.zwave
pydispatcher==2.0.5 pydispatcher==2.0.5

View File

@ -9,7 +9,8 @@ import unittest
import logging import logging
import homeassistant.core as ha import homeassistant.core as ha
from homeassistant.const import STATE_ON, STATE_OFF, STATE_HOME, STATE_UNKNOWN from homeassistant.const import (
STATE_ON, STATE_OFF, STATE_HOME, STATE_UNKNOWN, ATTR_ICON, ATTR_HIDDEN)
import homeassistant.components.group as group import homeassistant.components.group as group
@ -39,7 +40,7 @@ class TestComponentsGroup(unittest.TestCase):
def test_setup_group_with_mixed_groupable_states(self): def test_setup_group_with_mixed_groupable_states(self):
""" Try to setup a group with mixed groupable states """ """ Try to setup a group with mixed groupable states """
self.hass.states.set('device_tracker.Paulus', STATE_HOME) self.hass.states.set('device_tracker.Paulus', STATE_HOME)
group.setup_group( group.Group(
self.hass, 'person_and_light', self.hass, 'person_and_light',
['light.Bowl', 'device_tracker.Paulus']) ['light.Bowl', 'device_tracker.Paulus'])
@ -50,7 +51,7 @@ class TestComponentsGroup(unittest.TestCase):
def test_setup_group_with_a_non_existing_state(self): def test_setup_group_with_a_non_existing_state(self):
""" Try to setup a group with a non existing state """ """ Try to setup a group with a non existing state """
grp = group.setup_group( grp = group.Group(
self.hass, 'light_and_nothing', self.hass, 'light_and_nothing',
['light.Bowl', 'non.existing']) ['light.Bowl', 'non.existing'])
@ -60,7 +61,7 @@ class TestComponentsGroup(unittest.TestCase):
self.hass.states.set('cast.living_room', "Plex") self.hass.states.set('cast.living_room', "Plex")
self.hass.states.set('cast.bedroom', "Netflix") self.hass.states.set('cast.bedroom', "Netflix")
grp = group.setup_group( grp = group.Group(
self.hass, 'chromecasts', self.hass, 'chromecasts',
['cast.living_room', 'cast.bedroom']) ['cast.living_room', 'cast.bedroom'])
@ -68,7 +69,7 @@ class TestComponentsGroup(unittest.TestCase):
def test_setup_empty_group(self): def test_setup_empty_group(self):
""" Try to setup an empty group. """ """ Try to setup an empty group. """
grp = group.setup_group(self.hass, 'nothing', []) grp = group.Group(self.hass, 'nothing', [])
self.assertEqual(STATE_UNKNOWN, grp.state) self.assertEqual(STATE_UNKNOWN, grp.state)
@ -80,7 +81,7 @@ class TestComponentsGroup(unittest.TestCase):
group_state = self.hass.states.get(self.group_entity_id) group_state = self.hass.states.get(self.group_entity_id)
self.assertEqual(STATE_ON, group_state.state) self.assertEqual(STATE_ON, group_state.state)
self.assertTrue(group_state.attributes[group.ATTR_AUTO]) self.assertTrue(group_state.attributes.get(group.ATTR_AUTO))
def test_group_turns_off_if_all_off(self): def test_group_turns_off_if_all_off(self):
""" """
@ -199,17 +200,35 @@ class TestComponentsGroup(unittest.TestCase):
self.hass, self.hass,
{ {
group.DOMAIN: { group.DOMAIN: {
'second_group': 'light.Bowl, ' + self.group_entity_id 'second_group': {
'entities': 'light.Bowl, ' + self.group_entity_id,
'icon': 'mdi:work',
'view': True,
},
'test_group': 'hello.world,sensor.happy',
} }
})) }))
group_state = self.hass.states.get( group_state = self.hass.states.get(
group.ENTITY_ID_FORMAT.format('second_group')) group.ENTITY_ID_FORMAT.format('second_group'))
self.assertEqual(STATE_ON, group_state.state) self.assertEqual(STATE_ON, group_state.state)
self.assertEqual(set((self.group_entity_id, 'light.bowl')), self.assertEqual(set((self.group_entity_id, 'light.bowl')),
set(group_state.attributes['entity_id'])) set(group_state.attributes['entity_id']))
self.assertFalse(group_state.attributes[group.ATTR_AUTO]) self.assertIsNone(group_state.attributes.get(group.ATTR_AUTO))
self.assertEqual('mdi:work',
group_state.attributes.get(ATTR_ICON))
self.assertTrue(group_state.attributes.get(group.ATTR_VIEW))
self.assertTrue(group_state.attributes.get(ATTR_HIDDEN))
group_state = self.hass.states.get(
group.ENTITY_ID_FORMAT.format('test_group'))
self.assertEqual(STATE_UNKNOWN, group_state.state)
self.assertEqual(set(('sensor.happy', 'hello.world')),
set(group_state.attributes['entity_id']))
self.assertIsNone(group_state.attributes.get(group.ATTR_AUTO))
self.assertIsNone(group_state.attributes.get(ATTR_ICON))
self.assertIsNone(group_state.attributes.get(group.ATTR_VIEW))
self.assertIsNone(group_state.attributes.get(ATTR_HIDDEN))
def test_groups_get_unique_names(self): def test_groups_get_unique_names(self):
""" Two groups with same name should both have a unique entity id. """ """ Two groups with same name should both have a unique entity id. """

View File

@ -1,8 +1,8 @@
""" """
tests.components.automation.test_location tests.components.test_zone
±±±~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ±±±~~~~~~~~~~~~~~~~~~~~~~~
Tests location automation. Tests zone component.
""" """
import unittest import unittest
@ -11,8 +11,8 @@ from homeassistant.components import zone
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
class TestAutomationZone(unittest.TestCase): class TestComponentZone(unittest.TestCase):
""" Test the event automation. """ """ Test the zone component. """
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()

View File

@ -9,8 +9,11 @@ Tests event helpers.
import unittest import unittest
from datetime import datetime from datetime import datetime
from astral import Astral
import homeassistant.core as ha import homeassistant.core as ha
from homeassistant.helpers.event import * from homeassistant.helpers.event import *
from homeassistant.components import sun
class TestEventHelpers(unittest.TestCase): class TestEventHelpers(unittest.TestCase):
@ -121,6 +124,98 @@ class TestEventHelpers(unittest.TestCase):
self.assertEqual(1, len(specific_runs)) self.assertEqual(1, len(specific_runs))
self.assertEqual(3, len(wildcard_runs)) self.assertEqual(3, len(wildcard_runs))
def test_track_sunrise(self):
""" Test track sunrise """
latitude = 32.87336
longitude = 117.22743
# setup sun component
self.hass.config.latitude = latitude
self.hass.config.longitude = longitude
sun.setup(self.hass, {sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
# get next sunrise/sunset
astral = Astral()
utc_now = dt_util.utcnow()
mod = -1
while True:
next_rising = (astral.sunrise_utc(utc_now +
timedelta(days=mod), latitude, longitude))
if next_rising > utc_now:
break
mod += 1
# track sunrise
runs = []
track_sunrise(self.hass, lambda: runs.append(1))
offset_runs = []
offset = timedelta(minutes=30)
track_sunrise(self.hass, lambda: offset_runs.append(1), offset)
# run tests
self._send_time_changed(next_rising - offset)
self.hass.pool.block_till_done()
self.assertEqual(0, len(runs))
self.assertEqual(0, len(offset_runs))
self._send_time_changed(next_rising)
self.hass.pool.block_till_done()
self.assertEqual(1, len(runs))
self.assertEqual(0, len(offset_runs))
self._send_time_changed(next_rising + offset)
self.hass.pool.block_till_done()
self.assertEqual(2, len(runs))
self.assertEqual(1, len(offset_runs))
def test_track_sunset(self):
""" Test track sunset """
latitude = 32.87336
longitude = 117.22743
# setup sun component
self.hass.config.latitude = latitude
self.hass.config.longitude = longitude
sun.setup(self.hass, {sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
# get next sunrise/sunset
astral = Astral()
utc_now = dt_util.utcnow()
mod = -1
while True:
next_setting = (astral.sunset_utc(utc_now +
timedelta(days=mod), latitude, longitude))
if next_setting > utc_now:
break
mod += 1
# track sunset
runs = []
track_sunset(self.hass, lambda: runs.append(1))
offset_runs = []
offset = timedelta(minutes=30)
track_sunset(self.hass, lambda: offset_runs.append(1), offset)
# run tests
self._send_time_changed(next_setting - offset)
self.hass.pool.block_till_done()
self.assertEqual(0, len(runs))
self.assertEqual(0, len(offset_runs))
self._send_time_changed(next_setting)
self.hass.pool.block_till_done()
self.assertEqual(1, len(runs))
self.assertEqual(0, len(offset_runs))
self._send_time_changed(next_setting + offset)
self.hass.pool.block_till_done()
self.assertEqual(2, len(runs))
self.assertEqual(1, len(offset_runs))
def _send_time_changed(self, now): def _send_time_changed(self, now):
""" Send a time changed event. """ """ Send a time changed event. """
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now}) self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now})

View File

@ -0,0 +1,200 @@
"""
tests.helpers.test_event_decorators
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests event decorator helpers.
"""
# pylint: disable=protected-access,too-many-public-methods
# pylint: disable=too-few-public-methods
import unittest
from datetime import datetime, timedelta
from astral import Astral
import homeassistant.core as ha
import homeassistant.util.dt as dt_util
from homeassistant.helpers import event_decorators
from homeassistant.helpers.event_decorators import (
track_time_change, track_utc_time_change, track_state_change,
track_sunrise, track_sunset)
from homeassistant.components import sun
class TestEventDecoratorHelpers(unittest.TestCase):
"""
Tests the Home Assistant event helpers.
"""
def setUp(self): # pylint: disable=invalid-name
""" things to be run when tests are started. """
self.hass = ha.HomeAssistant()
self.hass.states.set("light.Bowl", "on")
self.hass.states.set("switch.AC", "off")
event_decorators.HASS = self.hass
def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """
self.hass.stop()
def test_track_sunrise(self):
""" Test track sunrise decorator """
latitude = 32.87336
longitude = 117.22743
# setup sun component
self.hass.config.latitude = latitude
self.hass.config.longitude = longitude
sun.setup(self.hass, {sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
# get next sunrise/sunset
astral = Astral()
utc_now = dt_util.utcnow()
mod = -1
while True:
next_rising = (astral.sunrise_utc(utc_now +
timedelta(days=mod), latitude, longitude))
if next_rising > utc_now:
break
mod += 1
# use decorator
runs = []
decor = track_sunrise()
decor(lambda x: runs.append(1))
offset_runs = []
offset = timedelta(minutes=30)
decor = track_sunrise(offset)
decor(lambda x: offset_runs.append(1))
# run tests
self._send_time_changed(next_rising - offset)
self.hass.pool.block_till_done()
self.assertEqual(0, len(runs))
self.assertEqual(0, len(offset_runs))
self._send_time_changed(next_rising)
self.hass.pool.block_till_done()
self.assertEqual(1, len(runs))
self.assertEqual(0, len(offset_runs))
self._send_time_changed(next_rising + offset)
self.hass.pool.block_till_done()
self.assertEqual(2, len(runs))
self.assertEqual(1, len(offset_runs))
def test_track_sunset(self):
""" Test track sunset decorator """
latitude = 32.87336
longitude = 117.22743
# setup sun component
self.hass.config.latitude = latitude
self.hass.config.longitude = longitude
sun.setup(self.hass, {sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
# get next sunrise/sunset
astral = Astral()
utc_now = dt_util.utcnow()
mod = -1
while True:
next_setting = (astral.sunset_utc(utc_now +
timedelta(days=mod), latitude, longitude))
if next_setting > utc_now:
break
mod += 1
# use decorator
runs = []
decor = track_sunset()
decor(lambda x: runs.append(1))
offset_runs = []
offset = timedelta(minutes=30)
decor = track_sunset(offset)
decor(lambda x: offset_runs.append(1))
# run tests
self._send_time_changed(next_setting - offset)
self.hass.pool.block_till_done()
self.assertEqual(0, len(runs))
self.assertEqual(0, len(offset_runs))
self._send_time_changed(next_setting)
self.hass.pool.block_till_done()
self.assertEqual(1, len(runs))
self.assertEqual(0, len(offset_runs))
self._send_time_changed(next_setting + offset)
self.hass.pool.block_till_done()
self.assertEqual(2, len(runs))
self.assertEqual(1, len(offset_runs))
def test_track_time_change(self):
""" Test tracking time change. """
wildcard_runs = []
specific_runs = []
decor = track_time_change()
decor(lambda x, y: wildcard_runs.append(1))
decor = track_utc_time_change(second=[0, 30])
decor(lambda x, y: specific_runs.append(1))
self._send_time_changed(datetime(2014, 5, 24, 12, 0, 0))
self.hass.pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(1, len(wildcard_runs))
self._send_time_changed(datetime(2014, 5, 24, 12, 0, 15))
self.hass.pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(2, len(wildcard_runs))
self._send_time_changed(datetime(2014, 5, 24, 12, 0, 30))
self.hass.pool.block_till_done()
self.assertEqual(2, len(specific_runs))
self.assertEqual(3, len(wildcard_runs))
def test_track_state_change(self):
""" Test track_state_change. """
# 2 lists to track how often our callbacks get called
specific_runs = []
wildcard_runs = []
decor = track_state_change('light.Bowl', 'on', 'off')
decor(lambda a, b, c, d: specific_runs.append(1))
decor = track_state_change('light.Bowl', ha.MATCH_ALL, ha.MATCH_ALL)
decor(lambda a, b, c, d: wildcard_runs.append(1))
# Set same state should not trigger a state change/listener
self.hass.states.set('light.Bowl', 'on')
self.hass.pool.block_till_done()
self.assertEqual(0, len(specific_runs))
self.assertEqual(0, len(wildcard_runs))
# State change off -> on
self.hass.states.set('light.Bowl', 'off')
self.hass.pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(1, len(wildcard_runs))
# State change off -> off
self.hass.states.set('light.Bowl', 'off', {"some_attr": 1})
self.hass.pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(2, len(wildcard_runs))
# State change off -> on
self.hass.states.set('light.Bowl', 'on')
self.hass.pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(3, len(wildcard_runs))
def _send_time_changed(self, now):
""" Send a time changed event. """
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now})

View File

@ -24,10 +24,23 @@ class TestServiceHelpers(unittest.TestCase):
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.calls = mock_service(self.hass, 'test_domain', 'test_service') self.calls = mock_service(self.hass, 'test_domain', 'test_service')
service.HASS = self.hass
def tearDown(self): # pylint: disable=invalid-name def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """ """ Stop down stuff we started. """
self.hass.stop() self.hass.stop()
def test_service(self):
""" Test service registration decorator. """
runs = []
decor = service.service('test', 'test')
decor(lambda x, y: runs.append(1))
self.hass.services.call('test', 'test')
self.hass.pool.block_till_done()
self.assertEqual(1, len(runs))
def test_split_entity_string(self): def test_split_entity_string(self):
service.call_from_config(self.hass, { service.call_from_config(self.hass, {
'service': 'test_domain.test_service', 'service': 'test_domain.test_service',
@ -74,7 +87,7 @@ class TestServiceHelpers(unittest.TestCase):
self.hass.states.set('light.Ceiling', STATE_OFF) self.hass.states.set('light.Ceiling', STATE_OFF)
self.hass.states.set('light.Kitchen', STATE_OFF) self.hass.states.set('light.Kitchen', STATE_OFF)
loader.get_component('group').setup_group( loader.get_component('group').Group(
self.hass, 'test', ['light.Ceiling', 'light.Kitchen']) self.hass, 'test', ['light.Ceiling', 'light.Kitchen'])
call = ha.ServiceCall('light', 'turn_on', call = ha.ServiceCall('light', 'turn_on',