summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md25
-rw-r--r--binding/bluetooth-api.c112
-rw-r--r--binding/bluetooth-api.h68
-rw-r--r--binding/bluetooth-bluez.c26
-rw-r--r--binding/bluetooth-common.h4
-rw-r--r--binding/bluetooth-util.c4
6 files changed, 204 insertions, 35 deletions
diff --git a/README.md b/README.md
index 24ea06b..e6997a2 100644
--- a/README.md
+++ b/README.md
@@ -156,6 +156,7 @@ To do the same for the respective device, verb, and for singular profile
| Name | Description | JSON Event Data |
|-------------------|------------------------------------------|-------------------------------------------|
| device_changes | report on bluetooth devices | see device_changes event section |
+| media | report on MediaPlayer1 events | see media event section |
| agent | PIN from BlueZ agent for confirmation | see agent event section |
@@ -211,6 +212,30 @@ Changed status events for a device:
}
</pre>
+### media event
+
+Playing audio reporting event (not all fields will be passed in every event):
+
+<pre>
+{
+ "adapter": "hci0",
+ "device": "dev_D0_81_7A_5A_BC_5E",
+ "track": {
+ "title": "True Colors",
+ "duration": 228000,
+ "album": "True Colors",
+ "tracknumber": 6,
+ "artist": "Zedd",
+ "numberoftracks": 11,
+ "genre": "Dance & DJ/General"
+ },
+ "position": 5600,
+ "status": "playing"
+ "connected": false
+}
+</pre>
+
+
### agent event
After pairing request agent will send event for a pincode that must be confirmed on both sides:
diff --git a/binding/bluetooth-api.c b/binding/bluetooth-api.c
index 7e3e1eb..0000d9b 100644
--- a/binding/bluetooth-api.c
+++ b/binding/bluetooth-api.c
@@ -58,6 +58,13 @@ void call_work_unlock(struct bluetooth_state *ns)
g_mutex_unlock(&ns->cw_mutex);
}
+static void mediaplayer1_set_path(struct bluetooth_state *ns, const char *path)
+{
+ if (ns->mediaplayer_path)
+ g_free(ns->mediaplayer_path);
+ ns->mediaplayer_path = g_strdup(path);
+}
+
struct call_work *call_work_lookup_unlocked(
struct bluetooth_state *ns,
const char *access_type, const char *type_arg,
@@ -226,6 +233,9 @@ static afb_event_t get_event_from_value(struct bluetooth_state *ns,
if (!g_strcmp0(value, "device_changes"))
return ns->device_changes_event;
+ if (!g_strcmp0(value, "media"))
+ return ns->media_event;
+
if (!g_strcmp0(value, "agent"))
return ns->agent_event;
@@ -248,8 +258,9 @@ static void bluez_devices_signal_callback(
const gchar *path = NULL;
const gchar *key = NULL;
json_object *jresp = NULL, *jobj;
- GVariantIter *array;
+ GVariantIter *array = NULL;
gboolean is_config, ret;
+ afb_event_t event = ns->device_changes_event;
/* AFB_INFO("sender=%s", sender_name);
AFB_INFO("object_path=%s", object_path);
@@ -263,8 +274,6 @@ static void bluez_devices_signal_callback(
jresp = json_object_new_object();
json_process_path(jresp, path);
- json_object_object_add(jresp, "action",
- json_object_new_string("added"));
jobj = json_object_new_object();
@@ -293,7 +302,16 @@ static void bluez_devices_signal_callback(
g_variant_iter_free(array);
if (array1) {
+ json_object_object_add(jresp, "action",
+ json_object_new_string("added"));
json_object_object_add(jresp, "properties", jobj);
+ } else if (is_mediaplayer1_interface(path) &&
+ g_str_has_suffix(path, BLUEZ_DEFAULT_PLAYER)) {
+
+ json_object_object_add(jresp, "connected",
+ json_object_new_boolean(TRUE));
+ mediaplayer1_set_path(ns, path);
+ event = ns->media_event;
} else {
json_object_put(jresp);
jresp = NULL;
@@ -305,10 +323,20 @@ static void bluez_devices_signal_callback(
g_variant_iter_free(array);
jresp = json_object_new_object();
-
- json_process_path(jresp, path);
- json_object_object_add(jresp, "action",
+ json_process_path(jresp, path);
+
+ if (is_mediaplayer1_interface(path)) {
+ json_object_object_add(jresp, "connected",
+ json_object_new_boolean(FALSE));
+ mediaplayer1_set_path(ns, NULL);
+ event = ns->media_event;
+ } else if (split_length(path) == 5) {
+ json_object_object_add(jresp, "action",
json_object_new_string("removed"));
+ } else {
+ json_object_put(jresp);
+ jresp = NULL;
+ }
} else if (!g_strcmp0(signal_name, "PropertiesChanged")) {
@@ -347,6 +375,33 @@ static void bluez_devices_signal_callback(
json_object_put(jresp);
jresp = NULL;
}
+
+ } else if (!g_strcmp0(path, BLUEZ_MEDIAPLAYER_INTERFACE)) {
+ int cnt = 0;
+ jresp = json_object_new_object();
+ json_process_path(jresp, object_path);
+
+ while (g_variant_iter_next(array, "{&sv}", &key, &var)) {
+ ret = mediaplayer_property_dbus2json(jresp,
+ key, var, &is_config, &error);
+ g_variant_unref(var);
+ if (!ret) {
+ //AFB_WARNING("%s property %s - %s",
+ // "mediaplayer",
+ // key, error->message);
+ g_clear_error(&error);
+ continue;
+ }
+ cnt++;
+ }
+
+ // NOTE: Possible to get a changed property for something we don't care about
+ if (!cnt) {
+ json_object_put(jresp);
+ jresp = NULL;
+ }
+
+ event = ns->media_event;
}
g_variant_iter_free(array);
@@ -354,7 +409,7 @@ static void bluez_devices_signal_callback(
}
if (jresp) {
- afb_event_push(ns->device_changes_event, jresp);
+ afb_event_push(event, jresp);
jresp = NULL;
}
@@ -390,10 +445,13 @@ static struct bluetooth_state *bluetooth_init(GMainLoop *loop)
ns->device_changes_event =
afb_daemon_make_event("device_changes");
+ ns->media_event =
+ afb_daemon_make_event("media");
ns->agent_event =
afb_daemon_make_event("agent");
if (!afb_event_is_valid(ns->device_changes_event) ||
+ !afb_event_is_valid(ns->media_event) ||
!afb_event_is_valid(ns->agent_event)) {
AFB_ERROR("Cannot create events");
goto err_no_events;
@@ -535,6 +593,24 @@ static int init(afb_api_t api)
return id->rc;
}
+static void mediaplayer1_send_event(struct bluetooth_state *ns)
+{
+ gchar *player = g_strdup(ns->mediaplayer_path);
+ json_object *jresp = mediaplayer_properties(ns, NULL, player);
+
+ if (!jresp)
+ goto out_err;
+
+ json_process_path(jresp, player);
+ json_object_object_add(jresp, "connected",
+ json_object_new_boolean(TRUE));
+
+ afb_event_push(ns->media_event, jresp);
+
+out_err:
+ g_free(player);
+}
+
static void bluetooth_subscribe_unsubscribe(afb_req_t request,
gboolean unsub)
{
@@ -558,10 +634,14 @@ static void bluetooth_subscribe_unsubscribe(afb_req_t request,
return;
}
- if (!unsub)
+ if (!unsub) {
rc = afb_req_subscribe(request, event);
- else
+
+ if (!g_strcmp0(value, "media"))
+ mediaplayer1_send_event(ns);
+ } else {
rc = afb_req_unsubscribe(request, event);
+ }
if (rc != 0) {
afb_req_fail_f(request, "failed",
"%s error on \"value\" event \"%s\"",
@@ -1043,15 +1123,19 @@ static void bluetooth_avrcp_controls(afb_req_t request)
}
device = return_bluez_path(request);
- if (!device) {
+ if (device) {
+ /* TODO: handle multiple players per device */
+ player = g_strconcat(device, "/", BLUEZ_DEFAULT_PLAYER, NULL);
+ g_free(device);
+ } else {
+ player = g_strdup(ns->mediaplayer_path);
+ }
+
+ if (!player) {
afb_req_fail(request, "failed", "No path given");
return;
}
- /* TODO: handle multiple players per device */
- player = g_strconcat(device, "/player0", NULL);
- g_free(device);
-
reply = mediaplayer_call(ns, player, action, NULL, &error);
if (!reply) {
diff --git a/binding/bluetooth-api.h b/binding/bluetooth-api.h
index 0d72afc..cd6a775 100644
--- a/binding/bluetooth-api.h
+++ b/binding/bluetooth-api.h
@@ -68,39 +68,56 @@
#define BLUEZ_AT_MEDIAPLAYER "mediaplayer"
#define BLUEZ_DEFAULT_ADAPTER "hci0"
+#define BLUEZ_DEFAULT_PLAYER "player0"
struct bluetooth_state;
-static inline gchar *bluez_return_adapter(const char *path)
+
+static inline int split_length(const char *path) {
+ gchar **strings = g_strsplit(path, "/", -1);
+ int ret = g_strv_length(strings) ;
+
+ g_strfreev(strings);
+ return ret;
+}
+
+static inline gchar *find_index(const char *path, int idx)
{
gchar **strings = g_strsplit(path, "/", -1);
- gchar *adapter;
+ gchar *item = NULL;
+
+ if (g_strv_length(strings) > idx)
+ item = g_strdup(strings[idx]);
- if (g_strv_length(strings) < 3) {
- g_strfreev(strings);
- return NULL;
- }
- adapter = g_strdup(strings[3]);
g_strfreev(strings);
+ return item;
+}
- return adapter;
+static inline gchar *bluez_return_adapter(const char *path)
+{
+ return find_index(path, 3);
}
static inline gchar *bluez_return_device(const char *path)
{
- const char *basename;
+ return find_index(path, 4);
+}
+
+static inline gboolean is_mediaplayer1_interface(const char *path)
+{
+ gchar *data = NULL;
+ gboolean ret;
- basename = strrchr(path, '/');
- if (!basename)
- return NULL;
- basename++;
+ // Don't trigger on NowPlaying, Item, etc paths
+ if (split_length(path) != 6)
+ return FALSE;
- /* be sure it is a bluez path with device */
- if (strncmp(basename, "dev_", 4))
- return NULL;
+ // TODO: allow mutiple players per device
+ data = find_index(path, 5);
+ ret = !g_strcmp0(data, BLUEZ_DEFAULT_PLAYER);
+ g_free(data);
- /* at least one character */
- return *basename ? g_strdup(basename) : NULL;
+ return ret;
}
struct call_work *call_work_create_unlocked(struct bluetooth_state *ns,
@@ -163,6 +180,14 @@ static inline gboolean agent_property_dbus2json(json_object *jprop,
jprop, key, var, is_config, error);
}
+static inline gboolean mediaplayer_property_dbus2json(json_object *jprop,
+ const gchar *key, GVariant *var, gboolean *is_config,
+ GError **error)
+{
+ return bluez_property_dbus2json(BLUEZ_AT_MEDIAPLAYER,
+ jprop, key, var, is_config, error);
+}
+
static inline GVariant *device_call(struct bluetooth_state *ns,
const char *device, const char *method,
GVariant *params, GError **error)
@@ -216,6 +241,13 @@ static inline json_object *adapter_properties(struct bluetooth_state *ns,
BLUEZ_AT_ADAPTER, adapter, error);
}
+static inline json_object *mediaplayer_properties(struct bluetooth_state *ns,
+ GError **error, const gchar *player)
+{
+ return bluez_get_properties(ns,
+ BLUEZ_AT_MEDIAPLAYER, player, error);
+}
+
static inline json_object *object_properties(struct bluetooth_state *ns,
GError **error)
{
diff --git a/binding/bluetooth-bluez.c b/binding/bluetooth-bluez.c
index 824843b..1c46106 100644
--- a/binding/bluetooth-bluez.c
+++ b/binding/bluetooth-bluez.c
@@ -72,6 +72,26 @@ static const struct property_info device_props[] = {
{ },
};
+static const struct property_info mediaplayer_props[] = {
+ { .name = "Position", .fmt = "u", },
+ { .name = "Status", .fmt = "s", },
+ {
+ .name = "Track",
+ .fmt = "{sv}",
+ .sub = (const struct property_info []) {
+ { .name = "Title", .fmt = "s", },
+ { .name = "Artist", .fmt = "s", },
+ { .name = "Album", .fmt = "s", },
+ { .name = "Genre", .fmt = "s", },
+ { .name = "NumberOfTracks", .fmt = "u", },
+ { .name = "TrackNumber", .fmt = "u", },
+ { .name = "Duration", .fmt = "u", },
+ { },
+ },
+ },
+ { },
+};
+
const struct property_info *bluez_get_property_info(
const char *access_type, GError **error)
{
@@ -81,6 +101,8 @@ const struct property_info *bluez_get_property_info(
pi = adapter_props;
else if (!strcmp(access_type, BLUEZ_AT_DEVICE))
pi = device_props;
+ else if (!strcmp(access_type, BLUEZ_AT_MEDIAPLAYER))
+ pi = mediaplayer_props;
else
g_set_error(error, NB_ERROR, NB_ERROR_ILLEGAL_ARGUMENT,
"illegal %s argument", access_type);
@@ -324,6 +346,7 @@ json_object *bluez_get_properties(struct bluetooth_state *ns,
gboolean is_config;
if (!strcmp(access_type, BLUEZ_AT_DEVICE) ||
+ !strcmp(access_type, BLUEZ_AT_MEDIAPLAYER) ||
!strcmp(access_type, BLUEZ_AT_ADAPTER)) {
pi = bluez_get_property_info(access_type, error);
@@ -341,6 +364,8 @@ json_object *bluez_get_properties(struct bluetooth_state *ns,
if (!strcmp(access_type, BLUEZ_AT_DEVICE))
interface2 = BLUEZ_DEVICE_INTERFACE;
+ else if (!strcmp(access_type, BLUEZ_AT_MEDIAPLAYER))
+ interface2 = BLUEZ_MEDIAPLAYER_INTERFACE;
else if (!strcmp(access_type, BLUEZ_AT_ADAPTER))
interface2 = BLUEZ_ADAPTER_INTERFACE;
else if (!strcmp(access_type, BLUEZ_AT_OBJECT))
@@ -365,6 +390,7 @@ json_object *bluez_get_properties(struct bluetooth_state *ns,
return NULL;
if (!strcmp(access_type, BLUEZ_AT_DEVICE) ||
+ !strcmp(access_type, BLUEZ_AT_MEDIAPLAYER) ||
!strcmp(access_type, BLUEZ_AT_ADAPTER)) {
jprop = json_object_new_object();
g_variant_get(reply, "(a{sv})", &array);
diff --git a/binding/bluetooth-common.h b/binding/bluetooth-common.h
index e6fb97a..68e747b 100644
--- a/binding/bluetooth-common.h
+++ b/binding/bluetooth-common.h
@@ -41,6 +41,7 @@ struct bluetooth_state {
guint autoconnect_sub;
afb_event_t device_changes_event;
+ afb_event_t media_event;
afb_event_t agent_event;
/* NOTE: single connection allowed for now */
@@ -56,6 +57,9 @@ struct bluetooth_state {
guint registration_id;
gchar *agent_path;
gboolean agent_registered;
+
+ /* mediaplayer */
+ gchar *mediaplayer_path;
};
struct init_data {
diff --git a/binding/bluetooth-util.c b/binding/bluetooth-util.c
index 963af82..e8a0781 100644
--- a/binding/bluetooth-util.c
+++ b/binding/bluetooth-util.c
@@ -1046,10 +1046,8 @@ gchar *return_bluez_path(afb_req_t request) {
adapter = adapter ? adapter : BLUEZ_DEFAULT_ADAPTER;
device = afb_req_value(request, "device");
- if (!device) {
- afb_req_fail(request, "failed", "No device parameter");
+ if (!device)
return NULL;
- }
tmp = device;