summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xautobuild/agl/autobuild131
-rwxr-xr-xautobuild/linux/autobuild131
-rw-r--r--conf.d/cmake/config.cmake2
-rw-r--r--conf.d/project/etc/vshl-core-api.json10
-rw-r--r--conf.d/wgt/config.xml.in3
-rw-r--r--src/plugins/VshlCoreApi.cpp209
-rw-r--r--src/plugins/VshlCoreApi.h5
-rw-r--r--src/plugins/core/VRRequestProcessor.h6
-rw-r--r--src/plugins/core/VRRequestProcessorImpl.cpp14
-rw-r--r--src/plugins/core/include/VRRequest.h6
-rw-r--r--src/plugins/core/include/VRRequestProcessorDelegate.h4
-rw-r--r--src/plugins/core/src/VRRequestImpl.cpp16
-rw-r--r--src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp26
-rw-r--r--src/plugins/voiceagents/VoiceAgentEventNames.h5
-rw-r--r--src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp14
15 files changed, 492 insertions, 90 deletions
diff --git a/autobuild/agl/autobuild b/autobuild/agl/autobuild
index db00c1a..16181b8 100755
--- a/autobuild/agl/autobuild
+++ b/autobuild/agl/autobuild
@@ -1,5 +1,6 @@
#!/usr/bin/make -f
# Copyright (C) 2015 - 2018 "IoT.bzh"
+# Copyright (C) 2020 Konsulko Group
# Author "Romain Forlot" <romain.forlot@iot.bzh>
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,65 +16,113 @@
# limitations under the License.
THISFILE := $(lastword $(MAKEFILE_LIST))
-BUILD_DIR := $(abspath $(dir $(THISFILE))/../../build)
-DEST := ${BUILD_DIR}
+ROOT_DIR := $(abspath $(dir $(THISFILE))/../..)
-.PHONY: all clean distclean configure build package help update
+# Build directories
+# Note that the debug/test/coverage directories are defined in relation
+# to the release directory (BUILD_DIR), this needs to be kept in mind
+# if over-riding it and building those widget types, the specific widget
+# type variable (e.g. BUILD_DIR_DEBUG) may also need to be specified
+# to yield the desired output hierarchy.
+BUILD_DIR = $(ROOT_DIR)/build
+BUILD_DIR_DEBUG = $(abspath $(BUILD_DIR)/../build-debug)
+BUILD_DIR_TEST = $(abspath $(BUILD_DIR)/../build-test)
+BUILD_DIR_COVERAGE = $(abspath $(BUILD_DIR)/../build-coverage)
-all: help
+# Output directory variable for use in pattern rules.
+# This is intended for internal use only, hence the explicit override
+# definition.
+override OUTPUT_DIR = $(BUILD_DIR)
+
+# Final install directory for widgets
+DEST = $(OUTPUT_DIR)
+
+# Default build type for release/test builds
+BUILD_TYPE = RELEASE
+
+.PHONY: all help update install distclean
+.PHONY: clean clean-release clean-debug clean-test clean-coverage clean-all
+.PHONY: configure configure-release configure-debug configure-test configure-coverage
+.PHONY: build build-release build-debug build-test build-coverage build-all
+.PHONY: package package-release package-debug package-test package-coverage package-all
help:
@echo "List of targets available:"
@echo ""
@echo "- all"
+ @echo "- help"
@echo "- clean"
@echo "- distclean"
@echo "- configure"
@echo "- build: compilation, link and prepare files for package into a widget"
@echo "- package: output a widget file '*.wgt'"
- @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory"
+ @echo "- install: install in your $(CMAKE_INSTALL_DIR) directory"
@echo ""
@echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt"
@echo "Don't use your build dir as DEST as wgt file is generated at this location"
-update: configure
- @cmake --build ${BUILD_DIR} --target autobuild
-
-clean:
- @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean
-
-distclean:
- @rm -rf ${BUILD_DIR}
-
-configure:
- @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
- @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
-
-build: configure
- @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
-
-package: build
- @mkdir -p ${BUILD_DIR}/$@/bin
- @mkdir -p ${BUILD_DIR}/$@/etc
- @mkdir -p ${BUILD_DIR}/$@/lib
- @mkdir -p ${BUILD_DIR}/$@/htdocs
- @mkdir -p ${BUILD_DIR}/$@/var
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
- @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
- mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+all: package-all
+
+# Target specific variable over-rides so static pattern rules can be
+# used for the various type-specific targets.
+
+configure-test build-test package-test clean-test: OUTPUT_DIR = $(BUILD_DIR_TEST)
+
+configure-coverage build-coverage package-coverage clean-coverage: OUTPUT_DIR = $(BUILD_DIR_COVERAGE)
+configure-coverage build-coverage package-coverage: BUILD_TYPE = COVERAGE
+
+configure-debug build-debug package-debug clean-debug: OUTPUT_DIR = $(BUILD_DIR_DEBUG)
+configure-debug build-debug package-debug: BUILD_TYPE = DEBUG
+
+clean-release clean-test clean-debug clean-coverage:
+ @if [ -d $(OUTPUT_DIR) ]; then \
+ $(MAKE) -C $(OUTPUT_DIR) $(CLEAN_ARGS) clean; \
+ else \
+ echo Nothing to clean; \
fi
-package-test: build
- @mkdir -p ${BUILD_DIR}/$@/bin
- @mkdir -p ${BUILD_DIR}/$@/etc
- @mkdir -p ${BUILD_DIR}/$@/lib
- @mkdir -p ${BUILD_DIR}/$@/htdocs
- @mkdir -p ${BUILD_DIR}/$@/var
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target test_widget
- @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
- mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+clean: clean-release
+
+clean-all: clean-release clean-test clean-debug clean-coverage
+
+distclean: clean-all
+
+configure-release configure-test configure-debug configure-coverage:
+ @mkdir -p $(OUTPUT_DIR)
+ @if [ ! -f $(OUTPUT_DIR)/Makefile ]; then \
+ (cd $(OUTPUT_DIR) && cmake -S $(ROOT_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) $(CONFIGURE_ARGS)); \
fi
+configure: configure-release
+
+build-release build-debug build-coverage: build-%: configure-%
+ @cmake --build $(OUTPUT_DIR) $(BUILD_ARGS) --target all
+
+# Kept for consistency, empty to avoid building everything for test widget
+build-test: configure-test
+
+build: build-release
+
+build-all: build-release build-debug build-test build-coverage
+
+package-release package-debug package-coverage: package-%: build-%
+ @cmake --build $(OUTPUT_DIR) $(PACKAGE_ARGS) --target widget
+ @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \
+ mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \
+ fi
+
+package-test: build-test
+ @cmake --build $(OUTPUT_DIR) $(PACKAGE_ARGS) --target test_widget
+ @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \
+ mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \
+ fi
+
+package: package-release
+
+package-all: package-release package-test package-coverage package-debug
+
+update: configure
+ @cmake --build $(BUILD_DIR) --target autobuild
+
install: build
- @cmake --build ${BUILD_DIR} ${INSTALL_ARGS} --target install
+ @cmake --build $(BUILD_DIR) $(INSTALL_ARGS) --target install
diff --git a/autobuild/linux/autobuild b/autobuild/linux/autobuild
index db00c1a..16181b8 100755
--- a/autobuild/linux/autobuild
+++ b/autobuild/linux/autobuild
@@ -1,5 +1,6 @@
#!/usr/bin/make -f
# Copyright (C) 2015 - 2018 "IoT.bzh"
+# Copyright (C) 2020 Konsulko Group
# Author "Romain Forlot" <romain.forlot@iot.bzh>
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,65 +16,113 @@
# limitations under the License.
THISFILE := $(lastword $(MAKEFILE_LIST))
-BUILD_DIR := $(abspath $(dir $(THISFILE))/../../build)
-DEST := ${BUILD_DIR}
+ROOT_DIR := $(abspath $(dir $(THISFILE))/../..)
-.PHONY: all clean distclean configure build package help update
+# Build directories
+# Note that the debug/test/coverage directories are defined in relation
+# to the release directory (BUILD_DIR), this needs to be kept in mind
+# if over-riding it and building those widget types, the specific widget
+# type variable (e.g. BUILD_DIR_DEBUG) may also need to be specified
+# to yield the desired output hierarchy.
+BUILD_DIR = $(ROOT_DIR)/build
+BUILD_DIR_DEBUG = $(abspath $(BUILD_DIR)/../build-debug)
+BUILD_DIR_TEST = $(abspath $(BUILD_DIR)/../build-test)
+BUILD_DIR_COVERAGE = $(abspath $(BUILD_DIR)/../build-coverage)
-all: help
+# Output directory variable for use in pattern rules.
+# This is intended for internal use only, hence the explicit override
+# definition.
+override OUTPUT_DIR = $(BUILD_DIR)
+
+# Final install directory for widgets
+DEST = $(OUTPUT_DIR)
+
+# Default build type for release/test builds
+BUILD_TYPE = RELEASE
+
+.PHONY: all help update install distclean
+.PHONY: clean clean-release clean-debug clean-test clean-coverage clean-all
+.PHONY: configure configure-release configure-debug configure-test configure-coverage
+.PHONY: build build-release build-debug build-test build-coverage build-all
+.PHONY: package package-release package-debug package-test package-coverage package-all
help:
@echo "List of targets available:"
@echo ""
@echo "- all"
+ @echo "- help"
@echo "- clean"
@echo "- distclean"
@echo "- configure"
@echo "- build: compilation, link and prepare files for package into a widget"
@echo "- package: output a widget file '*.wgt'"
- @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory"
+ @echo "- install: install in your $(CMAKE_INSTALL_DIR) directory"
@echo ""
@echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt"
@echo "Don't use your build dir as DEST as wgt file is generated at this location"
-update: configure
- @cmake --build ${BUILD_DIR} --target autobuild
-
-clean:
- @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean
-
-distclean:
- @rm -rf ${BUILD_DIR}
-
-configure:
- @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
- @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
-
-build: configure
- @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
-
-package: build
- @mkdir -p ${BUILD_DIR}/$@/bin
- @mkdir -p ${BUILD_DIR}/$@/etc
- @mkdir -p ${BUILD_DIR}/$@/lib
- @mkdir -p ${BUILD_DIR}/$@/htdocs
- @mkdir -p ${BUILD_DIR}/$@/var
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
- @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
- mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+all: package-all
+
+# Target specific variable over-rides so static pattern rules can be
+# used for the various type-specific targets.
+
+configure-test build-test package-test clean-test: OUTPUT_DIR = $(BUILD_DIR_TEST)
+
+configure-coverage build-coverage package-coverage clean-coverage: OUTPUT_DIR = $(BUILD_DIR_COVERAGE)
+configure-coverage build-coverage package-coverage: BUILD_TYPE = COVERAGE
+
+configure-debug build-debug package-debug clean-debug: OUTPUT_DIR = $(BUILD_DIR_DEBUG)
+configure-debug build-debug package-debug: BUILD_TYPE = DEBUG
+
+clean-release clean-test clean-debug clean-coverage:
+ @if [ -d $(OUTPUT_DIR) ]; then \
+ $(MAKE) -C $(OUTPUT_DIR) $(CLEAN_ARGS) clean; \
+ else \
+ echo Nothing to clean; \
fi
-package-test: build
- @mkdir -p ${BUILD_DIR}/$@/bin
- @mkdir -p ${BUILD_DIR}/$@/etc
- @mkdir -p ${BUILD_DIR}/$@/lib
- @mkdir -p ${BUILD_DIR}/$@/htdocs
- @mkdir -p ${BUILD_DIR}/$@/var
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target test_widget
- @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
- mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+clean: clean-release
+
+clean-all: clean-release clean-test clean-debug clean-coverage
+
+distclean: clean-all
+
+configure-release configure-test configure-debug configure-coverage:
+ @mkdir -p $(OUTPUT_DIR)
+ @if [ ! -f $(OUTPUT_DIR)/Makefile ]; then \
+ (cd $(OUTPUT_DIR) && cmake -S $(ROOT_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) $(CONFIGURE_ARGS)); \
fi
+configure: configure-release
+
+build-release build-debug build-coverage: build-%: configure-%
+ @cmake --build $(OUTPUT_DIR) $(BUILD_ARGS) --target all
+
+# Kept for consistency, empty to avoid building everything for test widget
+build-test: configure-test
+
+build: build-release
+
+build-all: build-release build-debug build-test build-coverage
+
+package-release package-debug package-coverage: package-%: build-%
+ @cmake --build $(OUTPUT_DIR) $(PACKAGE_ARGS) --target widget
+ @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \
+ mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \
+ fi
+
+package-test: build-test
+ @cmake --build $(OUTPUT_DIR) $(PACKAGE_ARGS) --target test_widget
+ @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \
+ mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \
+ fi
+
+package: package-release
+
+package-all: package-release package-test package-coverage package-debug
+
+update: configure
+ @cmake --build $(BUILD_DIR) --target autobuild
+
install: build
- @cmake --build ${BUILD_DIR} ${INSTALL_ARGS} --target install
+ @cmake --build $(BUILD_DIR) $(INSTALL_ARGS) --target install
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
index 099662c..d410d8b 100644
--- a/conf.d/cmake/config.cmake
+++ b/conf.d/cmake/config.cmake
@@ -43,7 +43,7 @@ set(PROJECT_CMAKE_CONF_DIR "conf.d")
# Compilation Mode (DEBUG, RELEASE)
# ----------------------------------
-set(CMAKE_BUILD_TYPE "RELEASE")
+set(BUILD_TYPE "RELEASE")
#set(USE_EFENCE 1)
# Helpers Submodule parameters
diff --git a/conf.d/project/etc/vshl-core-api.json b/conf.d/project/etc/vshl-core-api.json
index 44d578a..001ac42 100644
--- a/conf.d/project/etc/vshl-core-api.json
+++ b/conf.d/project/etc/vshl-core-api.json
@@ -4,13 +4,18 @@
"uid": "vshl-core",
"version": "1.0",
"api": "vshl-core",
- "info": "High Level Voice Service Core APIs"
+ "info": "High Level Voice Service Core APIs",
+ "require": ["signal-composer"]
},
"onload": [{
"uid": "loadVoiceAgentsConfig",
"info": "Loading the information about voice agents managed by the high level voice service.",
"action": "plugin://vshl-core#loadVoiceAgentsConfig",
+ }, {
+ "uid": "subscribeVoiceTriggerEvents",
+ "info": "Subscribe to voice recognition triggering events.",
+ "action": "plugin://vshl-core#subscribeVoiceTriggerEvents",
}],
"plugins": [{
@@ -38,5 +43,8 @@
"uid": "setDefaultVoiceAgent",
"privileges": "urn:AGL:permission:vshl-core:voiceagents:public",
"action": "plugin://vshl-core#setDefaultVoiceAgent"
+ }, {
+ "uid": "subscribeToLoginEvents",
+ "action": "plugin://vshl-core#subscribeToLoginEvents"
}]
}
diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in
index 3c0b0a0..ee70db0 100644
--- a/conf.d/wgt/config.xml.in
+++ b/conf.d/wgt/config.xml.in
@@ -17,4 +17,7 @@
<param name="urn:AGL:permission::public:hidden" value="required" />
<param name="urn:AGL:permission::platform:apis:auto-ws" value="required" />
</feature>
+ <feature name="urn:AGL:widget:required-api">
+ <param name="signal-composer" value="ws" />
+</feature>
</widget>
diff --git a/src/plugins/VshlCoreApi.cpp b/src/plugins/VshlCoreApi.cpp
index 4c32391..b14f949 100644
--- a/src/plugins/VshlCoreApi.cpp
+++ b/src/plugins/VshlCoreApi.cpp
@@ -48,6 +48,7 @@ static std::string VA_JSON_ATTR_DESCRIPTION = "description";
static std::string VA_JSON_ATTR_VENDOR = "vendor";
static std::string STARTLISTENING_JSON_ATTR_REQUEST = "request_id";
+static std::string SUBSCRIBE2LOGINEVENTS_JSON_ATTR_REQUEST = "request_id";
static std::string EVENTS_JSON_ATTR_VA_ID = "va_id";
static std::string EVENTS_JSON_ATTR_EVENTS = "events";
@@ -65,6 +66,11 @@ static std::unique_ptr<vshlcore::utilities::events::EventRouter> sEventRouter;
using json = nlohmann::json;
using Level = vshlcore::utilities::logging::Logger::Level;
+static const char *signalcomposer_events[] = {
+ "event.voice",
+ NULL,
+};
+
CTLP_ONLOAD(plugin, ret) {
if (plugin->api == nullptr) {
return -1;
@@ -158,6 +164,65 @@ CTLP_CAPI(onDialogStateEvent, source, argsJ, eventJ) {
return 0;
}
+CTLP_CAPI(onLoginPairReceivedEvent, source, argsJ, eventJ) {
+ if (sEventRouter == nullptr) {
+ return -1;
+ }
+
+ string eventName = vshlcore::voiceagents::VSHL_EVENT_LOGINPAIR_RECEIVED_EVENT;
+ json eventJson = json::parse(json_object_to_json_string(eventJ));
+ if (eventJson.find(EVENTS_JSON_ATTR_VA_ID) == eventJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "onLoginPairReceivedEvent: No voiceagent id found.");
+ return -1;
+ }
+ std::string voiceAgentId(eventJson[EVENTS_JSON_ATTR_VA_ID].get<string>());
+
+ sEventRouter->handleIncomingEvent(eventName, voiceAgentId, json_object_to_json_string(eventJ));
+
+ return 0;
+}
+
+CTLP_CAPI(onLoginPairExpiredEvent, source, argsJ, eventJ) {
+ if (sEventRouter == nullptr) {
+ return -1;
+ }
+
+ string eventName = vshlcore::voiceagents::VSHL_EVENT_LOGINPAIR_EXPIRED_EVENT;
+ json eventJson = json::parse(json_object_to_json_string(eventJ));
+ if (eventJson.find(EVENTS_JSON_ATTR_VA_ID) == eventJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "onLoginPairExpiredEvent: No voiceagent id found.");
+ return -1;
+ }
+ std::string voiceAgentId(eventJson[EVENTS_JSON_ATTR_VA_ID].get<string>());
+
+ sEventRouter->handleIncomingEvent(eventName, voiceAgentId, json_object_to_json_string(eventJ));
+
+ return 0;
+}
+
+CTLP_CAPI(onVoiceTriggerEvent, source, argsJ, eventJ) {
+ if (sVRRequestProcessor == nullptr) {
+ return -1;
+ }
+
+ json eventJson = json::parse(json_object_to_json_string(eventJ));
+ if (eventJson.find("uid") == eventJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "onVoiceTriggerEvent: No uid found.");
+ return -1;
+ }
+ if (eventJson.find("value") == eventJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "onVoiceTriggerEvent: No value found.");
+ return -1;
+ }
+ std::string uid(eventJson["uid"].get<std::string>());
+ bool value(eventJson["value"].get<bool>());
+ if (uid == "event.voice" && value == true) {
+ sVRRequestProcessor->startListening();
+ }
+
+ return 0;
+}
+
// Helper function to add events for voice agent to controller
// configuration. It relies on the controller configuration being
// available via the userdata of the API pointer.
@@ -208,6 +273,22 @@ static int AddVoiceAgentEvents(std::string &agentApi) {
json_object_object_add(eventJ, "action", actionJ);
json_object_array_add(eventsJ, eventJ);
+ eventJ = json_object_new_object();
+ uid = agentApi + "/voice_cbl_codepair_received_event";
+ uidJ = json_object_new_string(uid.c_str());
+ json_object_object_add(eventJ, "uid", uidJ);
+ actionJ = json_object_new_string("plugin://vshl-core#onLoginPairReceivedEvent");
+ json_object_object_add(eventJ, "action", actionJ);
+ json_object_array_add(eventsJ, eventJ);
+
+ eventJ = json_object_new_object();
+ uid = agentApi + "/voice_cbl_codepair_expired_event";
+ uidJ = json_object_new_string(uid.c_str());
+ json_object_object_add(eventJ, "uid", uidJ);
+ actionJ = json_object_new_string("plugin://vshl-core#onLoginPairExpiredEvent");
+ json_object_object_add(eventJ, "action", actionJ);
+ json_object_array_add(eventsJ, eventJ);
+
// Call into controller to add event actions
// NOTE: AFAICT the JSON objects end up reused by the controller data
// structures, so eventsJ should not be freed with a put after
@@ -299,7 +380,7 @@ CTLP_CAPI(loadVoiceAgentsConfig, source, argsJ, eventJ) {
return -1;
}
- string defaultVoiceAgent;
+ std::string defaultVoiceAgent;
if (argsJ != nullptr) {
// Parse any agent configs from controller arguments
@@ -353,6 +434,86 @@ CTLP_CAPI(loadVoiceAgentsConfig, source, argsJ, eventJ) {
return 0;
}
+// Helper function to add signal composer events for voice triggers to
+// controller configuration. It relies on the controller configuration
+// being available via the userdata of the API pointer.
+static int AddVoiceTriggerEvents(std::string &agentApi, std::string &uuid) {
+ // Retrieve section config from api handle
+ CtlConfigT *ctrlConfig = (CtlConfigT*) afb_api_get_userdata(sAfbApi->getApi());
+ if (ctrlConfig == nullptr) {
+ sLogger->log(Level::ERROR, TAG, "AddVoiceAgentEvents: ctrlConfig == nullptr");
+ return -1;
+ }
+
+ CtlSectionT* section = nullptr;
+ for (int idx = 0; ctrlConfig->sections[idx].key != NULL; ++idx) {
+ if (!strcasecmp(ctrlConfig->sections[idx].key, "events")) {
+ section = &(ctrlConfig->sections[idx]);
+ break;
+ }
+ }
+ if (section == nullptr) {
+ sLogger->log(Level::ERROR, TAG, "AddVoiceTriggerEvents: events section not found");
+ return -1;
+ }
+
+ // Fill out events JSON array to pass to the controller
+ json_object* eventsJ = json_object_new_array();
+
+ json_object *eventJ = json_object_new_object();
+ std::string uid = agentApi + "/" + uuid;
+ json_object *uidJ = json_object_new_string(uid.c_str());
+ json_object_object_add(eventJ, "uid", uidJ);
+ json_object *actionJ = json_object_new_string("plugin://vshl-core#onVoiceTriggerEvent");
+ json_object_object_add(eventJ, "action", actionJ);
+ json_object_array_add(eventsJ, eventJ);
+
+ // Call into controller to add event actions
+ // NOTE: AFAICT the JSON objects end up reused by the controller data
+ // structures, so eventsJ should not be freed with a put after
+ // this call.
+ int rc = AddActionsToSection(sAfbApi->getApi(), section, eventsJ, 0);
+ if (rc != 0) {
+ stringstream message;
+ message << "AddVoiceTriggerEvents: AddActionsToSection rc = " << rc;
+ sLogger->log(Level::WARNING, TAG, message.str().c_str());
+ }
+ return rc;
+}
+
+CTLP_CAPI(subscribeVoiceTriggerEvents, source, argsJ, eventJ) {
+ // Subscribe to signal-composer voice/PTT events
+ const char **tmp = signalcomposer_events;
+ json_object *args = json_object_new_object();
+ json_object *signals = json_object_new_array();
+
+ while (*tmp) {
+ json_object_array_add(signals, json_object_new_string(*tmp++));
+ }
+ json_object_object_add(args, "signal", signals);
+ if (json_object_array_length(signals)) {
+ struct json_object *responseJ;
+ int rc = afb_api_call_sync(sAfbApi->getApi(), "signal-composer",
+ "subscribe", args, &responseJ, NULL, NULL);
+ if (rc == 0 && responseJ) {
+ struct json_object *uuidJ;
+ json_object_object_get_ex(responseJ, "uuid", &uuidJ);
+ if (uuidJ) {
+ std::string uuid(json_object_get_string(uuidJ));
+ std::string api("signal-composer");
+ AddVoiceTriggerEvents(api, uuid);
+ }
+ }
+ if (responseJ)
+ json_object_put(responseJ);
+ } else {
+ json_object_put(args);
+ }
+
+ // Always return zero so service will start
+ return 0;
+}
+
CTLP_CAPI(startListening, source, argsJ, eventJ) {
if (sVoiceAgentsDataManager == nullptr) {
return -1;
@@ -363,7 +524,7 @@ CTLP_CAPI(startListening, source, argsJ, eventJ) {
}
int result = 0;
- string requestId = sVRRequestProcessor->startListening();
+ std::string requestId = sVRRequestProcessor->startListening();
if (!requestId.empty()) {
json responseJson;
@@ -384,7 +545,7 @@ CTLP_CAPI(enumerateVoiceAgents, source, argsJ, eventJ) {
if (sVoiceAgentsDataManager == nullptr) {
return -1;
}
-
+
auto agents = sVoiceAgentsDataManager->getAllVoiceAgents();
std::string defaultAgentId(sVoiceAgentsDataManager->getDefaultVoiceAgent());
@@ -442,7 +603,7 @@ CTLP_CAPI(subscribe, source, argsJ, eventJ) {
sLogger->log(Level::ERROR, TAG, "subscribe: No events array found in subscribe json");
return -1;
}
- list<string> events(subscribeJson[EVENTS_JSON_ATTR_EVENTS].get<list<string>>());
+ std::list<std::string> events(subscribeJson[EVENTS_JSON_ATTR_EVENTS].get<list<string>>());
// Subscribe this client for the listed events.
auto request = vshlcore::afb::AFBRequestImpl::create(source->request);
@@ -483,3 +644,43 @@ CTLP_CAPI(setDefaultVoiceAgent, source, argsJ, eventJ) {
afb_req_success(source->request, NULL, NULL);
return 0;
}
+
+CTLP_CAPI(subscribeToLoginEvents, source, argsJ, eventJ) {
+ if (sVoiceAgentsDataManager == nullptr) {
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "subscribeToLoginEvents: No arguments supplied.");
+ return -1;
+ }
+
+ if (sVRRequestProcessor == nullptr) {
+ return -1;
+ }
+
+ json subscribeJson = json::parse(json_object_to_json_string(eventJ));
+ if (subscribeJson.find(EVENTS_JSON_ATTR_VA_ID) == subscribeJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "subscribeToLoginEvents: No voiceagent id found in subscribe json");
+ return -1;
+ }
+ std::string voiceAgentId(subscribeJson[EVENTS_JSON_ATTR_VA_ID].get<string>());
+ if (subscribeJson.find(EVENTS_JSON_ATTR_EVENTS) == subscribeJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "subscribeToLoginEvents: No events array found in subscribe json");
+ return -1;
+ }
+ std::list<std::string> events(subscribeJson[EVENTS_JSON_ATTR_EVENTS].get<list<string>>());
+
+ int result = 0;
+ std::string requestId = sVRRequestProcessor->subscribeToLoginEvents(voiceAgentId, &events);
+
+ if (!requestId.empty()) {
+ json responseJson;
+ responseJson[SUBSCRIBE2LOGINEVENTS_JSON_ATTR_REQUEST] = requestId;
+ afb_req_success(source->request, json_tokener_parse(responseJson.dump().c_str()), NULL);
+ } else {
+ afb_req_fail(source->request, NULL, "Failed to subscribeToLoginEvents...");
+ }
+
+ return 0;
+}
diff --git a/src/plugins/VshlCoreApi.h b/src/plugins/VshlCoreApi.h
index 5e56b75..7c5bf69 100644
--- a/src/plugins/VshlCoreApi.h
+++ b/src/plugins/VshlCoreApi.h
@@ -27,12 +27,17 @@ CTLP_INIT(plugin, ret);
int onAuthStateEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
int onConnectionStateEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
int onDialogStateEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int onLoginPairReceivedEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int onLoginPairExpiredEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int onVoiceTriggerEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
int loadVoiceAgentsConfig(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int subscribeVoiceTriggerEvents(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
int startListening(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
int cancelListening(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
int enumerateVoiceAgents(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
int subscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
int setDefaultVoiceAgent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int subscribeToLoginEvents(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
#ifdef __cplusplus
}
diff --git a/src/plugins/core/VRRequestProcessor.h b/src/plugins/core/VRRequestProcessor.h
index 97e277a..037b27f 100644
--- a/src/plugins/core/VRRequestProcessor.h
+++ b/src/plugins/core/VRRequestProcessor.h
@@ -17,6 +17,7 @@
#include <memory>
#include <unordered_map>
+#include <list>
#include "core/include/VRRequest.h"
#include "core/include/VRRequestProcessorDelegate.h"
@@ -43,6 +44,11 @@ public:
// is returned.
string startListening();
+ // Triggers a voiceagent to generate events to provide info to allow
+ // the user to complete the authorization process (if required).
+ // Returns the request ID. An empty request ID is returned on failure.
+ string subscribeToLoginEvents(string va_id, list<string> *args);
+
// Cancels all the active requests
void cancel();
diff --git a/src/plugins/core/VRRequestProcessorImpl.cpp b/src/plugins/core/VRRequestProcessorImpl.cpp
index c07f745..d9fa846 100644
--- a/src/plugins/core/VRRequestProcessorImpl.cpp
+++ b/src/plugins/core/VRRequestProcessorImpl.cpp
@@ -57,6 +57,20 @@ string VRRequestProcessor::startListening() {
return mDelegate->startRequestForVoiceAgent(defaultVA);
}
+string VRRequestProcessor::subscribeToLoginEvents(std::string va_id, std::list<std::string> *args) {
+ // Currently simply send the request to the default voice agent, ignoring va_id
+ shared_ptr<vshlcore::common::interfaces::IVoiceAgent> defaultVA = mDelegate->getDefaultVoiceAgent();
+ if (!defaultVA) {
+ mLogger->log(Level::ERROR, TAG, "Failed to subscribeToLoginEvents. No default voiceagent found.");
+ return "";
+ }
+
+ // If the requests container is not empty, then clear the
+ // existing requests in flight and create a new request.
+ mDelegate->cancelAllRequests();
+ return mDelegate->loginEventsRequestForVoiceAgent(defaultVA, args);
+}
+
void VRRequestProcessor::cancel() {
// Cancel all pending requests
mDelegate->cancelAllRequests();
diff --git a/src/plugins/core/include/VRRequest.h b/src/plugins/core/include/VRRequest.h
index 8b9e842..feeac6d 100644
--- a/src/plugins/core/include/VRRequest.h
+++ b/src/plugins/core/include/VRRequest.h
@@ -16,6 +16,7 @@
#define VSHL_CORE_INCLUDE_VR_REQUEST_H_
#include <memory>
+#include <list>
#include "interfaces/afb/IAFBApi.h"
#include "interfaces/utilities/logging/ILogger.h"
@@ -32,6 +33,7 @@ class VRRequest {
public:
// API Verbs
static std::string VA_VERB_STARTLISTENING;
+ static std::string VA_VERB_SUBSCRIBETOCBLEVENTS;
static std::string VA_VERB_CANCEL;
// Create a VRRequest.
@@ -48,6 +50,10 @@ public:
// Returns true if started successfully. False otherwise.
bool startListening();
+ // Invokes the underlying voiceagent's subscribe to login events API.
+ // Returns true if successful, false otherwise.
+ bool subscribeToLoginEvents(std::list<std::string> *args);
+
// Cancels the voice recognition in the unlerlying voiceagent.
// Returns true if canceled successfully. False otherwise.
bool cancel();
diff --git a/src/plugins/core/include/VRRequestProcessorDelegate.h b/src/plugins/core/include/VRRequestProcessorDelegate.h
index 2c36d38..2ada2fb 100644
--- a/src/plugins/core/include/VRRequestProcessorDelegate.h
+++ b/src/plugins/core/include/VRRequestProcessorDelegate.h
@@ -17,6 +17,7 @@
#include <memory>
#include <unordered_map>
+#include <list>
#include "core/include/VRRequest.h"
#include "interfaces/afb/IAFBApi.h"
@@ -55,6 +56,9 @@ public:
// voiceagent is called.
string startRequestForVoiceAgent(shared_ptr<vshlcore::common::interfaces::IVoiceAgent> voiceAgent);
+ string loginEventsRequestForVoiceAgent(shared_ptr<vshlcore::common::interfaces::IVoiceAgent> voiceAgent,
+ std::list<std::string> *args);
+
// Cancel all requests
void cancelAllRequests();
diff --git a/src/plugins/core/src/VRRequestImpl.cpp b/src/plugins/core/src/VRRequestImpl.cpp
index 63302d8..d1a18ac 100644
--- a/src/plugins/core/src/VRRequestImpl.cpp
+++ b/src/plugins/core/src/VRRequestImpl.cpp
@@ -32,6 +32,7 @@ namespace core {
string VRRequest::VA_VERB_STARTLISTENING = "startListening";
string VRRequest::VA_VERB_CANCEL = "cancel";
+string VRRequest::VA_VERB_SUBSCRIBETOCBLEVENTS = "subscribeToCBLEvents";
unique_ptr<VRRequest> VRRequest::create(
shared_ptr<vshlcore::common::interfaces::ILogger> logger,
@@ -76,6 +77,21 @@ bool VRRequest::startListening() {
return true;
}
+ bool VRRequest::subscribeToLoginEvents(std::list<std::string> *args) {
+ json_object* argsJ = json_object_new_object();
+ json_object* evJ = json_object_new_array();
+ json_object* resultJ;
+ std::string error, info;
+ bool result = true;
+
+ json_object_object_add(argsJ, "events", evJ);
+ int rc = mApi->callSync(mVoiceAgent->getApi(), VA_VERB_SUBSCRIBETOCBLEVENTS, argsJ, &resultJ, error, info);
+
+ FREEIF(resultJ);
+
+ return true;
+}
+
bool VRRequest::cancel() {
json_object* object = NULL;
std::string error, info;
diff --git a/src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp b/src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp
index 78ef10a..57fc592 100644
--- a/src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp
+++ b/src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp
@@ -62,6 +62,32 @@ string VRRequestProcessorDelegate::startRequestForVoiceAgent(
return newReqId;
}
+string VRRequestProcessorDelegate::loginEventsRequestForVoiceAgent(
+ shared_ptr<vshlcore::common::interfaces::IVoiceAgent> voiceAgent,
+ std::list<std::string> *args) {
+ if (!mApi) {
+ mLogger->log(Level::ERROR, TAG, "Failed to loginEventsRequestForVoiceAgent: " + voiceAgent->getId() + ", No API.");
+ return "";
+ }
+
+ // Generate a new request ID.
+ string newReqId = vshlcore::utilities::uuid::generateUUID();
+
+ // Create a new request and start listening.
+ shared_ptr<VRRequest> newRequest = VRRequest::create(mLogger, mApi, newReqId, voiceAgent);
+
+ mLogger->log(Level::DEBUG, TAG, "Starting login request with ID: " + newReqId);
+ if (!newRequest->subscribeToLoginEvents(args)) {
+ mLogger->log(Level::ERROR, TAG, "Failed to subscribe to login events.");
+ return "";
+ }
+
+ // Insert only if its started successfully.
+ mVRRequests.insert(make_pair(voiceAgent->getId(), newRequest));
+
+ return newReqId;
+}
+
void VRRequestProcessorDelegate::cancelAllRequests() {
// Cancel Pending requests
if (!mVRRequests.empty()) {
diff --git a/src/plugins/voiceagents/VoiceAgentEventNames.h b/src/plugins/voiceagents/VoiceAgentEventNames.h
index 92cc8dc..abee50a 100644
--- a/src/plugins/voiceagents/VoiceAgentEventNames.h
+++ b/src/plugins/voiceagents/VoiceAgentEventNames.h
@@ -25,10 +25,13 @@ namespace voiceagents {
static string VSHL_EVENT_AUTH_STATE_EVENT = "voice_authstate_event";
static string VSHL_EVENT_CONNECTION_STATE_EVENT = "voice_connectionstate_event";
static string VSHL_EVENT_DIALOG_STATE_EVENT = "voice_dialogstate_event";
+static string VSHL_EVENT_LOGINPAIR_RECEIVED_EVENT = "voice_cbl_codepair_received_event";
+static string VSHL_EVENT_LOGINPAIR_EXPIRED_EVENT = "voice_cbl_codepair_expired_event";
static list<string> VSHL_EVENTS = {
VSHL_EVENT_AUTH_STATE_EVENT, VSHL_EVENT_CONNECTION_STATE_EVENT,
- VSHL_EVENT_DIALOG_STATE_EVENT,
+ VSHL_EVENT_DIALOG_STATE_EVENT, VSHL_EVENT_LOGINPAIR_RECEIVED_EVENT,
+ VSHL_EVENT_LOGINPAIR_EXPIRED_EVENT,
};
} // namespace voiceagents
diff --git a/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp b/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp
index 3b55505..bff9d8c 100644
--- a/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp
+++ b/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp
@@ -14,6 +14,8 @@
*/
#include "voiceagents/include/VoiceAgentEventsHandler.h"
+#include <json-c/json.h>
+
static string TAG = "vshlcore::voiceagents::VoiceAgentEventsHandler";
static string VA_VERB_SUBSCRIBE = "subscribe";
@@ -101,7 +103,17 @@ bool VoiceAgentEventsHandler::onIncomingEvent(const string eventName, const stri
string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgentId);
auto it = mEventsMap.find(eventNameWithVAId);
if (it != mEventsMap.end()) {
- return it->second->publishEvent(json_object_new_string(payload.c_str()));
+ json_object* payloadJ = NULL;
+ if(payload.length()) {
+ payloadJ = json_tokener_parse(payload.c_str());
+ } else {
+ payloadJ = json_object_new_string("");
+ }
+ if(!payloadJ) {
+ mLogger->log(Level::ERROR, TAG, "Unable to parse payload JSON: " + payload);
+ return false;
+ }
+ return it->second->publishEvent(payloadJ);
}
return true;