summaryrefslogtreecommitdiffstats
path: root/ahl-binding/ahl-binding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ahl-binding/ahl-binding.cpp')
-rw-r--r--ahl-binding/ahl-binding.cpp344
1 files changed, 344 insertions, 0 deletions
diff --git a/ahl-binding/ahl-binding.cpp b/ahl-binding/ahl-binding.cpp
new file mode 100644
index 0000000..6493267
--- /dev/null
+++ b/ahl-binding/ahl-binding.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Loïc Collignon <loic.collignon@iot.bzh>
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include "ahl-binding.hpp"
+
+afb_dynapi* AFB_default; // BUG: Is it possible to get rid of this ?
+
+ahl_binding_t::ahl_binding_t()
+ : handle_{nullptr}
+ , apihandle_{nullptr}
+{
+}
+
+ahl_binding_t& ahl_binding_t::instance()
+{
+ static ahl_binding_t s;
+ return s;
+}
+
+int ahl_binding_t::build(afb_dynapi* handle)
+{
+ using namespace std::placeholders;
+
+ if (!handle || handle_) return -1;
+ handle_ = handle;
+ AFB_default = handle;
+
+ return afb_dynapi_new_api(
+ handle,
+ HL_API_NAME,
+ HL_API_INFO,
+ 1,
+ [](void*, afb_dynapi* h) {
+ return ahl_binding_t::instance().preinit(h);
+ },
+ nullptr);
+}
+
+int ahl_binding_t::preinit(afb_dynapi* handle)
+{
+ apihandle_ = handle;
+
+ try
+ {
+ load_static_verbs();
+ load_controller_api();
+
+ if (afb_dynapi_on_event(handle,
+ [](afb_dynapi*, const char* e, struct json_object* o)
+ { ahl_binding_t::instance().event(e, o); }
+ )
+ ) throw std::runtime_error("Failed to register event handler callback.");
+
+ if (afb_dynapi_on_init(handle,
+ [](afb_dynapi*) { return ahl_binding_t::instance().init(); }
+ )) throw std::runtime_error("Failed to register init handler callback.");
+ }
+ catch(std::exception& e)
+ {
+ AFB_DYNAPI_ERROR(handle, "%s", e.what());
+ return -1;
+ }
+
+ return 0;
+}
+
+int ahl_binding_t::init()
+{
+ using namespace std::placeholders;
+
+ if (afb_dynapi_require_api(apihandle_, HAL_MGR_API, 1))
+ {
+ AFB_DYNAPI_ERROR(apihandle_, "Failed to require '%s' API!", HAL_MGR_API);
+ return -1;
+ }
+ AFB_DYNAPI_NOTICE(apihandle_, "Required '%s' API found!", HAL_MGR_API);
+
+ // Requires corresponding API
+ for(const auto& h : config_.hals())
+ {
+ if (afb_dynapi_require_api(apihandle_, h.c_str(), 1))
+ {
+ AFB_DYNAPI_ERROR(apihandle_, "Failed to require '%s' API!", h.c_str());
+ return -1;
+ }
+ AFB_DYNAPI_NOTICE(apihandle_, "Required '%s' API found!", h.c_str());
+ json_object* result = nullptr;
+ if(afb_dynapi_call_sync(apihandle_, h.c_str(), "init-mixer", nullptr, &result))
+ {
+ AFB_DYNAPI_ERROR(apihandle_, "Failed to call 'init-mixer' verb on '%s' API!", h.c_str());
+ if (result) AFB_DYNAPI_NOTICE(apihandle_, "%s", json_object_to_json_string(result));
+ return -1;
+ }
+ AFB_DYNAPI_NOTICE(apihandle_, "Mixer initialized using '%s' API.", h.c_str());
+
+ json_object* response = nullptr;
+ json_object* verbose = json_object_new_object();
+ json_object_object_add(verbose, "verbose", json_object_new_int(1));
+ if (afb_dynapi_call_sync(apihandle_, h.c_str(), "list", verbose, &response))
+ {
+ AFB_DYNAPI_ERROR(apihandle_, "Failed to call 'list' verb on '%s' API!", h.c_str());
+ if (result) AFB_DYNAPI_NOTICE(apihandle_, "%s", json_object_to_json_string(result));
+ return -1;
+ }
+ json_object* streams = json_object_object_get(response, "response");
+ AFB_DYNAPI_DEBUG(apihandle_, "Called 'list' verb on '%s' API: %s", h.c_str(), json_object_to_json_string(streams));
+ json_object* array = json_object_object_get(streams, "streams");
+ size_t streams_count = json_object_array_length(array);
+ for(size_t i = 0; i < streams_count; ++i)
+ {
+ std::string stream_name;
+ std::string device_uri;
+ json_object* item = json_object_array_get_idx(array, i);
+
+ jcast(stream_name, item, "name");
+ jcast(device_uri, item, "cardId");
+
+ config_.set_device_uri(stream_name, device_uri);
+ }
+ }
+
+ afb_dynapi_seal(apihandle_);
+ AFB_DYNAPI_NOTICE(apihandle_, "API is now sealed!");
+
+ actions_["volume"] = std::bind(&ahl_binding_t::volume, this, _1, _2, _3, _4);
+ actions_["open"] = std::bind(&ahl_binding_t::open, this, _1, _2, _3, _4);
+
+ return 0;
+}
+
+void ahl_binding_t::event(std::string name, json_object* arg)
+{
+ AFB_DYNAPI_DEBUG(apihandle_, "Event '%s' received with the following arg: %s", name.c_str(), json_object_to_json_string(arg));
+}
+
+void ahl_binding_t::load_static_verbs()
+{
+ if (afb_dynapi_add_verb(
+ apihandle_,
+ "get_roles",
+ "Retrieve array of available audio roles",
+ [](afb_request* r) { ahl_binding_t::instance().get_roles(r); },
+ nullptr,
+ nullptr,
+ AFB_SESSION_NONE_V2))
+ {
+ throw std::runtime_error("Failed to add 'get_role' verb to the API.");
+ }
+}
+
+void ahl_binding_t::load_controller_api()
+{
+ char* dir_list = getenv("CONTROL_CONFIG_PATH");
+ if (!dir_list) dir_list = strdup(CONTROL_CONFIG_PATH);
+ struct json_object* config_files = CtlConfigScan(dir_list, "policy");
+ if (!config_files) throw std::runtime_error("No config files found!");
+
+ // Only one file should be found this way, but read all just in case
+ size_t config_files_count = json_object_array_length(config_files);
+ for(size_t i = 0; i < config_files_count; ++i)
+ {
+ config_entry_t file {json_object_array_get_idx(config_files, i)};
+
+ if(load_controller_config(file.filepath()) < 0)
+ {
+ std::stringstream ss;
+ ss << "Failed to load config file '"
+ << file.filename()
+ << "' from '"
+ << file.fullpath()
+ << "'!";
+ throw std::runtime_error(ss.str());
+ }
+ }
+}
+
+int ahl_binding_t::load_controller_config(const std::string& path)
+{
+ CtlConfigT* controller_config;
+
+ controller_config = CtlLoadMetaData(apihandle_, path.c_str());
+ if (!controller_config)
+ {
+ AFB_DYNAPI_ERROR(apihandle_, "Failed to load controller from config file!");
+ return -1;
+ }
+
+ static CtlSectionT controller_sections[] =
+ {
+ {.key = "plugins", .uid = nullptr, .info = nullptr, .loadCB = PluginConfig, .handle = nullptr, .actions = nullptr},
+ {.key = "onload", .uid = nullptr, .info = nullptr, .loadCB = OnloadConfig, .handle = nullptr, .actions = nullptr},
+ {.key = "controls", .uid = nullptr, .info = nullptr, .loadCB = ControlConfig, .handle = nullptr, .actions = nullptr},
+ {.key = "events", .uid = nullptr, .info = nullptr, .loadCB = EventConfig, .handle = nullptr, .actions = nullptr},
+ {
+ .key = "ahl-4a",
+ .uid = nullptr,
+ .info = nullptr,
+ .loadCB = [](afb_dynapi*, CtlSectionT* s, json_object* o){
+ return ahl_binding_t::instance().load_config(s, o);
+ },
+ .handle = nullptr,
+ .actions = nullptr
+ },
+ {.key = nullptr, .uid = nullptr, .info = nullptr, .loadCB = nullptr, .handle = nullptr, .actions = nullptr}
+ };
+
+ CtlLoadSections(apihandle_, controller_config, controller_sections);
+
+ return 0;
+}
+
+int ahl_binding_t::load_config(CtlSectionT* section, json_object* o)
+{
+ config_ << o;
+
+ // Add corresponding verbs
+ for(const auto& r : config_.roles())
+ {
+ AFB_DYNAPI_NOTICE(apihandle_, "New audio role: %s", r.name().c_str());
+
+ if (afb_dynapi_add_verb(
+ apihandle_,
+ r.name().c_str(),
+ r.description().c_str(),
+ [](afb_request* r) { ahl_binding_t::instance().audiorole(r); },
+ nullptr,
+ nullptr,
+ AFB_SESSION_NONE_V2))
+ {
+ std::stringstream ss;
+ ss << "Failed to add '" << r.name() << "' verb to the API.";
+ throw std::runtime_error(ss.str());
+ }
+ }
+
+ return 0;
+}
+
+int afbBindingVdyn(afb_dynapi* handle)
+{
+ if (!handle) return -1;
+ return ahl_binding_t::instance().build(handle);
+}
+
+void ahl_binding_t::audiorole(afb_request* req)
+{
+ std::string verb = afb_request_get_verb(req);
+ const auto& roles = config_.roles();
+ auto r = std::find_if(roles.cbegin(), roles.cend(), [&verb](const role_t& r) { return r.name() == verb; });
+
+ if (r == roles.cend())
+ {
+ afb_request_fail(req, "The requested role doesn't exist!", nullptr);
+ return;
+ }
+
+ json_object* query = json_object_get(afb_request_json(req));
+
+ std::string action;
+ jcast(action, query, "action");
+ std::transform(action.begin(), action.end(), action.begin(), ::tolower);
+
+ auto a = actions_.find(action);
+ if (a == actions_.end())
+ {
+ afb_request_fail(req, "The requested action doesn't exist!", nullptr);
+ return;
+ }
+ a->second(req, verb, r->stream(), query);
+}
+
+void ahl_binding_t::get_roles(afb_request* req)
+{
+ json_object* result = json_object_new_array();
+ for(const auto& r : config_.roles())
+ json_object_array_add(result, json_object_new_string(r.name().c_str()));
+ afb_request_success(req, result, nullptr);
+}
+
+void ahl_binding_t::volume(afb_request* req, std::string role, std::string stream, json_object* arg)
+{
+ json_object* value = json_object_object_get(arg, "value");
+ json_object_get(value);
+
+ if (!value)
+ {
+ afb_request_fail(req, "No value given!", nullptr);
+ return;
+ }
+
+ json_object* a = json_object_new_object();
+ json_object_object_add(a, "volume", value);
+ AFB_DYNAPI_DEBUG(apihandle_, "Call the HAL with the following argument: %s", json_object_to_json_string(a));
+
+ afb_dynapi_call(
+ apihandle_,
+ config_.hals()[0].c_str(), // BUG: What to do if multiple hals ?
+ stream.c_str(),
+ a,
+ [](void* closure, int status, json_object* result, afb_dynapi* handle)
+ {
+ AFB_DYNAPI_DEBUG(handle, "Got the following answer: %s", json_object_to_json_string(result));
+ afb_request* r = (afb_request*)closure;
+
+ json_object_get(result);
+ if (status) afb_request_fail(r, json_object_to_json_string(result), nullptr);
+ else afb_request_success(r, result, nullptr);
+ afb_request_unref(r);
+ },
+ afb_request_addref(req));
+}
+
+void ahl_binding_t::open(afb_request* req, std::string role, std::string stream, json_object* arg)
+{
+ for(const auto& r : config_.roles())
+ {
+ if (r.name() == role)
+ {
+ AFB_DYNAPI_DEBUG(apihandle_, "HELLO");
+
+ json_object* result = json_object_new_object();
+ json_object_object_add(result, "device_uri", json_object_new_string(r.device_uri().c_str()));
+
+ afb_request_success(req, result, nullptr);
+ return;
+ }
+ }
+ afb_request_fail(req, "Can't open the specified role!", nullptr);
+}