Merge pull request #4796 from home-assistant/release-0-34-4

0.34.4
This commit is contained in:
Paulus Schoutsen 2016-12-06 22:44:57 -08:00 committed by GitHub
commit 91a9da8f0c
18 changed files with 167 additions and 125 deletions

View File

@ -59,8 +59,12 @@ def setup(hass, config):
for alarm in target_alarms:
getattr(alarm, method)(code)
if alarm.should_poll:
alarm.update_ha_state(True)
for alarm in target_alarms:
if not alarm.should_poll:
continue
alarm.update_ha_state(True)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))

View File

@ -86,6 +86,8 @@ class NestThermostat(ClimateDevice):
self._eco_temperature = None
self._is_locked = None
self._locked_temperature = None
self._min_temperature = None
self._max_temperature = None
@property
def name(self):
@ -204,18 +206,12 @@ class NestThermostat(ClimateDevice):
@property
def min_temp(self):
"""Identify min_temp in Nest API or defaults if not available."""
if self._is_locked:
return self._locked_temperature[0]
else:
return None
return self._min_temperature
@property
def max_temp(self):
"""Identify max_temp in Nest API or defaults if not available."""
if self._is_locked:
return self._locked_temperature[1]
else:
return None
return self._max_temperature
def update(self):
"""Cache value from Python-nest."""
@ -229,6 +225,8 @@ class NestThermostat(ClimateDevice):
self._away = self.structure.away == 'away'
self._eco_temperature = self.device.eco_temperature
self._locked_temperature = self.device.locked_temperature
self._min_temperature = self.device.min_temperature
self._max_temperature = self.device.max_temperature
self._is_locked = self.device.is_locked
if self.device.temperature_scale == 'C':
self._temperature_scale = TEMP_CELSIUS

View File

@ -135,12 +135,19 @@ def setup(hass, config):
params = service.data.copy()
params.pop(ATTR_ENTITY_ID, None)
if method:
for cover in component.extract_from_service(service):
getattr(cover, method['method'])(**params)
if not method:
return
if cover.should_poll:
cover.update_ha_state(True)
covers = component.extract_from_service(service)
for cover in covers:
getattr(cover, method['method'])(**params)
for cover in covers:
if not cover.should_poll:
continue
cover.update_ha_state(True)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))

View File

@ -32,7 +32,7 @@ from .const import (
KEY_USE_X_FORWARDED_FOR, KEY_TRUSTED_NETWORKS,
KEY_BANS_ENABLED, KEY_LOGIN_THRESHOLD,
KEY_DEVELOPMENT, KEY_AUTHENTICATED)
from .static import GZIP_FILE_SENDER, staticresource_middleware
from .static import FILE_SENDER, GZIP_FILE_SENDER, staticresource_middleware
from .util import get_real_ip
DOMAIN = 'http'
@ -344,7 +344,7 @@ class HomeAssistantView(object):
def file(self, request, fil):
"""Return a file."""
assert isinstance(fil, str), 'only string paths allowed'
response = yield from GZIP_FILE_SENDER.send(request, Path(fil))
response = yield from FILE_SENDER.send(request, Path(fil))
return response
def register(self, router):

View File

@ -63,6 +63,7 @@ class GzipFileSender(FileSender):
GZIP_FILE_SENDER = GzipFileSender()
FILE_SENDER = FileSender()
@asyncio.coroutine

View File

