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
18 changes: 18 additions & 0 deletions homeassistant/components/gpslogger/.translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"config": {
"title": "GPSLogger Webhook",
"step": {
"user": {
"title": "Set up the GPSLogger Webhook",
"description": "Are you sure you want to set up the GPSLogger Webhook?"
}
},
"abort": {
"one_instance_allowed": "Only a single instance is necessary.",
"not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive messages from GPSLogger."
},
"create_entry": {
"default": "To send events to Home Assistant, you will need to setup the webhook feature in GPSLogger.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details."
}
}
}
170 changes: 88 additions & 82 deletions homeassistant/components/gpslogger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,110 +5,116 @@
https://home-assistant.io/components/gpslogger/
"""
import logging
from hmac import compare_digest

import voluptuous as vol
from aiohttp.web_exceptions import HTTPUnauthorized
from aiohttp.web_request import Request
from aiohttp import web

import homeassistant.helpers.config_validation as cv
from homeassistant.components.http import HomeAssistantView, CONF_API_PASSWORD
from homeassistant.const import CONF_PASSWORD, HTTP_UNPROCESSABLE_ENTITY
from homeassistant.components.device_tracker import ATTR_BATTERY
from homeassistant.components.device_tracker.tile import ATTR_ALTITUDE
from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY, \
HTTP_OK, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_WEBHOOK_ID
from homeassistant.helpers import config_entry_flow
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send

_LOGGER = logging.getLogger(__name__)

DOMAIN = 'gpslogger'
DEPENDENCIES = ['http']
DEPENDENCIES = ['webhook']

CONFIG_SCHEMA = vol.Schema({
vol.Optional(DOMAIN): vol.Schema({
vol.Optional(CONF_PASSWORD): cv.string,
}),
}, extra=vol.ALLOW_EXTRA)
TRACKER_UPDATE = '{}_tracker_update'.format(DOMAIN)

URL = '/api/{}'.format(DOMAIN)
ATTR_ACCURACY = 'accuracy'
ATTR_ACTIVITY = 'activity'
ATTR_DEVICE = 'device'
ATTR_DIRECTION = 'direction'
ATTR_PROVIDER = 'provider'
ATTR_SPEED = 'speed'

DEFAULT_ACCURACY = 200
DEFAULT_BATTERY = -1


def _id(value: str) -> str:
"""Coerce id by removing '-'."""
return value.replace('-', '')

TRACKER_UPDATE = '{}_tracker_update'.format(DOMAIN)

WEBHOOK_SCHEMA = vol.Schema({
vol.Required(ATTR_LATITUDE): cv.latitude,
vol.Required(ATTR_LONGITUDE): cv.longitude,
vol.Required(ATTR_DEVICE): _id,
vol.Optional(ATTR_ACCURACY, default=DEFAULT_ACCURACY): vol.Coerce(float),
vol.Optional(ATTR_BATTERY, default=DEFAULT_BATTERY): vol.Coerce(float),
vol.Optional(ATTR_SPEED): vol.Coerce(float),
vol.Optional(ATTR_DIRECTION): vol.Coerce(float),
vol.Optional(ATTR_ALTITUDE): vol.Coerce(float),
vol.Optional(ATTR_PROVIDER): cv.string,
vol.Optional(ATTR_ACTIVITY): cv.string
})


async def async_setup(hass, hass_config):
"""Set up the GPSLogger component."""
config = hass_config[DOMAIN]
hass.http.register_view(GPSLoggerView(config))

hass.async_create_task(
async_load_platform(hass, 'device_tracker', DOMAIN, {}, hass_config)
Comment thread
rohankapoorcom marked this conversation as resolved.
)
return True


class GPSLoggerView(HomeAssistantView):
"""View to handle GPSLogger requests."""

url = URL
name = 'api:gpslogger'

def __init__(self, config):
"""Initialize GPSLogger url endpoints."""
self._password = config.get(CONF_PASSWORD)
# this component does not require external authentication if
# password is set
self.requires_auth = self._password is None

async def get(self, request: Request):
"""Handle for GPSLogger message received as GET."""
hass = request.app['hass']
data = request.query

if self._password is not None:
authenticated = CONF_API_PASSWORD in data and compare_digest(
self._password,
data[CONF_API_PASSWORD]
)
if not authenticated:
raise HTTPUnauthorized()

if 'latitude' not in data or 'longitude' not in data:
return ('Latitude and longitude not specified.',
HTTP_UNPROCESSABLE_ENTITY)

if 'device' not in data:
_LOGGER.error("Device id not specified")
return ('Device id not specified.',
HTTP_UNPROCESSABLE_ENTITY)

device = data['device'].replace('-', '')
gps_location = (data['latitude'], data['longitude'])
accuracy = 200
battery = -1

if 'accuracy' in data:
accuracy = int(float(data['accuracy']))
if 'battery' in data:
battery = float(data['battery'])

attrs = {}
if 'speed' in data:
attrs['speed'] = float(data['speed'])
if 'direction' in data:
attrs['direction'] = float(data['direction'])
if 'altitude' in data:
attrs['altitude'] = float(data['altitude'])
if 'provider' in data:
attrs['provider'] = data['provider']
if 'activity' in data:
attrs['activity'] = data['activity']

async_dispatcher_send(
hass,
TRACKER_UPDATE,
device,
gps_location,
battery,
accuracy,
attrs
async def handle_webhook(hass, webhook_id, request):
"""Handle incoming webhook with GPSLogger request."""
try:
data = WEBHOOK_SCHEMA(dict(await request.post()))
except vol.MultipleInvalid as error:
return web.Response(
body=error.error_message,
status=HTTP_UNPROCESSABLE_ENTITY
)

return 'Setting location for {}'.format(device)
attrs = {
ATTR_SPEED: data.get(ATTR_SPEED),
ATTR_DIRECTION: data.get(ATTR_DIRECTION),
ATTR_ALTITUDE: data.get(ATTR_ALTITUDE),
ATTR_PROVIDER: data.get(ATTR_PROVIDER),
ATTR_ACTIVITY: data.get(ATTR_ACTIVITY)
}

device = data[ATTR_DEVICE]

async_dispatcher_send(
hass,
TRACKER_UPDATE,
device,
(data[ATTR_LATITUDE], data[ATTR_LONGITUDE]),
data[ATTR_BATTERY],
data[ATTR_ACCURACY],
attrs
)

return web.Response(
body='Setting location for {}'.format(device),
status=HTTP_OK
)


async def async_setup_entry(hass, entry):
"""Configure based on config entry."""
hass.components.webhook.async_register(
DOMAIN, 'GPSLogger', entry.data[CONF_WEBHOOK_ID], handle_webhook)
return True


async def async_unload_entry(hass, entry):
"""Unload a config entry."""
hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID])
return True

config_entry_flow.register_webhook_flow(
DOMAIN,
'GPSLogger Webhook',
{
'docs_url': 'https://www.home-assistant.io/components/gpslogger/'
}
)
18 changes: 18 additions & 0 deletions homeassistant/components/gpslogger/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"config": {
"title": "GPSLogger Webhook",
"step": {
"user": {
"title": "Set up the GPSLogger Webhook",
"description": "Are you sure you want to set up the GPSLogger Webhook?"
}
},
"abort": {
"one_instance_allowed": "Only a single instance is necessary.",
"not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive messages from GPSLogger."
},
"create_entry": {
"default": "To send events to Home Assistant, you will need to setup the webhook feature in GPSLogger.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details."
}
}
}
1 change: 1 addition & 0 deletions homeassistant/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ async def async_step_discovery(info):
'esphome',
'emulated_roku',
'geofency',
'gpslogger',
'hangouts',
'homematicip_cloud',
'hue',
Expand Down
Loading