summaryrefslogtreecommitdiffstats
path: root/binding/afm-geoclue-binding.c
diff options
context:
space:
mode:
Diffstat (limited to 'binding/afm-geoclue-binding.c')
-rw-r--r--binding/afm-geoclue-binding.c198
1 files changed, 198 insertions, 0 deletions
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 <matt.ranostay@konsulko.com>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <geoclue.h>
+#include <glib-object.h>
+#include <json-c/json.h>
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+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,
+};