@ -24,23 +24,20 @@ CONF_TAGS = 'tags'
CONF_DEFAULT_MEASUREMENT = 'default_measurement'
DEFAULT_DATABASE = 'home_assistant'
DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 8086
DEFAULT_SSL = False
DEFAULT_VERIFY_SSL = False
DEFAULT_VERIFY_SSL = True
DOMAIN = 'influxdb'
TIMEOUT = 5
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_HOST): cv.string,
vol.Inclusive(CONF_USERNAME, 'authentication'): cv.string,
vol.Inclusive(CONF_PASSWORD, 'authentication'): cv.string,
vol.Optional(CONF_BLACKLIST, default=[]):
vol.All(cv.ensure_list, [cv.entity_id]),
vol.Optional(CONF_DB_NAME, default=DEFAULT_DATABASE): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
vol.Optional(CONF_PORT): cv.port,
vol.Optional(CONF_SSL): cv.boolean,
vol.Optional(CONF_DEFAULT_MEASUREMENT): cv.string,
vol.Optional(CONF_TAGS, default={}):
vol.Schema({cv.string: cv.string}),
@ -57,23 +54,34 @@ def setup(hass, config):
conf = config[DOMAIN]
host = conf.get(CONF_HOST)
port = conf.get(CONF_PORT)
database = conf.get(CONF_DB_NAME)
username = conf.get(CONF_USERNAME)
password = conf.get(CONF_PASSWORD)
ssl = conf.get(CONF_SSL)
verify_ssl = conf.get(CONF_VERIFY_SSL)
kwargs = {
'database': conf[CONF_DB_NAME],
'verify_ssl': conf[CONF_VERIFY_SSL],
'timeout': TIMEOUT
}
if CONF_HOST in conf:
kwargs['host'] = conf[CONF_HOST]
if CONF_PORT in conf:
kwargs['port'] = conf[CONF_PORT]
if CONF_USERNAME in conf:
kwargs['username'] = conf[CONF_USERNAME]
if CONF_PASSWORD in conf:
kwargs['password'] = conf[CONF_PASSWORD]
if CONF_SSL in conf:
kwargs['ssl'] = conf[CONF_SSL]
blacklist = conf.get(CONF_BLACKLIST)
whitelist = conf.get(CONF_WHITELIST)
tags = conf.get(CONF_TAGS)
default_measurement = conf.get(CONF_DEFAULT_MEASUREMENT)
try:
influx = InfluxDBClient(
host=host, port=port, username=username, password=password,
database=database, ssl=ssl, verify_ssl=verify_ssl,
timeout=TIMEOUT)
influx = InfluxDBClient(**kwargs)
influx.query("select * from /.*/ LIMIT 1;")
except exceptions.InfluxDBClientError as exc:
_LOGGER.error("Database host is not accessible due to '%s', please "

View File

@ -236,7 +236,6 @@ def async_setup(hass, config):
if color_name is not None:
params[ATTR_RGB_COLOR] = color_util.color_name_to_rgb(color_name)
update_tasks = []
for light in target_lights:
if service.service == SERVICE_TURN_ON:
yield from light.async_turn_on(**params)
@ -245,12 +244,18 @@ def async_setup(hass, config):
else:
yield from light.async_toggle(**params)
if light.should_poll:
update_coro = light.async_update_ha_state(True)
if hasattr(light, 'async_update'):
update_tasks.append(hass.loop.create_task(update_coro))
else:
yield from update_coro
update_tasks = []
for light in target_lights:
if not light.should_poll:
continue
update_coro = hass.loop.create_task(
light.async_update_ha_state(True))
if hasattr(light, 'async_update'):
update_tasks.append(hass.loop.create_task(update_coro))
else:
yield from update_coro
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View File

@ -85,8 +85,11 @@ def setup(hass, config):
else:
item.unlock(code=code)
if item.should_poll:
item.update_ha_state(True)
for item in target_locks:
if not item.should_poll:
continue
item.update_ha_state(True)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))

View File

@ -36,10 +36,10 @@ SUPPORT_KODI = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_TURN_OFF_ACTION, default=None): vol.In(TURN_OFF_ACTION),
vol.Optional(CONF_USERNAME): cv.string,
vol.Inclusive(CONF_USERNAME, 'auth'): cv.string,
vol.Inclusive(CONF_PASSWORD, 'auth'): cv.string,
})
@ -51,11 +51,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if jsonrpc_url:
url = jsonrpc_url.rstrip('/jsonrpc')
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
if username is not None:
auth = (username, password)
else:
auth = None
add_devices([
KodiDevice(
config.get(CONF_NAME),
url,
auth=(config.get(CONF_USERNAME), config.get(CONF_PASSWORD)),
auth=auth,
turn_off_action=config.get(CONF_TURN_OFF_ACTION)),
])
@ -68,10 +76,15 @@ class KodiDevice(MediaPlayerDevice):
import jsonrpc_requests
self._name = name
self._url = url
kwargs = {'timeout': 5}
if auth is not None:
kwargs['auth'] = auth
self._server = jsonrpc_requests.Server(
'{}/jsonrpc'.format(self._url),
auth=auth,
timeout=5)
'{}/jsonrpc'.format(self._url), **kwargs)
self._turn_off_action = turn_off_action
self._players = list()
self._properties = None

View File

@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = [
'http://github.com/technicalpickles/python-nest'
'/archive/2512973b4b390d3965da43529cd20402ad374bfa.zip' # nest-cam branch
'/archive/dd628f90772d170b9602f262d5d2e7d61bdd3cf5.zip' # nest-cam branch
'#python-nest==3.0.0']
DOMAIN = 'nest'

