File indexing completed on 2024-03-24 04:45:29
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"