diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 40fb6056684f9..e17bbad78d1ca 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -26,7 +26,7 @@ from homeassistant.loader import bind_hass from homeassistant.util.yaml import load_yaml -REQUIREMENTS = ['home-assistant-frontend==20180816.1'] +REQUIREMENTS = ['home-assistant-frontend==20180818.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index 13cd6203ed4bf..4de35d3f850d6 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -156,6 +156,8 @@ def async_notify_message(service): 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 diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 939985ebfb117..0dff21a5986a4 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -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): @@ -117,22 +131,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): diff --git a/homeassistant/components/tuya.py b/homeassistant/components/tuya.py index 490c11baad77c..33f34164b02cd 100644 --- a/homeassistant/components/tuya.py +++ b/homeassistant/components/tuya.py @@ -10,14 +10,14 @@ 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 @@ 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': {} } diff --git a/homeassistant/const.py b/homeassistant/const.py index 5a481e0a8c10c..855f973554b3b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 76 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) diff --git a/requirements_all.txt b/requirements_all.txt index d65ef4fce1814..a2b640cae1e7b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -432,7 +432,7 @@ hole==0.3.0 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180816.1 +home-assistant-frontend==20180818.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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8b1534940252a..24aa79587adcc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -81,7 +81,7 @@ hbmqtt==0.9.2 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180816.1 +home-assistant-frontend==20180818.0 # homeassistant.components.homematicip_cloud homematicip==0.9.8 diff --git a/tests/components/recorder/test_migrate.py b/tests/components/recorder/test_migrate.py index 5ac9b3adb817c..1c48c26137216 100644 --- a/tests/components/recorder/test_migrate.py +++ b/tests/components/recorder/test_migrate.py @@ -5,11 +5,11 @@ 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,18 @@ 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)', + ])