View File

@ -22,8 +22,8 @@ DEFAULT_PORT = 8080
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Inclusive(CONF_USERNAME, 'auth'): cv.string,
vol.Inclusive(CONF_PASSWORD, 'auth'): cv.string,
})
ATTR_DISPLAYTIME = 'displaytime'
@ -33,7 +33,13 @@ def get_service(hass, config):
"""Return the notify service."""
url = '{}:{}'.format(config.get(CONF_HOST), config.get(CONF_PORT))
auth = (config.get(CONF_USERNAME), config.get(CONF_PASSWORD))
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
if username is not None:
auth = (username, password)
else:
auth = None
return KODINotificationService(
url,
@ -48,10 +54,14 @@ class KODINotificationService(BaseNotificationService):
"""Initialize the service."""
import jsonrpc_requests
self._url = url
kwargs = {'timeout': 5}
if auth is not None:
kwargs['auth'] = auth
self._server = jsonrpc_requests.Server(
'{}/jsonrpc'.format(self._url),
auth=auth,
timeout=5)
'{}/jsonrpc'.format(self._url), **kwargs)
def send_message(self, message="", **kwargs):
"""Send a message to Kodi."""

View File

@ -4,7 +4,9 @@ Component to interface with universal remote control devices.
For more details about this component, please refer to the documentation
at https://home-assistant.io/components/remote/
"""
import asyncio
from datetime import timedelta
import functools as ft
import logging
import os
@ -80,21 +82,17 @@ def send_command(hass, device, command, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SEND_COMMAND, data)
def sync(hass, entity_id=None):
"""Sync remote device."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.services.call(DOMAIN, SERVICE_SYNC, data)
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for remotes."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_REMOTES)
component.setup(config)
yield from component.async_setup(config)
def handle_remote_service(service):
@asyncio.coroutine
def async_handle_remote_service(service):
"""Handle calls to the remote services."""
target_remotes = component.extract_from_service(service)
target_remotes = component.async_extract_from_service(service)
activity_id = service.data.get(ATTR_ACTIVITY)
device = service.data.get(ATTR_DEVICE)
@ -102,28 +100,43 @@ def setup(hass, config):
for remote in target_remotes:
if service.service == SERVICE_TURN_ON:
remote.turn_on(activity=activity_id)
yield from remote.async_turn_on(activity=activity_id)
elif service.service == SERVICE_SEND_COMMAND:
remote.send_command(device=device, command=command)
elif service.service == SERVICE_SYNC:
remote.sync()
yield from remote.async_send_command(
device=device, command=command)
else:
remote.turn_off()
yield from remote.async_turn_off()
if remote.should_poll:
remote.update_ha_state(True)
update_tasks = []
for remote in target_remotes:
if not remote.should_poll:
continue
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_remote_service,
descriptions.get(SERVICE_TURN_OFF),
schema=REMOTE_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_remote_service,
descriptions.get(SERVICE_TURN_ON),
schema=REMOTE_SERVICE_TURN_ON_SCHEMA)
hass.services.register(DOMAIN, SERVICE_SEND_COMMAND, handle_remote_service,
descriptions.get(SERVICE_SEND_COMMAND),
schema=REMOTE_SERVICE_SEND_COMMAND_SCHEMA)
update_coro = hass.loop.create_task(
remote.async_update_ha_state(True))
if hasattr(remote, 'async_update'):
update_tasks.append(hass.loop.create_task(update_coro))
else:
yield from update_coro
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
hass.services.async_register(
DOMAIN, SERVICE_TURN_OFF, async_handle_remote_service,
descriptions.get(SERVICE_TURN_OFF),
schema=REMOTE_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TURN_ON, async_handle_remote_service,
descriptions.get(SERVICE_TURN_ON),
schema=REMOTE_SERVICE_TURN_ON_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_SEND_COMMAND, async_handle_remote_service,
descriptions.get(SERVICE_SEND_COMMAND),
schema=REMOTE_SERVICE_SEND_COMMAND_SCHEMA)
return True
@ -131,14 +144,11 @@ def setup(hass, config):
class RemoteDevice(ToggleEntity):
"""Representation of a remote."""
def turn_on(self, **kwargs):
"""Turn a device on with the remote."""
raise NotImplementedError()
def turn_off(self, **kwargs):
"""Turn a device off with the remote."""
raise NotImplementedError()
def send_command(self, **kwargs):
"""Send a command to a device."""
raise NotImplementedError()
def async_send_command(self, **kwargs):
"""Send a command to a device."""
yield from self.hass.loop.run_in_executor(
None, ft.partial(self.send_command, **kwargs))

