mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # homeassistant/components/light/zwave.py # homeassistant/components/switch/zwave.py
This commit is contained in:
commit
5b6371ecda
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
@ -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)
|
|
||||||
|
271
homeassistant/components/light/lifx.py
Normal file
271
homeassistant/components/light/lifx.py
Normal 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]
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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,
|
||||||
|
@ -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. """
|
||||||
|
@ -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__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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. """
|
||||||
|
@ -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. """
|
||||||
|
@ -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
|
||||||
|
@ -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__)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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):
|
||||||
|
76
homeassistant/helpers/event_decorators.py
Normal file
76
homeassistant/helpers/event_decorators.py
Normal 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
|
@ -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])
|
||||||
|
@ -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
|
||||||
|
@ -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. """
|
||||||
|
@ -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()
|
||||||
|
@ -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})
|
||||||
|
200
tests/helpers/test_event_decorators.py
Normal file
200
tests/helpers/test_event_decorators.py
Normal 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})
|
@ -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',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user