Pull to refresh

Подключаем любой (почти) GPS трекер (на примере Sinotrack ST-901) к умному дому HomeAssistant

Reading time8 min
Views17K

Введение


Как то ко мне в руки попал китайский 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, в связи с чем новая версия мода и настроек:

новые настройки

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. Актуальные версии там.
Tags:
Hubs:
Total votes 7: ↑7 and ↓0+7
Comments4

Articles