View File

@ -14,7 +14,8 @@ import homeassistant.components.remote as remote
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_NAME, CONF_HOST, CONF_PORT, ATTR_ENTITY_ID)
from homeassistant.components.remote import PLATFORM_SCHEMA, DOMAIN
from homeassistant.components.remote import (
PLATFORM_SCHEMA, DOMAIN, ATTR_DEVICE, ATTR_COMMAND, ATTR_ACTIVITY)
from homeassistant.util import slugify
from homeassistant.config import load_yaml_config_file
@ -22,10 +23,6 @@ REQUIREMENTS = ['pyharmony==1.0.12']
_LOGGER = logging.getLogger(__name__)
ATTR_DEVICE = 'device'
ATTR_COMMAND = 'command'
ATTR_ACTIVITY = 'activity'
DEFAULT_PORT = 5222
DEVICES = []

View File

@ -82,7 +82,6 @@ def async_setup(hass, config):
"""Handle calls to the switch services."""
target_switches = component.async_extract_from_service(service)
update_tasks = []
for switch in target_switches:
if service.service == SERVICE_TURN_ON:
yield from switch.async_turn_on()
@ -91,12 +90,17 @@ def async_setup(hass, config):
else:
yield from switch.async_turn_off()
if switch.should_poll:
update_coro = switch.async_update_ha_state(True)
if hasattr(switch, 'async_update'):
update_tasks.append(hass.loop.create_task(update_coro))
else:
yield from update_coro
update_tasks = []
for switch in target_switches:
if not switch.should_poll:
continue
update_coro = hass.loop.create_task(
switch.async_update_ha_state(True))
if hasattr(switch, 'async_update'):
update_tasks.append(hass.loop.create_task(update_coro))
else:
yield from update_coro
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View File

@ -2,7 +2,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 34
PATCH_VERSION = '3'
PATCH_VERSION = '4'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 4, 2)

View File

@ -164,7 +164,7 @@ hikvision==0.4
# http://github.com/adafruit/Adafruit_Python_DHT/archive/310c59b0293354d07d94375f1365f7b9b9110c7d.zip#Adafruit_DHT==1.3.0
# homeassistant.components.nest
http://github.com/technicalpickles/python-nest/archive/2512973b4b390d3965da43529cd20402ad374bfa.zip#python-nest==3.0.0
http://github.com/technicalpickles/python-nest/archive/dd628f90772d170b9602f262d5d2e7d61bdd3cf5.zip#python-nest==3.0.0
# homeassistant.components.light.flux_led
https://github.com/Danielhiversen/flux_led/archive/0.9.zip#flux_led==0.9

View File

@ -9,7 +9,6 @@ from homeassistant.const import (
SERVICE_TURN_ON, SERVICE_TURN_OFF)
from tests.common import get_test_home_assistant, mock_service
SERVICE_SYNC = 'sync'
SERVICE_SEND_COMMAND = 'send_command'
@ -83,22 +82,6 @@ class TestDemoRemote(unittest.TestCase):
self.assertEqual(SERVICE_TURN_OFF, call.service)
self.assertEqual('entity_id_val', call.data[ATTR_ENTITY_ID])
# Test sync
sync_calls = mock_service(
self.hass, remote.DOMAIN, SERVICE_SYNC)
remote.sync(
self.hass, entity_id='entity_id_val')
self.hass.block_till_done()
self.assertEqual(1, len(sync_calls))
call = sync_calls[-1]
self.assertEqual(remote.DOMAIN, call.domain)
self.assertEqual(SERVICE_SYNC, call.service)
self.assertEqual('entity_id_val', call.data[ATTR_ENTITY_ID])
# Test send_command
send_command_calls = mock_service(
self.hass, remote.DOMAIN, SERVICE_SEND_COMMAND)

View File

@ -11,7 +11,6 @@ import homeassistant.components.remote as remote
from tests.common import mock_service, get_test_home_assistant
TEST_PLATFORM = {remote.DOMAIN: {CONF_PLATFORM: 'test'}}
SERVICE_SYNC = 'sync'
SERVICE_SEND_COMMAND = 'send_command'