Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 8 additions & 66 deletions homeassistant/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@
__version__, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, CONF_CUSTOMIZE_GLOB,
CONF_WHITELIST_EXTERNAL_DIRS, CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES,
CONF_TYPE, CONF_ID)
from homeassistant.core import callback, DOMAIN as CONF_CORE, HomeAssistant
from homeassistant.core import (
DOMAIN as CONF_CORE, SOURCE_DISCOVERED, SOURCE_YAML, HomeAssistant,
callback)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.loader import (
Integration, async_get_integration, IntegrationNotFound
)
from homeassistant.util.yaml import load_yaml, SECRET_YAML
import homeassistant.helpers.config_validation as cv
from homeassistant.util import dt as date_util, location as loc_util
from homeassistant.util import location as loc_util
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
from homeassistant.helpers.entity_values import EntityValues
from homeassistant.helpers import config_per_platform, extract_domain_configs
Expand All @@ -50,13 +52,6 @@
('ios.conf', '.ios.conf'),
)

CORE_STORAGE_KEY = 'homeassistant.core_config'
CORE_STORAGE_VERSION = 1

SOURCE_DISCOVERED = 'discovered'
SOURCE_STORAGE = 'storage'
SOURCE_YAML = 'yaml'

DEFAULT_CORE_CONFIG = (
# Tuples (attribute, default, auto detect property, description)
(CONF_NAME, 'Home', None, 'Name of the location where Home Assistant is '
Expand Down Expand Up @@ -480,42 +475,6 @@ def _format_config_error(ex: vol.Invalid, domain: str, config: Dict) -> str:
return message


def _set_time_zone(hass: HomeAssistant, time_zone_str: Optional[str]) -> None:
"""Help to set the time zone."""
if time_zone_str is None:
return

time_zone = date_util.get_time_zone(time_zone_str)

if time_zone:
hass.config.time_zone = time_zone
date_util.set_default_time_zone(time_zone)
else:
_LOGGER.error("Received invalid time zone %s", time_zone_str)


async def async_load_ha_core_config(hass: HomeAssistant) -> None:
"""Store [homeassistant] core config."""
store = hass.helpers.storage.Store(CORE_STORAGE_VERSION, CORE_STORAGE_KEY,
private=True)
data = await store.async_load()
if not data:
return

hac = hass.config
hac.config_source = SOURCE_STORAGE
hac.latitude = data['latitude']
hac.longitude = data['longitude']
hac.elevation = data['elevation']
unit_system = data['unit_system']
if unit_system == CONF_UNIT_SYSTEM_IMPERIAL:
hac.units = IMPERIAL_SYSTEM
else:
hac.units = METRIC_SYSTEM
hac.location_name = data['location_name']
_set_time_zone(hass, data['time_zone'])


async def async_process_ha_core_config(
hass: HomeAssistant, config: Dict,
api_password: Optional[str] = None,
Expand Down Expand Up @@ -554,7 +513,7 @@ async def async_process_ha_core_config(
auth_conf,
mfa_conf))

await async_load_ha_core_config(hass)
await hass.config.async_load()

hac = hass.config

Expand All @@ -570,7 +529,8 @@ async def async_process_ha_core_config(
if key in config:
setattr(hac, attr, config[key])

_set_time_zone(hass, config.get(CONF_TIME_ZONE))
if CONF_TIME_ZONE in config:
hac.set_time_zone(config[CONF_TIME_ZONE])

# Init whitelist external dir
hac.whitelist_external_dirs = {hass.config.path('www')}
Expand Down Expand Up @@ -651,7 +611,7 @@ async def async_process_ha_core_config(
discovered.append(('name', info.city))

if hac.time_zone is None:
_set_time_zone(hass, info.time_zone)
hac.set_time_zone(info.time_zone)
discovered.append(('time_zone', info.time_zone))

if hac.elevation is None and hac.latitude is not None and \
Expand All @@ -668,24 +628,6 @@ async def async_process_ha_core_config(
", ".join('{}: {}'.format(key, val) for key, val in discovered))


async def async_store_ha_core_config(hass: HomeAssistant) -> None:
"""Store [homeassistant] core config."""
config = hass.config.as_dict()

data = {
'latitude': config['latitude'],
'longitude': config['longitude'],
'elevation': config['elevation'],
'unit_system': hass.config.units.name,
'location_name': config['location_name'],
'time_zone': config['time_zone'],
}

store = hass.helpers.storage.Store(CORE_STORAGE_VERSION, CORE_STORAGE_KEY,
private=True)
await store.async_save(data)


def _log_pkg_error(
package: str, component: str, config: Dict, message: str) -> None:
"""Log an error while merging packages."""
Expand Down
18 changes: 10 additions & 8 deletions homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,21 +160,23 @@
CONF_ZONE = 'zone'

# #### EVENTS ####
EVENT_AUTOMATION_TRIGGERED = 'automation_triggered'
EVENT_CALL_SERVICE = 'call_service'
EVENT_COMPONENT_LOADED = 'component_loaded'
EVENT_CORE_CONFIG_UPDATE = 'core_config_updated'
EVENT_HOMEASSISTANT_CLOSE = 'homeassistant_close'
EVENT_HOMEASSISTANT_START = 'homeassistant_start'
EVENT_HOMEASSISTANT_STOP = 'homeassistant_stop'
EVENT_HOMEASSISTANT_CLOSE = 'homeassistant_close'
EVENT_STATE_CHANGED = 'state_changed'
EVENT_TIME_CHANGED = 'time_changed'
EVENT_CALL_SERVICE = 'call_service'
EVENT_LOGBOOK_ENTRY = 'logbook_entry'
EVENT_PLATFORM_DISCOVERED = 'platform_discovered'
EVENT_COMPONENT_LOADED = 'component_loaded'
EVENT_SCRIPT_STARTED = 'script_started'
EVENT_SERVICE_REGISTERED = 'service_registered'
EVENT_SERVICE_REMOVED = 'service_removed'
EVENT_LOGBOOK_ENTRY = 'logbook_entry'
EVENT_STATE_CHANGED = 'state_changed'
EVENT_THEMES_UPDATED = 'themes_updated'
EVENT_TIMER_OUT_OF_SYNC = 'timer_out_of_sync'
EVENT_AUTOMATION_TRIGGERED = 'automation_triggered'
EVENT_SCRIPT_STARTED = 'script_started'
EVENT_TIME_CHANGED = 'time_changed'


# #### DEVICE CLASSES ####
DEVICE_CLASS_BATTERY = 'battery'
Expand Down
112 changes: 102 additions & 10 deletions homeassistant/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
import voluptuous as vol

from homeassistant.const import (
ATTR_DOMAIN, ATTR_FRIENDLY_NAME, ATTR_NOW, ATTR_SERVICE,
ATTR_SERVICE_DATA, ATTR_SECONDS, EVENT_CALL_SERVICE,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
EVENT_HOMEASSISTANT_CLOSE, EVENT_SERVICE_REMOVED,
EVENT_SERVICE_REGISTERED, EVENT_STATE_CHANGED,
EVENT_TIME_CHANGED, EVENT_TIMER_OUT_OF_SYNC, MATCH_ALL, __version__)
ATTR_DOMAIN, ATTR_FRIENDLY_NAME, ATTR_NOW, ATTR_SERVICE, ATTR_SERVICE_DATA,
ATTR_SECONDS, CONF_UNIT_SYSTEM_IMPERIAL, EVENT_CALL_SERVICE,
EVENT_CORE_CONFIG_UPDATE, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_CLOSE, EVENT_SERVICE_REMOVED,
EVENT_SERVICE_REGISTERED, EVENT_STATE_CHANGED, EVENT_TIME_CHANGED,
EVENT_TIMER_OUT_OF_SYNC, MATCH_ALL, __version__)
from homeassistant import loader
from homeassistant.exceptions import (
HomeAssistantError, InvalidEntityFormatError, InvalidStateError,
Expand All @@ -43,7 +43,8 @@
from homeassistant import util
import homeassistant.util.dt as dt_util
from homeassistant.util import location, slugify
from homeassistant.util.unit_system import UnitSystem, METRIC_SYSTEM # NOQA
from homeassistant.util.unit_system import ( # NOQA
UnitSystem, IMPERIAL_SYSTEM, METRIC_SYSTEM)

# Typing imports that create a circular dependency
# pylint: disable=using-constant-test
Expand All @@ -56,11 +57,19 @@
CALLBACK_TYPE = Callable[[], None]
# pylint: enable=invalid-name

CORE_STORAGE_KEY = 'homeassistant.core_config'
CORE_STORAGE_VERSION = 1

DOMAIN = 'homeassistant'

# How long we wait for the result of a service call
SERVICE_CALL_LIMIT = 10 # seconds

# Source of core configuration
SOURCE_DISCOVERED = 'discovered'
SOURCE_STORAGE = 'storage'
SOURCE_YAML = 'yaml'

# How long to wait till things that run on startup have to finish.
TIMEOUT_EVENT_START = 15

Expand Down Expand Up @@ -144,7 +153,7 @@ def __init__(
self.bus = EventBus(self)
self.services = ServiceRegistry(self)
self.states = StateMachine(self.bus, self.loop)
self.config = Config() # type: Config
self.config = Config(self) # type: Config
self.components = loader.Components(self)
self.helpers = loader.Helpers(self)
# This is a dictionary that any component can store any data on.
Expand Down Expand Up @@ -1168,8 +1177,10 @@ async def _execute_service(self, handler: Service,
class Config:
"""Configuration settings for Home Assistant."""

def __init__(self) -> None:
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize a new config object."""
self.hass = hass

self.latitude = None # type: Optional[float]
self.longitude = None # type: Optional[float]
self.elevation = None # type: Optional[int]
Expand Down Expand Up @@ -1235,7 +1246,7 @@ def is_allowed_path(self, path: str) -> bool:
return False

def as_dict(self) -> Dict:
"""Create a dictionary representation of this dict.
"""Create a dictionary representation of the configuration.

Async friendly.
"""
Expand All @@ -1257,6 +1268,87 @@ def as_dict(self) -> Dict:
'config_source': self.config_source
}

def set_time_zone(self, time_zone_str: str) -> None:
"""Help to set the time zone."""
time_zone = dt_util.get_time_zone(time_zone_str)

if time_zone:
self.time_zone = time_zone
dt_util.set_default_time_zone(time_zone)
else:
raise ValueError(
"Received invalid time zone {}".format(time_zone_str))

@callback
def _update(self, *,
source: str,
Comment thread
balloob marked this conversation as resolved.
latitude: Optional[float] = None,
longitude: Optional[float] = None,
elevation: Optional[int] = None,
unit_system: Optional[str] = None,
location_name: Optional[str] = None,
time_zone: Optional[str] = None) -> None:
"""Update the configuration from a dictionary.

Async friendly.
"""
self.config_source = source
if latitude is not None:
self.latitude = latitude
if longitude is not None:
self.longitude = longitude
if elevation is not None:
self.elevation = elevation
if unit_system is not None:
if unit_system == CONF_UNIT_SYSTEM_IMPERIAL:
self.units = IMPERIAL_SYSTEM
else:
self.units = METRIC_SYSTEM
if location_name is not None:
self.location_name = location_name
if time_zone is not None:
self.set_time_zone(time_zone)

async def update(self, **kwargs: Any) -> None:
"""Update the configuration from a dictionary.

Async friendly.
"""
self._update(source=SOURCE_STORAGE, **kwargs)
await self.async_store()
self.hass.bus.async_fire(
EVENT_CORE_CONFIG_UPDATE, kwargs
)

async def async_load(self) -> None:
"""Load [homeassistant] core config."""
store = self.hass.helpers.storage.Store(
CORE_STORAGE_VERSION, CORE_STORAGE_KEY, private=True)
data = await store.async_load()
if not data:
return

self._update(source=SOURCE_STORAGE, **data)

async def async_store(self) -> None:
"""Store [homeassistant] core config."""
time_zone = dt_util.UTC.zone
if self.time_zone and getattr(self.time_zone, 'zone'):
time_zone = getattr(self.time_zone, 'zone')

data = {
'latitude': self.latitude,
'longitude': self.longitude,
'elevation': self.elevation,
'unit_system': self.units.name,
'location_name': self.location_name,
'time_zone': time_zone,
}

store = self.hass.helpers.storage.Store(
CORE_STORAGE_VERSION, CORE_STORAGE_KEY, private=True)
await store.async_save(data)


def _async_create_timer(hass: HomeAssistant) -> None:
"""Create a timer that will start on HOMEASSISTANT_START."""
Expand Down
1 change: 0 additions & 1 deletion tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ def stop_hass():
async def async_test_home_assistant(loop):
"""Return a Home Assistant object pointing at test config dir."""
hass = ha.HomeAssistant(loop)
hass.config.async_load = Mock()
store = auth_store.AuthStore(hass)
hass.auth = auth.AuthManager(hass, store, {}, {})
ensure_auth_manager_loaded(hass.auth)
Expand Down
Loading