File indexing completed on 2024-04-28 12:31:22

0001 /*
0002  * Copyright 2021 Aditya Mehra <aix.m@outlook.com>
0003  * Copyright 2018 by Marco Martin <mart@kde.org>
0004  * Copyright 2018 David Edmundson <davidedmundson@kde.org>
0005  *
0006  * Licensed under the Apache License, Version 2.0 (the "License");
0007  * you may not use this file except in compliance with the License.
0008  * You may obtain a copy of the License at
0009  *
0010  *    http://www.apache.org/licenses/LICENSE-2.0
0011  *
0012  * Unless required by applicable law or agreed to in writing, software
0013  * distributed under the License is distributed on an "AS IS" BASIS,
0014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015  * See the License for the specific language governing permissions and
0016  * limitations under the License.
0017  *
0018  */
0019 
0020 #include <QJsonObject>
0021 #include <QJsonArray>
0022 #include <QJsonDocument>
0023 #include <QQmlPropertyMap>
0024 #include <QQmlEngine>
0025 #include <QQmlContext>
0026 #include <QWebSocket>
0027 #include "controller.h"
0028 
0029 Controller *Controller::instance()
0030 {
0031     static Controller* s_self = nullptr;
0032     if (!s_self) {
0033         s_self = new Controller;
0034     }
0035     return s_self;
0036 }
0037 
0038 Controller::Controller(QObject *parent)
0039     : QObject(parent)
0040 {
0041     connect(&m_mainWebSocket, &QWebSocket::connected, this,
0042             [this] () {
0043         m_reconnectTimer.stop();
0044         emit socketStatusChanged();
0045     });
0046     connect(&m_mainWebSocket, &QWebSocket::disconnected, this, &Controller::closed);
0047     connect(&m_mainWebSocket, &QWebSocket::stateChanged, this,
0048             [this] (QAbstractSocket::SocketState state) {
0049         emit socketStatusChanged();
0050         if (state == QAbstractSocket::ConnectedState) {
0051             qWarning() << "Main Socket Connected";
0052             sendRequest(QStringLiteral("mycroft.skills.all_loaded"), QVariantMap());
0053         } else {
0054             if (m_serverReady) {
0055                 m_serverReady = false;
0056                 emit serverReadyChanged();
0057             }
0058         }
0059     });
0060 
0061     connect(&m_mainWebSocket, &QWebSocket::textMessageReceived, this, &Controller::onMainSocketMessageReceived);
0062 
0063     m_reconnectTimer.setInterval(1000);
0064     connect(&m_reconnectTimer, &QTimer::timeout, this, [this]() {
0065         QString socket = webSocketAddress();
0066         m_mainWebSocket.open(QUrl(socket));
0067     });
0068 }
0069 
0070 void Controller::start()
0071 {
0072     qDebug() << "Trying To Start Connection";
0073     QString socket = webSocketAddress();
0074     m_mainWebSocket.open(QUrl(socket));
0075     connect(&m_mainWebSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error),
0076             this, [this] (const QAbstractSocket::SocketError &error) {
0077 
0078         if (error != QAbstractSocket::HostNotFoundError && error != QAbstractSocket::ConnectionRefusedError) {
0079             qWarning() << "Mycroft is running but the connection failed for some reason. Kill Mycroft manually.";
0080 
0081             return;
0082         }
0083 
0084         m_reconnectTimer.start();
0085         emit socketStatusChanged();
0086     });
0087 
0088     emit socketStatusChanged();
0089 }
0090 
0091 
0092 void Controller::disconnectSocket()
0093 {
0094     qDebug() << "in reconnect";
0095     m_mainWebSocket.close();
0096     m_reconnectTimer.stop();
0097 
0098     emit socketStatusChanged();
0099 }
0100 
0101 void Controller::reconnect()
0102 {
0103     qDebug() << "in reconnect";
0104     m_mainWebSocket.close();
0105     m_reconnectTimer.start();
0106     emit socketStatusChanged();
0107 }
0108 
0109 void Controller::onMainSocketMessageReceived(const QString &message)
0110 {
0111     emit messageReceived(message);
0112     auto doc = QJsonDocument::fromJson(message.toUtf8());
0113 
0114     if (doc.isEmpty()) {
0115         qWarning() << "Empty or invalid JSON message arrived on the main socket:" << message;
0116         return;
0117     }
0118 
0119     auto type = doc[QStringLiteral("type")].toString();
0120 
0121     if (type.isEmpty()) {
0122         qWarning() << "Empty type in the JSON message on the main socket";
0123         return;
0124     }
0125 
0126     emit intentRecevied(type, doc[QStringLiteral("data")].toVariant().toMap());
0127 
0128     if (type == QLatin1String("complete_intent_failure")) {
0129         m_isListening = false;
0130         emit isListeningChanged();
0131         emit notUnderstood();
0132     }
0133     if (type == QLatin1String("recognizer_loop:audio_output_start")) {
0134         m_isSpeaking = true;
0135         emit isSpeakingChanged();
0136         return;
0137     }
0138     if (type == QLatin1String("recognizer_loop:audio_output_end")) {
0139         m_isSpeaking = false;
0140         emit isSpeakingChanged();
0141         return;
0142     }
0143     if (type == QLatin1String("recognizer_loop:wakeword")) {
0144         m_isListening = true;
0145         emit isListeningChanged();
0146         return;
0147     }
0148     if (type == QLatin1String("recognizer_loop:record_begin") && !m_isListening) {
0149         m_isListening = true;
0150         emit isListeningChanged();
0151         return;
0152     }
0153     if (type == QLatin1String("recognizer_loop:record_end")) {
0154         m_isListening = false;
0155         emit isListeningChanged();
0156         return;
0157     }
0158     if (type == QLatin1String("mycroft.speech.recognition.unknown")) {
0159         emit notUnderstood();
0160         return;
0161     }
0162 
0163     if (type == QLatin1String("mycroft.skill.handler.start")) {
0164         m_currentSkill = doc[QStringLiteral("data")][QStringLiteral("name")].toString();
0165         qDebug() << "Current intent:" << m_currentIntent;
0166         emit currentIntentChanged();
0167     } else if (type == QLatin1String("mycroft.skill.handler.complete")) {
0168         m_currentSkill = QString();
0169         emit currentSkillChanged();
0170     } else if (type == QLatin1String("speak")) {
0171         emit fallbackTextRecieved(m_currentSkill, doc[QStringLiteral("data")].toVariant().toMap());
0172     } else if (type == QLatin1String("mycroft.stop.handled") || type == QLatin1String("mycroft.stop")) {
0173         emit stopped();
0174     } else if (type == QLatin1String("mycroft.skills.all_loaded.response")) {
0175         if (doc[QStringLiteral("data")][QStringLiteral("status")].toBool() == true) {
0176             m_serverReady = true;
0177             emit serverReadyChanged();
0178         }
0179     } else if (type == QLatin1String("mycroft.ready")) {
0180         m_serverReady = true;
0181         emit serverReadyChanged();
0182     }
0183 
0184     // Check if it's an utterance recognized as an intent
0185     if (type.contains(QLatin1Char(':')) && !doc[QStringLiteral("data")][QStringLiteral("utterance")].toString().isEmpty()) {
0186         const QString skill = type.split(QLatin1Char(':')).first();
0187         if (skill.contains(QLatin1Char('.'))) {
0188             m_currentSkill = skill;
0189             qDebug() << "Current skill:" << m_currentSkill;
0190             emit utteranceManagedBySkill(m_currentSkill);
0191             emit currentSkillChanged();
0192         }
0193     }
0194 }
0195 
0196 void Controller::sendRequest(const QString &type, const QVariantMap &data)
0197 {
0198     if (m_mainWebSocket.state() != QAbstractSocket::ConnectedState) {
0199         qWarning() << "mycroft connection not open!";
0200         return;
0201     }
0202     QJsonObject root;
0203 
0204     root[QStringLiteral("type")] = type;
0205     root[QStringLiteral("data")] = QJsonObject::fromVariantMap(data);
0206 
0207     QJsonDocument doc(root);
0208     m_mainWebSocket.sendTextMessage(QString::fromUtf8(doc.toJson()));
0209 }
0210 
0211 void Controller::sendBinary(const QString &type, const QJsonObject &data)
0212 {
0213     if (m_mainWebSocket.state() != QAbstractSocket::ConnectedState) {
0214         qWarning() << "mycroft connection not open!";
0215         return;
0216     }
0217     QJsonObject socketObject;
0218     socketObject[QStringLiteral("type")] = type;
0219     socketObject[QStringLiteral("data")] = data;
0220 
0221     QJsonDocument doc;
0222     doc.setObject(socketObject);
0223     QByteArray docbin = doc.toJson(QJsonDocument::Compact);
0224     m_mainWebSocket.sendBinaryMessage(docbin);
0225 }
0226 
0227 void Controller::sendText(const QString &message)
0228 {
0229     sendRequest(QStringLiteral("recognizer_loop:utterance"), QVariantMap({{QStringLiteral("utterances"), QStringList({message})}}));
0230 }
0231 
0232 Controller::Status Controller::status() const
0233 {
0234     if (m_reconnectTimer.isActive()) {
0235         return Connecting;
0236     }
0237 
0238     switch(m_mainWebSocket.state())
0239     {
0240     case QAbstractSocket::ConnectingState:
0241     case QAbstractSocket::BoundState:
0242     case QAbstractSocket::HostLookupState:
0243         return Connecting;
0244     case QAbstractSocket::UnconnectedState:
0245         return Closed;
0246     case QAbstractSocket::ConnectedState:
0247         return Open;
0248     case QAbstractSocket::ClosingState:
0249         return Closing;
0250     default:
0251         return Connecting;
0252     }
0253 }
0254 
0255 QString Controller::currentSkill() const
0256 {
0257     return m_currentSkill;
0258 }
0259 
0260 QString Controller::currentIntent() const
0261 {
0262     return m_currentIntent;
0263 }
0264 
0265 bool Controller::isSpeaking() const
0266 {
0267     return m_isSpeaking;
0268 }
0269 
0270 bool Controller::isListening() const
0271 {
0272     return m_isListening;
0273 }
0274 
0275 bool Controller::serverReady() const
0276 {
0277     return m_serverReady;
0278 }
0279 
0280 QString Controller::webSocketAddress() const
0281 {
0282     if (Controller::useHivemind() == false){
0283         return m_settings.value(QStringLiteral("webSocketAddress"), QStringLiteral("ws://0.0.0.0:8181/core")).toString();
0284     } else {
0285         return m_settings.value(QStringLiteral("webSocketAddress"), QStringLiteral("ws://0.0.0.0::5678?accessKey=R1VJOmFzZGZnaGprbDEyMzQ1Njc=")).toString();
0286     }
0287 }
0288 
0289 void Controller::setWebSocketAddress(QString webSocketAddress)
0290 {
0291     if (Controller::webSocketAddress() == webSocketAddress) {
0292         return;
0293     }
0294     m_settings.setValue(QStringLiteral("webSocketAddress"), webSocketAddress);
0295     emit webSocketChanged();
0296 }
0297 
0298 bool Controller::useHivemind() const
0299 {
0300     return m_settings.value(QStringLiteral("useHivemind")).toBool();
0301 }
0302 
0303 void Controller::setUseHivemind(bool useHivemind)
0304 {
0305     if (Controller::useHivemind() == useHivemind) {
0306         return;
0307     }
0308 
0309     m_settings.setValue(QStringLiteral("useHivemind"), useHivemind);
0310     emit useHivemindChanged();
0311 }
0312 
0313 #include "moc_controller.cpp"