diff --git a/README.md b/README.md index cab4cae..93cbc8c 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ See [HACS](https://github.com/custom-components/hacs). Alternatively, place a copy of: [`__init__.py`](custom_components/composite/__init__.py) at `/custom_components/composite/__init__.py` +[`const.py`](custom_components/composite/const.py) at `/custom_components/composite/const.py` [`device_tracker.py`](custom_components/composite/device_tracker.py) at `/custom_components/composite/device_tracker.py` [`manifest.json`](custom_components/composite/manifest.json) at `/custom_components/composite/manifest.json` @@ -37,13 +38,21 @@ where `` is your Home Assistant configuration directory. ### numpy on Raspberry Pi -To determine time zone from GPS coordinates (see `time_as` configuration variable below) the package [timezonefinderL](https://pypi.org/project/timezonefinderL/) is used. That package requires the package [numpy](https://pypi.org/project/numpy/). These will both be installed automatically by HA. Note, however, that numpy on Pi _usually_ requires libatlas to be installed. (See [this web page](https://www.raspberrypi.org/forums/viewtopic.php?t=207058) for more details.) It can be installed using this command: +To determine time zone from GPS coordinates (see `time_as` configuration variable below) the package [timezonefinderL](https://pypi.org/project/timezonefinderL/) (by default) is used. That package requires the package [numpy](https://pypi.org/project/numpy/). These will both be installed automatically by HA. Note, however, that numpy on Pi _usually_ requires libatlas to be installed. (See [this web page](https://www.raspberrypi.org/forums/viewtopic.php?t=207058) for more details.) It can be installed using this command: ``` sudo apt install libatlas3-base ``` >Note: This is the same step that would be required if using a standard HA component that uses numpy (such as the [Trend Binary Sensor](https://www.home-assistant.io/components/binary_sensor.trend/)), and is only required if you use `device_or_utc` or `device_or_local` for `time_as`. ## Configuration variables +### `composite` integration + +- **tz_finder** (*Optional*): Specifies which `timezonefinder` package, and possibly version, to install. Must be formatted as required by `pip`. Default is `timezonefinderL==4.0.2`. Other common values: + +`timezonefinderL==2.0.1` +`timezonefinder` +`timezonefinder==4.2.0` +### `device_tracker` platform - **entity_id**: Entity IDs of watched device tracker devices. Can be a single entity ID, a list of entity IDs, or a string containing multiple entity IDs separated by commas. - **name**: Object ID (i.e., part of entity ID after the dot) of composite device. For example, `NAME` would result in an entity ID of `device_tracker.NAME`. @@ -84,6 +93,20 @@ source_type | Source of current location information: `binary_sensor`, `bluetoot time_zone | The name of the time zone in which the device is located, or `unknown` if it cannot be determined. Only exists if `device_or_utc` or `device_or_local` is chosen for `time_as`. ## Examples +### Example Full Config +```yaml +composite: + tz_finder: timezonefinderL==2.0.1 +device_tracker: + - platform: composite + name: me + time_as: device_or_local + require_movement: true + entity_id: + - device_tracker.platform1_me + - device_tracker.platform2_me +``` + ### Time zone examples This example assumes `time_as` is set to `device_or_utc` or `device_or_local`. It determines the difference between the time zone in which the device is located and the `time_zone` in HA's configuration. A positive value means the device's time zone is ahead of (or later than, or east of) the local time zone. diff --git a/custom_components/composite/__init__.py b/custom_components/composite/__init__.py index d7b4788..c6899ce 100644 --- a/custom_components/composite/__init__.py +++ b/custom_components/composite/__init__.py @@ -1 +1,53 @@ """Composite Device Tracker.""" +import asyncio +import logging + +import voluptuous as vol + +from homeassistant.requirements import ( + async_process_requirements, RequirementsNotFound) +from homeassistant.components.device_tracker import DOMAIN as DT_DOMAIN +from homeassistant.const import CONF_PLATFORM +import homeassistant.helpers.config_validation as cv + +from .const import CONF_TIME_AS, DOMAIN, TZ_DEVICE_LOCAL, TZ_DEVICE_UTC + +__version__ = '2.0.0' + +CONF_TZ_FINDER = 'tz_finder' +DEFAULT_TZ_FINDER = 'timezonefinderL==4.0.2' + +CONFIG_SCHEMA = vol.Schema({ + vol.Optional(DOMAIN, default=dict): vol.Schema({ + vol.Optional(CONF_TZ_FINDER, default=DEFAULT_TZ_FINDER): + cv.string, + }), +}, extra=vol.ALLOW_EXTRA) + +_LOGGER = logging.getLogger(__name__) + + +def setup(hass, config): + if (any(conf[CONF_TIME_AS] in (TZ_DEVICE_UTC, TZ_DEVICE_LOCAL) + for conf in (config.get(DT_DOMAIN) or []) + if conf[CONF_PLATFORM] == DOMAIN)): + pkg = config[DOMAIN][CONF_TZ_FINDER] + try: + asyncio.run_coroutine_threadsafe( + async_process_requirements( + hass, '{}.{}'.format(DOMAIN, DT_DOMAIN), [pkg]), + hass.loop + ).result() + except RequirementsNotFound: + _LOGGER.debug('Process requirements failed: %s', pkg) + return False + else: + _LOGGER.debug('Process requirements suceeded: %s', pkg) + + if pkg.split('==')[0].strip().endswith('L'): + from timezonefinderL import TimezoneFinder + else: + from timezonefinder import TimezoneFinder + hass.data[DOMAIN] = TimezoneFinder() + + return True diff --git a/custom_components/composite/const.py b/custom_components/composite/const.py new file mode 100644 index 0000000..bf42516 --- /dev/null +++ b/custom_components/composite/const.py @@ -0,0 +1,12 @@ +"""Constants for Composite Integration.""" +DOMAIN = 'composite' + +CONF_REQ_MOVEMENT = 'require_movement' +CONF_TIME_AS = 'time_as' + +TZ_UTC = 'utc' +TZ_LOCAL = 'local' +TZ_DEVICE_UTC = 'device_or_utc' +TZ_DEVICE_LOCAL = 'device_or_local' +# First item in list is default. +TIME_AS_OPTS = [TZ_UTC, TZ_LOCAL, TZ_DEVICE_UTC, TZ_DEVICE_LOCAL] diff --git a/custom_components/composite/device_tracker.py b/custom_components/composite/device_tracker.py index 3489a1f..0abc14f 100644 --- a/custom_components/composite/device_tracker.py +++ b/custom_components/composite/device_tracker.py @@ -24,19 +24,11 @@ import homeassistant.util.dt as dt_util from homeassistant.util.location import distance -_LOGGER = logging.getLogger(__name__) - -__version__ = '1.11.2' +from .const import ( + CONF_REQ_MOVEMENT, CONF_TIME_AS, DOMAIN, TIME_AS_OPTS, TZ_DEVICE_LOCAL, + TZ_DEVICE_UTC, TZ_LOCAL, TZ_UTC) -CONF_TIME_AS = 'time_as' -CONF_REQ_MOVEMENT = 'require_movement' - -TZ_UTC = 'utc' -TZ_LOCAL = 'local' -TZ_DEVICE_UTC = 'device_or_utc' -TZ_DEVICE_LOCAL = 'device_or_local' -# First item in list is default. -TIME_AS_OPTS = [TZ_UTC, TZ_LOCAL, TZ_DEVICE_UTC, TZ_DEVICE_LOCAL] +_LOGGER = logging.getLogger(__name__) ATTR_CHARGING = 'charging' ATTR_LAST_SEEN = 'last_seen' @@ -95,8 +87,7 @@ def __init__(self, hass, config, see): self._entity_id = ENTITY_ID_FORMAT.format(self._dev_id) self._time_as = config[CONF_TIME_AS] if self._time_as in [TZ_DEVICE_UTC, TZ_DEVICE_LOCAL]: - from timezonefinderL import TimezoneFinder - self._tf = TimezoneFinder() + self._tf = hass.data[DOMAIN] self._req_movement = config[CONF_REQ_MOVEMENT] self._lock = threading.Lock() self._prev_seen = None diff --git a/custom_components/composite/manifest.json b/custom_components/composite/manifest.json index 845c828..379fae9 100644 --- a/custom_components/composite/manifest.json +++ b/custom_components/composite/manifest.json @@ -2,11 +2,7 @@ "domain": "composite", "name": "Composite", "documentation": "https://github.com/pnbruckner/homeassistant-config/blob/master/docs/composite.md", - "requirements": [ - "timezonefinderL==4.0.2" - ], + "requirements": [], "dependencies": [], - "codeowners": [ - "@pnbruckner" - ] + "codeowners": ["@pnbruckner"] }