diff --git a/homeassistant/components/__init__.py b/homeassistant/components/__init__.py index 38780ed9b28..7d025bac765 100644 --- a/homeassistant/components/__init__.py +++ b/homeassistant/components/__init__.py @@ -11,7 +11,6 @@ import itertools as it import logging import homeassistant.core as ha -from homeassistant.helpers.entity import split_entity_id from homeassistant.helpers.service import extract_entity_ids from homeassistant.loader import get_component from homeassistant.const import ( @@ -35,7 +34,7 @@ def is_on(hass, entity_id=None): entity_ids = hass.states.entity_ids() for entity_id in entity_ids: - domain = split_entity_id(entity_id)[0] + domain = ha.split_entity_id(entity_id)[0] module = get_component(domain) @@ -95,7 +94,7 @@ def setup(hass, config): # Group entity_ids by domain. groupby requires sorted data. by_domain = it.groupby(sorted(entity_ids), - lambda item: split_entity_id(item)[0]) + lambda item: ha.split_entity_id(item)[0]) for domain, ent_ids in by_domain: # We want to block for all calls and only return when all calls diff --git a/homeassistant/components/group.py b/homeassistant/components/group.py index 9e0c0f897e5..be998b48f23 100644 --- a/homeassistant/components/group.py +++ b/homeassistant/components/group.py @@ -14,8 +14,7 @@ from homeassistant.const import ( ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_CLOSED, STATE_HOME, STATE_NOT_HOME, STATE_OFF, STATE_ON, STATE_OPEN, STATE_LOCKED, STATE_UNLOCKED, STATE_UNKNOWN, ATTR_ASSUMED_STATE) -from homeassistant.helpers.entity import ( - Entity, generate_entity_id, split_entity_id) +from homeassistant.helpers.entity import Entity, generate_entity_id from homeassistant.helpers.event import track_state_change import homeassistant.helpers.config_validation as cv @@ -101,7 +100,7 @@ def expand_entity_ids(hass, entity_ids): try: # If entity_id points at a group, expand it - domain, _ = split_entity_id(entity_id) + domain, _ = ha.split_entity_id(entity_id) if domain == DOMAIN: found_ids.extend( diff --git a/homeassistant/components/http.py b/homeassistant/components/http.py index 96a4d196272..38f7b66f97d 100644 --- a/homeassistant/components/http.py +++ b/homeassistant/components/http.py @@ -20,7 +20,7 @@ from homeassistant.const import ( HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, ALLOWED_CORS_HEADERS, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START) -from homeassistant.helpers.entity import split_entity_id +from homeassistant.core import split_entity_id import homeassistant.util.dt as dt_util import homeassistant.helpers.config_validation as cv diff --git a/homeassistant/components/logbook.py b/homeassistant/components/logbook.py index 6508318a907..5adf21e1f3a 100644 --- a/homeassistant/components/logbook.py +++ b/homeassistant/components/logbook.py @@ -18,10 +18,8 @@ from homeassistant.components.http import HomeAssistantView from homeassistant.const import (EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_STATE_CHANGED, STATE_NOT_HOME, STATE_OFF, STATE_ON) -from homeassistant.core import DOMAIN as HA_DOMAIN -from homeassistant.core import State +from homeassistant.core import State, split_entity_id, DOMAIN as HA_DOMAIN from homeassistant.helpers import template -from homeassistant.helpers.entity import split_entity_id DOMAIN = "logbook" DEPENDENCIES = ['recorder', 'frontend'] diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index fdb5642562f..fe67476b11e 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -9,9 +9,8 @@ from sqlalchemy import (Boolean, Column, DateTime, ForeignKey, Index, Integer, from sqlalchemy.ext.declarative import declarative_base import homeassistant.util.dt as dt_util -from homeassistant.core import Event, EventOrigin, State +from homeassistant.core import Event, EventOrigin, State, split_entity_id from homeassistant.remote import JSONEncoder -from homeassistant.helpers.entity import split_entity_id # SQLAlchemy Schema # pylint: disable=invalid-name diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 5f1e63f5d00..b235c4d4eb7 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -14,7 +14,8 @@ import voluptuous as vol from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_TOGGLE, STATE_ON, CONF_ALIAS) -from homeassistant.helpers.entity import ToggleEntity, split_entity_id +from homeassistant.core import split_entity_id +from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity_component import EntityComponent import homeassistant.helpers.config_validation as cv diff --git a/homeassistant/config.py b/homeassistant/config.py index a614f139e2f..1d280b40b1a 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -14,11 +14,12 @@ from homeassistant.const import ( CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS, __version__) +from homeassistant.core import valid_entity_id from homeassistant.exceptions import HomeAssistantError from homeassistant.util.yaml import load_yaml import homeassistant.helpers.config_validation as cv from homeassistant.helpers.unit_system import (IMPERIAL_SYSTEM, METRIC_SYSTEM) -from homeassistant.helpers.entity import valid_entity_id, set_customize +from homeassistant.helpers.entity import set_customize from homeassistant.util import dt as date_util, location as loc_util _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/core.py b/homeassistant/core.py index 107008216da..bd33cae792a 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -9,22 +9,17 @@ import enum import functools as ft import logging import os +import re import signal import threading import time from types import MappingProxyType # pylint: disable=unused-import -from typing import Optional, Any, Callable # NOQA +from typing import Optional, Any, Callable, List # NOQA import voluptuous as vol -from homeassistant.helpers.typing import UnitSystemType # NOQA - -import homeassistant.util as util -import homeassistant.util.dt as dt_util -import homeassistant.util.location as location -from homeassistant.config import get_default_config_dir from homeassistant.const import ( ATTR_DOMAIN, ATTR_FRIENDLY_NAME, ATTR_NOW, ATTR_SERVICE, ATTR_SERVICE_CALL_ID, ATTR_SERVICE_DATA, EVENT_CALL_SERVICE, @@ -34,10 +29,11 @@ from homeassistant.const import ( SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP, __version__) from homeassistant.exceptions import ( HomeAssistantError, InvalidEntityFormatError) -from homeassistant.helpers.entity import split_entity_id, valid_entity_id -from homeassistant.helpers.unit_system import ( - METRIC_SYSTEM, -) +from homeassistant.helpers.typing import UnitSystemType # NOQA +from homeassistant.helpers.unit_system import METRIC_SYSTEM +import homeassistant.util as util +import homeassistant.util.dt as dt_util +import homeassistant.util.location as location DOMAIN = "homeassistant" @@ -52,9 +48,22 @@ SERVICE_CALL_LIMIT = 10 # seconds # will be added for each component that polls devices. MIN_WORKER_THREAD = 2 +# Pattern for validating entity IDs (format: .) +ENTITY_ID_PATTERN = re.compile(r"^(\w+)\.(\w+)$") + _LOGGER = logging.getLogger(__name__) +def split_entity_id(entity_id: str) -> List[str]: + """Split a state entity_id into domain, object_id.""" + return entity_id.split(".", 1) + + +def valid_entity_id(entity_id: str) -> bool: + """Test if an entity ID is a valid format.""" + return ENTITY_ID_PATTERN.match(entity_id) is not None + + class CoreState(enum.Enum): """Represent the current state of Home Assistant.""" @@ -734,7 +743,7 @@ class Config(object): self.api = None # Directory that holds the configuration - self.config_dir = get_default_config_dir() + self.config_dir = None def distance(self: object, lat: float, lon: float) -> float: """Calculate distance from Home Assistant.""" @@ -743,6 +752,8 @@ class Config(object): def path(self, *path): """Generate path to the file within the config dir.""" + if self.config_dir is None: + raise HomeAssistantError("config_dir is not set") return os.path.join(self.config_dir, *path) def as_dict(self): diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index a9b965930ae..2c4ed6795e9 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -12,7 +12,7 @@ from homeassistant.const import ( CONF_ALIAS, CONF_ENTITY_ID, CONF_VALUE_TEMPLATE, WEEKDAYS, CONF_CONDITION, CONF_BELOW, CONF_ABOVE, SUN_EVENT_SUNSET, SUN_EVENT_SUNRISE, CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_METRIC) -from homeassistant.helpers.entity import valid_entity_id +from homeassistant.core import valid_entity_id import homeassistant.util.dt as dt_util from homeassistant.util import slugify diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 6e75e8ce59b..2ec64b3ea09 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -1,6 +1,5 @@ """An abstract class for entities.""" import logging -import re from typing import Any, Optional, List, Dict @@ -19,9 +18,6 @@ _OVERWRITE = {} # type: Dict[str, Any] _LOGGER = logging.getLogger(__name__) -# Pattern for validating entity IDs (format: .) -ENTITY_ID_PATTERN = re.compile(r"^(\w+)\.(\w+)$") - def generate_entity_id(entity_id_format: str, name: Optional[str], current_ids: Optional[List[str]]=None, @@ -45,16 +41,6 @@ def set_customize(customize: Dict[str, Any]) -> None: _OVERWRITE = {key.lower(): val for key, val in customize.items()} -def split_entity_id(entity_id: str) -> List[str]: - """Split a state entity_id into domain, object_id.""" - return entity_id.split(".", 1) - - -def valid_entity_id(entity_id: str) -> bool: - """Test if an entity ID is a valid format.""" - return ENTITY_ID_PATTERN.match(entity_id) is not None - - class Entity(object): """An abstract class for Home Assistant entities.""" diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py index a465c2f2c74..593e8b433c0 100644 --- a/tests/helpers/test_entity.py +++ b/tests/helpers/test_entity.py @@ -37,11 +37,6 @@ class TestHelpersEntity(unittest.TestCase): state = self.hass.states.get(self.entity.entity_id) self.assertTrue(state.attributes.get(ATTR_HIDDEN)) - def test_split_entity_id(self): - """Test split_entity_id.""" - self.assertEqual(['domain', 'object_id'], - entity.split_entity_id('domain.object_id')) - def test_generate_entity_id_requires_hass_or_ids(self): """Ensure we require at least hass or current ids.""" fmt = 'test.{}' diff --git a/tests/test_core.py b/tests/test_core.py index 56a44964e79..ea9f96ab808 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -25,6 +25,15 @@ from tests.common import get_test_home_assistant PST = pytz.timezone('America/Los_Angeles') +class TestMethods(unittest.TestCase): + """Test the Home Assistant helper methods.""" + + def test_split_entity_id(self): + """Test split_entity_id.""" + self.assertEqual(['domain', 'object_id'], + ha.split_entity_id('domain.object_id')) + + class TestHomeAssistant(unittest.TestCase): """Test the Home Assistant core classes.""" @@ -442,28 +451,19 @@ class TestConfig(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" self.config = ha.Config() - - def test_config_dir_set_correct(self): - """Test config dir set correct.""" - data_dir = os.getenv('APPDATA') if os.name == "nt" \ - else os.path.expanduser('~') - self.assertEqual(os.path.join(data_dir, ".homeassistant"), - self.config.config_dir) + self.assertIsNone(self.config.config_dir) def test_path_with_file(self): """Test get_config_path method.""" - data_dir = os.getenv('APPDATA') if os.name == "nt" \ - else os.path.expanduser('~') - self.assertEqual(os.path.join(data_dir, ".homeassistant", "test.conf"), + self.config.config_dir = '/tmp/ha-config' + self.assertEqual("/tmp/ha-config/test.conf", self.config.path("test.conf")) def test_path_with_dir_and_file(self): """Test get_config_path method.""" - data_dir = os.getenv('APPDATA') if os.name == "nt" \ - else os.path.expanduser('~') - self.assertEqual( - os.path.join(data_dir, ".homeassistant", "dir", "test.conf"), - self.config.path("dir", "test.conf")) + self.config.config_dir = '/tmp/ha-config' + self.assertEqual("/tmp/ha-config/dir/test.conf", + self.config.path("dir", "test.conf")) def test_as_dict(self): """Test as dict.""" diff --git a/tests/test_remote.py b/tests/test_remote.py index 873fa423568..7c87bc4af33 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -10,7 +10,8 @@ import homeassistant.components.http as http from homeassistant.const import HTTP_HEADER_HA_AUTH, EVENT_STATE_CHANGED import homeassistant.util.dt as dt_util -from tests.common import get_test_instance_port, get_test_home_assistant +from tests.common import ( + get_test_instance_port, get_test_home_assistant, get_test_config_dir) API_PASSWORD = "test1234" MASTER_PORT = get_test_instance_port() @@ -52,6 +53,7 @@ def setUpModule(): # pylint: disable=invalid-name # Start slave slave = remote.HomeAssistant(master_api) + slave.config.config_dir = get_test_config_dir() bootstrap.setup_component( slave, http.DOMAIN, {http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD,