Merge pull request #1594 from balloob/dev

0.16
This commit is contained in:
Paulus Schoutsen 2016-03-26 01:02:12 -07:00
commit 763a9ce8c6
108 changed files with 3398 additions and 1965 deletions

View File

@ -26,11 +26,13 @@ omit =
homeassistant/components/modbus.py
homeassistant/components/*/modbus.py
homeassistant/components/tellstick.py
homeassistant/components/*/tellstick.py
homeassistant/components/tellduslive.py
homeassistant/components/*/tellduslive.py
homeassistant/components/vera.py
homeassistant/components/*/vera.py
homeassistant/components/ecobee.py
@ -57,9 +59,6 @@ omit =
homeassistant/components/nest.py
homeassistant/components/*/nest.py
homeassistant/components/rfxtrx.py
homeassistant/components/*/rfxtrx.py
homeassistant/components/rpi_gpio.py
homeassistant/components/*/rpi_gpio.py
@ -108,9 +107,12 @@ omit =
homeassistant/components/media_player/snapcast.py
homeassistant/components/media_player/sonos.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/media_player/yamaha.py
homeassistant/components/notify/free_mobile.py
homeassistant/components/notify/googlevoice.py
homeassistant/components/notify/gntp.py
homeassistant/components/notify/instapush.py
homeassistant/components/notify/message_bird.py
homeassistant/components/notify/nma.py
homeassistant/components/notify/pushbullet.py
homeassistant/components/notify/pushetta.py
@ -149,6 +151,7 @@ omit =
homeassistant/components/sensor/torque.py
homeassistant/components/sensor/transmission.py
homeassistant/components/sensor/twitch.py
homeassistant/components/sensor/uber.py
homeassistant/components/sensor/worldclock.py
homeassistant/components/switch/arest.py
homeassistant/components/switch/dlink.py
@ -156,8 +159,10 @@ omit =
homeassistant/components/switch/hikvisioncam.py
homeassistant/components/switch/mystrom.py
homeassistant/components/switch/orvibo.py
homeassistant/components/switch/pulseaudio_loopback.py
homeassistant/components/switch/rest.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wake_on_lan.py
homeassistant/components/thermostat/heatmiser.py
homeassistant/components/thermostat/homematic.py
homeassistant/components/thermostat/proliphix.py

View File

@ -10,16 +10,15 @@
**Checklist:**
- [ ] Local tests with `tox` run successfully.
- [ ] TravisCI does not fail. **Your PR cannot be merged unless CI is green!**
- [ ] [Fork is up to date][fork] and was rebased on the `dev` branch before creating the PR.
- [ ] Commits have been [squashed][squash].
- If code communicates with devices:
If code communicates with devices:
- [ ] Local tests with `tox` run successfully. **Your PR cannot be merged unless tests pass**
- [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]).
- [ ] New dependencies are only imported inside functions that use them ([example][ex-import]).
- [ ] New dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`.
- [ ] New files were added to `.coveragerc`.
- If the code does not interact with devices:
If the code does not interact with devices:
- [ ] Local tests with `tox` run successfully. **Your PR cannot be merged unless tests pass**
- [ ] Tests have been added to verify that the new code works.
[fork]: http://stackoverflow.com/a/7244456

View File

@ -83,11 +83,13 @@ def setup(hass, config):
hass.http.register_path(
'GET', URL_API_COMPONENTS, _handle_get_api_components)
# /error_log
hass.http.register_path('GET', URL_API_ERROR_LOG,
_handle_get_api_error_log)
hass.http.register_path('POST', URL_API_LOG_OUT, _handle_post_api_log_out)
# /template
hass.http.register_path('POST', URL_API_TEMPLATE,
_handle_post_api_template)

View File

@ -9,8 +9,9 @@ import logging
from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.const import CONF_PLATFORM
from homeassistant.components import logbook
from homeassistant.helpers.service import call_from_config
from homeassistant.helpers.service import validate_service_call
from homeassistant.helpers import extract_domain_configs
from homeassistant.helpers.service import (call_from_config,
validate_service_call)
DOMAIN = 'automation'
@ -35,30 +36,17 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
"""Setup the automation."""
config_key = DOMAIN
found = 1
for config_key in extract_domain_configs(config, DOMAIN):
conf = config[config_key]
while config_key in config:
# Check for one block syntax
if isinstance(config[config_key], dict):
config_block = _migrate_old_config(config[config_key])
name = config_block.get(CONF_ALIAS, config_key)
if not isinstance(conf, list):
conf = [conf]
for list_no, config_block in enumerate(conf):
name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key,
list_no))
_setup_automation(hass, config_block, name, config)
# Check for multiple block syntax
elif isinstance(config[config_key], list):
for list_no, config_block in enumerate(config[config_key]):
name = config_block.get(CONF_ALIAS,
"{}, {}".format(config_key, list_no))
_setup_automation(hass, config_block, name, config)
# Any scalar value is incorrect
else:
_LOGGER.error('Error in config in section %s.', config_key)
found += 1
config_key = "{} {}".format(DOMAIN, found)
return True
@ -97,40 +85,6 @@ def _get_action(hass, config, name):
return action
def _migrate_old_config(config):
"""Migrate old configuration to new."""
if CONF_PLATFORM not in config:
return config
_LOGGER.warning(
'You are using an old configuration format. Please upgrade: '
'https://home-assistant.io/components/automation/')
new_conf = {
CONF_TRIGGER: dict(config),
CONF_CONDITION: config.get('if', []),
CONF_ACTION: dict(config),
}
for cat, key, new_key in (('trigger', 'mqtt_topic', 'topic'),
('trigger', 'mqtt_payload', 'payload'),
('trigger', 'state_entity_id', 'entity_id'),
('trigger', 'state_before', 'before'),
('trigger', 'state_after', 'after'),
('trigger', 'state_to', 'to'),
('trigger', 'state_from', 'from'),
('trigger', 'state_hours', 'hours'),
('trigger', 'state_minutes', 'minutes'),
('trigger', 'state_seconds', 'seconds'),
('action', 'execute_service', 'service'),
('action', 'service_entity_id', 'entity_id'),
('action', 'service_data', 'data')):
if key in new_conf[cat]:
new_conf[cat][new_key] = new_conf[cat].pop(key)
return new_conf
def _process_if(hass, config, p_config, action):
"""Process if checks."""
cond_type = p_config.get(CONF_CONDITION_TYPE,

View File

@ -55,8 +55,13 @@ def _check_template(hass, value_template):
"""Check if result of template is true."""
try:
value = template.render(hass, value_template, {})
except TemplateError:
_LOGGER.exception('Error parsing template')
except TemplateError as ex:
if ex.args and ex.args[0].startswith(
"UndefinedError: 'None' has no attribute"):
# Common during HA startup - so just a warning
_LOGGER.warning(ex)
else:
_LOGGER.error(ex)
return False
return value.lower() == 'true'

View File

@ -9,7 +9,8 @@ import logging
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.const import (STATE_ON, STATE_OFF)
from homeassistant.components import (bloomsky, mysensors, zwave, wemo, wink)
from homeassistant.components import (
bloomsky, mysensors, zwave, vera, wemo, wink)
DOMAIN = 'binary_sensor'
SCAN_INTERVAL = 30
@ -37,6 +38,7 @@ DISCOVERY_PLATFORMS = {
bloomsky.DISCOVER_BINARY_SENSORS: 'bloomsky',
mysensors.DISCOVER_BINARY_SENSORS: 'mysensors',
zwave.DISCOVER_BINARY_SENSORS: 'zwave',
vera.DISCOVER_BINARY_SENSORS: 'vera',
wemo.DISCOVER_BINARY_SENSORS: 'wemo',
wink.DISCOVER_BINARY_SENSORS: 'wink'
}

View File

@ -6,7 +6,8 @@ https://home-assistant.io/components/binary_sensor.rest/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.binary_sensor import (BinarySensorDevice,
SENSOR_CLASSES)
from homeassistant.components.sensor.rest import RestData
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers import template
@ -19,12 +20,17 @@ DEFAULT_METHOD = 'GET'
# pylint: disable=unused-variable
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup REST binary sensors."""
"""Setup the REST binary sensor."""
resource = config.get('resource', None)
method = config.get('method', DEFAULT_METHOD)
payload = config.get('payload', None)
verify_ssl = config.get('verify_ssl', True)
sensor_class = config.get('sensor_class')
if sensor_class not in SENSOR_CLASSES:
_LOGGER.warning('Unknown sensor class: %s', sensor_class)
sensor_class = None
rest = RestData(method, resource, payload, verify_ssl)
rest.update()
@ -33,19 +39,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return False
add_devices([RestBinarySensor(
hass, rest, config.get('name', DEFAULT_NAME),
hass,
rest,
config.get('name', DEFAULT_NAME),
sensor_class,
config.get(CONF_VALUE_TEMPLATE))])
# pylint: disable=too-many-arguments
class RestBinarySensor(BinarySensorDevice):
"""A REST binary sensor."""
"""Representation of a REST binary sensor."""
def __init__(self, hass, rest, name, value_template):
def __init__(self, hass, rest, name, sensor_class, value_template):
"""Initialize a REST binary sensor."""
self._hass = hass
self.rest = rest
self._name = name
self._sensor_class = sensor_class
self._state = False
self._value_template = value_template
self.update()
@ -55,6 +65,11 @@ class RestBinarySensor(BinarySensorDevice):
"""Return the name of the binary sensor."""
return self._name
@property
def sensor_class(self):
"""Return the class of this sensor."""
return self._sensor_class
@property
def is_on(self):
"""Return true if the binary sensor is on."""

View File

@ -7,7 +7,7 @@ https://home-assistant.io/components/binary_sensor.template/
import logging
from homeassistant.components.binary_sensor import (BinarySensorDevice,
DOMAIN,
ENTITY_ID_FORMAT,
SENSOR_CLASSES)
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_VALUE_TEMPLATE
from homeassistant.core import EVENT_STATE_CHANGED
@ -16,7 +16,6 @@ from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers import template
from homeassistant.util import slugify
ENTITY_ID_FORMAT = DOMAIN + '.{}'
CONF_SENSORS = 'sensors'
_LOGGER = logging.getLogger(__name__)
@ -76,34 +75,22 @@ class BinarySensorTemplate(BinarySensorDevice):
def __init__(self, hass, device, friendly_name, sensor_class,
value_template):
"""Initialize the Template binary sensor."""
self._hass = hass
self._device = device
self.hass = hass
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, device,
hass=hass)
self._name = friendly_name
self._sensor_class = sensor_class
self._template = value_template
self._state = None
self.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, device,
hass=hass)
self.update()
_LOGGER.info('Started template sensor %s', device)
hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
def template_bsensor_event_listener(event):
"""Called when the target device changes state."""
self.update_ha_state(True)
def _event_listener(self, event):
if not hasattr(self, 'hass'):
return
self.update_ha_state(True)
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def sensor_class(self):
"""Return the sensor class of the sensor."""
return self._sensor_class
hass.bus.listen(EVENT_STATE_CHANGED,
template_bsensor_event_listener)
@property
def name(self):
@ -115,10 +102,21 @@ class BinarySensorTemplate(BinarySensorDevice):
"""Return true if sensor is on."""
return self._state
@property
def sensor_class(self):
"""Return the sensor class of the sensor."""
return self._sensor_class
@property
def should_poll(self):
"""No polling needed."""
return False
def update(self):
"""Get the latest data and update the state."""
try:
value = template.render(self._hass, self._template)
self._state = template.render(self.hass,
self._template).lower() == 'true'
except TemplateError as ex:
if ex.args and ex.args[0].startswith(
"UndefinedError: 'None' has no attribute"):
@ -126,5 +124,4 @@ class BinarySensorTemplate(BinarySensorDevice):
_LOGGER.warning(ex)
return
_LOGGER.error(ex)
value = 'false'
self._state = value.lower() == 'true'
self._state = False

View File

@ -0,0 +1,69 @@
"""
Support for Vera binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.vera/
"""
import logging
import homeassistant.util.dt as dt_util
from homeassistant.const import (
ATTR_ARMED, ATTR_BATTERY_LEVEL, ATTR_LAST_TRIP_TIME, ATTR_TRIPPED)
from homeassistant.components.binary_sensor import (
BinarySensorDevice)
from homeassistant.components.vera import (
VeraDevice, VERA_DEVICES, VERA_CONTROLLER)
DEPENDENCIES = ['vera']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Perform the setup for Vera controller devices."""
add_devices_callback(
VeraBinarySensor(device, VERA_CONTROLLER)
for device in VERA_DEVICES['binary_sensor'])
class VeraBinarySensor(VeraDevice, BinarySensorDevice):
"""Representation of a Vera Binary Sensor."""
def __init__(self, vera_device, controller):
"""Initialize the binary_sensor."""
self._state = False
VeraDevice.__init__(self, vera_device, controller)
@property
def device_state_attributes(self):
"""Return the state attributes."""
attr = {}
if self.vera_device.has_battery:
attr[ATTR_BATTERY_LEVEL] = self.vera_device.battery_level + '%'
if self.vera_device.is_armable:
armed = self.vera_device.is_armed
attr[ATTR_ARMED] = 'True' if armed else 'False'
if self.vera_device.is_trippable:
last_tripped = self.vera_device.last_trip
if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
utc_time)
else:
attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped
attr[ATTR_TRIPPED] = 'True' if tripped else 'False'
attr['Vera Device Id'] = self.vera_device.vera_device_id
return attr
@property
def is_on(self):
"""Return true if sensor is on."""
return self._state
def update(self):
"""Get the latest data and update the state."""
self._state = self.vera_device.is_tripped

View File

@ -45,6 +45,9 @@ class WemoBinarySensor(BinarySensorDevice):
_LOGGER.info(
'Subscription update for %s',
_device)
if not hasattr(self, 'hass'):
self.update()
return
self.update_ha_state(True)
@property

View File

@ -10,7 +10,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['python-wink==0.6.2']
REQUIREMENTS = ['python-wink==0.6.4']
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
@ -77,6 +77,11 @@ class WinkBinarySensorDevice(BinarySensorDevice, Entity):
"""Return the name of the sensor if any."""
return self.wink.name()
@property
def available(self):
"""True if connection == True."""
return self.wink.available
def update(self):
"""Update state of the sensor."""
self.wink.update_state()

View File

@ -12,7 +12,7 @@ from homeassistant.helpers.event import track_utc_time_change
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pyicloud==0.7.2']
REQUIREMENTS = ['pyicloud==0.8.1']
CONF_INTERVAL = 'interval'
DEFAULT_INTERVAL = 8

View File

@ -89,4 +89,9 @@ class NetgearDeviceScanner(object):
with self.lock:
_LOGGER.info("Scanning")
self.last_results = self._api.get_attached_devices() or []
results = self._api.get_attached_devices()
if results is None:
_LOGGER.warning('Error scanning devices')
self.last_results = results or []

View File

@ -24,7 +24,7 @@ _LOGGER = logging.getLogger(__name__)
# interval in minutes to exclude devices from a scan while they are home
CONF_HOME_INTERVAL = "home_interval"
REQUIREMENTS = ['python-nmap==0.4.3']
REQUIREMENTS = ['python-nmap==0.6.0']
def get_scanner(hass, config):

View File

@ -99,14 +99,11 @@ def setup_scanner(hass, config, see):
_LOGGER.info("Added beacon %s", location)
else:
# Normal region
if not zone.attributes.get('passive'):
kwargs['location_name'] = location
regions = REGIONS_ENTERED[dev_id]
if location not in regions:
regions.append(location)
_LOGGER.info("Enter region %s", location)
_set_gps_from_zone(kwargs, zone)
_set_gps_from_zone(kwargs, location, zone)
see(**kwargs)
see_beacons(dev_id, kwargs)
@ -121,9 +118,7 @@ def setup_scanner(hass, config, see):
if new_region:
# Exit to previous region
zone = hass.states.get("zone.{}".format(new_region))
if not zone.attributes.get('passive'):
kwargs['location_name'] = new_region
_set_gps_from_zone(kwargs, zone)
_set_gps_from_zone(kwargs, new_region, zone)
_LOGGER.info("Exit to %s", new_region)
see(**kwargs)
see_beacons(dev_id, kwargs)
@ -184,11 +179,12 @@ def _parse_see_args(topic, data):
return dev_id, kwargs
def _set_gps_from_zone(kwargs, zone):
def _set_gps_from_zone(kwargs, location, zone):
"""Set the see parameters from the zone parameters."""
if zone is not None:
kwargs['gps'] = (
zone.attributes['latitude'],
zone.attributes['longitude'])
kwargs['gps_accuracy'] = zone.attributes['radius']
kwargs['location_name'] = location
return kwargs

View File

@ -15,7 +15,7 @@ from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED)
DOMAIN = "discovery"
REQUIREMENTS = ['netdisco==0.5.4']
REQUIREMENTS = ['netdisco==0.5.5']
SCAN_INTERVAL = 300 # seconds
@ -25,6 +25,7 @@ SERVICE_CAST = 'google_cast'
SERVICE_NETGEAR = 'netgear_router'
SERVICE_SONOS = 'sonos'
SERVICE_PLEX = 'plex_mediaserver'
SERVICE_SQUEEZEBOX = 'logitech_mediaserver'
SERVICE_HANDLERS = {
SERVICE_WEMO: "wemo",
@ -33,6 +34,7 @@ SERVICE_HANDLERS = {
SERVICE_NETGEAR: 'device_tracker',
SERVICE_SONOS: 'media_player',
SERVICE_PLEX: 'media_player',
SERVICE_SQUEEZEBOX: 'media_player',
}

View File

@ -1,2 +1,2 @@
"""DO NOT MODIFY. Auto-generated by update_mdi script."""
VERSION = "e85dc66e1a0730e44f79ed11501cd79a"
VERSION = "df49e6b7c930eb39b42ff1909712e95e"

View File

@ -1,2 +1,2 @@
"""DO NOT MODIFY. Auto-generated by build_frontend script."""
VERSION = "30bcc0eacc13a2317000824741dc9ac0"
VERSION = "49974cb3bb443751f7548e4e3b353304"

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit 0fed700045d6faba8eda8ec713ee9e6bc763507c
Subproject commit a4217e0f620366e47dde82f7ce2d8f7b2bb6a079

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ import logging
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.2']
REQUIREMENTS = ['python-wink==0.6.4']
def setup_platform(hass, config, add_devices, discovery_info=None):
@ -57,6 +57,11 @@ class WinkGarageDoorDevice(GarageDoorDevice):
"""Return true if door is closed."""
return self.wink.state() == 0
@property
def available(self):
"""True if connection == True."""
return self.wink.available
def close_door(self):
"""Close the door."""
self.wink.set_state(0)

View File

@ -74,7 +74,8 @@ def setup(hass, config):
hass.bus.listen_once(
ha.EVENT_HOMEASSISTANT_START,
lambda event:
threading.Thread(target=server.start, daemon=True).start())
threading.Thread(target=server.start, daemon=True,
name='HTTP-server').start())
hass.http = server
hass.config.api = rem.API(util.get_local_ip(), api_password, server_port,
@ -236,9 +237,10 @@ class RequestHandler(SimpleHTTPRequestHandler):
# Did we find a handler for the incoming request?
if handle_request_method:
# For some calls we need a valid password
msg = "API password missing or incorrect."
if require_auth and not self.authenticated:
self.write_json_message(
"API password missing or incorrect.", HTTP_UNAUTHORIZED)
self.write_json_message(msg, HTTP_UNAUTHORIZED)
_LOGGER.warning(msg)
return
handle_request_method(self, path_match, data)
@ -277,8 +279,11 @@ class RequestHandler(SimpleHTTPRequestHandler):
def write_json(self, data=None, status_code=HTTP_OK, location=None):
"""Helper method to return JSON to the caller."""
json_data = json.dumps(data, indent=4, sort_keys=True,
cls=rem.JSONEncoder).encode('UTF-8')
self.send_response(status_code)
self.send_header(HTTP_HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON)
self.send_header(HTTP_HEADER_CONTENT_LENGTH, str(len(json_data)))
if location:
self.send_header('Location', location)
@ -288,20 +293,20 @@ class RequestHandler(SimpleHTTPRequestHandler):
self.end_headers()
if data is not None:
self.wfile.write(
json.dumps(data, indent=4, sort_keys=True,
cls=rem.JSONEncoder).encode("UTF-8"))
self.wfile.write(json_data)
def write_text(self, message, status_code=HTTP_OK):
"""Helper method to return a text message to the caller."""
msg_data = message.encode('UTF-8')
self.send_response(status_code)
self.send_header(HTTP_HEADER_CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN)
self.send_header(HTTP_HEADER_CONTENT_LENGTH, str(len(msg_data)))
self.set_session_cookie_header()
self.end_headers()
self.wfile.write(message.encode("UTF-8"))
self.wfile.write(msg_data)
def write_file(self, path, cache_headers=True):
"""Return a file to the user."""

View File

@ -0,0 +1,145 @@
"""
Component to offer a way to select a value from a slider.
For more details about this component, please refer to the documentation
at https://home-assistant.io/components/input_slider/
"""
import logging
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
DOMAIN = 'input_slider'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
_LOGGER = logging.getLogger(__name__)
CONF_NAME = 'name'
CONF_INITIAL = 'initial'
CONF_MIN = 'min'
CONF_MAX = 'max'
CONF_ICON = 'icon'
CONF_STEP = 'step'
ATTR_VALUE = 'value'
ATTR_MIN = 'min'
ATTR_MAX = 'max'
ATTR_STEP = 'step'
SERVICE_SELECT_VALUE = 'select_value'
def select_value(hass, entity_id, value):
"""Set input_slider to value."""
hass.services.call(DOMAIN, SERVICE_SELECT_VALUE, {
ATTR_ENTITY_ID: entity_id,
ATTR_VALUE: value,
})
def setup(hass, config):
"""Set up input slider."""
if not isinstance(config.get(DOMAIN), dict):
_LOGGER.error('Expected %s config to be a dictionary', DOMAIN)
return False
component = EntityComponent(_LOGGER, DOMAIN, hass)
entities = []
for object_id, cfg in config[DOMAIN].items():
if object_id != slugify(object_id):
_LOGGER.warning("Found invalid key for boolean input: %s. "
"Use %s instead", object_id, slugify(object_id))
continue
if not cfg:
_LOGGER.warning("No configuration specified for %s", object_id)
continue
name = cfg.get(CONF_NAME)
minimum = cfg.get(CONF_MIN)
maximum = cfg.get(CONF_MAX)
state = cfg.get(CONF_INITIAL)
step = cfg.get(CONF_STEP)
icon = cfg.get(CONF_ICON)
if state < minimum:
state = minimum
if state > maximum:
state = maximum
entities.append(
InputSlider(object_id, name, state, minimum, maximum, step, icon)
)
if not entities:
return False
def select_value_service(call):
"""Handle a calls to the input slider services."""
target_inputs = component.extract_from_service(call)
for input_slider in target_inputs:
input_slider.select_value(call.data.get(ATTR_VALUE))
hass.services.register(DOMAIN, SERVICE_SELECT_VALUE,
select_value_service)
component.add_entities(entities)
return True
class InputSlider(Entity):
"""Represent an slider."""
# pylint: disable=too-many-arguments
def __init__(self, object_id, name, state, minimum, maximum, step, icon):
"""Initialize a select input."""
self.entity_id = ENTITY_ID_FORMAT.format(object_id)
self._name = name
self._current_value = state
self._minimum = minimum
self._maximum = maximum
self._step = step
self._icon = icon
@property
def should_poll(self):
"""If entity should be polled."""
return False
@property
def name(self):
"""Name of the select input."""
return self._name
@property
def icon(self):
"""Icon to be used for this entity."""
return self._icon
@property
def state(self):
"""State of the component."""
return self._current_value
@property
def state_attributes(self):
"""State attributes."""
return {
ATTR_MIN: self._minimum,
ATTR_MAX: self._maximum,
ATTR_STEP: self._step
}
def select_value(self, value):
"""Select new value."""
num_value = int(value)
if num_value < self._minimum or num_value > self._maximum:
_LOGGER.warning('Invalid value: %s (range %s - %s)',
num_value, self._minimum, self._maximum)
return
self._current_value = num_value
self.update_ha_state()

View File

@ -9,7 +9,8 @@ import os
import csv
from homeassistant.components import (
group, discovery, wemo, wink, isy994, zwave, insteon_hub, mysensors)
group, discovery, wemo, wink, isy994,
zwave, insteon_hub, mysensors, tellstick, vera)
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
@ -64,6 +65,8 @@ DISCOVERY_PLATFORMS = {
discovery.SERVICE_HUE: 'hue',
zwave.DISCOVER_LIGHTS: 'zwave',
mysensors.DISCOVER_LIGHTS: 'mysensors',
tellstick.DISCOVER_LIGHTS: 'tellstick',
vera.DISCOVER_LIGHTS: 'vera',
}
PROP_TO_ATTR = {
@ -224,7 +227,7 @@ def setup(hass, config):
pass
if ATTR_COLOR_TEMP in dat:
# color_temp should be an int of mirads value
# color_temp should be an int of mireds value
colortemp = dat.get(ATTR_COLOR_TEMP)
# Without this check, a ctcolor with value '99' would work
@ -295,7 +298,7 @@ class Light(ToggleEntity):
@property
def color_temp(self):
"""Return the CT color value in mirads."""
"""Return the CT color value in mireds."""
return None
@property

View File

@ -53,6 +53,8 @@ def _find_host_from_config(hass, filename=PHUE_CONFIG_FILE):
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup the Hue lights."""
filename = config.get(CONF_FILENAME, PHUE_CONFIG_FILE)
allow_unreachable = config.get('allow_unreachable', False)
if discovery_info is not None:
host = urlparse(discovery_info[1]).hostname
else:
@ -69,10 +71,11 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
if host in _CONFIGURING:
return
setup_bridge(host, hass, add_devices_callback, filename)
setup_bridge(host, hass, add_devices_callback, filename, allow_unreachable)
def setup_bridge(host, hass, add_devices_callback, filename):
def setup_bridge(host, hass, add_devices_callback, filename,
allow_unreachable):
"""Setup a phue bridge based on host parameter."""
import phue
@ -88,7 +91,8 @@ def setup_bridge(host, hass, add_devices_callback, filename):
except phue.PhueRegistrationException:
_LOGGER.warning("Connected to Hue at %s but not registered.", host)
request_configuration(host, hass, add_devices_callback, filename)
request_configuration(host, hass, add_devices_callback, filename,
allow_unreachable)
return
@ -130,7 +134,7 @@ def setup_bridge(host, hass, add_devices_callback, filename):
if light_id not in lights:
lights[light_id] = HueLight(int(light_id), info,
bridge, update_lights,
bridge_type=bridge_type)
bridge_type, allow_unreachable)
new_lights.append(lights[light_id])
else:
lights[light_id].info = info
@ -141,7 +145,8 @@ def setup_bridge(host, hass, add_devices_callback, filename):
update_lights()
def request_configuration(host, hass, add_devices_callback, filename):
def request_configuration(host, hass, add_devices_callback, filename,
allow_unreachable):
"""Request configuration steps from the user."""
configurator = get_component('configurator')
@ -155,7 +160,8 @@ def request_configuration(host, hass, add_devices_callback, filename):
# pylint: disable=unused-argument
def hue_configuration_callback(data):
"""The actions to do when our configuration callback is called."""
setup_bridge(host, hass, add_devices_callback, filename)
setup_bridge(host, hass, add_devices_callback, filename,
allow_unreachable)
_CONFIGURING[host] = configurator.request_config(
hass, "Philips Hue", hue_configuration_callback,
@ -171,7 +177,7 @@ class HueLight(Light):
# pylint: disable=too-many-arguments
def __init__(self, light_id, info, bridge, update_lights,
bridge_type='hue'):
bridge_type, allow_unreachable):
"""Initialize the light."""
self.light_id = light_id
self.info = info
@ -179,6 +185,8 @@ class HueLight(Light):
self.update_lights = update_lights
self.bridge_type = bridge_type
self.allow_unreachable = allow_unreachable
@property
def unique_id(self):
"""Return the ID of this Hue light."""
@ -209,7 +217,11 @@ class HueLight(Light):
def is_on(self):
"""Return true if device is on."""
self.update_lights()
return self.info['state']['reachable'] and self.info['state']['on']
if self.allow_unreachable:
return self.info['state']['on']
else:
return self.info['state']['reachable'] and self.info['state']['on']
def turn_on(self, **kwargs):
"""Turn the specified or all lights on."""

View File

@ -4,127 +4,80 @@ Support for Tellstick lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.tellstick/
"""
from homeassistant.components import tellstick
from homeassistant.components.light import ATTR_BRIGHTNESS, Light
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
REQUIREMENTS = ['tellcore-py==1.1.2']
SIGNAL_REPETITIONS = 1
from homeassistant.components.tellstick import (DEFAULT_SIGNAL_REPETITIONS,
ATTR_DISCOVER_DEVICES,
ATTR_DISCOVER_CONFIG)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Tellstick lights."""
import tellcore.telldus as telldus
from tellcore.library import DirectCallbackDispatcher
import tellcore.constants as tellcore_constants
if (discovery_info is None or
discovery_info[ATTR_DISCOVER_DEVICES] is None or
tellstick.TELLCORE_REGISTRY is None):
return
core = telldus.TelldusCore(callback_dispatcher=DirectCallbackDispatcher())
signal_repetitions = config.get('signal_repetitions', SIGNAL_REPETITIONS)
signal_repetitions = discovery_info.get(ATTR_DISCOVER_CONFIG,
DEFAULT_SIGNAL_REPETITIONS)
switches_and_lights = core.devices()
lights = []
for switch in switches_and_lights:
if switch.methods(tellcore_constants.TELLSTICK_DIM):
lights.append(TellstickLight(switch, signal_repetitions))
def _device_event_callback(id_, method, data, cid):
"""Called from the TelldusCore library to update one device."""
for light_device in lights:
if light_device.tellstick_device.id == id_:
# Execute the update in another thread
light_device.update_ha_state(True)
break
callback_id = core.register_device_event(_device_event_callback)
def unload_telldus_lib(event):
"""Un-register the callback bindings."""
if callback_id is not None:
core.unregister_callback(callback_id)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, unload_telldus_lib)
add_devices_callback(lights)
add_devices(TellstickLight(
tellstick.TELLCORE_REGISTRY.get_device(switch_id), signal_repetitions)
for switch_id in discovery_info[ATTR_DISCOVER_DEVICES])
class TellstickLight(Light):
class TellstickLight(tellstick.TellstickDevice, Light):
"""Representation of a Tellstick light."""
def __init__(self, tellstick_device, signal_repetitions):
"""Initialize the light."""
import tellcore.constants as tellcore_constants
self.tellstick_device = tellstick_device
self.signal_repetitions = signal_repetitions
self._brightness = 0
self.last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON |
tellcore_constants.TELLSTICK_TURNOFF |
tellcore_constants.TELLSTICK_DIM |
tellcore_constants.TELLSTICK_UP |
tellcore_constants.TELLSTICK_DOWN)
self.update()
@property
def name(self):
"""Return the name of the switch if any."""
return self.tellstick_device.name
self._brightness = 255
tellstick.TellstickDevice.__init__(self,
tellstick_device,
signal_repetitions)
@property
def is_on(self):
"""Return true if switch is on."""
return self._brightness > 0
return self._state
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
return self._brightness
def turn_off(self, **kwargs):
"""Turn the switch off."""
for _ in range(self.signal_repetitions):
def set_tellstick_state(self, last_command_sent, last_data_sent):
"""Update the internal representation of the switch."""
from tellcore.constants import TELLSTICK_TURNON, TELLSTICK_DIM
if last_command_sent == TELLSTICK_DIM:
if last_data_sent is not None:
self._brightness = int(last_data_sent)
self._state = self._brightness > 0
else:
self._state = last_command_sent == TELLSTICK_TURNON
def _send_tellstick_command(self, command, data):
"""Handle the turn_on / turn_off commands."""
from tellcore.constants import (TELLSTICK_TURNOFF, TELLSTICK_DIM)
if command == TELLSTICK_TURNOFF:
self.tellstick_device.turn_off()
self._brightness = 0
self.update_ha_state()
elif command == TELLSTICK_DIM:
self.tellstick_device.dim(self._brightness)
else:
raise NotImplementedError(
"Command not implemented: {}".format(command))
def turn_on(self, **kwargs):
"""Turn the switch on."""
from tellcore.constants import TELLSTICK_DIM
brightness = kwargs.get(ATTR_BRIGHTNESS)
if brightness is None:
self._brightness = 255
else:
if brightness is not None:
self._brightness = brightness
for _ in range(self.signal_repetitions):
self.tellstick_device.dim(self._brightness)
self.update_ha_state()
self.call_tellstick(TELLSTICK_DIM, self._brightness)
def update(self):
"""Update state of the light."""
import tellcore.constants as tellcore_constants
last_command = self.tellstick_device.last_sent_command(
self.last_sent_command_mask)
if last_command == tellcore_constants.TELLSTICK_TURNON:
self._brightness = 255
elif last_command == tellcore_constants.TELLSTICK_TURNOFF:
self._brightness = 0
elif (last_command == tellcore_constants.TELLSTICK_DIM or
last_command == tellcore_constants.TELLSTICK_UP or
last_command == tellcore_constants.TELLSTICK_DOWN):
last_sent_value = self.tellstick_device.last_sent_value()
if last_sent_value is not None:
self._brightness = last_sent_value
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def assumed_state(self):
"""Tellstick devices are always assumed state."""
return True
def turn_off(self, **kwargs):
"""Turn the switch off."""
from tellcore.constants import TELLSTICK_TURNOFF
self.call_tellstick(TELLSTICK_TURNOFF)

View File

@ -6,15 +6,15 @@ https://home-assistant.io/components/light.vera/
"""
import logging
from requests.exceptions import RequestException
import homeassistant.util.dt as dt_util
from homeassistant.components.light import ATTR_BRIGHTNESS, Light
from homeassistant.const import (
ATTR_ARMED, ATTR_BATTERY_LEVEL, ATTR_LAST_TRIP_TIME, ATTR_TRIPPED,
EVENT_HOMEASSISTANT_STOP, STATE_OFF, STATE_ON)
STATE_OFF, STATE_ON)
from homeassistant.components.vera import (
VeraDevice, VERA_DEVICES, VERA_CONTROLLER)
REQUIREMENTS = ['pyvera==0.2.8']
DEPENDENCIES = ['vera']
_LOGGER = logging.getLogger(__name__)
@ -22,74 +22,17 @@ _LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup Vera lights."""
import pyvera as veraApi
base_url = config.get('vera_controller_url')
if not base_url:
_LOGGER.error(
"The required parameter 'vera_controller_url'"
" was not found in config"
)
return False
device_data = config.get('device_data', {})
vera_controller, created = veraApi.init_controller(base_url)
if created:
def stop_subscription(event):
"""Shutdown Vera subscriptions and subscription thread on exit."""
_LOGGER.info("Shutting down subscriptions.")
vera_controller.stop()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_subscription)
devices = []
try:
devices = vera_controller.get_devices([
'Switch',
'On/Off Switch',
'Dimmable Switch'])
except RequestException:
# There was a network related error connecting to the vera controller.
_LOGGER.exception("Error communicating with Vera API")
return False
lights = []
for device in devices:
extra_data = device_data.get(device.device_id, {})
exclude = extra_data.get('exclude', False)
if exclude is not True:
lights.append(VeraLight(device, vera_controller, extra_data))
add_devices_callback(lights)
add_devices_callback(
VeraLight(device, VERA_CONTROLLER) for device in VERA_DEVICES['light'])
class VeraLight(Light):
class VeraLight(VeraDevice, Light):
"""Representation of a Vera Light, including dimmable."""
def __init__(self, vera_device, controller, extra_data=None):
def __init__(self, vera_device, controller):
"""Initialize the light."""
self.vera_device = vera_device
self.extra_data = extra_data
self.controller = controller
if self.extra_data and self.extra_data.get('name'):
self._name = self.extra_data.get('name')
else:
self._name = self.vera_device.name
self._state = STATE_OFF
self.controller.register(vera_device, self._update_callback)
self.update()
def _update_callback(self, _device):
self.update_ha_state(True)
@property
def name(self):
"""Return the name of the light."""
return self._name
self._state = False
VeraDevice.__init__(self, vera_device, controller)
@property
def brightness(self):
@ -137,20 +80,13 @@ class VeraLight(Light):
attr[ATTR_TRIPPED] = 'True' if tripped else 'False'
attr['Vera Device Id'] = self.vera_device.vera_device_id
@property
def should_poll(self):
"""No polling needed."""
return False
return attr
@property
def is_on(self):
"""Return true if device is on."""
return self._state == STATE_ON
return self._state
def update(self):
"""Called by the vera device callback to update state."""
if self.vera_device.is_switched_on():
self._state = STATE_ON
else:
self._state = STATE_OFF
self._state = self.vera_device.is_switched_on()

View File

@ -8,8 +8,10 @@ import logging
from datetime import timedelta
import homeassistant.util as util
import homeassistant.util.color as color_util
from homeassistant.components.light import (
Light, ATTR_BRIGHTNESS)
Light, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_RGB_COLOR, ATTR_TRANSITION,
ATTR_XY_COLOR)
DEPENDENCIES = ['wemo']
@ -39,17 +41,14 @@ def setup_bridge(bridge, add_devices_callback):
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_lights():
"""Update the WeMo led objects with latest info from the bridge."""
bridge.bridge_get_lights()
bridge.bridge_update()
new_lights = []
for light_id, info in bridge.Lights.items():
for light_id, device in bridge.Lights.items():
if light_id not in lights:
lights[light_id] = WemoLight(bridge, light_id, info,
update_lights)
lights[light_id] = WemoLight(device, update_lights)
new_lights.append(lights[light_id])
else:
lights[light_id].info = info
if new_lights:
add_devices_callback(new_lights)
@ -60,44 +59,73 @@ def setup_bridge(bridge, add_devices_callback):
class WemoLight(Light):
"""Representation of a WeMo light."""
def __init__(self, bridge, light_id, info, update_lights):
def __init__(self, device, update_lights):
"""Initialize the light."""
self.bridge = bridge
self.light_id = light_id
self.info = info
self.light_id = device.name
self.device = device
self.update_lights = update_lights
@property
def unique_id(self):
"""Return the ID of this light."""
deviceid = self.bridge.light_get_id(self.info)
deviceid = self.device.uniqueID
return "{}.{}".format(self.__class__, deviceid)
@property
def name(self):
"""Return the name of the light."""
return self.bridge.light_name(self.info)
return self.device.name
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
state = self.bridge.light_get_state(self.info)
return int(state['dim'])
return self.device.state.get('level', 255)
@property
def xy_color(self):
"""Return the XY color values of this light."""
return self.device.state.get('color_xy')
@property
def color_temp(self):
"""Return the color temperature of this light in mireds."""
return self.device.state.get('temperature_mireds')
@property
def is_on(self):
"""True if device is on."""
state = self.bridge.light_get_state(self.info)
return int(state['state'])
return self.device.state['onoff'] != 0
def turn_on(self, **kwargs):
"""Turn the light on."""
dim = kwargs.get(ATTR_BRIGHTNESS, self.brightness)
self.bridge.light_set_state(self.info, state=1, dim=dim)
transitiontime = int(kwargs.get(ATTR_TRANSITION, 0))
if ATTR_XY_COLOR in kwargs:
xycolor = kwargs[ATTR_XY_COLOR]
elif ATTR_RGB_COLOR in kwargs:
xycolor = color_util.color_RGB_to_xy(
*(int(val) for val in kwargs[ATTR_RGB_COLOR]))
else:
xycolor = None
if xycolor is not None:
self.device.set_color(xycolor, transition=transitiontime)
if ATTR_COLOR_TEMP in kwargs:
colortemp = kwargs[ATTR_COLOR_TEMP]
self.device.set_temperature(mireds=colortemp,
transition=transitiontime)
if ATTR_BRIGHTNESS in kwargs:
brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness or 255)
self.device.turn_on(level=brightness, transition=transitiontime)
else:
self.device.turn_on(transition=transitiontime)
def turn_off(self, **kwargs):
"""Turn the light off."""
self.bridge.light_set_state(self.info, state=0, dim=0)
transitiontime = int(kwargs.get(ATTR_TRANSITION, 0))
self.device.turn_off(transition=transitiontime)
def update(self):
"""Synchronize state with bridge."""

View File

@ -9,7 +9,7 @@ import logging
from homeassistant.components.light import ATTR_BRIGHTNESS, Light
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.2']
REQUIREMENTS = ['python-wink==0.6.4']
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
@ -58,6 +58,11 @@ class WinkLight(Light):
"""Return the brightness of the light."""
return int(self.wink.brightness() * 255)
@property
def available(self):
"""True if connection == True."""
return self.wink.available
# pylint: disable=too-few-public-methods
def turn_on(self, **kwargs):
"""Turn the switch on."""

View File

@ -101,7 +101,7 @@ class ZwaveDimmer(ZWaveDeviceEntity, Light):
# Zwave multilevel switches use a range of [0, 99] to control
# brightness.
brightness = (self._brightness / 255) * 99
brightness = int((self._brightness / 255) * 99)
if self._value.node.set_dimmer(self._value.value_id, brightness):
self._state = STATE_ON

View File

@ -9,7 +9,7 @@ import logging
from homeassistant.components.lock import LockDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.2']
REQUIREMENTS = ['python-wink==0.6.4']
def setup_platform(hass, config, add_devices, discovery_info=None):
@ -56,6 +56,11 @@ class WinkLockDevice(LockDevice):
"""Return true if device is locked."""
return self.wink.state()
@property
def available(self):
"""True if connection == True."""
return self.wink.available
def lock(self, **kwargs):
"""Lock the device."""
self.wink.set_state(True)

View File

@ -19,6 +19,8 @@ from homeassistant.const import (
SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK)
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'media_player'
SCAN_INTERVAL = 10
@ -28,6 +30,7 @@ DISCOVERY_PLATFORMS = {
discovery.SERVICE_CAST: 'cast',
discovery.SERVICE_SONOS: 'sonos',
discovery.SERVICE_PLEX: 'plex',
discovery.SERVICE_SQUEEZEBOX: 'squeezebox',
}
SERVICE_PLAY_MEDIA = 'play_media'
@ -229,11 +232,9 @@ def setup(hass, config):
def media_player_service_handler(service):
"""Map services to methods on MediaPlayerDevice."""
target_players = component.extract_from_service(service)
method = SERVICE_TO_METHOD[service.service]
for player in target_players:
for player in component.extract_from_service(service):
getattr(player, method)()
if player.should_poll:
@ -245,14 +246,15 @@ def setup(hass, config):
def volume_set_service(service):
"""Set specified volume on the media player."""
target_players = component.extract_from_service(service)
volume = service.data.get(ATTR_MEDIA_VOLUME_LEVEL)
if ATTR_MEDIA_VOLUME_LEVEL not in service.data:
if volume is None:
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, ATTR_MEDIA_VOLUME_LEVEL)
return
volume = service.data[ATTR_MEDIA_VOLUME_LEVEL]
for player in target_players:
for player in component.extract_from_service(service):
player.set_volume_level(volume)
if player.should_poll:
@ -263,14 +265,15 @@ def setup(hass, config):
def volume_mute_service(service):
"""Mute (true) or unmute (false) the media player."""
target_players = component.extract_from_service(service)
mute = service.data.get(ATTR_MEDIA_VOLUME_MUTED)
if ATTR_MEDIA_VOLUME_MUTED not in service.data:
if mute is None:
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, ATTR_MEDIA_VOLUME_MUTED)
return
mute = service.data[ATTR_MEDIA_VOLUME_MUTED]
for player in target_players:
for player in component.extract_from_service(service):
player.mute_volume(mute)
if player.should_poll:
@ -281,14 +284,15 @@ def setup(hass, config):
def media_seek_service(service):
"""Seek to a position."""
target_players = component.extract_from_service(service)
position = service.data.get(ATTR_MEDIA_SEEK_POSITION)
if ATTR_MEDIA_SEEK_POSITION not in service.data:
if position is None:
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, ATTR_MEDIA_SEEK_POSITION)
return
position = service.data[ATTR_MEDIA_SEEK_POSITION]
for player in target_players:
for player in component.extract_from_service(service):
player.media_seek(position)
if player.should_poll:
@ -302,10 +306,12 @@ def setup(hass, config):
media_type = service.data.get(ATTR_MEDIA_CONTENT_TYPE)
media_id = service.data.get(ATTR_MEDIA_CONTENT_ID)
if media_type is None:
return
if media_id is None:
if media_type is None or media_id is None:
missing_attr = (ATTR_MEDIA_CONTENT_TYPE if media_type is None
else ATTR_MEDIA_CONTENT_ID)
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, missing_attr)
return
for player in component.extract_from_service(service):

View File

@ -239,7 +239,7 @@ class DemoMusicPlayer(AbstractDemoPlayer):
if self._cur_track > 0:
support |= SUPPORT_PREVIOUS_TRACK
if self._cur_track < len(self.tracks)-1:
if self._cur_track < len(self.tracks) - 1:
support |= SUPPORT_NEXT_TRACK
return support
@ -252,7 +252,7 @@ class DemoMusicPlayer(AbstractDemoPlayer):
def media_next_track(self):
"""Send next track command."""
if self._cur_track < len(self.tracks)-1:
if self._cur_track < len(self.tracks) - 1:
self._cur_track += 1
self.update_ha_state()

View File

@ -50,7 +50,7 @@ class KodiDevice(MediaPlayerDevice):
self._server = jsonrpc_requests.Server(
'{}/jsonrpc'.format(self._url),
auth=auth)
self._players = None
self._players = list()
self._properties = None
self._item = None
self._app_properties = None
@ -67,8 +67,9 @@ class KodiDevice(MediaPlayerDevice):
try:
return self._server.Player.GetActivePlayers()
except jsonrpc_requests.jsonrpc.TransportError:
_LOGGER.warning('Unable to fetch kodi data')
_LOGGER.debug('Unable to fetch kodi data', exc_info=True)
if self._players is not None:
_LOGGER.warning('Unable to fetch kodi data')
_LOGGER.debug('Unable to fetch kodi data', exc_info=True)
return None
@property

View File

@ -14,7 +14,7 @@ from homeassistant.components.media_player import (
from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['python-mpd2==0.5.4']
REQUIREMENTS = ['python-mpd2==0.5.5']
SUPPORT_MPD = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_TURN_OFF | \
SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK

View File

@ -0,0 +1,128 @@
# Describes the format for available media_player services
turn_on:
description: Turn a media player power on
fields:
entity_id:
description: Name(s) of entities to turn on
example: 'media_player.living_room_chromecast'
turn_off:
description: Turn a media player power off
fields:
entity_id:
description: Name(s) of entities to turn off
example: 'media_player.living_room_chromecast'
toggle:
description: Toggles a media player power state
fields:
entity_id:
description: Name(s) of entities to toggle
example: 'media_player.living_room_chromecast'
volume_up:
description: Turn a media player volume up
fields:
entity_id:
description: Name(s) of entities to turn volume up on
example: 'media_player.living_room_sonos'
volume_down:
description: Turn a media player volume down
fields:
entity_id:
description: Name(s) of entities to turn volume down on
example: 'media_player.living_room_sonos'
mute_volume:
description: Mute a media player's volume
fields:
entity_id:
description: Name(s) of entities to mute
example: 'media_player.living_room_sonos'
mute:
description: True/false for mute/unmute
example: true
set_volume_level:
description: Set a media player's volume level
fields:
entity_id:
description: Name(s) of entities to set volume level on
example: 'media_player.living_room_sonos'
volume:
description: Volume level to set
example: 60
media_play_pause:
description: Toggle media player play/pause state
fields:
entity_id:
description: Name(s) of entities to toggle play/pause state on
example: 'media_player.living_room_sonos'
media_play:
description: Send the media player the command for play.
fields:
entity_id:
description: Name(s) of entities to play on
example: 'media_player.living_room_sonos'
media_pause:
description: Send the media player the command for pause.
fields:
entity_id:
description: Name(s) of entities to pause on
example: 'media_player.living_room_sonos'
media_next_track:
description: Send the media player the command for next track.
fields:
entity_id:
description: Name(s) of entities to send next track command to
example: 'media_player.living_room_sonos'
media_previous_track:
description: Send the media player the command for previous track.
fields:
entity_id:
description: Name(s) of entities to send previous track command to
example: 'media_player.living_room_sonos'
media_seek:
description: Send the media player the command to seek in current playing media.
fields:
entity_id:
description: Name(s) of entities to seek media on
example: 'media_player.living_room_chromecast'
position:
description: Position to seek to. The format is platform dependent.
example: 100
play_media:
description: Send the media player the command for playing media.
fields:
entity_id:
description: Name(s) of entities to seek media on
example: 'media_player.living_room_chromecast'
media_content_id:
description: The ID of the content to play. Platform dependent.
example: 'https://home-assistant.io/images/cast/splash.png'
media_content_type:
description: The type of the content to play. Must be one of MUSIC, TVSHOW, VIDEO, EPISODE, CHANNEL or PLAYLIST
example: 'MUSIC'

View File

@ -9,8 +9,9 @@ import logging
from homeassistant.components.media_player import (
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE,
SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_VOLUME_MUTE,
SUPPORT_VOLUME_SET, MediaPlayerDevice)
SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK,
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
MediaPlayerDevice)
from homeassistant.const import (
STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN)
@ -27,7 +28,8 @@ _REQUESTS_LOGGER = logging.getLogger('requests')
_REQUESTS_LOGGER.setLevel(logging.ERROR)
SUPPORT_SONOS = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_PLAY_MEDIA |\
SUPPORT_SEEK
# pylint: disable=unused-argument
@ -222,7 +224,7 @@ class SonosDevice(MediaPlayerDevice):
@only_if_coordinator
def media_play(self):
"""Send paly command."""
"""Send play command."""
self._player.play()
@only_if_coordinator
@ -249,3 +251,8 @@ class SonosDevice(MediaPlayerDevice):
def turn_on(self):
"""Turn the media player on."""
self._player.play()
@only_if_coordinator
def play_media(self, media_type, media_id):
"""Send the play_media command to the media player."""
self._player.play_uri(media_id)

View File

@ -22,19 +22,32 @@ SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \
SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
SUPPORT_SEEK | SUPPORT_TURN_ON | SUPPORT_TURN_OFF
KNOWN_DEVICES = []
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the squeezebox platform."""
if not config.get(CONF_HOST):
if discovery_info is not None:
host = discovery_info[0]
port = 9090
else:
host = config.get(CONF_HOST)
port = int(config.get('port', 9090))
if not host:
_LOGGER.error(
"Missing required configuration items in %s: %s",
DOMAIN,
CONF_HOST)
return False
# Only add a media server once
if host in KNOWN_DEVICES:
return False
KNOWN_DEVICES.append(host)
lms = LogitechMediaServer(
config.get(CONF_HOST),
config.get('port', '9090'),
host, port,
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD))

View File

@ -0,0 +1,91 @@
"""
Support for Yamaha Receivers.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.yamaha/
"""
import logging
from homeassistant.components.media_player import (
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
MediaPlayerDevice)
from homeassistant.const import STATE_OFF, STATE_ON
REQUIREMENTS = ['rxv==0.1.9']
_LOGGER = logging.getLogger(__name__)
SUPPORT_YAMAHA = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
SUPPORT_TURN_ON | SUPPORT_TURN_OFF
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Yamaha platform."""
import rxv
add_devices(YamahaDevice(config.get("name"), receiver)
for receiver in rxv.find())
class YamahaDevice(MediaPlayerDevice):
"""Representation of a Yamaha device."""
# pylint: disable=too-many-public-methods, abstract-method
def __init__(self, name, receiver):
"""Initialize the Yamaha Receiver."""
self._receiver = receiver
self._muted = False
self._volume = 0
self._pwstate = STATE_OFF
self.update()
self._name = name
def update(self):
"""Get the latest details from the device."""
if self._receiver.on:
self._pwstate = STATE_ON
else:
self._pwstate = STATE_OFF
self._muted = self._receiver.mute
self._volume = (self._receiver.volume/100) + 1
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def state(self):
"""Return the state of the device."""
return self._pwstate
@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
return self._volume
@property
def is_volume_muted(self):
"""Boolean if volume is currently muted."""
return self._muted
@property
def supported_media_commands(self):
"""Flag of media commands that are supported."""
return SUPPORT_YAMAHA
def turn_off(self):
"""Turn off media player."""
self._receiver.on = False
def set_volume_level(self, volume):
"""Set volume level, range 0..1."""
receiver_vol = 100-(volume * 100)
negative_receiver_vol = -receiver_vol
self._receiver.volume = negative_receiver_vol
def mute_volume(self, mute):
"""Mute (true) or unmute (false) media player."""
self._receiver.mute = mute
def turn_on(self):
"""Turn the media player on."""
self._receiver.on = True
self._volume = (self._receiver.volume/100) + 1

View File

@ -10,11 +10,11 @@ import socket
import time
from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.config import load_yaml_config_file
from homeassistant.exceptions import HomeAssistantError
import homeassistant.util as util
from homeassistant.helpers import template
from homeassistant.helpers import validate_config
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
@ -29,6 +29,7 @@ EVENT_MQTT_MESSAGE_RECEIVED = 'mqtt_message_received'
REQUIREMENTS = ['paho-mqtt==1.1']
CONF_EMBEDDED = 'embedded'
CONF_BROKER = 'broker'
CONF_PORT = 'port'
CONF_CLIENT_ID = 'client_id'
@ -92,21 +93,50 @@ def subscribe(hass, topic, callback, qos=DEFAULT_QOS):
MQTT_CLIENT.subscribe(topic, qos)
def _setup_server(hass, config):
"""Try to start embedded MQTT broker."""
conf = config.get(DOMAIN, {})
# Only setup if embedded config passed in or no broker specified
if CONF_EMBEDDED not in conf and CONF_BROKER in conf:
return None
server = prepare_setup_platform(hass, config, DOMAIN, 'server')
if server is None:
_LOGGER.error('Unable to load embedded server.')
return None
success, broker_config = server.start(hass, conf.get(CONF_EMBEDDED))
return success and broker_config
def setup(hass, config):
"""Start the MQTT protocol service."""
if not validate_config(config, {DOMAIN: ['broker']}, _LOGGER):
return False
# pylint: disable=too-many-locals
conf = config.get(DOMAIN, {})
conf = config[DOMAIN]
broker = conf[CONF_BROKER]
port = util.convert(conf.get(CONF_PORT), int, DEFAULT_PORT)
client_id = util.convert(conf.get(CONF_CLIENT_ID), str)
keepalive = util.convert(conf.get(CONF_KEEPALIVE), int, DEFAULT_KEEPALIVE)
username = util.convert(conf.get(CONF_USERNAME), str)
password = util.convert(conf.get(CONF_PASSWORD), str)
certificate = util.convert(conf.get(CONF_CERTIFICATE), str)
protocol = util.convert(conf.get(CONF_PROTOCOL), str, DEFAULT_PROTOCOL)
broker_config = _setup_server(hass, config)
# Only auto config if no server config was passed in
if broker_config and CONF_EMBEDDED not in conf:
broker, port, username, password, certificate, protocol = broker_config
elif not broker_config and (CONF_EMBEDDED in conf or
CONF_BROKER not in conf):
_LOGGER.error('Unable to start broker and auto-configure MQTT.')
return False
if CONF_BROKER in conf:
broker = conf[CONF_BROKER]
port = util.convert(conf.get(CONF_PORT), int, DEFAULT_PORT)
username = util.convert(conf.get(CONF_USERNAME), str)
password = util.convert(conf.get(CONF_PASSWORD), str)
certificate = util.convert(conf.get(CONF_CERTIFICATE), str)
protocol = util.convert(conf.get(CONF_PROTOCOL), str, DEFAULT_PROTOCOL)
if protocol not in (PROTOCOL_31, PROTOCOL_311):
_LOGGER.error('Invalid protocol specified: %s. Allowed values: %s, %s',

View File

@ -0,0 +1,114 @@
"""MQTT server."""
import asyncio
import logging
import tempfile
import threading
from homeassistant.components.mqtt import PROTOCOL_311
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
REQUIREMENTS = ['hbmqtt==0.6.3']
DEPENDENCIES = ['http']
@asyncio.coroutine
def broker_coro(loop, config):
"""Start broker coroutine."""
from hbmqtt.broker import Broker
broker = Broker(config, loop)
yield from broker.start()
return broker
def loop_run(loop, broker, shutdown_complete):
"""Run broker and clean up when done."""
loop.run_forever()
# run_forever ends when stop is called because we're shutting down
loop.run_until_complete(broker.shutdown())
loop.close()
shutdown_complete.set()
def start(hass, server_config):
"""Initialize MQTT Server."""
from hbmqtt.broker import BrokerException
loop = asyncio.new_event_loop()
try:
passwd = tempfile.NamedTemporaryFile()
if server_config is None:
server_config, client_config = generate_config(hass, passwd)
else:
client_config = None
start_server = asyncio.gather(broker_coro(loop, server_config),
loop=loop)
loop.run_until_complete(start_server)
# Result raises exception if one was raised during startup
broker = start_server.result()[0]
except BrokerException:
logging.getLogger(__name__).exception('Error initializing MQTT server')
loop.close()
return False, None
finally:
passwd.close()
shutdown_complete = threading.Event()
def shutdown(event):
"""Gracefully shutdown MQTT broker."""
loop.call_soon_threadsafe(loop.stop)
shutdown_complete.wait()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, shutdown)
threading.Thread(target=loop_run, args=(loop, broker, shutdown_complete),
name="MQTT-server").start()
return True, client_config
def generate_config(hass, passwd):
"""Generate a configuration based on current Home Assistant instance."""
config = {
'listeners': {
'default': {
'max-connections': 50000,
'bind': '0.0.0.0:1883',
'type': 'tcp',
},
'ws-1': {
'bind': '0.0.0.0:8080',
'type': 'ws',
},
},
'auth': {
'allow-anonymous': hass.config.api.api_password is None
},
'plugins': ['auth_anonymous'],
}
if hass.config.api.api_password:
username = 'homeassistant'
password = hass.config.api.api_password
# Encrypt with what hbmqtt uses to verify
from passlib.apps import custom_app_context
passwd.write(
'homeassistant:{}\n'.format(
custom_app_context.encrypt(
hass.config.api.api_password)).encode('utf-8'))
passwd.flush()
config['auth']['password-file'] = passwd.name
config['plugins'].append('auth_file')
else:
username = None
password = None
client_config = ('localhost', 1883, username, password, None, PROTOCOL_311)
return config, client_config

View File

@ -71,6 +71,9 @@ def setup(hass, config):
message = call.data.get(ATTR_MESSAGE)
if message is None:
_LOGGER.error(
'Received call to %s without attribute %s',
call.service, ATTR_MESSAGE)
return
title = template.render(

View File

@ -45,7 +45,7 @@ class FileNotificationService(BaseNotificationService):
title = '{} notifications (Log started: {})\n{}\n'.format(
kwargs.get(ATTR_TITLE),
dt_util.strip_microseconds(dt_util.utcnow()),
'-'*80)
'-' * 80)
file.write(title)
if self.add_timestamp == 1:

View File

@ -0,0 +1,58 @@
"""
GNTP (aka Growl) notification service.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.gntp/
"""
import logging
import os
from homeassistant.components.notify import (
ATTR_TITLE, BaseNotificationService)
REQUIREMENTS = ['gntp==1.0.3']
_LOGGER = logging.getLogger(__name__)
_GNTP_LOGGER = logging.getLogger('gntp')
_GNTP_LOGGER.setLevel(logging.ERROR)
def get_service(hass, config):
"""Get the GNTP notification service."""
if config.get('app_icon') is None:
icon_file = os.path.join(os.path.dirname(__file__), "..", "frontend",
"www_static", "favicon-192x192.png")
app_icon = open(icon_file, 'rb').read()
else:
app_icon = config.get('app_icon')
return GNTPNotificationService(config.get('app_name', 'HomeAssistant'),
config.get('app_icon', app_icon),
config.get('hostname', 'localhost'),
config.get('password'),
config.get('port', 23053))
# pylint: disable=too-few-public-methods
class GNTPNotificationService(BaseNotificationService):
"""Implement the notification service for GNTP."""
# pylint: disable=too-many-arguments
def __init__(self, app_name, app_icon, hostname, password, port):
"""Initialize the service."""
from gntp import notifier
self.gntp = notifier.GrowlNotifier(
applicationName=app_name,
notifications=["Notification"],
applicationIcon=app_icon,
hostname=hostname,
password=password,
port=port
)
self.gntp.register()
def send_message(self, message="", **kwargs):
"""Send a message to a user."""
self.gntp.notify(noteType="Notification", title=kwargs.get(ATTR_TITLE),
description=message)

View File

@ -0,0 +1,87 @@
"""
MessageBird platform for notify component.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.message_bird/
"""
import logging
from homeassistant.components.notify import (
ATTR_TARGET, DOMAIN, BaseNotificationService)
from homeassistant.const import CONF_API_KEY
from homeassistant.helpers import validate_config
CONF_SENDER = 'sender'
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['messagebird==1.1.1']
def is_valid_sender(sender):
"""Test if the sender config option is valid."""
length = len(sender)
if length > 1:
if sender[0] == '+':
return sender[1:].isdigit()
elif length <= 11:
return sender.isalpha()
return False
# pylint: disable=unused-argument
def get_service(hass, config):
"""Get the MessageBird notification service."""
import messagebird
if not validate_config({DOMAIN: config},
{DOMAIN: [CONF_API_KEY]},
_LOGGER):
return None
sender = config.get(CONF_SENDER, 'HA')
if not is_valid_sender(sender):
_LOGGER.error('Sender is invalid: It must be a phone number or '
'a string not longer than 11 characters.')
return None
client = messagebird.Client(config[CONF_API_KEY])
try:
# validates the api key
client.balance()
except messagebird.client.ErrorException:
_LOGGER.error('The specified MessageBird API key is invalid.')
return None
return MessageBirdNotificationService(sender, client)
# pylint: disable=too-few-public-methods
class MessageBirdNotificationService(BaseNotificationService):
"""Implement the notification service for MessageBird."""
def __init__(self, sender, client):
"""Initialize the service."""
self.sender = sender
self.client = client
def send_message(self, message=None, **kwargs):
"""Send a message to a specified target."""
from messagebird.client import ErrorException
targets = kwargs.get(ATTR_TARGET)
if not targets:
_LOGGER.error('No target specified.')
return
if not isinstance(targets, list):
targets = [targets]
for target in targets:
try:
self.client.message_create(self.sender,
target,
message,
{'reference': 'HA'})
except ErrorException as exception:
_LOGGER.error('Failed to notify %s: %s', target, exception)
continue

View File

@ -14,7 +14,7 @@ from homeassistant.helpers import validate_config
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['python-telegram-bot==3.2.0']
REQUIREMENTS = ['python-telegram-bot==3.4']
def get_service(hass, config):

View File

@ -7,9 +7,9 @@ https://home-assistant.io/components/rfxtrx/
import logging
from homeassistant.util import slugify
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/' +
'archive/0.5.zip#pyRFXtrx==0.5']
REQUIREMENTS = ['pyRFXtrx==0.6.5']
DOMAIN = "rfxtrx"
@ -72,6 +72,10 @@ def setup(hass, config):
else:
RFXOBJECT = rfxtrxmod.Core(device, handle_receive, debug=debug)
def _shutdown_rfxtrx(event):
RFXOBJECT.close_connection()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown_rfxtrx)
return True

View File

@ -19,7 +19,8 @@ from homeassistant.const import (
from homeassistant.helpers.entity import ToggleEntity, split_entity_id
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.helpers.service import call_from_config
from homeassistant.helpers.service import (call_from_config,
validate_service_call)
from homeassistant.util import slugify
DOMAIN = "script"
@ -30,9 +31,7 @@ STATE_NOT_RUNNING = 'Not Running'
CONF_ALIAS = "alias"
CONF_SERVICE = "service"
CONF_SERVICE_OLD = "execute_service"
CONF_SERVICE_DATA = "data"
CONF_SERVICE_DATA_OLD = "service_data"
CONF_SEQUENCE = "sequence"
CONF_EVENT = "event"
CONF_EVENT_DATA = "event_data"
@ -174,7 +173,7 @@ class Script(ToggleEntity):
for cur, action in islice(enumerate(self.sequence), self._cur,
None):
if CONF_SERVICE in action or CONF_SERVICE_OLD in action:
if validate_service_call(action) is None:
self._call_service(action)
elif CONF_EVENT in action:
@ -211,14 +210,7 @@ class Script(ToggleEntity):
def _call_service(self, action):
"""Call the service specified in the action."""
# Backwards compatibility
if CONF_SERVICE not in action and CONF_SERVICE_OLD in action:
action[CONF_SERVICE] = action[CONF_SERVICE_OLD]
if CONF_SERVICE_DATA not in action and CONF_SERVICE_DATA_OLD in action:
action[CONF_SERVICE_DATA] = action[CONF_SERVICE_DATA_OLD]
self._last_action = action.get(CONF_ALIAS, action[CONF_SERVICE])
self._last_action = action.get(CONF_ALIAS, 'call service')
_LOGGER.info("Executing script %s step %s", self._name,
self._last_action)
call_from_config(self.hass, action, True)

View File

@ -8,7 +8,8 @@ import logging
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import (
wink, zwave, isy994, verisure, ecobee, tellduslive, mysensors, bloomsky)
wink, zwave, isy994, verisure, ecobee, tellduslive, mysensors,
bloomsky, vera)
DOMAIN = 'sensor'
SCAN_INTERVAL = 30
@ -25,6 +26,7 @@ DISCOVERY_PLATFORMS = {
ecobee.DISCOVER_SENSORS: 'ecobee',
tellduslive.DISCOVER_SENSORS: 'tellduslive',
mysensors.DISCOVER_SENSORS: 'mysensors',
vera.DISCOVER_SENSORS: 'vera',
}

View File

@ -1,5 +1,5 @@
"""
Bitcoin information service that uses blockchain.info and its online wallet.
Bitcoin information service that uses blockchain.info.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.bitcoin/
@ -10,10 +10,9 @@ from datetime import timedelta
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
REQUIREMENTS = ['blockchain==1.2.1']
REQUIREMENTS = ['blockchain==1.3.1']
_LOGGER = logging.getLogger(__name__)
OPTION_TYPES = {
'wallet': ['Wallet balance', 'BTC'],
'exchangerate': ['Exchange rate (1 BTC)', None],
'trade_volume_btc': ['Trade volume', 'BTC'],
'miners_revenue_usd': ['Miners revenue', 'USD'],
@ -43,33 +42,17 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Bitcoin sensor."""
from blockchain.wallet import Wallet
from blockchain import exchangerates, exceptions
"""Setup the Bitcoin sensors."""
from blockchain import exchangerates
wallet_id = config.get('wallet', None)
password = config.get('password', None)
currency = config.get('currency', 'USD')
if currency not in exchangerates.get_ticker():
_LOGGER.error('Currency "%s" is not available. Using "USD".', currency)
_LOGGER.error('Currency "%s" is not available. Using "USD"', currency)
currency = 'USD'
if wallet_id is not None and password is not None:
wallet = Wallet(wallet_id, password)
try:
wallet.get_balance()
except exceptions.APIException as error:
_LOGGER.error(error)
wallet = None
else:
wallet = None
data = BitcoinData()
dev = []
if wallet is not None and password is not None:
dev.append(BitcoinSensor(data, 'wallet', currency, wallet))
for variable in config['display_options']:
if variable not in OPTION_TYPES:
_LOGGER.error('Option type: "%s" does not exist', variable)
@ -83,13 +66,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class BitcoinSensor(Entity):
"""Representation of a Bitcoin sensor."""
def __init__(self, data, option_type, currency, wallet=''):
def __init__(self, data, option_type, currency):
"""Initialize the sensor."""
self.data = data
self._name = OPTION_TYPES[option_type][0]
self._unit_of_measurement = OPTION_TYPES[option_type][1]
self._currency = currency
self._wallet = wallet
self.type = option_type
self._state = None
self.update()
@ -122,10 +104,7 @@ class BitcoinSensor(Entity):
ticker = self.data.ticker
# pylint: disable=no-member
if self.type == 'wallet' and self._wallet is not None:
self._state = '{0:.8f}'.format(self._wallet.get_balance() *
0.00000001)
elif self.type == 'exchangerate':
if self.type == 'exchangerate':
self._state = ticker[self._currency].p15min
self._unit_of_measurement = self._currency
elif self.type == 'trade_volume_btc':

View File

@ -11,7 +11,7 @@ from homeassistant.const import CONF_API_KEY, TEMP_CELCIUS
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
REQUIREMENTS = ['python-forecastio==1.3.3']
REQUIREMENTS = ['python-forecastio==1.3.4']
_LOGGER = logging.getLogger(__name__)
# Sensor types are defined like so:

View File

@ -61,12 +61,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return False
except requests.exceptions.MissingSchema:
_LOGGER.error("Missing resource or schema in configuration. "
"Please check the details in the configuration file.")
"Please check the details in the configuration file")
return False
except requests.exceptions.ConnectionError:
_LOGGER.error("No route to resource/endpoint: '%s'. "
"Please check the details in the configuration file.",
url)
_LOGGER.error("No route to resource/endpoint: %s", url)
return False
rest = GlancesData(url)
@ -167,6 +165,5 @@ class GlancesData(object):
response = requests.get(self._resource, timeout=10)
self.data = response.json()
except requests.exceptions.ConnectionError:
_LOGGER.error("No route to host/endpoint '%s'. Is device offline?",
self._resource)
_LOGGER.error("No route to host/endpoint: %s", self._resource)
self.data = None

View File

@ -32,10 +32,7 @@ JSON_VARIABLE_NAMES = {'weather_humidity': 'humidity',
SENSOR_UNITS = {'humidity': '%', 'battery_level': 'V',
'kph': 'kph', 'temperature': '°C'}
SENSOR_TEMP_TYPES = ['temperature',
'target',
'away_temperature[0]',
'away_temperature[1]']
SENSOR_TEMP_TYPES = ['temperature', 'target']
def setup_platform(hass, config, add_devices, discovery_info=None):

View File

@ -10,7 +10,7 @@ import homeassistant.util.dt as dt_util
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['psutil==4.0.0']
REQUIREMENTS = ['psutil==4.1.0']
SENSOR_TYPES = {
'disk_use_percent': ['Disk Use', '%', 'mdi:harddisk'],
'disk_use': ['Disk Use', 'GiB', 'mdi:harddisk'],
@ -38,7 +38,7 @@ _LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the sensors."""
"""Setup the System sensors."""
dev = []
for resource in config['resources']:
if 'arg' not in resource:

View File

@ -6,7 +6,7 @@ https://home-assistant.io/components/sensor.template/
"""
import logging
from homeassistant.components.sensor import DOMAIN
from homeassistant.components.sensor import ENTITY_ID_FORMAT
from homeassistant.const import (
ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE)
from homeassistant.core import EVENT_STATE_CHANGED
@ -15,11 +15,8 @@ from homeassistant.helpers.entity import Entity, generate_entity_id
from homeassistant.helpers import template
from homeassistant.util import slugify
ENTITY_ID_FORMAT = DOMAIN + '.{}'
_LOGGER = logging.getLogger(__name__)
CONF_SENSORS = 'sensors'
STATE_ERROR = 'error'
# pylint: disable=unused-argument
@ -70,21 +67,21 @@ class SensorTemplate(Entity):
def __init__(self, hass, device_id, friendly_name, unit_of_measurement,
state_template):
"""Initialize the sensor."""
self.hass = hass
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, device_id,
hass=hass)
self.hass = hass
self._name = friendly_name
self._unit_of_measurement = unit_of_measurement
self._template = state_template
self.update()
self.hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
self._state = None
def _event_listener(self, event):
"""Called when the target device changes state."""
if not hasattr(self, 'hass'):
return
self.update_ha_state(True)
self.update()
def template_sensor_event_listener(event):
"""Called when the target device changes state."""
self.update_ha_state(True)
hass.bus.listen(EVENT_STATE_CHANGED, template_sensor_event_listener)
@property
def name(self):
@ -111,10 +108,10 @@ class SensorTemplate(Entity):
try:
self._state = template.render(self.hass, self._template)
except TemplateError as ex:
self._state = STATE_ERROR
if ex.args and ex.args[0].startswith(
"UndefinedError: 'None' has no attribute"):
# Common during HA startup - so just a warning
_LOGGER.warning(ex)
return
self._state = None
_LOGGER.error(ex)

View File

@ -0,0 +1,208 @@
"""
Support for the Uber API.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.uber/
"""
import logging
from datetime import timedelta
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['https://github.com/denismakogon/rides-python-sdk/archive/'
'py3-support.zip#'
'uber_rides==0.1.2-dev']
ICON = 'mdi:taxi'
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Uber sensor."""
if None in (config.get('start_latitude'), config.get('start_longitude')):
_LOGGER.error(
"You must set start latitude and longitude to use the Uber sensor!"
)
return False
if config.get('server_token') is None:
_LOGGER.error("You must set a server_token to use the Uber sensor!")
return False
from uber_rides.session import Session
session = Session(server_token=config.get('server_token'))
wanted_product_ids = config.get('product_ids')
dev = []
timeandpriceest = UberEstimate(session, config['start_latitude'],
config['start_longitude'],
config.get('end_latitude'),
config.get('end_longitude'))
for product_id, product in timeandpriceest.products.items():
if (wanted_product_ids is not None) and \
(product_id not in wanted_product_ids):
continue
dev.append(UberSensor('time', timeandpriceest, product_id, product))
dev.append(UberSensor('price', timeandpriceest, product_id, product))
add_devices(dev)
# pylint: disable=too-few-public-methods
class UberSensor(Entity):
"""Implementation of an Uber sensor."""
def __init__(self, sensorType, products, product_id, product):
"""Initialize the Uber sensor."""
self.data = products
self._product_id = product_id
self._product = product
self._sensortype = sensorType
self._name = "{} {}".format(self._product['display_name'],
self._sensortype)
if self._sensortype == "time":
self._unit_of_measurement = "min"
time_estimate = self._product.get('time_estimate_seconds', 0)
self._state = int(time_estimate / 60)
elif self._sensortype == "price":
price_details = self._product['price_details']
if price_details['low_estimate'] is None:
self._unit_of_measurement = price_details['currency_code']
self._state = int(price_details['minimum'])
else:
self._unit_of_measurement = price_details['currency_code']
self._state = int(price_details['low_estimate'])
self.update()
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def state(self):
"""Return the state of the sensor."""
return self._state
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return self._unit_of_measurement
@property
def device_state_attributes(self):
"""Return the state attributes."""
price_details = self._product['price_details']
distance_key = 'Trip distance (in {}s)'.format(price_details[
'distance_unit'])
distance_val = self._product.get('distance')
if (price_details.get('distance_unit') is None) or \
(self._product.get('distance') is None):
distance_key = 'Trip distance'
distance_val = 'N/A'
time_estimate = self._product['time_estimate_seconds']
return {
'Product ID': self._product['product_id'],
'Product short description': self._product['short_description'],
'Product display name': self._product['display_name'],
'Product description': self._product['description'],
'Pickup time estimate (in seconds)': time_estimate,
'Trip duration (in seconds)': self._product.get('duration', 'N/A'),
distance_key: distance_val,
'Vehicle Capacity': self._product['capacity'],
'Minimum price': price_details['minimum'],
'Cost per minute': price_details['cost_per_minute'],
'Distance units': price_details['distance_unit'],
'Cancellation fee': price_details['cancellation_fee'],
'Cost per distance unit': price_details['cost_per_distance'],
'Base price': price_details['base'],
'Price estimate': price_details.get('estimate', 'N/A'),
'Price currency code': price_details.get('currency_code'),
'High price estimate': price_details.get('high_estimate', 'N/A'),
'Low price estimate': price_details.get('low_estimate', 'N/A'),
'Surge multiplier': price_details.get('surge_multiplier', 'N/A')
}
@property
def icon(self):
"""Icon to use in the frontend, if any."""
return ICON
# pylint: disable=too-many-branches
def update(self):
"""Get the latest data from the Uber API and update the states."""
self.data.update()
self._product = self.data.products[self._product_id]
if self._sensortype == "time":
time_estimate = self._product.get('time_estimate_seconds', 0)
self._state = int(time_estimate / 60)
elif self._sensortype == "price":
price_details = self._product['price_details']
min_price = price_details['minimum']
self._state = int(price_details.get('low_estimate', min_price))
# pylint: disable=too-few-public-methods
class UberEstimate(object):
"""The class for handling the time and price estimate."""
# pylint: disable=too-many-arguments
def __init__(self, session, start_latitude, start_longitude,
end_latitude=None, end_longitude=None):
"""Initialize the UberEstimate object."""
self._session = session
self.start_latitude = start_latitude
self.start_longitude = start_longitude
self.end_latitude = end_latitude
self.end_longitude = end_longitude
self.products = None
self.update()
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest product info and estimates from the Uber API."""
from uber_rides.client import UberRidesClient
client = UberRidesClient(self._session)
self.products = {}
products_response = client.get_products(
self.start_latitude, self.start_longitude)
products = products_response.json.get('products')
for product in products:
self.products[product['product_id']] = product
if self.end_latitude is not None and self.end_longitude is not None:
price_response = client.get_price_estimates(
self.start_latitude,
self.start_longitude,
self.end_latitude,
self.end_longitude)
prices = price_response.json.get('prices')
for price in prices:
product = self.products[price['product_id']]
price_details = product["price_details"]
product["duration"] = price['duration']
product["distance"] = price['distance']
price_details["estimate"] = price['estimate']
price_details["high_estimate"] = price['high_estimate']
price_details["low_estimate"] = price['low_estimate']
price_details["surge_multiplier"] = price['surge_multiplier']
estimate_response = client.get_pickup_time_estimates(
self.start_latitude, self.start_longitude)
estimates = estimate_response.json.get('times')
for estimate in estimates:
self.products[estimate['product_id']][
"time_estimate_seconds"] = estimate.get('estimate', '0')

View File

@ -6,109 +6,40 @@ https://home-assistant.io/components/sensor.vera/
"""
import logging
from requests.exceptions import RequestException
import homeassistant.util.dt as dt_util
from homeassistant.const import (
ATTR_ARMED, ATTR_BATTERY_LEVEL, ATTR_LAST_TRIP_TIME, ATTR_TRIPPED,
EVENT_HOMEASSISTANT_STOP, TEMP_CELCIUS, TEMP_FAHRENHEIT)
TEMP_CELCIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import Entity
from homeassistant.components.vera import (
VeraDevice, VERA_DEVICES, VERA_CONTROLLER)
REQUIREMENTS = ['pyvera==0.2.8']
DEPENDENCIES = ['vera']
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def get_devices(hass, config):
"""Setup the Vera Sensors."""
import pyvera as veraApi
base_url = config.get('vera_controller_url')
if not base_url:
_LOGGER.error(
"The required parameter 'vera_controller_url'"
" was not found in config"
)
return False
device_data = config.get('device_data', {})
vera_controller, created = veraApi.init_controller(base_url)
if created:
def stop_subscription(event):
"""Shutdown Vera subscriptions and subscription thread on exit."""
_LOGGER.info("Shutting down subscriptions.")
vera_controller.stop()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_subscription)
categories = ['Temperature Sensor',
'Light Sensor',
'Humidity Sensor',
'Sensor']
devices = []
try:
devices = vera_controller.get_devices(categories)
except RequestException:
# There was a network related error connecting to the vera controller.
_LOGGER.exception("Error communicating with Vera API")
return False
vera_sensors = []
for device in devices:
extra_data = device_data.get(device.device_id, {})
exclude = extra_data.get('exclude', False)
if exclude is not True:
vera_sensors.append(
VeraSensor(device, vera_controller, extra_data))
return vera_sensors
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Perform the setup for Vera controller devices."""
add_devices(get_devices(hass, config))
add_devices_callback(
VeraSensor(device, VERA_CONTROLLER)
for device in VERA_DEVICES['sensor'])
class VeraSensor(Entity):
class VeraSensor(VeraDevice, Entity):
"""Representation of a Vera Sensor."""
def __init__(self, vera_device, controller, extra_data=None):
def __init__(self, vera_device, controller):
"""Initialize the sensor."""
self.vera_device = vera_device
self.controller = controller
self.extra_data = extra_data
if self.extra_data and self.extra_data.get('name'):
self._name = self.extra_data.get('name')
else:
self._name = self.vera_device.name
self.current_value = ''
self.current_value = None
self._temperature_units = None
self.controller.register(vera_device, self._update_callback)
self.update()
def _update_callback(self, _device):
"""Called by the vera device callback to update state."""
self.update_ha_state(True)
def __str__(self):
"""String representation of sensor."""
return "%s %s %s" % (self.name, self.vera_device.device_id, self.state)
VeraDevice.__init__(self, vera_device, controller)
@property
def state(self):
"""Return the name of the sensor."""
return self.current_value
@property
def name(self):
"""Return the mame of the sensor."""
return self._name
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
@ -144,11 +75,6 @@ class VeraSensor(Entity):
attr['Vera Device Id'] = self.vera_device.vera_device_id
return attr
@property
def should_poll(self):
"""No polling needed."""
return False
def update(self):
"""Update the state."""
if self.vera_device.category == "Temperature Sensor":

View File

@ -10,7 +10,7 @@ from homeassistant.const import (CONF_ACCESS_TOKEN, STATE_CLOSED,
STATE_OPEN, TEMP_CELCIUS)
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['python-wink==0.6.2']
REQUIREMENTS = ['python-wink==0.6.4']
SENSOR_TYPES = ['temperature', 'humidity']
@ -74,6 +74,11 @@ class WinkSensorDevice(Entity):
"""Return the name of the sensor if any."""
return self.wink.name()
@property
def available(self):
"""True if connection == True."""
return self.wink.available
def update(self):
"""Update state of the sensor."""
self.wink.update_state()

View File

@ -16,7 +16,8 @@ from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
ATTR_ENTITY_ID)
from homeassistant.components import (
group, wemo, wink, isy994, verisure, zwave, tellduslive, mysensors)
group, wemo, wink, isy994, verisure,
zwave, tellduslive, tellstick, mysensors, vera)
DOMAIN = 'switch'
SCAN_INTERVAL = 30
@ -40,6 +41,8 @@ DISCOVERY_PLATFORMS = {
zwave.DISCOVER_SWITCHES: 'zwave',
tellduslive.DISCOVER_SWITCHES: 'tellduslive',
mysensors.DISCOVER_SWITCHES: 'mysensors',
tellstick.DISCOVER_SWITCHES: 'tellstick',
vera.DISCOVER_SWITCHES: 'vera',
}
PROP_TO_ATTR = {

View File

@ -28,24 +28,31 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
pins = config.get('pins')
for pinnum, pin in pins.items():
if pin.get('name'):
switches.append(ArduinoSwitch(pin.get('name'),
pinnum,
pin.get('type')))
switches.append(ArduinoSwitch(pinnum, pin))
add_devices(switches)
class ArduinoSwitch(SwitchDevice):
"""Representation of an Arduino switch."""
def __init__(self, name, pin, pin_type):
def __init__(self, pin, options):
"""Initialize the Pin."""
self._pin = pin
self._name = name or DEVICE_DEFAULT_NAME
self.pin_type = pin_type
self._name = options.get('name') or DEVICE_DEFAULT_NAME
self.pin_type = options.get('type')
self.direction = 'out'
self._state = False
self._state = options.get('initial', False)
if options.get('negate', False):
self.turn_on_handler = arduino.BOARD.set_digital_out_low
self.turn_off_handler = arduino.BOARD.set_digital_out_high
else:
self.turn_on_handler = arduino.BOARD.set_digital_out_high
self.turn_off_handler = arduino.BOARD.set_digital_out_low
arduino.BOARD.set_mode(self._pin, self.direction, self.pin_type)
(self.turn_on_handler if self._state else self.turn_off_handler)(pin)
@property
def name(self):
@ -60,9 +67,9 @@ class ArduinoSwitch(SwitchDevice):
def turn_on(self):
"""Turn the pin to high/on."""
self._state = True
arduino.BOARD.set_digital_out_high(self._pin)
self.turn_on_handler(self._pin)
def turn_off(self):
"""Turn the pin to low/off."""
self._state = False
arduino.BOARD.set_digital_out_low(self._pin)
self.turn_off_handler(self._pin)

View File

@ -33,6 +33,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
add_devices_callback(devices)
# pylint: disable=too-many-instance-attributes
class CommandSwitch(SwitchDevice):
"""Representation a switch that can be toggled using shell commands."""
@ -92,6 +93,11 @@ class CommandSwitch(SwitchDevice):
"""Return true if device is on."""
return self._state
@property
def assumed_state(self):
"""Return true if we do optimistic updates."""
return self._command_state is False
def _query_state(self):
"""Query for state."""
if not self._command_state:

View File

@ -0,0 +1,131 @@
"""
Switch logic for loading/unloading pulseaudio loopback modules.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.pulseaudio_loopback/
"""
import logging
import re
import socket
from homeassistant.components.switch import SwitchDevice
from homeassistant.util import convert
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "paloopback"
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 4712
DEFAULT_BUFFER_SIZE = 1024
DEFAULT_TCP_TIMEOUT = 3
LOAD_CMD = "load-module module-loopback sink={0} source={1}"
UNLOAD_CMD = "unload-module {0}"
MOD_REGEX = r"index: ([0-9]+)\s+name: <module-loopback>" \
r"\s+argument: <sink={0} source={1}>"
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Read in all of our configuration, and initialize the loopback switch."""
if config.get('sink_name') is None:
_LOGGER.error("Missing required variable: sink_name")
return False
if config.get('source_name') is None:
_LOGGER.error("Missing required variable: source_name")
return False
add_devices_callback([PALoopbackSwitch(
hass,
convert(config.get('name'), str, DEFAULT_NAME),
convert(config.get('host'), str, DEFAULT_HOST),
convert(config.get('port'), int, DEFAULT_PORT),
convert(config.get('buffer_size'), int, DEFAULT_BUFFER_SIZE),
convert(config.get('tcp_timeout'), int, DEFAULT_TCP_TIMEOUT),
config.get('sink_name'),
config.get('source_name')
)])
# pylint: disable=too-many-arguments, too-many-instance-attributes
class PALoopbackSwitch(SwitchDevice):
"""Represents the presence or absence of a pa loopback module."""
def __init__(self, hass, name, pa_host, pa_port, buff_sz,
tcp_timeout, sink_name, source_name):
"""Initialize the switch."""
self._module_idx = -1
self._hass = hass
self._name = name
self._pa_host = pa_host
self._pa_port = int(pa_port)
self._sink_name = sink_name
self._source_name = source_name
self._buffer_size = int(buff_sz)
self._tcp_timeout = int(tcp_timeout)
@property
def name(self):
"""Return the name of the switch."""
return self._name
@property
def is_on(self):
"""Tell the core logic if device is on."""
return self._module_idx > 0
def _send_command(self, cmd, response_expected):
"""Send a command to the pa server using a socket."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(self._tcp_timeout)
try:
sock.connect((self._pa_host, self._pa_port))
_LOGGER.info("Calling pulseaudio:" + cmd)
sock.send((cmd + "\n").encode("utf-8"))
if response_expected:
return_data = self._get_full_response(sock)
_LOGGER.debug("Data received from pulseaudio: " + return_data)
else:
return_data = ""
finally:
sock.close()
return return_data
def _get_full_response(self, sock):
"""Helper method to get the full response back from pulseaudio."""
result = ""
rcv_buffer = sock.recv(self._buffer_size)
result += rcv_buffer.decode("utf-8")
while len(rcv_buffer) == self._buffer_size:
rcv_buffer = sock.recv(self._buffer_size)
result += rcv_buffer.decode("utf-8")
return result
def turn_on(self, **kwargs):
"""Turn the device on."""
self._send_command(str.format(LOAD_CMD,
self._sink_name,
self._source_name),
False)
self.update()
self.update_ha_state()
def turn_off(self, **kwargs):
"""Turn the device off."""
self._send_command(str.format(UNLOAD_CMD, self._module_idx), False)
self.update()
self.update_ha_state()
def update(self):
"""Refresh state in case an alternate process modified this data."""
return_data = self._send_command("list-modules", True)
result = re.search(str.format(MOD_REGEX,
re.escape(self._sink_name),
re.escape(self._source_name)),
return_data)
if result and result.group(1).isdigit():
self._module_idx = int(result.group(1))
else:
self._module_idx = -1

View File

@ -4,98 +4,56 @@ Support for Tellstick switches.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.tellstick/
"""
import logging
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.components import tellstick
from homeassistant.components.tellstick import (ATTR_DISCOVER_DEVICES,
ATTR_DISCOVER_CONFIG)
from homeassistant.helpers.entity import ToggleEntity
SIGNAL_REPETITIONS = 1
REQUIREMENTS = ['tellcore-py==1.1.2']
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Tellstick switches."""
import tellcore.telldus as telldus
import tellcore.constants as tellcore_constants
from tellcore.library import DirectCallbackDispatcher
if (discovery_info is None or
discovery_info[ATTR_DISCOVER_DEVICES] is None or
tellstick.TELLCORE_REGISTRY is None):
return
core = telldus.TelldusCore(callback_dispatcher=DirectCallbackDispatcher())
signal_repetitions = config.get('signal_repetitions', SIGNAL_REPETITIONS)
switches_and_lights = core.devices()
# Allow platform level override, fallback to module config
signal_repetitions = discovery_info.get(
ATTR_DISCOVER_CONFIG, tellstick.DEFAULT_SIGNAL_REPETITIONS)
switches = []
for switch in switches_and_lights:
if not switch.methods(tellcore_constants.TELLSTICK_DIM):
switches.append(
TellstickSwitchDevice(switch, signal_repetitions))
def _device_event_callback(id_, method, data, cid):
"""Called from the TelldusCore library to update one device."""
for switch_device in switches:
if switch_device.tellstick_device.id == id_:
switch_device.update_ha_state()
break
callback_id = core.register_device_event(_device_event_callback)
def unload_telldus_lib(event):
"""Un-register the callback bindings."""
if callback_id is not None:
core.unregister_callback(callback_id)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, unload_telldus_lib)
add_devices_callback(switches)
add_devices(TellstickSwitchDevice(
tellstick.TELLCORE_REGISTRY.get_device(switch_id), signal_repetitions)
for switch_id in discovery_info[ATTR_DISCOVER_DEVICES])
class TellstickSwitchDevice(ToggleEntity):
class TellstickSwitchDevice(tellstick.TellstickDevice, ToggleEntity):
"""Representation of a Tellstick switch."""
def __init__(self, tellstick_device, signal_repetitions):
"""Initialize the Tellstick switch."""
import tellcore.constants as tellcore_constants
self.tellstick_device = tellstick_device
self.signal_repetitions = signal_repetitions
self.last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON |
tellcore_constants.TELLSTICK_TURNOFF)
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def assumed_state(self):
"""The Tellstick devices are always assumed state."""
return True
@property
def name(self):
"""Return the name of the switch if any."""
return self.tellstick_device.name
@property
def is_on(self):
"""Return true if switch is on."""
import tellcore.constants as tellcore_constants
return self._state
last_command = self.tellstick_device.last_sent_command(
self.last_sent_command_mask)
def set_tellstick_state(self, last_command_sent, last_data_sent):
"""Update the internal representation of the switch."""
from tellcore.constants import TELLSTICK_TURNON
self._state = last_command_sent == TELLSTICK_TURNON
return last_command == tellcore_constants.TELLSTICK_TURNON
def _send_tellstick_command(self, command, data):
"""Handle the turn_on / turn_off commands."""
from tellcore.constants import TELLSTICK_TURNON, TELLSTICK_TURNOFF
if command == TELLSTICK_TURNON:
self.tellstick_device.turn_on()
elif command == TELLSTICK_TURNOFF:
self.tellstick_device.turn_off()
def turn_on(self, **kwargs):
"""Turn the switch on."""
for _ in range(self.signal_repetitions):
self.tellstick_device.turn_on()
self.update_ha_state()
from tellcore.constants import TELLSTICK_TURNON
self.call_tellstick(TELLSTICK_TURNON)
def turn_off(self, **kwargs):
"""Turn the switch off."""
for _ in range(self.signal_repetitions):
self.tellstick_device.turn_off()
self.update_ha_state()
from tellcore.constants import TELLSTICK_TURNOFF
self.call_tellstick(TELLSTICK_TURNOFF)

View File

@ -6,7 +6,7 @@ https://home-assistant.io/components/switch.template/
"""
import logging
from homeassistant.components.switch import DOMAIN, SwitchDevice
from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchDevice
from homeassistant.const import (
ATTR_FRIENDLY_NAME, CONF_VALUE_TEMPLATE, STATE_OFF, STATE_ON)
from homeassistant.core import EVENT_STATE_CHANGED
@ -16,17 +16,14 @@ from homeassistant.helpers.service import call_from_config
from homeassistant.helpers import template
from homeassistant.util import slugify
ENTITY_ID_FORMAT = DOMAIN + '.{}'
_LOGGER = logging.getLogger(__name__)
CONF_SWITCHES = 'switches'
STATE_ERROR = 'error'
ON_ACTION = 'turn_on'
OFF_ACTION = 'turn_off'
_LOGGER = logging.getLogger(__name__)
_VALID_STATES = [STATE_ON, STATE_OFF, 'true', 'false']
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
@ -84,32 +81,44 @@ class SwitchTemplate(SwitchDevice):
def __init__(self, hass, device_id, friendly_name, state_template,
on_action, off_action):
"""Initialize the Template switch."""
self.hass = hass
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, device_id,
hass=hass)
self.hass = hass
self._name = friendly_name
self._template = state_template
self._on_action = on_action
self._off_action = off_action
self.update()
self.hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
self._state = False
def _event_listener(self, event):
"""Called when the target device changes state."""
if not hasattr(self, 'hass'):
return
self.update_ha_state(True)
self.update()
def template_switch_event_listener(event):
"""Called when the target device changes state."""
self.update_ha_state(True)
hass.bus.listen(EVENT_STATE_CHANGED,
template_switch_event_listener)
@property
def name(self):
"""Return the name of the switch."""
return self._name
@property
def is_on(self):
"""Return true if device is on."""
return self._state
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def available(self):
"""If switch is available."""
return self._state is not None
def turn_on(self, **kwargs):
"""Fire the on action."""
call_from_config(self.hass, self._on_action, True)
@ -118,30 +127,19 @@ class SwitchTemplate(SwitchDevice):
"""Fire the off action."""
call_from_config(self.hass, self._off_action, True)
@property
def is_on(self):
"""Return true if device is on."""
return self._value.lower() == 'true' or self._value == STATE_ON
@property
def is_off(self):
"""Return true if device is off."""
return self._value.lower() == 'false' or self._value == STATE_OFF
@property
def available(self):
"""Return true if entity is available."""
return self.is_on or self.is_off
def update(self):
"""Update the state from the template."""
try:
self._value = template.render(self.hass, self._template)
if not self.available:
state = template.render(self.hass, self._template).lower()
if state in _VALID_STATES:
self._state = state in ('true', STATE_ON)
else:
_LOGGER.error(
"`%s` is not a switch state, setting %s to unavailable",
self._value, self.entity_id)
'Received invalid switch is_on state: %s. Expected: %s',
state, ', '.join(_VALID_STATES))
self._state = None
except TemplateError as ex:
self._value = STATE_ERROR
_LOGGER.error(ex)
self._state = None

View File

@ -6,94 +6,33 @@ https://home-assistant.io/components/switch.vera/
"""
import logging
from requests.exceptions import RequestException
import homeassistant.util.dt as dt_util
from homeassistant.components.switch import SwitchDevice
from homeassistant.const import (
ATTR_ARMED, ATTR_BATTERY_LEVEL, ATTR_LAST_TRIP_TIME, ATTR_TRIPPED,
EVENT_HOMEASSISTANT_STOP, STATE_OFF, STATE_ON)
STATE_OFF, STATE_ON)
from homeassistant.components.vera import (
VeraDevice, VERA_DEVICES, VERA_CONTROLLER)
REQUIREMENTS = ['pyvera==0.2.8']
DEPENDENCIES = ['vera']
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def get_devices(hass, config):
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Find and return Vera switches."""
import pyvera as veraApi
base_url = config.get('vera_controller_url')
if not base_url:
_LOGGER.error(
"The required parameter 'vera_controller_url'"
" was not found in config"
)
return False
device_data = config.get('device_data', {})
vera_controller, created = veraApi.init_controller(base_url)
if created:
def stop_subscription(event):
"""Shutdown Vera subscriptions and subscription thread on exit."""
_LOGGER.info("Shutting down subscriptions.")
vera_controller.stop()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_subscription)
devices = []
try:
devices = vera_controller.get_devices([
'Switch', 'Armable Sensor', 'On/Off Switch'])
except RequestException:
# There was a network related error connecting to the vera controller.
_LOGGER.exception("Error communicating with Vera API")
return False
vera_switches = []
for device in devices:
extra_data = device_data.get(device.device_id, {})
exclude = extra_data.get('exclude', False)
if exclude is not True:
vera_switches.append(
VeraSwitch(device, vera_controller, extra_data))
return vera_switches
add_devices_callback(
VeraSwitch(device, VERA_CONTROLLER) for
device in VERA_DEVICES['switch'])
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Find and return Vera lights."""
add_devices(get_devices(hass, config))
class VeraSwitch(SwitchDevice):
class VeraSwitch(VeraDevice, SwitchDevice):
"""Representation of a Vera Switch."""
def __init__(self, vera_device, controller, extra_data=None):
def __init__(self, vera_device, controller):
"""Initialize the Vera device."""
self.vera_device = vera_device
self.extra_data = extra_data
self.controller = controller
if self.extra_data and self.extra_data.get('name'):
self._name = self.extra_data.get('name')
else:
self._name = self.vera_device.name
self._state = STATE_OFF
self.controller.register(vera_device, self._update_callback)
self.update()
def _update_callback(self, _device):
self.update_ha_state(True)
@property
def name(self):
"""Return the mame of the switch."""
return self._name
self._state = False
VeraDevice.__init__(self, vera_device, controller)
@property
def device_state_attributes(self):
@ -134,19 +73,11 @@ class VeraSwitch(SwitchDevice):
self._state = STATE_OFF
self.update_ha_state()
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def is_on(self):
"""Return true if device is on."""
return self._state == STATE_ON
return self._state
def update(self):
"""Called by the vera device callback to update state."""
if self.vera_device.is_switched_on():
self._state = STATE_ON
else:
self._state = STATE_OFF
self._state = self.vera_device.is_switched_on()

View File

@ -0,0 +1,79 @@
"""
Support for wake on lan.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.wake_on_lan/
"""
import logging
import platform
import subprocess as sp
from homeassistant.components.switch import SwitchDevice
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['wakeonlan==0.2.2']
DEFAULT_NAME = "Wake on LAN"
DEFAULT_PING_TIMEOUT = 1
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Add wake on lan switch."""
if config.get('mac_address') is None:
_LOGGER.error("Missing required variable: mac_address")
return False
add_devices_callback([WOLSwitch(
hass,
config.get('name', DEFAULT_NAME),
config.get('host'),
config.get('mac_address'),
)])
class WOLSwitch(SwitchDevice):
"""Representation of a wake on lan switch."""
def __init__(self, hass, name, host, mac_address):
"""Initialize the WOL switch."""
from wakeonlan import wol
self._hass = hass
self._name = name
self._host = host
self._mac_address = mac_address
self._state = False
self._wol = wol
self.update()
@property
def should_poll(self):
"""Poll for status regularly."""
return True
@property
def is_on(self):
"""True if switch is on."""
return self._state
@property
def name(self):
"""The name of the switch."""
return self._name
def turn_on(self):
"""Turn the device on."""
self._wol.send_magic_packet(self._mac_address)
self.update_ha_state()
def update(self):
"""Check if device is on and update the state."""
if platform.system().lower() == "windows":
ping_cmd = "ping -n 1 -w {} {}"\
.format(DEFAULT_PING_TIMEOUT * 1000, self._host)
else:
ping_cmd = "ping -c 1 -W {} {}"\
.format(DEFAULT_PING_TIMEOUT, self._host)
status = sp.getstatusoutput(ping_cmd)[0]
self._state = not bool(status)

View File

@ -63,6 +63,9 @@ class WemoSwitch(SwitchDevice):
_LOGGER.info(
'Subscription update for %s',
_device)
if not hasattr(self, 'hass'):
self.update()
return
self.update_ha_state(True)
@property

View File

@ -9,7 +9,7 @@ import logging
from homeassistant.components.wink import WinkToggleDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.2']
REQUIREMENTS = ['python-wink==0.6.4']
def setup_platform(hass, config, add_devices, discovery_info=None):

View File

@ -0,0 +1,217 @@
"""
Tellstick Component.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/Tellstick/
"""
import logging
import threading
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE,
EVENT_PLATFORM_DISCOVERED, EVENT_HOMEASSISTANT_STOP)
from homeassistant.loader import get_component
from homeassistant.helpers.entity import Entity
DOMAIN = "tellstick"
REQUIREMENTS = ['tellcore-py==1.1.2']
_LOGGER = logging.getLogger(__name__)
ATTR_SIGNAL_REPETITIONS = "signal_repetitions"
DEFAULT_SIGNAL_REPETITIONS = 1
DISCOVER_SWITCHES = "tellstick.switches"
DISCOVER_LIGHTS = "tellstick.lights"
DISCOVERY_TYPES = {"switch": DISCOVER_SWITCHES,
"light": DISCOVER_LIGHTS}
ATTR_DISCOVER_DEVICES = "devices"
ATTR_DISCOVER_CONFIG = "config"
# Use a global tellstick domain lock to handle
# tellcore errors then calling to concurrently
TELLSTICK_LOCK = threading.Lock()
# Keep a reference the the callback registry
# Used from entities that register callback listeners
TELLCORE_REGISTRY = None
def _discover(hass, config, found_devices, component_name):
"""Setup and send the discovery event."""
if not len(found_devices):
return
_LOGGER.info("discovered %d new %s devices",
len(found_devices), component_name)
component = get_component(component_name)
bootstrap.setup_component(hass, component.DOMAIN,
config)
signal_repetitions = config[DOMAIN].get(
ATTR_SIGNAL_REPETITIONS, DEFAULT_SIGNAL_REPETITIONS)
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: DISCOVERY_TYPES[component_name],
ATTR_DISCOVERED: {ATTR_DISCOVER_DEVICES: found_devices,
ATTR_DISCOVER_CONFIG:
signal_repetitions}})
def setup(hass, config):
"""Setup the Tellstick component."""
# pylint: disable=global-statement, import-error
global TELLCORE_REGISTRY
import tellcore.telldus as telldus
import tellcore.constants as tellcore_constants
from tellcore.library import DirectCallbackDispatcher
core = telldus.TelldusCore(callback_dispatcher=DirectCallbackDispatcher())
TELLCORE_REGISTRY = TellstickRegistry(hass, core)
devices = core.devices()
# Register devices
TELLCORE_REGISTRY.register_devices(devices)
# Discover the switches
_discover(hass, config, [switch.id for switch in
devices if not switch.methods(
tellcore_constants.TELLSTICK_DIM)],
"switch")
# Discover the lights
_discover(hass, config, [light.id for light in
devices if light.methods(
tellcore_constants.TELLSTICK_DIM)],
"light")
return True
class TellstickRegistry:
"""Handle everything around tellstick callbacks.
Keeps a map device ids to home-assistant entities.
Also responsible for registering / cleanup of callbacks.
All device specific logic should be elsewhere (Entities).
"""
def __init__(self, hass, tellcore_lib):
"""Init the tellstick mappings and callbacks."""
self._core_lib = tellcore_lib
# used when map callback device id to ha entities.
self._id_to_entity_map = {}
self._id_to_device_map = {}
self._setup_device_callback(hass, tellcore_lib)
def _device_callback(self, tellstick_id, method, data, cid):
"""Handle the actual callback from tellcore."""
entity = self._id_to_entity_map.get(tellstick_id, None)
if entity is not None:
entity.set_tellstick_state(method, data)
entity.update_ha_state()
def _setup_device_callback(self, hass, tellcore_lib):
"""Register the callback handler."""
callback_id = tellcore_lib.register_device_event(
self._device_callback)
def clean_up_callback(event):
"""Unregister the callback bindings."""
if callback_id is not None:
tellcore_lib.unregister_callback(callback_id)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, clean_up_callback)
def register_entity(self, tellcore_id, entity):
"""Register a new entity to receive callback updates."""
self._id_to_entity_map[tellcore_id] = entity
def register_devices(self, devices):
"""Register a list of devices."""
self._id_to_device_map.update({device.id:
device for device in devices})
def get_device(self, tellcore_id):
"""Return a device by tellcore_id."""
return self._id_to_device_map.get(tellcore_id, None)
class TellstickDevice(Entity):
"""Represents a Tellstick device.
Contains the common logic for all Tellstick devices.
"""
def __init__(self, tellstick_device, signal_repetitions):
"""Init the tellstick device."""
self.signal_repetitions = signal_repetitions
self._state = None
self.tellstick_device = tellstick_device
# add to id to entity mapping
TELLCORE_REGISTRY.register_entity(tellstick_device.id, self)
# Query tellcore for the current state
self.update()
@property
def should_poll(self):
"""Tell Home Assistant not to poll this entity."""
return False
@property
def assumed_state(self):
"""Tellstick devices are always assumed state."""
return True
@property
def name(self):
"""Return the name of the switch if any."""
return self.tellstick_device.name
def set_tellstick_state(self, last_command_sent, last_data_sent):
"""Set the private switch state."""
raise NotImplementedError(
"set_tellstick_state needs to be implemented.")
def _send_tellstick_command(self, command, data):
"""Do the actual call to the tellstick device."""
raise NotImplementedError(
"_call_tellstick needs to be implemented.")
def call_tellstick(self, command, data=None):
"""Send a command to the device."""
from tellcore.library import TelldusError
with TELLSTICK_LOCK:
try:
for _ in range(self.signal_repetitions):
self._send_tellstick_command(command, data)
# Update the internal state
self.set_tellstick_state(command, data)
self.update_ha_state()
except TelldusError:
_LOGGER.error(TelldusError)
def update(self):
"""Poll the current state of the device."""
import tellcore.constants as tellcore_constants
from tellcore.library import TelldusError
try:
last_command = self.tellstick_device.last_sent_command(
tellcore_constants.TELLSTICK_TURNON |
tellcore_constants.TELLSTICK_TURNOFF |
tellcore_constants.TELLSTICK_DIM
)
last_value = self.tellstick_device.last_sent_value()
self.set_tellstick_state(last_command, last_value)
except TelldusError:
_LOGGER.error(TelldusError)

View File

@ -123,6 +123,9 @@ def setup(hass, config):
service.data.get(ATTR_TEMPERATURE), float)
if temperature is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_TEMPERATURE, ATTR_TEMPERATURE)
return
for thermostat in target_thermostats:

View File

@ -11,8 +11,8 @@ from homeassistant.const import TEMP_CELCIUS, TEMP_FAHRENHEIT
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo thermostats."""
add_devices([
DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19),
DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77),
DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19, False),
DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77, True),
])
@ -21,13 +21,14 @@ class DemoThermostat(ThermostatDevice):
"""Representation of a demo thermostat."""
def __init__(self, name, target_temperature, unit_of_measurement,
away, current_temperature):
away, current_temperature, is_fan_on):
"""Initialize the thermostat."""
self._name = name
self._target_temperature = target_temperature
self._unit_of_measurement = unit_of_measurement
self._away = away
self._current_temperature = current_temperature
self._is_fan_on = is_fan_on
@property
def should_poll(self):
@ -59,6 +60,11 @@ class DemoThermostat(ThermostatDevice):
"""Return if away mode is on."""
return self._away
@property
def is_fan_on(self):
"""Return true if the fan is on."""
return self._is_fan_on
def set_temperature(self, temperature):
"""Set new target temperature."""
self._target_temperature = temperature
@ -70,3 +76,11 @@ class DemoThermostat(ThermostatDevice):
def turn_away_mode_off(self):
"""Turn away mode off."""
self._away = False
def turn_fan_on(self):
"""Turn fan on."""
self._is_fan_on = True
def turn_fan_off(self):
"""Turn fan off."""
self._is_fan_on = False

View File

@ -0,0 +1,142 @@
"""
Support for Vera devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/vera/
"""
import logging
from collections import defaultdict
from requests.exceptions import RequestException
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_SERVICE, ATTR_DISCOVERED,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component
REQUIREMENTS = ['pyvera==0.2.8']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'vera'
VERA_CONTROLLER = None
CONF_EXCLUDE = 'exclude'
CONF_LIGHTS = 'lights'
BINARY_SENSOR = 'binary_sensor'
SENSOR = 'sensor'
LIGHT = 'light'
SWITCH = 'switch'
DEVICE_CATEGORIES = {
'Sensor': BINARY_SENSOR,
'Temperature Sensor': SENSOR,
'Light Sensor': SENSOR,
'Humidity Sensor': SENSOR,
'Dimmable Switch': LIGHT,
'Switch': SWITCH,
'Armable Sensor': SWITCH,
'On/Off Switch': SWITCH,
# 'Window Covering': NOT SUPPORTED YET
}
DISCOVER_BINARY_SENSORS = 'vera.binary_sensors'
DISCOVER_SENSORS = 'vera.sensors'
DISCOVER_LIGHTS = 'vera.lights'
DISCOVER_SWITCHES = 'vera.switchs'
VERA_DEVICES = defaultdict(list)
# pylint: disable=unused-argument, too-many-function-args
def setup(hass, base_config):
"""Common setup for Vera devices."""
global VERA_CONTROLLER
import pyvera as veraApi
config = base_config.get(DOMAIN)
base_url = config.get('vera_controller_url')
if not base_url:
_LOGGER.error(
"The required parameter 'vera_controller_url'"
" was not found in config"
)
return False
VERA_CONTROLLER, _ = veraApi.init_controller(base_url)
def stop_subscription(event):
"""Shutdown Vera subscriptions and subscription thread on exit."""
_LOGGER.info("Shutting down subscriptions.")
VERA_CONTROLLER.stop()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_subscription)
try:
all_devices = VERA_CONTROLLER.get_devices(
list(DEVICE_CATEGORIES.keys()))
except RequestException:
# There was a network related error connecting to the vera controller.
_LOGGER.exception("Error communicating with Vera API")
return False
exclude = config.get(CONF_EXCLUDE, [])
if not isinstance(exclude, list):
_LOGGER.error("'exclude' must be a list of device_ids")
return False
lights_ids = config.get(CONF_LIGHTS, [])
if not isinstance(lights_ids, list):
_LOGGER.error("'lights' must be a list of device_ids")
return False
for device in all_devices:
if device.device_id in exclude:
continue
dev_type = DEVICE_CATEGORIES.get(device.category)
if dev_type is None:
continue
if dev_type == SWITCH and device.device_id in lights_ids:
dev_type = LIGHT
VERA_DEVICES[dev_type].append(device)
for comp_name, discovery in (((BINARY_SENSOR, DISCOVER_BINARY_SENSORS),
(SENSOR, DISCOVER_SENSORS),
(LIGHT, DISCOVER_LIGHTS),
(SWITCH, DISCOVER_SWITCHES))):
component = get_component(comp_name)
bootstrap.setup_component(hass, component.DOMAIN, config)
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery,
ATTR_DISCOVERED: {}})
return True
class VeraDevice(Entity):
"""Representation of a Vera devicetity."""
def __init__(self, vera_device, controller):
"""Initialize the device."""
self.vera_device = vera_device
self.controller = controller
self._name = self.vera_device.name
self.controller.register(vera_device, self._update_callback)
self.update()
def _update_callback(self, _device):
self.update_ha_state(True)
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def should_poll(self):
"""No polling needed."""
return False

View File

@ -23,7 +23,7 @@ DISCOVER_SWITCHES = 'verisure.switches'
DISCOVER_ALARMS = 'verisure.alarm_control_panel'
DISCOVER_LOCKS = 'verisure.lock'
REQUIREMENTS = ['vsure==0.6.1']
REQUIREMENTS = ['vsure==0.7.1']
_LOGGER = logging.getLogger(__name__)

View File

@ -9,7 +9,7 @@ import logging
from homeassistant.components import discovery
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
REQUIREMENTS = ['pywemo==0.3.12']
REQUIREMENTS = ['pywemo==0.3.19']
DOMAIN = 'wemo'
DISCOVER_LIGHTS = 'wemo.light'

View File

@ -15,7 +15,7 @@ from homeassistant.helpers.entity import ToggleEntity
from homeassistant.loader import get_component
DOMAIN = "wink"
REQUIREMENTS = ['python-wink==0.6.2']
REQUIREMENTS = ['python-wink==0.6.4']
DISCOVER_LIGHTS = "wink.lights"
DISCOVER_SWITCHES = "wink.switches"
@ -84,6 +84,11 @@ class WinkToggleDevice(ToggleEntity):
"""Return true if device is on."""
return self.wink.state()
@property
def available(self):
"""True if connection == True."""
return self.wink.available
def turn_on(self, **kwargs):
"""Turn the device on."""
self.wink.set_state(True)

View File

@ -28,6 +28,8 @@ DEFAULT_ZWAVE_CONFIG_PATH = os.path.join(sys.prefix, 'share',
SERVICE_ADD_NODE = "add_node"
SERVICE_REMOVE_NODE = "remove_node"
SERVICE_HEAL_NETWORK = "heal_network"
SERVICE_SOFT_RESET = "soft_reset"
DISCOVER_SENSORS = "zwave.sensors"
DISCOVER_SWITCHES = "zwave.switch"
@ -149,6 +151,7 @@ def get_config_value(node, value_index):
return get_config_value(node, value_index)
# pylint: disable=R0914
def setup(hass, config):
"""Setup Z-Wave.
@ -249,6 +252,14 @@ def setup(hass, config):
"""Switch into exclusion mode."""
NETWORK.controller.begin_command_remove_device()
def heal_network(event):
"""Heal the network."""
NETWORK.heal()
def soft_reset(event):
"""Soft reset the controller."""
NETWORK.controller.soft_reset()
def stop_zwave(event):
"""Stop Z-Wave."""
NETWORK.stop()
@ -268,6 +279,8 @@ def setup(hass, config):
# hardware inclusion button
hass.services.register(DOMAIN, SERVICE_ADD_NODE, add_node)
hass.services.register(DOMAIN, SERVICE_REMOVE_NODE, remove_node)
hass.services.register(DOMAIN, SERVICE_HEAL_NETWORK, heal_network)
hass.services.register(DOMAIN, SERVICE_SOFT_RESET, soft_reset)
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave)

View File

@ -1,7 +1,7 @@
# coding: utf-8
"""Constants used by Home Assistant components."""
__version__ = "0.15.0"
__version__ = "0.16.0"
REQUIRED_PYTHON_VER = (3, 4)
# Can be used to specify a catch all when registering state or event listeners.

View File

@ -775,7 +775,7 @@ def create_timer(hass, interval=TIMER_INTERVAL):
def start_timer(event):
"""Start the timer."""
thread = threading.Thread(target=timer)
thread = threading.Thread(target=timer, name='Timer')
thread.daemon = True
thread.start()

View File

@ -18,7 +18,7 @@ ENTITY_ID_PATTERN = re.compile(r"^(\w+)\.(\w+)$")
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None):
"""Generate a unique entity ID based on given entity IDs or used ids."""
"""Generate a unique entity ID based on given entity IDs or used IDs."""
name = (name or DEVICE_DEFAULT_NAME).lower()
if current_ids is None:
if hass is None:
@ -41,12 +41,12 @@ def valid_entity_id(entity_id):
class Entity(object):
"""ABC for Home Assistant entities."""
"""An abstract class for Home Assistant entities."""
# pylint: disable=no-self-use
# SAFE TO OVERWRITE
# The properties and methods here are safe to overwrite when inherting this
# class. These may be used to customize the behavior of the entity.
# The properties and methods here are safe to overwrite when inheriting
# this class. These may be used to customize the behavior of the entity.
@property
def should_poll(self):
"""Return True if entity has to be polled for state.
@ -57,7 +57,7 @@ class Entity(object):
@property
def unique_id(self):
"""Return a unique id."""
"""Return an unique ID."""
return "{}.{}".format(self.__class__, id(self))
@property
@ -113,7 +113,7 @@ class Entity(object):
@property
def assumed_state(self):
"""Return True if unable to access real state of entity."""
"""Return True if unable to access real state of the entity."""
return False
def update(self):
@ -223,7 +223,7 @@ class Entity(object):
class ToggleEntity(Entity):
"""ABC for entities that can be turned on and off."""
"""An abstract class for entities that can be turned on and off."""
# pylint: disable=no-self-use
@property

View File

@ -331,7 +331,9 @@ class ThreadPool(object):
if not self.running:
raise RuntimeError("ThreadPool not running")
worker = threading.Thread(target=self._worker)
worker = threading.Thread(
target=self._worker,
name='ThreadPool Worker {}'.format(self.worker_count))
worker.daemon = True
worker.start()

View File

@ -31,7 +31,7 @@ astral==0.9
blinkstick==1.1.7
# homeassistant.components.sensor.bitcoin
blockchain==1.2.1
blockchain==1.3.1
# homeassistant.components.notify.xmpp
dnspython3==1.12.0
@ -54,6 +54,12 @@ freesms==0.1.0
# homeassistant.components.conversation
fuzzywuzzy==0.8.0
# homeassistant.components.notify.gntp
gntp==1.0.3
# homeassistant.components.mqtt.server
hbmqtt==0.6.3
# homeassistant.components.thermostat.heatmiser
heatmiserV3==0.9.1
@ -63,9 +69,6 @@ hikvision==0.4
# homeassistant.components.sensor.dht
# http://github.com/mala-zaba/Adafruit_Python_DHT/archive/4101340de8d2457dd194bca1e8d11cbfc237e919.zip#Adafruit_DHT==1.1.0
# homeassistant.components.rfxtrx
https://github.com/Danielhiversen/pyRFXtrx/archive/0.5.zip#pyRFXtrx==0.5
# homeassistant.components.sensor.netatmo
https://github.com/HydrelioxGitHub/netatmo-api-python/archive/43ff238a0122b0939a0dc4e8836b6782913fb6e2.zip#lnetatmo==0.4.0
@ -78,6 +81,9 @@ https://github.com/Xorso/pyalarmdotcom/archive/0.1.1.zip#pyalarmdotcom==0.1.1
# homeassistant.components.modbus
https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b612661.zip#pymodbus==1.2.0
# homeassistant.components.sensor.uber
https://github.com/denismakogon/rides-python-sdk/archive/py3-support.zip#uber_rides==0.1.2-dev
# homeassistant.components.sensor.sabnzbd
https://github.com/jamespcole/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1
@ -114,12 +120,15 @@ liffylights==0.9.4
# homeassistant.components.light.limitlessled
limitlessled==1.0.0
# homeassistant.components.notify.message_bird
messagebird==1.1.1
# homeassistant.components.sensor.mfi
# homeassistant.components.switch.mfi
mficlient==0.3.0
# homeassistant.components.discovery
netdisco==0.5.4
netdisco==0.5.5
# homeassistant.components.sensor.neurio_energy
neurio==0.2.10
@ -143,7 +152,7 @@ plexapi==1.1.0
proliphix==0.1.0
# homeassistant.components.sensor.systemmonitor
psutil==4.0.0
psutil==4.1.0
# homeassistant.components.notify.pushbullet
pushbullet.py==0.9.0
@ -154,6 +163,9 @@ pushetta==1.0.15
# homeassistant.components.sensor.cpuspeed
py-cpuinfo==0.2.3
# homeassistant.components.rfxtrx
pyRFXtrx==0.6.5
# homeassistant.components.media_player.cast
pychromecast==0.7.2
@ -164,7 +176,7 @@ pydispatcher==2.0.5
pyfttt==0.3
# homeassistant.components.device_tracker.icloud
pyicloud==0.7.2
pyicloud==0.8.1
# homeassistant.components.device_tracker.netgear
pynetgear==0.3.2
@ -180,16 +192,16 @@ pyowm==2.3.0
pysnmp==4.2.5
# homeassistant.components.sensor.forecast
python-forecastio==1.3.3
python-forecastio==1.3.4
# homeassistant.components.media_player.mpd
python-mpd2==0.5.4
python-mpd2==0.5.5
# homeassistant.components.nest
python-nest==2.6.0
# homeassistant.components.device_tracker.nmap_tracker
python-nmap==0.4.3
python-nmap==0.6.0
# homeassistant.components.notify.pushover
python-pushover==0.2
@ -198,7 +210,7 @@ python-pushover==0.2
python-statsd==1.7.2
# homeassistant.components.notify.telegram
python-telegram-bot==3.2.0
python-telegram-bot==3.4
# homeassistant.components.sensor.twitch
python-twitch==1.2.0
@ -210,22 +222,23 @@ python-twitch==1.2.0
# homeassistant.components.lock.wink
# homeassistant.components.sensor.wink
# homeassistant.components.switch.wink
python-wink==0.6.2
python-wink==0.6.4
# homeassistant.components.keyboard
pyuserinput==0.1.9
# homeassistant.components.light.vera
# homeassistant.components.sensor.vera
# homeassistant.components.switch.vera
# homeassistant.components.vera
pyvera==0.2.8
# homeassistant.components.wemo
pywemo==0.3.12
pywemo==0.3.19
# homeassistant.components.thermostat.radiotherm
radiotherm==1.2
# homeassistant.components.media_player.yamaha
rxv==0.1.9
# homeassistant.components.media_player.samsungtv
samsungctl==0.5.1
@ -256,9 +269,8 @@ speedtest-cli==0.3.4
# homeassistant.components.sensor.steam_online
steamodd==4.21
# homeassistant.components.light.tellstick
# homeassistant.components.tellstick
# homeassistant.components.sensor.tellstick
# homeassistant.components.switch.tellstick
tellcore-py==1.1.2
# homeassistant.components.tellduslive
@ -278,7 +290,10 @@ urllib3
uvcclient==0.8
# homeassistant.components.verisure
vsure==0.6.1
vsure==0.7.1
# homeassistant.components.switch.wake_on_lan
wakeonlan==0.2.2
# homeassistant.components.zigbee
xbee-helper==0.0.6

View File

@ -24,50 +24,6 @@ class TestAutomationEvent(unittest.TestCase):
""""Stop everything that was started."""
self.hass.stop()
def test_old_config_if_fires_on_event(self):
"""."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation'
}
}))
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_on_event_with_data(self):
"""Test old configuration ."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'event_data': {'some_attr': 'some_value'},
'execute_service': 'test.automation'
}
}))
self.hass.bus.fire('test_event', {'some_attr': 'some_value'})
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_not_fires_if_event_data_not_matches(self):
"""test old configuration."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'event_data': {'some_attr': 'some_value'},
'execute_service': 'test.automation'
}
}))
self.hass.bus.fire('test_event', {'some_attr': 'some_other_value'})
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_fires_on_event(self):
"""Test the firing of events."""
self.assertTrue(automation.setup(self.hass, {

View File

@ -24,71 +24,6 @@ class TestAutomation(unittest.TestCase):
"""Stop everything that was started."""
self.hass.stop()
def test_old_config_service_data_not_a_dict(self):
"""Test old configuration service data."""
automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'service_data': 100
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_service_specify_data(self):
"""Test old configuration service data."""
automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'service_data': {'some': 'data'}
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual('data', self.calls[0].data['some'])
def test_old_config_service_specify_entity_id(self):
"""Test old configuration service data."""
automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'service_entity_id': 'hello.world'
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(['hello.world'],
self.calls[0].data.get(ATTR_ENTITY_ID))
def test_old_config_service_specify_entity_id_list(self):
"""Test old configuration service data."""
automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'service_entity_id': ['hello.world', 'hello.world2']
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(['hello.world', 'hello.world2'],
self.calls[0].data.get(ATTR_ENTITY_ID))
def test_service_data_not_a_dict(self):
"""Test service data not dict."""
automation.setup(self.hass, {

View File

@ -24,50 +24,6 @@ class TestAutomationMQTT(unittest.TestCase):
"""Stop everything that was started."""
self.hass.stop()
def test_old_config_if_fires_on_topic_match(self):
"""Test if message is fired on topic match."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'mqtt',
'mqtt_topic': 'test-topic',
'execute_service': 'test.automation'
}
}))
fire_mqtt_message(self.hass, 'test-topic', '')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_on_topic_and_payload_match(self):
"""Test if message is fired on topic and payload match."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'mqtt',
'mqtt_topic': 'test-topic',
'mqtt_payload': 'hello',
'execute_service': 'test.automation'
}
}))
fire_mqtt_message(self.hass, 'test-topic', 'hello')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_not_fires_on_topic_but_no_payload_match(self):
"""Test if message is not fired on topic but no payload."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'mqtt',
'mqtt_topic': 'test-topic',
'mqtt_payload': 'hello',
'execute_service': 'test.automation'
}
}))
fire_mqtt_message(self.hass, 'test-topic', 'no-hello')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_fires_on_topic_match(self):
"""Test if message is fired on topic match."""
self.assertTrue(automation.setup(self.hass, {

View File

@ -28,143 +28,6 @@ class TestAutomationState(unittest.TestCase):
"""Stop everything that was started."""
self.hass.stop()
def test_old_config_if_fires_on_entity_change(self):
"""Test for firing if entity change ."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'state',
'state_entity_id': 'test.entity',
'execute_service': 'test.automation'
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_on_entity_change_with_from_filter(self):
"""Test for firing on entity change with filter."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'state',
'state_entity_id': 'test.entity',
'state_from': 'hello',
'execute_service': 'test.automation'
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_on_entity_change_with_to_filter(self):
"""Test for firing on entity change no filter."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'state',
'state_entity_id': 'test.entity',
'state_to': 'world',
'execute_service': 'test.automation'
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_on_entity_change_with_both_filters(self):
"""Test for firing on entity change with both filters."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'state',
'state_entity_id': 'test.entity',
'state_from': 'hello',
'state_to': 'world',
'execute_service': 'test.automation'
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_not_fires_if_to_filter_not_match(self):
"""Test for not firing if no match."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'state',
'state_entity_id': 'test.entity',
'state_from': 'hello',
'state_to': 'world',
'execute_service': 'test.automation'
}
}))
self.hass.states.set('test.entity', 'moon')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_old_config_if_not_fires_if_from_filter_not_match(self):
"""Test for no firing if no match."""
self.hass.states.set('test.entity', 'bye')
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'state',
'state_entity_id': 'test.entity',
'state_from': 'hello',
'state_to': 'world',
'execute_service': 'test.automation'
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_old_config_if_not_fires_if_entity_not_match(self):
"""Test for not firing if no match."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'state',
'state_entity_id': 'test.another_entity',
'execute_service': 'test.automation'
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_old_config_if_action(self):
"""Test for if action."""
entity_id = 'domain.test_entity'
test_state = 'new_state'
automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'if': [{
'platform': 'state',
'entity_id': entity_id,
'state': test_state,
}]
}
})
self.hass.states.set(entity_id, test_state)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set(entity_id, test_state + 'something')
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change(self):
"""Test for firing on entity change."""
self.assertTrue(automation.setup(self.hass, {

View File

@ -5,8 +5,6 @@ from unittest.mock import patch
import homeassistant.util.dt as dt_util
import homeassistant.components.automation as automation
from homeassistant.components.automation import time, event
from homeassistant.const import CONF_PLATFORM
from tests.common import fire_time_changed, get_test_home_assistant
@ -28,207 +26,6 @@ class TestAutomationTime(unittest.TestCase):
"""Stop everything that was started."""
self.hass.stop()
def test_old_config_if_fires_when_hour_matches(self):
"""Test for firing if hours are matching."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'time',
time.CONF_HOURS: 0,
'execute_service': 'test.automation'
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_when_minute_matches(self):
"""Test for firing if minutes are matching."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'time',
time.CONF_MINUTES: 0,
'execute_service': 'test.automation'
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(minute=0))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_when_second_matches(self):
"""Test for firing if seconds are matching."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'time',
time.CONF_SECONDS: 0,
'execute_service': 'test.automation'
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(second=0))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_when_all_matches(self):
"""Test for firing if everything matches."""
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'time',
time.CONF_HOURS: 0,
time.CONF_MINUTES: 0,
time.CONF_SECONDS: 0,
'execute_service': 'test.automation'
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=0, minute=0, second=0))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_action_before(self):
"""Test for action before."""
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
'execute_service': 'test.automation',
'if': {
CONF_PLATFORM: 'time',
time.CONF_BEFORE: '10:00'
}
}
})
before_10 = dt_util.now().replace(hour=8)
after_10 = dt_util.now().replace(hour=14)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=before_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=after_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_action_after(self):
"""Test for action after."""
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
'execute_service': 'test.automation',
'if': {
CONF_PLATFORM: 'time',
time.CONF_AFTER: '10:00'
}
}
})
before_10 = dt_util.now().replace(hour=8)
after_10 = dt_util.now().replace(hour=14)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=before_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=after_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_action_one_weekday(self):
"""Test for action with one weekday."""
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
'execute_service': 'test.automation',
'if': {
CONF_PLATFORM: 'time',
time.CONF_WEEKDAY: 'mon',
}
}
})
days_past_monday = dt_util.now().weekday()
monday = dt_util.now() - timedelta(days=days_past_monday)
tuesday = monday + timedelta(days=1)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=monday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=tuesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_action_list_weekday(self):
"""Test for action with a list of weekdays."""
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
'execute_service': 'test.automation',
'if': {
CONF_PLATFORM: 'time',
time.CONF_WEEKDAY: ['mon', 'tue'],
}
}
})
days_past_monday = dt_util.now().weekday()
monday = dt_util.now() - timedelta(days=days_past_monday)
tuesday = monday + timedelta(days=1)
wednesday = tuesday + timedelta(days=1)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=monday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=tuesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=wednesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))
def test_if_fires_when_hour_matches(self):
"""Test for firing if hour is matching."""
self.assertTrue(automation.setup(self.hass, {

View File

@ -2,9 +2,12 @@
import unittest
from unittest import mock
from homeassistant.const import EVENT_STATE_CHANGED
from homeassistant.components.binary_sensor import template
from homeassistant.exceptions import TemplateError
from tests.common import get_test_home_assistant
class TestBinarySensorTemplate(unittest.TestCase):
"""Test for Binary sensor template platform."""
@ -88,21 +91,19 @@ class TestBinarySensorTemplate(unittest.TestCase):
def test_event(self):
""""Test the event."""
hass = mock.MagicMock()
hass = get_test_home_assistant()
vs = template.BinarySensorTemplate(hass, 'parent', 'Parent',
'motion', '{{ 1 > 1 }}')
with mock.patch.object(vs, 'update_ha_state') as mock_update:
vs._event_listener(None)
mock_update.assert_called_once_with(True)
vs.update_ha_state()
hass.pool.block_till_done()
def test_update(self):
""""Test the update."""
hass = mock.MagicMock()
vs = template.BinarySensorTemplate(hass, 'parent', 'Parent',
'motion', '{{ 2 > 1 }}')
self.assertEqual(None, vs._state)
vs.update()
self.assertTrue(vs._state)
with mock.patch.object(vs, 'update') as mock_update:
hass.bus.fire(EVENT_STATE_CHANGED)
hass.pool.block_till_done()
try:
assert mock_update.call_count == 1
finally:
hass.stop()
@mock.patch('homeassistant.helpers.template.render')
def test_update_template_error(self, mock_render):

View File

@ -133,15 +133,6 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase):
'radius': 100000
})
self.hass.states.set(
'zone.passive', 'zoning',
{
'name': 'zone',
'latitude': 3.0,
'longitude': 1.0,
'radius': 10,
'passive': True
})
# Clear state between teste
self.hass.states.set(DEVICE_TRACKER_STATE, None)
owntracks.REGIONS_ENTERED = defaultdict(list)
@ -325,43 +316,6 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase):
self.send_message(EVENT_TOPIC, message)
self.assert_location_state('outer')
def test_event_entry_exit_passive_zone(self):
"""Test the event for passive zone exits."""
# Enter passive zone
message = REGION_ENTER_MESSAGE.copy()
message['desc'] = "passive"
self.send_message(EVENT_TOPIC, message)
# Should pick up gps put not zone
self.assert_location_state('not_home')
self.assert_location_latitude(3.0)
self.assert_location_accuracy(10.0)
# Enter inner2 zone
message = REGION_ENTER_MESSAGE.copy()
message['desc'] = "inner_2"
self.send_message(EVENT_TOPIC, message)
self.assert_location_state('inner_2')
self.assert_location_latitude(2.1)
self.assert_location_accuracy(10.0)
# Exit inner_2 - should be in 'passive'
# ie gps co-ords - but not zone
message = REGION_LEAVE_MESSAGE.copy()
message['desc'] = "inner_2"
self.send_message(EVENT_TOPIC, message)
self.assert_location_state('not_home')
self.assert_location_latitude(3.0)
self.assert_location_accuracy(10.0)
# Exit passive - should be in 'outer'
message = REGION_LEAVE_MESSAGE.copy()
message['desc'] = "passive"
self.send_message(EVENT_TOPIC, message)
self.assert_location_state('outer')
self.assert_location_latitude(2.0)
self.assert_location_accuracy(60.0)
def test_event_entry_unknown_zone(self):
"""Test the event for unknown zone."""
# Just treat as location update

View File

@ -5,12 +5,9 @@ from homeassistant.components import rfxtrx as rfxtrx_core
from homeassistant.components.light import rfxtrx
from unittest.mock import patch
import pytest
from tests.common import get_test_home_assistant
@pytest.mark.skipif(True, reason='Does not clean up properly, takes 100% CPU')
class TestLightRfxtrx(unittest.TestCase):
"""Test the Rfxtrx light platform."""
@ -22,6 +19,8 @@ class TestLightRfxtrx(unittest.TestCase):
"""Stop everything that was started."""
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS = []
rfxtrx_core.RFX_DEVICES = {}
if rfxtrx_core.RFXOBJECT:
rfxtrx_core.RFXOBJECT.close_connection()
self.hass.stop()
def test_default_config(self):

View File

@ -25,6 +25,11 @@ class TestDemoMediaPlayer(unittest.TestCase):
state = self.hass.states.get(entity_id)
assert 1.0 == state.attributes.get('volume_level')
mp.set_volume_level(self.hass, None, entity_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id)
assert 1.0 == state.attributes.get('volume_level')
mp.set_volume_level(self.hass, 0.5, entity_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id)
@ -41,6 +46,12 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert 0.5 == state.attributes.get('volume_level')
assert False is state.attributes.get('is_volume_muted')
mp.mute_volume(self.hass, None, entity_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id)
assert False is state.attributes.get('is_volume_muted')
mp.mute_volume(self.hass, True, entity_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id)
@ -87,7 +98,7 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert self.hass.states.is_state(entity_id, 'playing')
def test_prev_next_track(self):
"""Test media_next_track and media_prevoius_track ."""
"""Test media_next_track and media_previous_track ."""
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
state = self.hass.states.get(entity_id)
assert 1 == state.attributes.get('media_track')
@ -115,6 +126,27 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands'))
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
ent_id = 'media_player.lounge_room'
state = self.hass.states.get(ent_id)
assert 1 == state.attributes.get('media_episode')
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands'))
mp.media_next_track(self.hass, ent_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id)
assert 2 == state.attributes.get('media_episode')
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands'))
mp.media_previous_track(self.hass, ent_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id)
assert 1 == state.attributes.get('media_episode')
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands'))
@patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.'
'media_seek')
def test_play_media(self, mock_seek):
@ -126,6 +158,13 @@ class TestDemoMediaPlayer(unittest.TestCase):
state.attributes.get('supported_media_commands'))
assert state.attributes.get('media_content_id') is not None
mp.play_media(self.hass, None, 'some_id', ent_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id)
assert 0 < (mp.SUPPORT_PLAY_MEDIA &
state.attributes.get('supported_media_commands'))
assert not 'some_id' == state.attributes.get('media_content_id')
mp.play_media(self.hass, 'youtube', 'some_id', ent_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id)
@ -133,6 +172,9 @@ class TestDemoMediaPlayer(unittest.TestCase):
state.attributes.get('supported_media_commands'))
assert 'some_id' == state.attributes.get('media_content_id')
assert not mock_seek.called
mp.media_seek(self.hass, None, ent_id)
self.hass.pool.block_till_done()
assert not mock_seek.called
mp.media_seek(self.hass, 100, ent_id)
self.hass.pool.block_till_done()

View File

@ -25,8 +25,37 @@ class MockMediaPlayer(media_player.MediaPlayerDevice):
self._media_title = None
self._supported_media_commands = 0
self.turn_off_service_calls = mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TURN_OFF)
self.service_calls = {
'turn_on': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TURN_ON),
'turn_off': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TURN_OFF),
'mute_volume': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_MUTE),
'set_volume_level': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_SET),
'media_play': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PLAY),
'media_pause': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PAUSE),
'media_previous_track': mock_service(
hass, media_player.DOMAIN,
media_player.SERVICE_MEDIA_PREVIOUS_TRACK),
'media_next_track': mock_service(
hass, media_player.DOMAIN,
media_player.SERVICE_MEDIA_NEXT_TRACK),
'media_seek': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_SEEK),
'play_media': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_PLAY_MEDIA),
'volume_up': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_UP),
'volume_down': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_DOWN),
'media_play_pause': mock_service(
hass, media_player.DOMAIN,
media_player.SERVICE_MEDIA_PLAY_PAUSE),
}
@property
def name(self):
@ -97,23 +126,26 @@ class TestMediaPlayer(unittest.TestCase):
self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format('state')
self.hass.states.set(self.mock_state_switch_id, STATE_OFF)
self.config_children_only = \
{'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')]}
self.config_children_and_attr = \
{'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')],
'attributes': {
'is_volume_muted': self.mock_mute_switch_id,
'state': self.mock_state_switch_id}}
self.config_children_only = {
'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')]
}
self.config_children_and_attr = {
'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')],
'attributes': {
'is_volume_muted': self.mock_mute_switch_id,
'state': self.mock_state_switch_id
}
}
def tearDown(self): # pylint: disable=invalid-name
"""Stop everything that was started."""
self.hass.stop()
def test_check_config_children_only(self):
def test_config_children_only(self):
"""Check config with only children."""
config_start = copy(self.config_children_only)
del config_start['platform']
@ -125,7 +157,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual(config_start, self.config_children_only)
def test_check_config_children_and_attr(self):
def test_config_children_and_attr(self):
"""Check config with children and attributes."""
config_start = copy(self.config_children_and_attr)
del config_start['platform']
@ -136,13 +168,13 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual(config_start, self.config_children_and_attr)
def test_check_config_no_name(self):
def test_config_no_name(self):
"""Check config with no Name entry."""
response = universal.validate_config({'platform': 'universal'})
self.assertFalse(response)
def test_check_config_bad_children(self):
def test_config_bad_children(self):
"""Check config with bad children entry."""
config_no_children = {'name': 'test', 'platform': 'universal'}
config_bad_children = {'name': 'test', 'children': {},
@ -156,7 +188,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual([], config_bad_children['children'])
def test_check_config_bad_commands(self):
def test_config_bad_commands(self):
"""Check config with bad commands entry."""
config = {'name': 'test', 'commands': [], 'platform': 'universal'}
@ -164,7 +196,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual({}, config['commands'])
def test_check_config_bad_attributes(self):
def test_config_bad_attributes(self):
"""Check config with bad attributes."""
config = {'name': 'test', 'attributes': [], 'platform': 'universal'}
@ -172,7 +204,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual({}, config['attributes'])
def test_check_config_bad_key(self):
def test_config_bad_key(self):
"""Check config with bad key."""
config = {'name': 'test', 'asdf': 5, 'platform': 'universal'}
@ -183,6 +215,7 @@ class TestMediaPlayer(unittest.TestCase):
def test_platform_setup(self):
"""Test platform setup."""
config = {'name': 'test', 'platform': 'universal'}
bad_config = {'platform': 'universal'}
entities = []
def add_devices(new_entities):
@ -190,8 +223,10 @@ class TestMediaPlayer(unittest.TestCase):
for dev in new_entities:
entities.append(dev)
universal.setup_platform(self.hass, config, add_devices)
universal.setup_platform(self.hass, bad_config, add_devices)
self.assertEqual(0, len(entities))
universal.setup_platform(self.hass, config, add_devices)
self.assertEqual(1, len(entities))
self.assertEqual('test', entities[0].name)
@ -263,6 +298,15 @@ class TestMediaPlayer(unittest.TestCase):
self.assertEqual(config['name'], ump.name)
def test_polling(self):
"""Test should_poll property."""
config = self.config_children_only
universal.validate_config(config)
ump = universal.UniversalMediaPlayer(self.hass, **config)
self.assertEqual(False, ump.should_poll)
def test_state_children_only(self):
"""Test media player state with only children."""
config = self.config_children_only
@ -388,8 +432,7 @@ class TestMediaPlayer(unittest.TestCase):
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name'])
ump.update()
self.mock_mp_1._supported_media_commands = \
universal.SUPPORT_VOLUME_SET
self.mock_mp_1._supported_media_commands = universal.SUPPORT_VOLUME_SET
self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.update_ha_state()
ump.update()
@ -400,7 +443,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertEqual(check_flags, ump.supported_media_commands)
def test_service_call_to_child(self):
"""Test a service call that should be routed to a child."""
"""Test service calls that should be routed to a child."""
config = self.config_children_only
universal.validate_config(config)
@ -413,13 +456,53 @@ class TestMediaPlayer(unittest.TestCase):
ump.update()
ump.turn_off()
self.assertEqual(1, len(self.mock_mp_2.turn_off_service_calls))
self.assertEqual(1, len(self.mock_mp_2.service_calls['turn_off']))
ump.turn_on()
self.assertEqual(1, len(self.mock_mp_2.service_calls['turn_on']))
ump.mute_volume(True)
self.assertEqual(1, len(self.mock_mp_2.service_calls['mute_volume']))
ump.set_volume_level(0.5)
self.assertEqual(
1, len(self.mock_mp_2.service_calls['set_volume_level']))
ump.media_play()
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_play']))
ump.media_pause()
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_pause']))
ump.media_previous_track()
self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_previous_track']))
ump.media_next_track()
self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_next_track']))
ump.media_seek(100)
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_seek']))
ump.play_media('movie', 'batman')
self.assertEqual(1, len(self.mock_mp_2.service_calls['play_media']))
ump.volume_up()
self.assertEqual(1, len(self.mock_mp_2.service_calls['volume_up']))
ump.volume_down()
self.assertEqual(1, len(self.mock_mp_2.service_calls['volume_down']))
ump.media_play_pause()
self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_play_pause']))
def test_service_call_to_command(self):
"""Test service call to command."""
config = self.config_children_only
config['commands'] = \
{'turn_off': {'service': 'test.turn_off', 'data': {}}}
config['commands'] = {'turn_off': {
'service': 'test.turn_off', 'data': {}}}
universal.validate_config(config)
service = mock_service(self.hass, 'test', 'turn_off')

View File

@ -44,10 +44,6 @@ class TestMQTT(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertTrue(mqtt.MQTT_CLIENT.stop.called)
def test_setup_fails_if_no_broker_config(self):
"""Test for setup failure if broker configuration is missing."""
self.assertFalse(mqtt.setup(self.hass, {mqtt.DOMAIN: {}}))
def test_setup_fails_if_no_connect_broker(self):
"""Test for setup failure if connection to broker is missing."""
with mock.patch('homeassistant.components.mqtt.MQTT',

View File

@ -0,0 +1,55 @@
"""The tests for the MQTT component embedded server."""
from unittest.mock import MagicMock, patch
import homeassistant.components.mqtt as mqtt
from tests.common import get_test_home_assistant
class TestMQTT:
"""Test the MQTT component."""
def setup_method(self, method):
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
def teardown_method(self, method):
"""Stop everything that was started."""
self.hass.stop()
@patch('homeassistant.components.mqtt.MQTT')
@patch('asyncio.gather')
@patch('asyncio.new_event_loop')
def test_creating_config_with_http_pass(self, mock_new_loop, mock_gather,
mock_mqtt):
"""Test if the MQTT server gets started and subscribe/publish msg."""
self.hass.config.components.append('http')
password = 'super_secret'
self.hass.config.api = MagicMock(api_password=password)
assert mqtt.setup(self.hass, {})
assert mock_mqtt.called
assert mock_mqtt.mock_calls[0][1][5] == 'homeassistant'
assert mock_mqtt.mock_calls[0][1][6] == password
mock_mqtt.reset_mock()
self.hass.config.api = MagicMock(api_password=None)
assert mqtt.setup(self.hass, {})
assert mock_mqtt.called
assert mock_mqtt.mock_calls[0][1][5] is None
assert mock_mqtt.mock_calls[0][1][6] is None
@patch('asyncio.gather')
@patch('asyncio.new_event_loop')
def test_broker_config_fails(self, mock_new_loop, mock_gather):
"""Test if the MQTT component fails if server fails."""
self.hass.config.components.append('http')
from hbmqtt.broker import BrokerException
mock_gather.side_effect = BrokerException
self.hass.config.api = MagicMock(api_password=None)
assert not mqtt.setup(self.hass, {
'mqtt': {'embedded': {}}
})

View File

@ -3,8 +3,10 @@ import os
import tempfile
import unittest
from homeassistant import core
import homeassistant.components.notify as notify
from tests.common import get_test_home_assistant
from unittest.mock import patch
@ -13,12 +15,27 @@ class TestCommandLine(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = core.HomeAssistant()
self.hass = get_test_home_assistant()
def tearDown(self): # pylint: disable=invalid-name
"""Stop down everything that was started."""
self.hass.stop()
def test_bad_config(self):
"""Test set up the platform with bad/missing config."""
self.assertFalse(notify.setup(self.hass, {
'notify': {
'name': 'test',
'platform': 'bad_platform',
}
}))
self.assertFalse(notify.setup(self.hass, {
'notify': {
'name': 'test',
'platform': 'command_line',
}
}))
def test_command_line_output(self):
"""Test the command line output."""
with tempfile.TemporaryDirectory() as tempdirname:
@ -41,7 +58,7 @@ class TestCommandLine(unittest.TestCase):
@patch('homeassistant.components.notify.command_line._LOGGER.error')
def test_error_for_none_zero_exit_code(self, mock_error):
"""Test if an error if logged for non zero exit codes."""
"""Test if an error is logged for non zero exit codes."""
self.assertTrue(notify.setup(self.hass, {
'notify': {
'name': 'test',

View File

@ -30,6 +30,12 @@ class TestNotifyDemo(unittest.TestCase):
""""Stop down everything that was started."""
self.hass.stop()
def test_sending_none_message(self):
"""Test send with None as message."""
notify.send_message(self.hass, None)
self.hass.pool.block_till_done()
self.assertTrue(len(self.events) == 0)
def test_sending_templated_message(self):
"""Send a templated message."""
self.hass.states.set('sensor.temperature', 10)

View File

@ -0,0 +1,56 @@
"""The tests for the notify file platform."""
import os
import unittest
import tempfile
import homeassistant.components.notify as notify
from homeassistant.components.notify import (
ATTR_TITLE_DEFAULT)
import homeassistant.util.dt as dt_util
from tests.common import get_test_home_assistant
class TestNotifyFile(unittest.TestCase):
"""Test the file notify."""
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
def tearDown(self): # pylint: disable=invalid-name
""""Stop down everything that was started."""
self.hass.stop()
def test_bad_config(self):
"""Test set up the platform with bad/missing config."""
self.assertFalse(notify.setup(self.hass, {
'notify': {
'name': 'test',
'platform': 'file',
}
}))
def test_notify_file(self):
"""Test the notify file output."""
with tempfile.TemporaryDirectory() as tempdirname:
filename = os.path.join(tempdirname, 'notify.txt')
message = 'one, two, testing, testing'
self.assertTrue(notify.setup(self.hass, {
'notify': {
'name': 'test',
'platform': 'file',
'filename': filename,
'timestamp': 0
}
}))
title = '{} notifications (Log started: {})\n{}\n'.format(
ATTR_TITLE_DEFAULT,
dt_util.strip_microseconds(dt_util.utcnow()),
'-' * 80)
self.hass.services.call('notify', 'test', {'message': message},
blocking=True)
result = open(filename).read()
self.assertEqual(result, "{}{}\n".format(title, message))

View File

@ -54,7 +54,7 @@ class TestTemplateSensor:
self.hass.states.set('sensor.test_state', 'Works')
self.hass.pool.block_till_done()
state = self.hass.states.get('sensor.test_template_sensor')
assert state.state == 'error'
assert state.state == 'unknown'
def test_template_attribute_missing(self):
"""Test missing attribute template."""
@ -71,7 +71,7 @@ class TestTemplateSensor:
})
state = self.hass.states.get('sensor.test_template_sensor')
assert state.state == 'error'
assert state.state == 'unknown'
def test_invalid_name_does_not_create(self):
"""Test invalid name."""

View File

@ -6,6 +6,7 @@ import unittest
from homeassistant.const import STATE_ON, STATE_OFF
import homeassistant.components.switch as switch
import homeassistant.components.switch.command_line as command_line
from tests.common import get_test_home_assistant
@ -155,3 +156,21 @@ class TestCommandSwitch(unittest.TestCase):
state = self.hass.states.get('switch.test')
self.assertEqual(STATE_ON, state.state)
def test_assumed_state_should_be_true_if_command_state_is_false(self):
"""Test with state value."""
self.hass = get_test_home_assistant()
# Set state command to false
statecmd = False
no_state_device = command_line.CommandSwitch(self.hass, "Test", "echo",
"echo", statecmd, None)
self.assertTrue(no_state_device.assumed_state)
# Set state command
statecmd = 'cat {}'
state_device = command_line.CommandSwitch(self.hass, "Test", "echo",
"echo", statecmd, None)
self.assertFalse(state_device.assumed_state)

Some files were not shown because too many files have changed in this diff Show More