Compare commits

..

21 Commits

Author SHA1 Message Date
Paulus Schoutsen
5a15b2c036 Merge pull request #16094 from home-assistant/rc
0.76.2
2018-08-21 12:20:06 +02:00
Paulus Schoutsen
977d86e7ca Bumped version to 0.76.2 2018-08-21 11:43:14 +02:00
Paulus Schoutsen
bd776c84bc Forgiving add index in migration (#16092) 2018-08-21 11:43:08 +02:00
Paulus Schoutsen
1be388c587 Update frontend to 20180820.0 2018-08-20 11:52:47 +02:00
Paulus Schoutsen
e1b2e00cf6 Merge pull request #16064 from home-assistant/rc
0.76.1
2018-08-19 20:07:44 +02:00
Paulus Schoutsen
3c5e62d47e Column syntax fix + Add a file if migration in progress (#16061)
* Add a file if migration in progress

* Warning

* Convert message for migration to warning
2018-08-19 19:01:11 +02:00
Paulus Schoutsen
3be301fac9 Bumped version to 0.76.1 2018-08-19 18:58:21 +02:00
Paulus Schoutsen
9c3251b5f0 Add notify platforms to loaded components (#16063) 2018-08-19 18:58:13 +02:00
huangyupeng
cb44607e96 Tuya fix login problem and add login platform param (#16058)
* add a platform param to distinguish different app's account.

* fix requirements
2018-08-19 18:58:12 +02:00
Paulus Schoutsen
1c8ef4e196 Add forgiving add column (#16057)
* Add forgiving add column

* Lint
2018-08-19 18:58:12 +02:00
Paulus Schoutsen
6827256586 Bump frontend to 20180818.0 2018-08-18 11:15:46 +02:00
Paulus Schoutsen
70412fc0ba Merge pull request #16027 from home-assistant/rc
0.76.0
2018-08-17 18:41:40 +02:00
Paulus Schoutsen
e4425e6a37 Version 0.76.0 2018-08-17 17:23:20 +02:00
Paulus Schoutsen
c09e7e620f Bumped version to 0.76.0b5 2018-08-16 23:02:34 +02:00
Paulus Schoutsen
92e26495da Disable the DLNA component discovery (#16006) 2018-08-16 23:02:26 +02:00
Steven Looman
061859cc4d Fix message "Updating dlna_dmr media_player took longer than ..." (#16005) 2018-08-16 23:02:26 +02:00
Paulus Schoutsen
e41ce1d6ec Bump frontend to 20180816.1 2018-08-16 22:18:10 +02:00
Paulus Schoutsen
5eccfc2604 Bumped version to 0.76.0b4 2018-08-16 14:26:52 +02:00
Paulus Schoutsen
2469bc7e2e Fix Nest async from sync (#15997) 2018-08-16 14:26:44 +02:00
Martin Hjelmare
d540a084dd Fix mysensors connection task blocking setup (#15938)
* Fix mysensors connection task blocking setup

* Schedule the connection task without having the core track the task
  to avoid blocking setup.
* Cancel the connection task, if not cancelled already, when
  home assistant stops.

* Use done instead of cancelled
2018-08-16 14:26:44 +02:00
Paulus Schoutsen
11eb29f520 Bump frontend to 20180816.0 2018-08-16 14:22:01 +02:00
13 changed files with 133 additions and 55 deletions

View File

@@ -85,11 +85,11 @@ SERVICE_HANDLERS = {
'volumio': ('media_player', 'volumio'),
'nanoleaf_aurora': ('light', 'nanoleaf_aurora'),
'freebox': ('device_tracker', 'freebox'),
'dlna_dmr': ('media_player', 'dlna_dmr'),
}
OPTIONAL_SERVICE_HANDLERS = {
SERVICE_HOMEKIT: ('homekit_controller', None),
'dlna_dmr': ('media_player', 'dlna_dmr'),
}
CONF_IGNORE = 'ignore'

View File

@@ -26,7 +26,7 @@ from homeassistant.helpers.translation import async_get_translations
from homeassistant.loader import bind_hass
from homeassistant.util.yaml import load_yaml
REQUIREMENTS = ['home-assistant-frontend==20180813.0']
REQUIREMENTS = ['home-assistant-frontend==20180820.0']
DOMAIN = 'frontend'
DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log',

View File

@@ -10,5 +10,5 @@ DOMAIN = 'map'
async def async_setup(hass, config):
"""Register the built-in map panel."""
await hass.components.frontend.async_register_built_in_panel(
'map', 'map', 'mdi:account-location')
'map', 'map', 'hass:account-location')
return True

View File

@@ -35,7 +35,7 @@ from homeassistant.util import get_local_ip
DLNA_DMR_DATA = 'dlna_dmr'
REQUIREMENTS = [
'async-upnp-client==0.12.2',
'async-upnp-client==0.12.3',
]
DEFAULT_NAME = 'DLNA Digital Media Renderer'
@@ -126,7 +126,7 @@ async def async_setup_platform(hass: HomeAssistant,
name = config.get(CONF_NAME)
elif discovery_info is not None:
url = discovery_info['ssdp_description']
name = discovery_info['name']
name = discovery_info.get('name')
if DLNA_DMR_DATA not in hass.data:
hass.data[DLNA_DMR_DATA] = {}

View File

@@ -186,12 +186,16 @@ def _discover_mysensors_platform(hass, platform, new_devices):
async def _gw_start(hass, gateway):
"""Start the gateway."""
# Don't use hass.async_create_task to avoid holding up setup indefinitely.
connect_task = hass.loop.create_task(gateway.start())
@callback
def gw_stop(event):
"""Trigger to stop the gateway."""
hass.async_add_job(gateway.stop())
if not connect_task.done():
connect_task.cancel()
await gateway.start()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gw_stop)
if gateway.device == 'mqtt':
# Gatways connected via mqtt doesn't send gateway ready message.

View File

@@ -4,10 +4,10 @@ Support for Nest devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/nest/
"""
from concurrent.futures import ThreadPoolExecutor
import logging
import socket
from datetime import datetime, timedelta
import threading
import voluptuous as vol
@@ -16,8 +16,9 @@ from homeassistant.const import (
CONF_STRUCTURE, CONF_FILENAME, CONF_BINARY_SENSORS, CONF_SENSORS,
CONF_MONITORED_CONDITIONS,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send, \
from homeassistant.helpers.dispatcher import dispatcher_send, \
async_dispatcher_connect
from homeassistant.helpers.entity import Entity
@@ -71,24 +72,25 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA)
async def async_nest_update_event_broker(hass, nest):
def nest_update_event_broker(hass, nest):
"""
Dispatch SIGNAL_NEST_UPDATE to devices when nest stream API received data.
nest.update_event.wait will block the thread in most of time,
so specific an executor to save default thread pool.
Runs in its own thread.
"""
_LOGGER.debug("listening nest.update_event")
with ThreadPoolExecutor(max_workers=1) as executor:
while True:
await hass.loop.run_in_executor(executor, nest.update_event.wait)
if hass.is_running:
nest.update_event.clear()
_LOGGER.debug("dispatching nest data update")
async_dispatcher_send(hass, SIGNAL_NEST_UPDATE)
else:
_LOGGER.debug("stop listening nest.update_event")
return
while hass.is_running:
nest.update_event.wait()
if not hass.is_running:
break
nest.update_event.clear()
_LOGGER.debug("dispatching nest data update")
dispatcher_send(hass, SIGNAL_NEST_UPDATE)
_LOGGER.debug("stop listening nest.update_event")
async def async_setup(hass, config):
@@ -167,16 +169,21 @@ async def async_setup_entry(hass, entry):
hass.services.async_register(
DOMAIN, 'set_mode', set_mode, schema=AWAY_SCHEMA)
@callback
def start_up(event):
"""Start Nest update event listener."""
hass.async_add_job(async_nest_update_event_broker, hass, nest)
threading.Thread(
name='Nest update listener',
target=nest_update_event_broker,
args=(hass, nest)
).start()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_up)
@callback
def shut_down(event):
"""Stop Nest update event listener."""
if nest:
nest.update_event.set()
nest.update_event.set()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, shut_down)

View File

@@ -156,6 +156,8 @@ def async_setup(hass, config):
DOMAIN, platform_name_slug, async_notify_message,
schema=NOTIFY_SERVICE_SCHEMA)
hass.config.components.add('{}.{}'.format(DOMAIN, p_type))
return True
setup_tasks = [async_setup_platform(p_type, p_config) for p_type, p_config

View File

@@ -1,39 +1,53 @@
"""Schema migration helpers."""
import logging
import os
from .util import session_scope
_LOGGER = logging.getLogger(__name__)
PROGRESS_FILE = '.migration_progress'
def migrate_schema(instance):
"""Check if the schema needs to be upgraded."""
from .models import SchemaChanges, SCHEMA_VERSION
progress_path = instance.hass.config.path(PROGRESS_FILE)
with session_scope(session=instance.get_session()) as session:
res = session.query(SchemaChanges).order_by(
SchemaChanges.change_id.desc()).first()
current_version = getattr(res, 'schema_version', None)
if current_version == SCHEMA_VERSION:
# Clean up if old migration left file
if os.path.isfile(progress_path):
_LOGGER.warning("Found existing migration file, cleaning up")
os.remove(instance.hass.config.path(PROGRESS_FILE))
return
_LOGGER.debug("Database requires upgrade. Schema version: %s",
current_version)
with open(progress_path, 'w'):
pass
_LOGGER.warning("Database requires upgrade. Schema version: %s",
current_version)
if current_version is None:
current_version = _inspect_schema_version(instance.engine, session)
_LOGGER.debug("No schema version found. Inspected version: %s",
current_version)
for version in range(current_version, SCHEMA_VERSION):
new_version = version + 1
_LOGGER.info("Upgrading recorder db schema to version %s",
new_version)
_apply_update(instance.engine, new_version, current_version)
session.add(SchemaChanges(schema_version=new_version))
try:
for version in range(current_version, SCHEMA_VERSION):
new_version = version + 1
_LOGGER.info("Upgrading recorder db schema to version %s",
new_version)
_apply_update(instance.engine, new_version, current_version)
session.add(SchemaChanges(schema_version=new_version))
_LOGGER.info("Upgrade to version %s done", new_version)
_LOGGER.info("Upgrade to version %s done", new_version)
finally:
os.remove(instance.hass.config.path(PROGRESS_FILE))
def _create_index(engine, table_name, index_name):
@@ -43,6 +57,7 @@ def _create_index(engine, table_name, index_name):
within the table definition described in the models
"""
from sqlalchemy import Table
from sqlalchemy.exc import OperationalError
from . import models
table = Table(table_name, models.Base.metadata)
@@ -53,7 +68,15 @@ def _create_index(engine, table_name, index_name):
_LOGGER.info("Adding index `%s` to database. Note: this can take several "
"minutes on large databases and slow computers. Please "
"be patient!", index_name)
index.create(engine)
try:
index.create(engine)
except OperationalError as err:
if 'already exists' not in str(err).lower():
raise
_LOGGER.warning('Index %s already exists on %s, continueing',
index_name, table_name)
_LOGGER.debug("Finished creating %s", index_name)
@@ -117,22 +140,37 @@ def _drop_index(engine, table_name, index_name):
def _add_columns(engine, table_name, columns_def):
"""Add columns to a table."""
from sqlalchemy import text
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.exc import OperationalError
columns_def = ['ADD COLUMN {}'.format(col_def) for col_def in columns_def]
_LOGGER.info("Adding columns %s to table %s. Note: this can take several "
"minutes on large databases and slow computers. Please "
"be patient!",
', '.join(column.split(' ')[0] for column in columns_def),
table_name)
columns_def = ['ADD {}'.format(col_def) for col_def in columns_def]
try:
engine.execute(text("ALTER TABLE {table} {columns_def}".format(
table=table_name,
columns_def=', '.join(columns_def))))
return
except SQLAlchemyError:
pass
except OperationalError:
# Some engines support adding all columns at once,
# this error is when they dont'
_LOGGER.info('Unable to use quick column add. Adding 1 by 1.')
for column_def in columns_def:
engine.execute(text("ALTER TABLE {table} {column_def}".format(
table=table_name,
column_def=column_def)))
try:
engine.execute(text("ALTER TABLE {table} {column_def}".format(
table=table_name,
column_def=column_def)))
except OperationalError as err:
if 'duplicate' not in str(err).lower():
raise
_LOGGER.warning('Column %s already exists on %s, continueing',
column_def.split(' ')[1], table_name)
def _apply_update(engine, new_version, old_version):

View File

@@ -10,14 +10,14 @@ import voluptuous as vol
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD)
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, CONF_PLATFORM)
from homeassistant.helpers import discovery
from homeassistant.helpers.dispatcher import (
dispatcher_send, async_dispatcher_connect)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import track_time_interval
REQUIREMENTS = ['tuyapy==0.1.2']
REQUIREMENTS = ['tuyapy==0.1.3']
_LOGGER = logging.getLogger(__name__)
@@ -45,7 +45,8 @@ CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_COUNTRYCODE): cv.string
vol.Required(CONF_COUNTRYCODE): cv.string,
vol.Optional(CONF_PLATFORM, default='tuya'): cv.string,
})
}, extra=vol.ALLOW_EXTRA)
@@ -58,9 +59,10 @@ def setup(hass, config):
username = config[DOMAIN][CONF_USERNAME]
password = config[DOMAIN][CONF_PASSWORD]
country_code = config[DOMAIN][CONF_COUNTRYCODE]
platform = config[DOMAIN][CONF_PLATFORM]
hass.data[DATA_TUYA] = tuya
tuya.init(username, password, country_code)
tuya.init(username, password, country_code, platform)
hass.data[DOMAIN] = {
'entities': {}
}

View File

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

View File

@@ -138,7 +138,7 @@ apns2==0.3.0
asterisk_mbox==0.4.0
# homeassistant.components.media_player.dlna_dmr
async-upnp-client==0.12.2
async-upnp-client==0.12.3
# homeassistant.components.light.avion
# avion==0.7
@@ -432,7 +432,7 @@ hole==0.3.0
holidays==0.9.6
# homeassistant.components.frontend
home-assistant-frontend==20180813.0
home-assistant-frontend==20180820.0
# homeassistant.components.homekit_controller
# homekit==0.10
@@ -1389,7 +1389,7 @@ tplink==0.2.1
transmissionrpc==0.11
# homeassistant.components.tuya
tuyapy==0.1.2
tuyapy==0.1.3
# homeassistant.components.twilio
twilio==5.7.0

View File

@@ -81,7 +81,7 @@ hbmqtt==0.9.2
holidays==0.9.6
# homeassistant.components.frontend
home-assistant-frontend==20180813.0
home-assistant-frontend==20180820.0
# homeassistant.components.homematicip_cloud
homematicip==0.9.8

View File

@@ -5,11 +5,11 @@ from unittest.mock import patch, call
import pytest
from sqlalchemy import create_engine
from sqlalchemy.pool import StaticPool
from homeassistant.bootstrap import async_setup_component
from homeassistant.components.recorder import wait_connection_ready, migration
from homeassistant.components.recorder.models import SCHEMA_VERSION
from homeassistant.components.recorder.const import DATA_INSTANCE
from homeassistant.components.recorder import (
wait_connection_ready, migration, const, models)
from tests.components.recorder import models_original
@@ -37,8 +37,8 @@ def test_schema_update_calls(hass):
yield from wait_connection_ready(hass)
update.assert_has_calls([
call(hass.data[DATA_INSTANCE].engine, version+1, 0) for version
in range(0, SCHEMA_VERSION)])
call(hass.data[const.DATA_INSTANCE].engine, version+1, 0) for version
in range(0, models.SCHEMA_VERSION)])
@asyncio.coroutine
@@ -65,3 +65,28 @@ def test_invalid_update():
"""Test that an invalid new version raises an exception."""
with pytest.raises(ValueError):
migration._apply_update(None, -1, 0)
def test_forgiving_add_column():
"""Test that add column will continue if column exists."""
engine = create_engine(
'sqlite://',
poolclass=StaticPool
)
engine.execute('CREATE TABLE hello (id int)')
migration._add_columns(engine, 'hello', [
'context_id CHARACTER(36)',
])
migration._add_columns(engine, 'hello', [
'context_id CHARACTER(36)',
])
def test_forgiving_add_index():
"""Test that add index will continue if index exists."""
engine = create_engine(
'sqlite://',
poolclass=StaticPool
)
models.Base.metadata.create_all(engine)
migration._create_index(engine, "states", "ix_states_context_id")