diff --git a/hassio/bootstrap.py b/hassio/bootstrap.py index 1f1efa11b..f6c07118c 100644 --- a/hassio/bootstrap.py +++ b/hassio/bootstrap.py @@ -22,10 +22,11 @@ from .host import HostManager from .ingress import Ingress from .services import ServiceManager from .snapshots import SnapshotManager -from .supervisor import Supervisor from .store import StoreManager +from .supervisor import Supervisor from .tasks import Tasks from .updater import Updater +from .utils.dt import fetch_timezone _LOGGER = logging.getLogger(__name__) @@ -66,6 +67,10 @@ async def initialize_coresys(): if MACHINE_ID.exists(): coresys.machine_id = MACHINE_ID.read_text().strip() + # Init TimeZone + if coresys.config.timezone == "UTC": + coresys.config.timezone = await fetch_timezone(coresys.websession) + return coresys diff --git a/hassio/config.py b/hassio/config.py index eb569beb5..37b59362e 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -3,9 +3,6 @@ from datetime import datetime import logging import os from pathlib import Path, PurePath -import re - -import pytz from .const import ( ATTR_ADDONS_CUSTOM_LIST, @@ -40,8 +37,6 @@ APPARMOR_DATA = PurePath("apparmor") DEFAULT_BOOT_TIME = datetime.utcfromtimestamp(0).isoformat() -RE_TIMEZONE = re.compile(r"time_zone: (?P[\w/\-+]+)") - class CoreConfig(JsonConfig): """Hold all core config data.""" @@ -53,21 +48,7 @@ class CoreConfig(JsonConfig): @property def timezone(self): """Return system timezone.""" - config_file = Path(self.path_homeassistant, "configuration.yaml") - try: - assert config_file.exists() - configuration = config_file.read_text() - - data = RE_TIMEZONE.search(configuration) - assert data - - timezone = data.group("timezone") - pytz.timezone(timezone) - except (pytz.exceptions.UnknownTimeZoneError, OSError, AssertionError): - _LOGGER.debug("Can't parse Home Assistant timezone") - return self._data[ATTR_TIMEZONE] - - return timezone + return self._data[ATTR_TIMEZONE] @timezone.setter def timezone(self, value): diff --git a/hassio/utils/dt.py b/hassio/utils/dt.py index 59992b79f..afa3f8ea5 100644 --- a/hassio/utils/dt.py +++ b/hassio/utils/dt.py @@ -1,13 +1,17 @@ """Tools file for Hass.io.""" +import asyncio from datetime import datetime, timedelta, timezone, tzinfo import logging import re from typing import Any, Dict, Optional +import aiohttp import pytz UTC = pytz.utc +GEOIP_URL = "http://ip-api.com/json/" + _LOGGER = logging.getLogger(__name__) @@ -22,6 +26,21 @@ DATETIME_RE = re.compile( ) +async def fetch_timezone(websession): + """Read timezone from freegeoip.""" + data = {} + try: + async with websession.get(GEOIP_URL, timeout=10) as request: + data = await request.json() + + except (aiohttp.ClientError, asyncio.TimeoutError) as err: + _LOGGER.warning("Can't fetch freegeoip data: %s", err) + except ValueError as err: + _LOGGER.warning("Error on parse freegeoip data: %s", err) + + return data.get("timezone", "UTC") + + # Copyright (c) Django Software Foundation and individual contributors. # All rights reserved. # https://github.com/django/django/blob/master/LICENSE diff --git a/tests/common.py b/tests/common.py index c432bc037..eed83c84d 100644 --- a/tests/common.py +++ b/tests/common.py @@ -7,3 +7,20 @@ def load_json_fixture(filename): """Load a fixture.""" path = Path(Path(__file__).parent.joinpath("fixtures"), filename) return json.loads(path.read_text()) + + +def mock_coro(return_value=None, exception=None): + """Return a coro that returns a value or raise an exception.""" + return mock_coro_func(return_value, exception)() + + +def mock_coro_func(return_value=None, exception=None): + """Return a method to create a coro function that returns a value.""" + + async def coro(*args, **kwargs): + """Fake coroutine.""" + if exception: + raise exception + return return_value + + return coro diff --git a/tests/conftest.py b/tests/conftest.py index d30c8dcae..ac105e071 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,8 @@ import pytest from hassio.bootstrap import initialize_coresys +from tests.common import mock_coro + # pylint: disable=redefined-outer-name @@ -18,7 +20,10 @@ def docker(): @pytest.fixture async def coresys(loop, docker): """Create a CoreSys Mock.""" - with patch("hassio.bootstrap.initialize_system_data"): + with patch("hassio.bootstrap.initialize_system_data"), patch( + "hassio.bootstrap.fetch_timezone", + return_value=mock_coro(return_value="Europe/Zurich"), + ): coresys_obj = await initialize_coresys() coresys_obj.ingress.save_data = MagicMock()