diff options
author | Raquel Medina <raquel.medina@konsulko.com> | 2019-11-29 23:01:41 +0100 |
---|---|---|
committer | Raquel Medina <raquel.medina@konsulko.com> | 2019-11-29 23:41:25 +0100 |
commit | da091649e775b23a35eb88df1c2cf9ce3fb350f7 (patch) | |
tree | 0eb475a54336b12c6796f93dbedce60d2e30199d | |
parent | 5cd66764ad6f7397feeb3bf30f24c58ab62c1064 (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.txt | 1 | ||||
-rw-r--r-- | message.h | 9 | ||||
-rw-r--r-- | messageengine.cpp | 10 | ||||
-rw-r--r-- | voice/CMakeLists.txt | 4 | ||||
-rw-r--r-- | voice/voice.cpp | 213 | ||||
-rw-r--r-- | voice/voice.h | 74 | ||||
-rw-r--r-- | voice/voiceagentmodel.cpp | 187 | ||||
-rw-r--r-- | voice/voiceagentmodel.h | 63 | ||||
-rw-r--r-- | voice/voiceagentprofile.cpp | 142 | ||||
-rw-r--r-- | voice/voiceagentprofile.h | 78 | ||||
-rw-r--r-- | voice/voiceagentregistry.cpp | 145 | ||||
-rw-r--r-- | voice/voiceagentregistry.h | 76 | ||||
-rw-r--r-- | voice/voicemessage.cpp | 43 | ||||
-rw-r--r-- | voice/voicemessage.h | 112 |
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) @@ -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 |