summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaquel Medina <raquel.medina@konsulko.com>2019-11-29 23:01:41 +0100
committerRaquel Medina <raquel.medina@konsulko.com>2019-11-29 23:41:25 +0100
commitda091649e775b23a35eb88df1c2cf9ce3fb350f7 (patch)
tree0eb475a54336b12c6796f93dbedce60d2e30199d
parent5cd66764ad6f7397feeb3bf30f24c58ab62c1064 (diff)
voice: add initial support for voice agents configuration
Bug-AGL: SPEC-2981 Signed-off-by: Raquel Medina <raquel.medina@konsulko.com> Change-Id: I0195d914dc10f4fcdea1cb6df0e6a5859ad8269d
-rw-r--r--CMakeLists.txt1
-rw-r--r--message.h9
-rw-r--r--messageengine.cpp10
-rw-r--r--voice/CMakeLists.txt4
-rw-r--r--voice/voice.cpp213
-rw-r--r--voice/voice.h74
-rw-r--r--voice/voiceagentmodel.cpp187
-rw-r--r--voice/voiceagentmodel.h63
-rw-r--r--voice/voiceagentprofile.cpp142
-rw-r--r--voice/voiceagentprofile.h78
-rw-r--r--voice/voiceagentregistry.cpp145
-rw-r--r--voice/voiceagentregistry.h76
-rw-r--r--voice/voicemessage.cpp43
-rw-r--r--voice/voicemessage.h112
14 files changed, 1153 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6421e36..c9b77f9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -55,6 +55,7 @@ set (SUBDIRS
pbap
radio
telephony
+ voice
weather)
add_headers(message.h messageengine.h responsemessage.h)
diff --git a/message.h b/message.h
index c39b107..229ba80 100644
--- a/message.h
+++ b/message.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017, 2018 Konsulko Group
+ * Copyright (C) 2017, 2018, 2019 Konsulko Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,6 +42,7 @@ enum MessageType {
RadioEventMessage,
MapEventMessage,
NavigationEventMessage,
+ VoiceEventMessage,
};
class Message : public QObject
@@ -87,17 +88,17 @@ class Message : public QObject
return m_reply_data;
}
- inline bool isEvent() const
+ inline bool isEvent() const
{
return m_event;
}
- inline bool isReply() const
+ inline bool isReply() const
{
return m_reply;
}
- inline bool isValid() const
+ inline bool isValid() const
{
return m_init;
}
diff --git a/messageengine.cpp b/messageengine.cpp
index d057d11..4920043 100644
--- a/messageengine.cpp
+++ b/messageengine.cpp
@@ -26,6 +26,7 @@
#include "responsemessage.h"
#include "telephonymessage.h"
#include "weathermessage.h"
+#include "voicemessage.h"
#include <QJsonArray>
@@ -137,6 +138,15 @@ void MessageEngine::onTextMessageReceived(QString jsonStr)
} else if (api == "bluetooth-map") {
message = new MapMessage;
type = MapEventMessage;
+ } else if (api == "vshl-core" ) {
+ message = new VshlCoreVoiceMessage;
+ type = VoiceEventMessage;
+ } else if (api == "vshl-capabilities") {
+ message = new VshlCpbltsVoiceMessage;
+ type = VoiceEventMessage;
+ } else if (api == "alexa-voiceagent") {
+ message = new AlexaVoiceMessage;
+ type = VoiceEventMessage;
} else {
message = new Message;
type = GenericMessage;
diff --git a/voice/CMakeLists.txt b/voice/CMakeLists.txt
new file mode 100644
index 0000000..61b8fe2
--- /dev/null
+++ b/voice/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_headers(voice.h voicemessage.h
+ voiceagentregistry.h voiceagentprofile.h voiceagentmodel.h)
+add_sources(voice.cpp voicemessage.cpp
+ voiceagentregistry.cpp voiceagentprofile.cpp voiceagentmodel.cpp)
diff --git a/voice/voice.cpp b/voice/voice.cpp
new file mode 100644
index 0000000..71c93d2
--- /dev/null
+++ b/voice/voice.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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 <QStringList>
+#include "voice.h"
+#include "message.h"
+#include "messageengine.h"
+#include "responsemessage.h"
+#include "voicemessage.h"
+#include "voiceagentregistry.h"
+
+Voice::Voice (QUrl &url, QQmlContext *context, QObject *parent) :
+ QObject(parent),
+ m_loop(nullptr)
+{
+ m_loop = new MessageEngine(url);
+ m_var = new VoiceAgentRegistry(this, context, parent);
+
+ QObject::connect(m_loop, &MessageEngine::connected,
+ this, &Voice::onConnected);
+ QObject::connect(m_loop, &MessageEngine::disconnected,
+ this, &Voice::onDisconnected);
+ QObject::connect(m_loop, &MessageEngine::messageReceived,
+ this, &Voice::onMessageReceived);
+}
+
+Voice::~Voice()
+{
+ delete m_loop;
+ delete m_var;
+}
+
+void Voice::scan()
+{
+ VoiceMessage *vmsg = new VshlCoreVoiceMessage();
+ QJsonObject parameter;
+
+ vmsg->createRequest("enumerateVoiceAgents", parameter);
+ m_loop->sendMessage(vmsg);
+ delete vmsg;
+}
+
+void Voice::getCBLpair(QString id)
+{
+ VoiceMessage *vmsg = new AlexaVoiceMessage();
+ QJsonObject parameter;
+
+ vmsg->createRequest("subscribeToCBLEvents", parameter);
+ m_loop->sendMessage(vmsg);
+ delete vmsg;
+}
+
+void Voice::subscribeAgentToVshlEvents(QString id)
+{
+ QJsonArray events = QJsonArray::fromStringList(vshl_events);
+ VoiceMessage *vmsg = new VshlCoreVoiceMessage();
+ QJsonObject parameter;
+
+ parameter.insert("va_id", id);
+ parameter.insert("events", events);
+ vmsg->createRequest("subscribe", parameter);
+ m_loop->sendMessage(vmsg);
+ delete vmsg;
+}
+
+void Voice::unsubscribeAgentFromVshlEvents(QString id)
+{
+ QJsonArray events = QJsonArray::fromStringList(vshl_events);
+ VoiceMessage *vmsg = new VshlCoreVoiceMessage();
+ QJsonObject parameter;
+
+ parameter.insert("va_id", id);
+ parameter.insert("events", events);
+ vmsg->createRequest("unsubscribe", parameter);
+ m_loop->sendMessage(vmsg);
+ delete vmsg;
+}
+
+void Voice::subscribeAgentToCblEvents(QString id)
+{
+ QJsonArray events = QJsonArray::fromStringList(cbl_events);
+ VoiceMessage *vmsg = new AlexaVoiceMessage();
+ QJsonObject parameter;
+
+ parameter.insert("va_id", id);
+ parameter.insert("events", events);
+ vmsg->createRequest("subscribeToCBLEvent", parameter);
+ m_loop->sendMessage(vmsg);
+ delete vmsg;
+}
+
+void Voice::parseAgentsList(QJsonArray agents)
+{
+ for (auto value : agents) {
+ QJsonObject a = value.toObject();
+ QString id = m_var->addAgent(a);
+ subscribeAgentToVshlEvents(id);
+ }
+}
+
+void Voice::processVshlEvent(VoiceMessage *vmsg)
+{
+ const QString str = vmsg->eventName();
+ const QJsonObject obj = vmsg->eventData();
+ QStringList strlist;
+
+ if (str.contains('#'))
+ strlist = str.split('#');
+ QString agentId = (strlist.isEmpty())? m_var->getDefaultId() :
+ strlist.takeLast();
+ if (vmsg->isAuthStateEvent()) {
+ const QString authstate = obj.value("state").toString();
+ m_var->setAuthState(
+ agentId,
+ static_cast<VoiceAgentRegistry::ServiceAuthState>(
+ m_var->stringToEnum(authstate, "ServiceAuthState")));
+ } else if (vmsg->isConnectionStateEvent()) {
+ const QString connstate = obj.value("state").toString();
+ m_var->setConnectionState(
+ agentId,
+ static_cast<VoiceAgentRegistry::AgentConnectionState>(
+ m_var->stringToEnum(connstate, "AgentConnectionState")));
+ } else if (vmsg->isDialogStateEvent()) {
+ const QString dialogstate = obj.value("state").toString();
+ m_var->setDialogState(
+ agentId,
+ static_cast<VoiceAgentRegistry::VoiceDialogState>(
+ m_var->stringToEnum(dialogstate, "VoiceDialogState")));
+ } else
+ qWarning() << "Discarding vshl event:" << str;
+}
+
+void Voice::processCblEvent(VoiceMessage *vmsg)
+{
+ const QString str = vmsg->eventName();
+ const QJsonObject obj = vmsg->eventData();
+ QStringList strlist;
+
+ if (str.contains('#'))
+ strlist = str.split('#');
+ QString cblevent = (strlist.isEmpty())? QString() : strlist.takeFirst();
+ QString agentId = (strlist.isEmpty())? m_var->getDefaultId() :
+ strlist.takeLast();
+ if (cblevent == "voice_cbl_codepair_received_event") {
+ QString code = obj.value("code").toString();
+ QString url = obj.value("url").toString();
+ m_var->updateCblPair(agentId, code, url, false);
+ } else if (cblevent == "voice_cbl_codepair_expired_event") {
+ QString code = obj.value("code").toString();
+ QString url = obj.value("url").toString();
+ m_var->updateCblPair(agentId, code, url, true);
+ } else
+ qWarning() << "Discarding cbl event:" << str;
+}
+
+void Voice::processEvent(VoiceMessage *vmsg)
+{
+ const QString api = vmsg->eventApi();
+ if (api == "vshl-core")
+ processVshlEvent(vmsg);
+ else if (api == "alexa-voiceagent")
+ processCblEvent(vmsg);
+ else
+ qWarning() << "Unknown api:" << api;
+}
+
+void Voice::processReply(ResponseMessage *rmsg)
+{
+ if (rmsg->requestVerb() == "enumerateVoiceAgents") {
+ parseAgentsList(rmsg->replyData().value("agents").toArray());
+ m_var->setDefaultId(
+ rmsg->replyData().value("default").toString());
+ } else
+ qWarning() << "Reply received for unknown verb:" <<
+ rmsg->requestVerb();
+}
+
+void Voice::onConnected()
+{
+ scan();
+}
+
+void Voice::onDisconnected()
+{
+ QStringList mvarlist = m_var->getAgentsListById();
+ QStringList::iterator it;
+ for (it = mvarlist.begin(); it != mvarlist.end(); ++it)
+ unsubscribeAgentFromVshlEvents(*it);
+}
+
+void Voice::onMessageReceived(MessageType type, Message *msg)
+{
+ if (msg->isEvent() && type == VoiceEventMessage) {
+ processEvent(qobject_cast<VoiceMessage*>(msg));
+ } else if (msg->isReply() && (type == ResponseRequestMessage)) {
+ processReply(qobject_cast<ResponseMessage*>(msg));
+ } else
+ qWarning() << "Received unknown message type:" << type;
+ msg->deleteLater();
+}
diff --git a/voice/voice.h b/voice/voice.h
new file mode 100644
index 0000000..1da7ca6
--- /dev/null
+++ b/voice/voice.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef VOICE_H
+#define VOICE_H
+
+#include <QDebug>
+#include <QObject>
+#include <QJsonArray>
+#include <QtQml/QQmlContext>
+#include "message.h"
+
+class MessageEngine;
+class ResponseMessage;
+class VoiceMessage;
+class VoiceAgentRegistry;
+
+class Voice : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit Voice(QUrl &url, QQmlContext *context,
+ QObject * parent = Q_NULLPTR);
+ virtual ~Voice();
+
+ // controls
+ Q_INVOKABLE void scan();
+ Q_INVOKABLE void getCBLpair(QString id);
+
+ private:
+ MessageEngine *m_loop;
+ VoiceAgentRegistry *m_var;
+
+ void subscribeAgentToVshlEvents(QString id);
+ void unsubscribeAgentFromVshlEvents(QString id);
+ void subscribeAgentToCblEvents(QString id);
+ void parseAgentsList(QJsonArray agents);
+ void processVshlEvent(VoiceMessage *vmsg);
+ void processCblEvent(VoiceMessage *vmsg);
+
+ void processEvent(VoiceMessage *vmsg);
+ void processReply(ResponseMessage *rmsg);
+
+ // slots
+ void onConnected();
+ void onDisconnected();
+ void onMessageReceived(MessageType type, Message *msg);
+
+ const QStringList vshl_events {
+ "voice_authstate_event",
+ "voice_dialogstate_event",
+ "voice_connectionstate_event",
+ };
+ const QStringList cbl_events {
+ "voice_cbl_codepair_received_event",
+ "voice_cbl_codepair_expired_event",
+ };
+};
+
+#endif // VOICE_H
diff --git a/voice/voiceagentmodel.cpp b/voice/voiceagentmodel.cpp
new file mode 100644
index 0000000..e0f9bfb
--- /dev/null
+++ b/voice/voiceagentmodel.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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 "voiceagentmodel.h"
+#include "voiceagentprofile.h"
+#include <QVector>
+#include <QDebug>
+
+VoiceAgentModel::VoiceAgentModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+QVariant VoiceAgentModel::data(const QModelIndex &index, int role) const
+{
+ QVariant ret;
+
+ if (!index.isValid())
+ return ret;
+
+ if (index.row() < 0 || index.row() >= m_agents.count())
+ return ret;
+
+ const VoiceAgentProfile *vap = m_agents[index.row()];
+ switch (role) {
+ case IdRole:
+ return vap->vaid();
+ case NameRole:
+ return vap->name();
+ case WuwRole:
+ return vap->activewuw();
+ case AuthStateRole:
+ return vap->authstate();
+ case ConnStateRole:
+ return vap->connstate();
+ case DialogStateRole:
+ return vap->dialogstate();
+ case LoginParamsRole:
+ return readLoginParams(index);
+ case ActiveRole:
+ return vap->isactive()? "active" : "inactive";
+ }
+ return ret;
+}
+
+int VoiceAgentModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return m_agents.count();
+}
+
+QVariantList VoiceAgentModel::readLoginParams(const QModelIndex &index) const
+{
+ QVariantList ret;
+
+ if (!index.isValid())
+ return ret;
+
+ if (index.row() < 0 || index.row() >= this->m_agents.count())
+ return ret;
+
+ const VoiceAgentProfile *vap = this->m_agents[index.row()];
+ ret.append(vap->loginurl());
+ ret.append(vap->logincode());
+ ret.append(vap->isloginpairexpired()? "expired" : "valid");
+ return ret;
+}
+
+void VoiceAgentModel::addAgent(VoiceAgentProfile *vap)
+{
+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
+ m_agents.insert(rowCount(), vap);
+ endInsertRows();
+}
+
+void VoiceAgentModel::removeAgent(VoiceAgentProfile *vap)
+{
+ if (m_agents.isEmpty())
+ return;
+
+ int row = m_agents.indexOf(vap);
+ beginRemoveRows(QModelIndex(), row, row);
+ m_agents.removeAt(row);
+ endRemoveRows();
+ delete vap;
+}
+
+void VoiceAgentModel::removeAll()
+{
+ if (m_agents.isEmpty())
+ return;
+
+ beginRemoveRows(QModelIndex(), 0, m_agents.count() -1);
+ qDeleteAll(m_agents.begin(), m_agents.end());
+ endRemoveRows();
+ m_agents.clear();
+}
+
+bool VoiceAgentModel::agentExists(QString name, QString id, QString api) const
+{
+ VoiceAgentProfile *vap = getAgentFromName(name);
+ if (!vap)
+ return false;
+ bool sameid = id == vap->vaid();
+ bool sameapi = api == vap->vaapi();
+ return sameapi && (sameid || id != "UNKNOWN");
+}
+
+VoiceAgentProfile* VoiceAgentModel::getAgentFromName(QString name) const
+{
+ if (m_agents.isEmpty())
+ return nullptr;
+
+ for (auto agent : m_agents) {
+ if (agent->name() == name)
+ return agent;
+ }
+ return nullptr;
+}
+
+VoiceAgentProfile* VoiceAgentModel::getAgentFromId(QString id) const
+{
+ if (m_agents.isEmpty())
+ return nullptr;
+
+ for (auto agent : m_agents) {
+ if (agent->vaid() == id)
+ return agent;
+ }
+ return nullptr;
+}
+
+void VoiceAgentModel::updateAgentProperties(QString name, QString id, QString api,
+ bool active, QString wuw)
+{
+ QVector<int> vroles;
+ VoiceAgentProfile *vap = getAgentFromName(name);
+ if (!vap) {
+ qWarning() << "Unknown agent";
+ return;
+ }
+ if ((vap->vaapi() == api) && (vap->vaid() != id) && (id != "UNKNOWN")) {
+ vap->setVaid(id);
+ vroles.push_back(IdRole);
+ }
+ vap->setActive(active);
+ vroles.push_back(ActiveRole);
+ if (!wuw.isEmpty()) {
+ vap->setWuw(wuw);
+ vroles.push_back(WuwRole);
+ }
+ if (!vroles.isEmpty())
+ emit dataChanged(indexOf(vap), indexOf(vap), vroles);
+}
+
+QModelIndex VoiceAgentModel::indexOf(VoiceAgentProfile *vap)
+{
+ int row = m_agents.indexOf(vap);
+ return index(row);
+}
+
+QHash<int, QByteArray> VoiceAgentModel::roleNames() const
+{
+ QHash<int, QByteArray> roles;
+ roles[NameRole] = "name";
+ roles[IdRole] = "id";
+ roles[WuwRole] = "wuw";
+ roles[AuthStateRole] = "authstate";
+ roles[ConnStateRole] = "connstate";
+ roles[DialogStateRole] = "dialogstate";
+ roles[LoginParamsRole] = "usrauth";
+ roles[ActiveRole] = "active";
+ return roles;
+}
diff --git a/voice/voiceagentmodel.h b/voice/voiceagentmodel.h
new file mode 100644
index 0000000..30d295b
--- /dev/null
+++ b/voice/voiceagentmodel.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef VOICEAGENTMODEL_H
+#define VOICEAGENTMODEL_H
+
+#include <QAbstractListModel>
+#include <QStringList>
+#include <QtQml/QQmlContext>
+#include <QJsonObject>
+
+#include "voiceagentprofile.h"
+
+class VoiceAgentModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+ public:
+ enum VoiceAgentRoles {
+ IdRole = Qt::UserRole + 1,
+ NameRole,
+ WuwRole,
+ AuthStateRole,
+ ConnStateRole,
+ DialogStateRole,
+ LoginParamsRole,
+ ActiveRole,
+ };
+
+ VoiceAgentModel(QObject *parent = Q_NULLPTR);
+
+ QVariant data(const QModelIndex &index,
+ int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariantList readLoginParams(const QModelIndex &index) const;
+ void addAgent(VoiceAgentProfile *vap);
+ void removeAgent(VoiceAgentProfile* vap);
+ void removeAll();
+ bool agentExists(QString name, QString id, QString api) const;
+ VoiceAgentProfile *getAgentFromName(QString name) const;
+ VoiceAgentProfile *getAgentFromId(QString id) const;
+ void updateAgentProperties(QString name, QString id,
+ QString api, bool active, QString wuw);
+
+ private:
+ QList<VoiceAgentProfile *> m_agents;
+ QModelIndex indexOf(VoiceAgentProfile *agent);
+ QHash<int, QByteArray> roleNames() const;
+};
+#endif // VOICEAGENTMODEL_H
diff --git a/voice/voiceagentprofile.cpp b/voice/voiceagentprofile.cpp
new file mode 100644
index 0000000..05e3839
--- /dev/null
+++ b/voice/voiceagentprofile.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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 "voiceagentprofile.h"
+
+VoiceAgentProfile::VoiceAgentProfile(const QString &name,
+ const QString &id,
+ const QString &api,
+ bool active,
+ const QString &wuw,
+ const QString &vendor,
+ const QString &wuws)
+ : m_name(name), m_vaid(id), m_vaapi(api), m_active(active),
+ m_activewuw(wuw), m_vendor(vendor), m_wuws(wuws),
+ m_authstate("UNITIALIZED"), m_connstate("DISCONNECTED"),
+ m_dialogstate("MICROPHONEOFF"), m_logincode(QString()),
+ m_loginurl(QString()), m_expired(true)
+{
+}
+
+QString VoiceAgentProfile::name() const
+{
+ return m_name;
+}
+
+QString VoiceAgentProfile::vaid() const
+{
+ return m_vaid;
+}
+
+QString VoiceAgentProfile::vaapi() const
+{
+ return m_vaapi;
+}
+
+bool VoiceAgentProfile::isactive() const
+{
+ return m_active;
+}
+
+QString VoiceAgentProfile::activewuw() const
+{
+ return m_activewuw;
+}
+
+QString VoiceAgentProfile::vendor() const
+{
+ return m_vendor;
+}
+
+QString VoiceAgentProfile::wuws() const
+{
+ return m_wuws;
+}
+
+QString VoiceAgentProfile::authstate() const
+{
+ return m_authstate;
+}
+
+QString VoiceAgentProfile::connstate() const
+{
+ return m_connstate;
+}
+
+QString VoiceAgentProfile::dialogstate() const
+{
+ return m_dialogstate;
+}
+
+QString VoiceAgentProfile::logincode() const
+{
+ return m_logincode;
+}
+
+QString VoiceAgentProfile::loginurl() const
+{
+ return m_loginurl;
+}
+
+bool VoiceAgentProfile::isloginpairexpired() const
+{
+ return m_expired;
+}
+
+void VoiceAgentProfile::setVaid(const QString id)
+{
+ m_vaid = id;
+}
+
+void VoiceAgentProfile::setActive(bool active)
+{
+ m_active = active;
+}
+
+void VoiceAgentProfile::setAuthState(const QString state)
+{
+ m_authstate = state;
+}
+
+void VoiceAgentProfile::setConnState(const QString state)
+{
+ m_connstate = state;
+}
+
+void VoiceAgentProfile::setDialogState(const QString state)
+{
+ m_dialogstate = state;
+}
+
+void VoiceAgentProfile::setLoginCode(const QString usrcode)
+{
+ m_logincode = usrcode;
+}
+
+void VoiceAgentProfile::setLoginUrl(const QString usrurl)
+{
+ m_loginurl = usrurl;
+}
+
+void VoiceAgentProfile::setLoginPairExpired(bool expired)
+{
+ m_expired = expired;
+}
+
+void VoiceAgentProfile::setWuw(const QString newwuw)
+{
+ m_activewuw = newwuw;
+}
diff --git a/voice/voiceagentprofile.h b/voice/voiceagentprofile.h
new file mode 100644
index 0000000..dda96c5
--- /dev/null
+++ b/voice/voiceagentprofile.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef VOICEAGENTPROFILE_H
+#define VOICEAGENTPROFILE_H
+
+#include <QString>
+
+class VoiceAgentProfile
+{
+ public:
+ VoiceAgentProfile(const QString &name,
+ const QString &id,
+ const QString &api,
+ bool active,
+ const QString &wuw,
+ const QString &vendor,
+ const QString &wuws);
+
+ QString name() const;
+ QString vaid() const;
+ QString vaapi() const;
+ bool isactive() const;
+ QString activewuw() const;
+ QString vendor() const;
+ QString wuws() const;
+ QString authstate() const;
+ QString connstate() const;
+ QString dialogstate() const;
+ QString logincode() const;
+ QString loginurl() const;
+ bool isloginpairexpired() const;
+
+ void setVaid(const QString newid);
+ void setActive(bool activemode);
+ void setAuthState(const QString newauthstate);
+ void setConnState(const QString newconnstate);
+ void setDialogState(const QString newdialogstate);
+ void setLoginCode(const QString newtoken);
+ void setLoginUrl(const QString newurl);
+ void setLoginPairExpired(bool expired);
+ void setWuw(const QString newwuw);
+
+ bool operator==(const VoiceAgentProfile& rhs) {
+ return (m_name == rhs.name() &&
+ m_vaid == rhs.vaid() &&
+ m_vaapi == rhs.vaapi()); };
+
+ private:
+ QString m_name;
+ QString m_vaid;
+ QString m_vaapi;
+ bool m_active;
+ QString m_activewuw;
+ QString m_vendor;
+ QString m_wuws;
+ QString m_authstate;
+ QString m_connstate;
+ QString m_dialogstate;
+ QString m_logincode;
+ QString m_loginurl;
+ bool m_expired;
+};
+
+#endif // VOICEAGENTPROFILE_H
diff --git a/voice/voiceagentregistry.cpp b/voice/voiceagentregistry.cpp
new file mode 100644
index 0000000..22c7051
--- /dev/null
+++ b/voice/voiceagentregistry.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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 <QMetaEnum>
+#include <QSortFilterProxyModel>
+#include <QtQml/QQmlEngine>
+
+#include "voice.h"
+#include "voiceagentregistry.h"
+#include "voiceagentmodel.h"
+#include "voiceagentprofile.h"
+
+VoiceAgentRegistry::VoiceAgentRegistry(Voice *voice, QQmlContext *context, QObject *parent) :
+ QObject(parent),
+ m_model(nullptr),
+ m_voice(voice)
+{
+ m_model = new VoiceAgentModel();
+ QSortFilterProxyModel *model = new QSortFilterProxyModel();
+ model->setSourceModel(m_model);
+ model->setSortRole(VoiceAgentModel::VoiceAgentRoles::IdRole);
+ model->setSortCaseSensitivity(Qt::CaseInsensitive);
+ model->sort(0);
+
+ context->setContextProperty("VoiceAgentModel", m_model);
+ context->setContextProperty("VoiceAgent", this);
+}
+
+VoiceAgentRegistry::~VoiceAgentRegistry()
+{
+ delete m_model;
+}
+
+QString VoiceAgentRegistry::addAgent(QJsonObject va)
+{
+ bool active = va.value("active").toBool();
+ QString wuw = va.value("activewakeword").toString();
+ QString api = va.value("api").toString();
+ QString desc = va.value("description").toString();
+ QString id = va.value("id").toString();
+ QString name = va.value("name").toString();
+ QString vendor = va.value("vendor").toString();
+ QString wuws = va.value("wakewords").toString();
+
+ if (!m_model->agentExists(name, id, api)) {
+ VoiceAgentProfile *vap = new VoiceAgentProfile(name, id, api,
+ active, wuw,
+ vendor, wuws);
+ m_model->addAgent(vap);
+ m_regids.append(id);
+ }
+ else
+ m_model->updateAgentProperties(name, id, api, active, wuw);
+ return id;
+}
+
+bool VoiceAgentRegistry::removeAgent(QString id)
+{
+ VoiceAgentProfile *vap = m_model->getAgentFromId(id);
+ if (!vap)
+ return false;
+ m_model->removeAgent(vap);
+ return true;
+}
+
+void VoiceAgentRegistry::clearRegistry()
+{
+ m_default_aid.clear();
+ m_regids.clear();
+ m_model->removeAll();
+}
+
+QStringList VoiceAgentRegistry::getAgentsListById() const
+{
+ return m_regids;
+}
+
+QString VoiceAgentRegistry::getDefaultId() const
+{
+ return m_default_aid.isEmpty()? "UNKNOWN" : m_default_aid;
+}
+void VoiceAgentRegistry::setDefaultId(QString id)
+{
+ m_default_aid = id;
+}
+
+void VoiceAgentRegistry::setAuthState(QString id, ServiceAuthState state)
+{
+ const auto stateStr =
+ QMetaEnum::fromType<VoiceAgentRegistry::ServiceAuthState>().valueToKey(state);
+ VoiceAgentProfile *vap = m_model->getAgentFromId(id);
+ if (!vap)
+ vap->setAuthState(stateStr);
+}
+
+void VoiceAgentRegistry::setConnectionState(QString id, AgentConnectionState state)
+{
+ const auto stateStr =
+ QMetaEnum::fromType<VoiceAgentRegistry::AgentConnectionState>().valueToKey(state);
+ VoiceAgentProfile *vap = m_model->getAgentFromId(id);
+ if (!vap)
+ vap->setConnState(stateStr);
+}
+
+void VoiceAgentRegistry::setDialogState(QString id, VoiceDialogState state)
+{
+ const auto stateStr =
+ QMetaEnum::fromType<VoiceAgentRegistry::VoiceDialogState>().valueToKey(state);
+ VoiceAgentProfile *vap = m_model->getAgentFromId(id);
+ if (!vap)
+ vap->setDialogState(stateStr);
+}
+
+void VoiceAgentRegistry::updateCblPair(QString id, QString code, QString url,
+ bool expired)
+{
+ VoiceAgentProfile *vap = m_model->getAgentFromId(id);
+ if (!vap) {
+ vap->setLoginCode(code);
+ vap->setLoginUrl(code);
+ vap->setLoginPairExpired(expired);
+ };
+}
+
+int VoiceAgentRegistry::stringToEnum(const QString key, const QString enumtype)
+{
+ const QMetaObject *metaObject = VoiceAgentRegistry::metaObject();
+ int enumIndex = metaObject->indexOfEnumerator(enumtype.toUtf8().data());
+ QMetaEnum metaEnum = metaObject->enumerator(enumIndex);
+ int value = metaEnum.keyToValue(key.toUtf8().data());
+ return (value < 0)? 0 : value;
+}
diff --git a/voice/voiceagentregistry.h b/voice/voiceagentregistry.h
new file mode 100644
index 0000000..2a69294
--- /dev/null
+++ b/voice/voiceagentregistry.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef VOICEAGENTREGISTRY_H
+#define VOICEAGENTREGISTRY_H
+
+#include <QDebug>
+#include <QObject>
+#include <QJsonArray>
+#include <QtQml/QQmlContext>
+
+class Voice;
+class VoiceAgentModel;
+
+class VoiceAgentRegistry : public QObject
+{
+ Q_OBJECT
+ public:
+ explicit VoiceAgentRegistry(Voice *voice, QQmlContext *context,
+ QObject *parent);
+ virtual ~VoiceAgentRegistry();
+
+ enum AgentConnectionState {
+ DISCONNECTED = 0,
+ CONNECTED,
+ };
+ Q_ENUM(AgentConnectionState)
+
+ enum VoiceDialogState {
+ IDLE = 0,
+ LISTENING,
+ THINKING,
+ SPEAKING,
+ MICROPHONEOFF,
+ };
+ Q_ENUM(VoiceDialogState)
+
+ enum ServiceAuthState {
+ UNITIALIZED = 0,
+ REFRESHED,
+ };
+ Q_ENUM(ServiceAuthState)
+
+ QString addAgent(QJsonObject va);
+ bool removeAgent(QString id);
+ void clearRegistry();
+ QStringList getAgentsListById() const;
+ QString getDefaultId() const;
+ void setDefaultId(QString id);
+ void setAuthState(QString id, ServiceAuthState state);
+ void setConnectionState(QString id, AgentConnectionState state);
+ void setDialogState(QString id, VoiceDialogState state);
+ void updateCblPair(QString id, QString code, QString url,
+ bool expired);
+ int stringToEnum(QString value, QString enumtype);
+ private:
+ VoiceAgentModel *m_model;
+ Voice *m_voice;
+ QString m_default_aid;
+ QStringList m_regids;
+};
+
+#endif // VOICEAGENTREGISTRY_H
diff --git a/voice/voicemessage.cpp b/voice/voicemessage.cpp
new file mode 100644
index 0000000..c338879
--- /dev/null
+++ b/voice/voicemessage.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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 <QDebug>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+#include "voicemessage.h"
+
+bool VshlCoreVoiceMessage::createRequest(QString verb, QJsonObject parameter)
+{
+ if (!verbs.contains(verb))
+ return false;
+ return Message::createRequest("vshl-core", verb, parameter);
+}
+
+bool VshlCpbltsVoiceMessage::createRequest(QString verb, QJsonObject parameter)
+{
+ if (!verbs.contains(verb))
+ return false;
+ return Message::createRequest("vshl-capabilities", verb, parameter);
+}
+
+bool AlexaVoiceMessage::createRequest(QString verb, QJsonObject parameter)
+{
+ if (!verbs.contains(verb))
+ return false;
+ return Message::createRequest("alexa-voiceagent", verb, parameter);
+}
diff --git a/voice/voicemessage.h b/voice/voicemessage.h
new file mode 100644
index 0000000..1de5dd2
--- /dev/null
+++ b/voice/voicemessage.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef VOICE_MESSAGE_H
+#define VOICE_MESSAGE_H
+
+#include "message.h"
+
+class VoiceMessage : public Message
+{
+ Q_OBJECT
+ public:
+ virtual ~VoiceMessage() {};
+ virtual bool isAuthStateEvent() const = 0;
+ virtual bool isConnectionStateEvent() const = 0;
+ virtual bool isDialogStateEvent() const = 0;
+ virtual bool createRequest(QString verb, QJsonObject parameter) = 0;
+};
+
+class VshlCoreVoiceMessage : public VoiceMessage
+{
+ Q_OBJECT
+ public:
+ virtual ~VshlCoreVoiceMessage() {};
+ bool isAuthStateEvent() const override {
+ return (this->eventName() == "voice_authstate_event"); };
+ bool isConnectionStateEvent() const override {
+ return (this->eventName() == "voice_connectionstate_event"); };
+ bool isDialogStateEvent() const override {
+ return (this->eventName() == "voice_dialogstate_event"); };
+ bool createRequest(QString verb, QJsonObject parameter) override;
+
+ private:
+ QStringList verbs {
+ "startListening",
+ "cancelListening",
+ "subscribe",
+ "unsubscribe",
+ "enumerateVoiceAgents",
+ "setDefaultVoiceAgent",
+ };
+ QStringList events {
+ "",
+ };
+};
+
+class VshlCpbltsVoiceMessage : public VoiceMessage
+{
+ Q_OBJECT
+ public:
+ virtual ~VshlCpbltsVoiceMessage() {};
+ bool isAuthStateEvent() const override { return false; };
+ bool isConnectionStateEvent() const override { return false; };
+ bool isDialogStateEvent() const override { return false; };
+ bool createRequest(QString verb, QJsonObject parameter) override;
+
+ private:
+ QStringList verbs {
+ "guiMetadataSubscribe",
+ "guiMetadataPublish",
+ "phonecontrolSubscribe",
+ "phonecontrolPublish",
+ "navigationSubscribe",
+ "navigationPublish",
+ "playbackControllerSubscribe",
+ "playbackControllerPublish",
+ };
+ QStringList events {
+ "voice_authstate_event",
+ "voice_dialogstate_event",
+ "voice_connectionstate_event",
+ };
+};
+
+/* We shouldnt access an agent directly, but CBL events
+ * are not abstracted/forwarded by vshl bindings.
+ */
+class AlexaVoiceMessage : public VoiceMessage
+{
+ Q_OBJECT
+ public:
+ virtual ~AlexaVoiceMessage() {};
+ bool isAuthStateEvent() const override {
+ return (!events.contains(this->eventName())); };
+ bool isConnectionStateEvent() const override { return false; };
+ bool isDialogStateEvent() const override { return false; };
+ bool createRequest(QString verb, QJsonObject parameter) override;
+
+ private:
+ QStringList verbs {
+ "subscribeToCBLEvents",
+ };
+ QStringList events {
+ "voice_cbl_codepair_received_event",
+ "voice_cbl_codepair_expired_event",
+ };
+};
+
+#endif // VOICE_MESSAGE_H