From e233cb38142ea12b331134b9449dcdddfcc102f3 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Thu, 31 Aug 2017 15:45:40 -0700 Subject: binding: geoclue: initial geoclue support Bug-AGL: SPEC-851 Signed-off-by: Matt Ranostay --- binding/CMakeLists.txt | 39 +++++++++ binding/afm-geoclue-binding.c | 198 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 binding/CMakeLists.txt create mode 100644 binding/afm-geoclue-binding.c (limited to 'binding') diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt new file mode 100644 index 0000000..2ff30e0 --- /dev/null +++ b/binding/CMakeLists.txt @@ -0,0 +1,39 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# contrib: Romain Forlot +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Add target to project dependency list +PROJECT_TARGET_ADD(afm-geoclue-binding) + + # Define project Targets + add_library(afm-geoclue-binding MODULE afm-geoclue-binding.c) + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} ${link_libraries}) + + # installation directory + INSTALL(TARGETS ${TARGET_NAME} + LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}) + diff --git a/binding/afm-geoclue-binding.c b/binding/afm-geoclue-binding.c new file mode 100644 index 0000000..ad93a87 --- /dev/null +++ b/binding/afm-geoclue-binding.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2017 Konsulko Group + * Author: Matt Ranostay + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AFB_BINDING_VERSION 2 +#include + +static struct afb_event location_event; +static GClueSimple *simple; + +/* + * @latitude - latitude in degrees + * @longitude - longitude in degrees + * @accuracy - accuracy in meters + * @altitude - altitude in meters + */ + +static json_object *populate_json(json_object *jresp, GClueLocation *location) +{ + json_object *value; + double altitude = gclue_location_get_altitude(location); + + value = json_object_new_double(gclue_location_get_latitude(location)); + json_object_object_add(jresp, "latitude", value); + + value = json_object_new_double(gclue_location_get_longitude(location)); + json_object_object_add(jresp, "longitude", value); + + value = json_object_new_double(gclue_location_get_accuracy(location)); + json_object_object_add(jresp, "accuracy", value); + + if (altitude != -G_MAXDOUBLE) { + value = json_object_new_double(altitude); + json_object_object_add(jresp, "altitude", value); + } + + return jresp; +} + +/* + * @heading - heading in degrees + * @speed - speed in meters per second + */ + +static json_object *populate_velocity_json(json_object *jresp, GClueLocation *location) +{ + json_object *value; + double heading = gclue_location_get_heading(location); + double speed = gclue_location_get_speed(location); + + if (heading >= 0) { + value = json_object_new_double(heading); + json_object_object_add(jresp, "heading", value); + } + + if (speed >= 0) { + value = json_object_new_double(speed); + json_object_object_add(jresp, "speed", value); + } + + return jresp; +} + +static void send_event(GClueSimple *simple) +{ + json_object *jresp; + GClueLocation *location; + + location = gclue_simple_get_location(simple); + jresp = json_object_new_object(); + + populate_json(jresp, location); + populate_velocity_json(jresp, location); + afb_event_push(location_event, jresp); +} + +static void get_data(struct afb_req request) +{ + json_object *jresp = json_object_new_object(); + GClueLocation *location; + + if (simple == NULL) { + afb_req_fail(request, "failed", "No GeoClue instance available"); + return; + } + + location = gclue_simple_get_location(simple); + + populate_json(jresp, location); + afb_req_success(request, jresp, "GeoClue location data"); +} + +static void on_ready(GObject *source_object, GAsyncResult *res, gpointer ptr) +{ + GError *error = NULL; + + simple = gclue_simple_new_finish(res, &error); + if (error != NULL) { + AFB_ERROR("Failed to connect to GeoClue2 service: %s", error->message); + abort(); + } + + g_signal_connect(simple, "notify::location", G_CALLBACK(send_event), NULL); +} + +static void *geoclue_loop_thread(void *ptr) +{ + gclue_simple_new("geoclue-binding", GCLUE_ACCURACY_LEVEL_EXACT, NULL, on_ready, NULL); + + g_main_loop_run(g_main_loop_new(NULL, FALSE)); + + return NULL; +} + +static int init() +{ + pthread_t thread_id; + + location_event = afb_daemon_make_event("location"); + + return pthread_create(&thread_id, NULL, geoclue_loop_thread, NULL); +} + +static void subscribe(struct afb_req request) +{ + const char *value = afb_req_value(request, "value"); + + if (value && !strcasecmp(value, "location")) { + afb_req_subscribe(request, location_event); + afb_req_success(request, NULL, NULL); + + /* + * Sent out an initial event on subsciption since if location is + * static another event may not happen. + */ + if (simple) { + send_event(simple); + } + return; + } + + afb_req_fail(request, "failed", "Invalid event"); +} + +static void unsubscribe(struct afb_req request) +{ + const char *value = afb_req_value(request, "value"); + + if (value && !strcasecmp(value, "location")) { + afb_req_unsubscribe(request, location_event); + afb_req_success(request, NULL, NULL); + return; + } + + afb_req_fail(request, "failed", "Invalid event"); +} + +static const struct afb_verb_v2 binding_verbs[] = { + { .verb = "location", .callback = get_data, .info = "Get GeoClue coordinates" }, + { .verb = "subscribe", .callback = subscribe, .info = "Subscribe to GeoClue events" }, + { .verb = "unsubscribe", .callback = unsubscribe, .info = "Unsubscribe to GeoClue events" }, + { } +}; + +/* + * binder API description + */ +const struct afb_binding_v2 afbBindingV2 = { + .api = "geoclue", + .specification = "GeoClue service API", + .verbs = binding_verbs, + .init = init, +}; -- cgit 1.2.3-korg