Введение
Как то ко мне в руки попал китайский GPS трекер ST-901. Устройство рассчитано в основном для использования в авто- и мото-технике, обладает gsm 2G модулем для связи с внешним миром, герметичным водонепроницаемым корпусом, небольшим встроенным аккумулятором, позволяющем работать без внешнего питания порядка 2-3 суток при передаче сигнала раз в 3 минуты, а также сигнальным проводом зажигания, позволяющем предупреждать о старте двигателя. Управлять данным трекером можно посредством SMS-команд на номер трекера, а общаться и получать уведомления как по SMS, так и подключив его к облаку через GPRS. Побаловавшись с ним некоторое время, я забросил его в ящик, пока дома не появился HomeAssistant. Возникла идея подключить его к умному дому.
Задачи
Для подключения трекера к HomeAssistant необходимо решить две задачи: получить координаты с трекера и записать их в HomeAssistant. Если для второй задачи есть сразу несколько возможных решений (например, gpslogger или owntracks_http), то решение первой задачи в моем случае усложнялось тем фактом, что в настройках трекера для передачи координат можно указать только IP адрес, а не доменное имя. Так как у меня дома нет статического адреса, то возникла идея использовать посредника. Замечу, что подобным образом можно подключить практически любой GPS трекер (а не только рассматриваемый мной в статье), совместимый с ресурсами посредника. Всем, кому интересно, что из этого вышло, добро пожаловать под кат.
Идея
Как я уже говорил выше, данный трекер можно подключать ко многим облачным сервисам. Некоторые из них с определенными ограничениями позволяют пользоваться услугами бесплатно. Некоторые сервисы имеют полноценные API для взаимодействия с ними, однако среди бесплатных я таких не нашел. Зато почти все сервисы предоставляют услугу «расшаривания» местоположения трекера по постоянной ссылке. Перебрав несколько таких сервисов и покопавшись в исходном коде расшаренных страниц, я нашел искомое в сервисе livegpstracks: запрос на получение координат. Таким образом, общая схема работы такова: трекер соединяется с сервисом livegpstracks и передает свои координаты, HomeAssistant периодически делает http запрос к сервису и получает последние записанные координаты, которые другим http запросом записываются в HomeAssistant. Вот ссылка на список всех совместимых с сервисом трекеров.
Реализация
1. Получение координат запросом
Регистрируемся в сервисе livegpstracks и подключаем свой трекер (на сайте есть подробные инструкции для различных моделей). После этого через панель инструментов на сайте создаем приватную ссылку для слежения. Ссылка имеет вид:
https://livegpstracks.com/dv_USERID.html
где USERID – цифровой ID вашей шары.
Все. Можно обращаться к сервису через запросы. Чтобы не мучить Вас долго просто приведу формат запроса:
https://livegpstracks.com/viewer_coos_s.php?username=USER&ctp=one&code=USERID&tgst=site&tgsv=12&tkv11=TIMENOWMS
Здесь USER – пользователь, под которым вы регистрировались в сервисе livegpstracks, USERID – цифровой ID, который присваивается расшаренной ссылке, TIMENOWMS – текущее время в миллисекундах (unix time).
Типичный ответ имеет вид:
[{"code":"xxx","id":"xxx","lat":"44","lng":"48","speed":"0","azimuth":"0","d":"2018-06-19","t":"09:35:17","altitude":"0","battery":"0","gpsaccuracy":""}]
Примечание: я существенно сократил вывод, а также изменил параметры code, id, lat, lng.
Метод для получения координат на python выглядит так:
def getInfoFrom(self): timenow = int(datetime.now().strftime("%s")) * 1000 response = requests.get('https://livegpstracks.com/viewer_coos_s.php', params={'username': self._user, 'ctp': 'one', 'code': self._myid, 'tgst': 'site', 'tgsv': 12, 'tkv11': timenow}) data = response.json() self._lat = data[0]["lat"] self._lon = data[0]["lng"] self._speed = data[0]["speed"] self._direction = data[0]["azimuth"] self._last_time_rcv = data[0]["d"] + ' ' + data[0]["t"]
Думаю, ничего пояснять в этом коде не нужно: получаем текущее время, делаем get запрос, получаем в ответ json, парсим его и получаем широту, долготу, скорость, направление движения и время последнего получения координат сервером.
2. Запись координат
Для записи я воспользовался модулем GPSLogger для HomeAssistant, так как он работает через http запрос и позволяет использовать отдельный пароль, отличный от пароля на весь HA. Из документации (gpslogger) видно, что запрос имеет следующий формат:
https://HAADRESS:HAPORT/api/gpslogger?latitude=LAT&longitude=LON&device=DEV&accuracy=ACC&speed=SPD&direction=DIR&api_password=PASS
Здесь HAADRESS – ip адрес или имя сервера с HA, HAPORT – порт сервера, LAT – широта, LON – долгота, DEV – имя устройства для отображения в HA, ACC – точность определения координат (почему то не работает в HA, выдает ошибку, я его не использовал), SPD – скорость, DIR – направление движения, PASS – пароль для передачи координат
Метод для записи координат на python выглядит так:
def putInfoTo(self): if self._lat != '' and self._lon != '': req_str = self._haddr+'/api/gpslogger' response = requests.get(req_str, params={'latitude': self._lat, 'longitude': self._lon, 'accuracy': 30, 'speed': self._speed, 'direction': self._direction, 'device': self._name, ' api_password': self._pwd}) self._last_time_upd = time.strftime("%Y.%m.%d %H:%M")
Думаю, тут тоже комментарии излишни.
3. Модуль
Полный код модуля получения и записи координат приведен ниже.
Код модуля
#!/usr/local/bin/python3 # coding: utf-8 import time import requests import json import logging from datetime import datetime from datetime import timedelta import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import (CONF_NAME) from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) CONF_USER = 'user' CONF_ID = 'myid' CONF_PWD = 'pwd' CONF_SITE = 'haddr' ATTR_LAT = 'Широта' ATTR_LON = 'Долгота' ATTR_SPEED = 'Скорость' DEFAULT_NAME = 'GPS_Sensor' SCAN_INTERVAL = timedelta(seconds=120) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_USER): cv.string, vol.Required(CONF_ID): cv.string, vol.Required(CONF_PWD): cv.string, vol.Required(CONF_SITE): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) def setup_platform(hass, config, add_devices, discovery_info=None): user = config.get(CONF_USER) name = config.get(CONF_NAME) pwd = config.get(CONF_PWD) myid = config.get(CONF_ID) haddr = config.get(CONF_SITE) add_devices([CarGPS(name, user, myid, haddr, pwd)]) class CarGPS(Entity): def __init__(self, name, user, myid, haddr, pwd): self._name = name self._user = user self._myid = myid self._haddr = haddr self._pwd = pwd self._lat = '' self._lon = '' self._speed = '0' self._direction = '0' self._last_time_rcv = '' self._last_time_upd = '' def getInfoFrom(self): try: today = int(datetime.now().strftime("%s")) * 1000 response = requests.get('https://livegpstracks.com/viewer_coos_s.php', params={'username': self._user, 'ctp': 'one', 'code': self._myid, 'tgst': 'site', 'tgsv': 12, 'tkv11': today}) data = response.json() self._lat = data[0]["lat"] self._lon = data[0]["lng"] self._speed = data[0]["speed"] self._direction = data[0]["azimuth"] self._last_time_rcv = data[0]["d"] + ' ' + data[0]["t"] except: _LOGGER.error('coudnt get parameters') def putInfoTo(self): if self._lat != '' and self._lon != '': try: req_str = self._haddr+'/api/gpslogger' response = requests.get(req_str, params={'latitude': self._lat, 'longitude': self._lon, 'accuracy': 30, 'speed': self._speed, 'direction': self._direction, 'device': self._name, ' api_password': self._pwd}) _LOGGER.info(response) self._last_time_upd = time.strftime("%Y.%m.%d %H:%M") except: _LOGGER.error('coudnt put parameters') #for HASS @property def name(self): return self._name @property def state(self): return self._last_time_upd def update(self): self.getInfoFrom() self.putInfoTo() @property def device_state_attributes(self): attr = {} attr[ATTR_LAT] = self._lat attr[ATTR_LON] = self._lon attr[ATTR_SPEED] = self._speed return attr
Для подключения данного модуля код необходимо скопировать в директорию «config_folder_homeassistant/custom_components/sensor/car_location.py», а также добавить в конфигурацию следующие строки:
device_tracker: - platform: gpslogger password: !secret gpslogger_password sensor: - platform: car_location name: car_sensor user: USER myid: USERID haddr: YOUR_HA_ADDRESS pwd: !secret gpslogger_password
Здесь все переменные из раздела «Получение координат запросом».
Данный модуль трудится в HA уже не один месяц безо всяких сбоев и иных проблем.
На этом все, спасибо за внимание.
UPD:
HomeAssistant обновили компоненту GPSLogger, в связи с чем новая версия мода и настроек:
новые настройки
YOUR_HA_ADDRESS_WEBHOOK — адрес вебхука GPSLogger, получить его можно в разделе Настройки — Интеграции — GPSLogger.
device_tracker: - platform: gpslogger sensor: - platform: car_location name: car_sensor user: USER myid: USERID haddr: YOUR_HA_ADDRESS_WEBHOOK
YOUR_HA_ADDRESS_WEBHOOK — адрес вебхука GPSLogger, получить его можно в разделе Настройки — Интеграции — GPSLogger.
новый код модуля
#!/usr/local/bin/python3 # coding: utf-8 import time import requests import json import logging from datetime import datetime from datetime import timedelta import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import (CONF_NAME) from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) CONF_USER = 'user' CONF_ID = 'myid' CONF_SITE = 'haddr' CONF_NAME = 'name' ATTR_LAT = 'Широта' ATTR_LON = 'Долгота' ATTR_SPEED = 'Скорость' ATTR_DATE = 'Обновлено' DEFAULT_NAME = 'GPS_Sensor' SCAN_INTERVAL = timedelta(seconds=120) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_USER): cv.string, vol.Required(CONF_ID): cv.string, vol.Required(CONF_SITE): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) def setup_platform(hass, config, add_devices, discovery_info=None): user = config.get(CONF_USER) name = config.get(CONF_NAME) myid = config.get(CONF_ID) haddr = config.get(CONF_SITE) add_devices([CarGPS(name, user, myid, haddr)]) class CarGPS(Entity): def __init__(self, name, user, myid, haddr): self._name = name self._user = user self._myid = myid self._haddr = haddr self._lat = '' self._lon = '' self._speed = '0' self._direction = '0' self._last_time_rcv = '' self._last_time_upd = '' def getInfoFrom(self): try: today = int(datetime.now().strftime("%s")) * 1000 response = requests.get('https://livegpstracks.com/viewer_coos_s.php', params={'username': self._user, 'ctp': 'one', 'code': self._myid, 'tgst': 'site', 'tgsv': 12, 'tkv11': today}) data = response.json() self._lat = str(data[0]["lat"]) self._lon = str(data[0]["lng"]) self._speed = str(data[0]["speed"]) self._direction = str(data[0]["azimuth"]) self._last_time_rcv = data[0]["d"] + ' ' + data[0]["t"] except: _LOGGER.error('coudnt get parameters') def putInfoTo(self): if self._lat != '' and self._lon != '': try: header = {'Content-Type': 'application/x-www-form-urlencoded'} body = 'latitude=' + self._lat + '&longitude=' + self._lon + '&device=' + self._name + '&accuracy=30&battery=100&speed=' + self._speed + '&direction=' + self._direction + '&altitude=0&provider=0&activity=0' response = requests.post(self._haddr, headers=header, data=body) self._last_time_upd = time.strftime("%Y.%m.%d %H:%M") except: _LOGGER.error('coudnt put parameters') #for HASS @property def name(self): return self._name @property def state(self): return self._last_time_upd def update(self): self.getInfoFrom() self.putInfoTo() @property def device_state_attributes(self): attr = {} attr[ATTR_LAT] = self._lat attr[ATTR_LON] = self._lon attr[ATTR_SPEED] = self._speed attr[ATTR_DATE] = self._last_time_rcv return attr
UPD2:
HomeAssistant обновили логику работы с версии 0.88, в связи с чем новая версия мода: sensor
UPD3:
Новая версия интеграции. Проект переехал на GitHub. Актуальные версии там.
