diff --git a/autocomplete.sh b/autocomplete.sh new file mode 100644 index 0000000..72c45bc --- /dev/null +++ b/autocomplete.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +function _r3ctl_autocomplete() +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + + if [[ "$COMP_CWORD" -eq 1 ]] + then + COMPREPLY=( $(compgen -W "log script sendYmhButton" "$cur") ) + elif [[ "$COMP_CWORD" -eq 2 ]] + then + if [[ "${COMP_WORDS[1]}" = "sendYmhButton" ]] + then + COMPREPLY=( $(compgen -W "ymhpower ymhcd ymhtuner ymhtape ymhwdtv ymhsattv ymhvcr ymh7 ymhaux ymhextdec ymhtest ymhtunabcde ymheffect ymhtvlolup ymhtvvoldown ymhvoldown ymhvolup ymhmute ymhsleep ymhp5 ymhtunminus ymhtunplus ymhprgup ymhprgdown ymhminus ymhplus ymhtimelevel ymhmenu" "$cur") ) + fi + fi +} +complete -F _r3ctl_autocomplete r3ctl \ No newline at end of file diff --git a/main.cpp b/main.cpp index 35f591c..8bfb909 100644 --- a/main.cpp +++ b/main.cpp @@ -1,53 +1,102 @@ #include -#include #include -#include -#include -#include #include +#include +#include + +#include "r3client.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); - QWebSocket webSocket; - QObject::connect(&webSocket, &QWebSocket::connected, [&](){ qDebug() << "connected()"; }); - QObject::connect(&webSocket, &QWebSocket::disconnected, [&](){ qDebug() << "disconnected()"; }); -// QObject::connect(&webSocket, &QWebSocket::textFrameReceived, [&](const QString &frame, bool isLastFrame){ -// qDebug() << "textFrameReceived()"; -// }); -// QObject::connect(&webSocket, &QWebSocket::binaryFrameReceived, [&](const QByteArray &frame, bool isLastFrame){ -// qDebug() << "binaryFrameReceived()"; -// }); - QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [&](const QString &message){ - qDebug() << "textMessageReceived()" << message; - }); - QObject::connect(&webSocket, &QWebSocket::binaryMessageReceived, [&](const QByteArray &message){ - qDebug() << "binaryMessageReceived()" << message; - }); - QObject::connect(&webSocket, qOverload(&QWebSocket::error), [&](QAbstractSocket::SocketError error){ - qDebug() << "error()" << error; - }); - webSocket.open(QUrl{"ws://licht.realraum.at/sock"}); + const auto arguments = [&](){ + auto arguments = a.arguments(); + arguments.removeFirst(); + return arguments; + }(); - const auto sendMQTT = [&](const QString &ctx, const QJsonValue &data){ - const QJsonObject m{{"ctx", ctx}, {"data", data}}; - const auto text = QJsonDocument{m}.toJson(); - qDebug() << "sendTextMessage()" << text; - webSocket.sendTextMessage(text); - }; + if (arguments.isEmpty()) + { + puts("No cmd given!"); + return -1; + } - const auto mqtttopic_golightctrl = [](const QString &lightname){ - return QString{"action/GoLightCtrl/%0"}.arg(lightname); - }; + R3Client client; - const auto sendYmhButton = [&](const QString &btn){ - sendMQTT(mqtttopic_golightctrl(btn), QJsonObject{{"Action", "send"}}); - }; + QObject::connect(&client, &R3Client::disconnected, [](){ qDebug() << "disconnected()"; }); + QObject::connect(&client, &R3Client::error, [](QAbstractSocket::SocketError error){ qDebug() << "error()" << error; }); - QTimer::singleShot(1000, [&](){ - sendYmhButton("ymhvolup"); - }); + if (arguments.first() == "log") + { + QObject::connect(&client, &R3Client::connected, [](){ qDebug() << "connected()"; }); + QObject::connect(&client, &R3Client::statusReceived, [](const QString &ctx, const QJsonValue &jsonValue){ qDebug() << "statusReceived()" << ctx << jsonValue; }); + } + else if (arguments.first() == "script") + { + QObject::connect(&client, &R3Client::connected, [&](){ + client.sendMQTT("action/ceilingscripts/activatescript", QJsonObject{ + {"colourlist", QJsonArray{ + QJsonObject{{"b", 0}, {"cw", 0}, {"g", 0}, {"r", 1000}, {"ww", 0}}, + QJsonObject{{"b", 100}, {"cw", 0}, {"g", 0}, {"r", 800}, {"ww", 0}}, + QJsonObject{{"b", 300}, {"cw", 0}, {"g", 0}, {"r", 0}, {"ww", 0}}, + QJsonObject{{"b", 100}, {"cw", 0}, {"g", 500}, {"r", 0}, {"ww", 0}}, + QJsonObject{{"b", 0}, {"cw", 0}, {"g", 800}, {"r", 0}, {"ww", 0}}, + QJsonObject{{"b", 0}, {"cw", 0}, {"g", 200}, {"r", 800}, {"ww", 0}} + }}, + {"fadeduration", 500}, + {"script", "wave"} + }); + }); + } + else if (arguments.first() == "sendYmhButton") + { + if (arguments.size() < 2) + { + puts("No button given for sendYmhButton"); + return -3; + } + + const auto btn = arguments.at(1); + + QObject::connect(&client, &R3Client::connected, [&client,btn](){ + client.sendYmhButton(btn); + }); + QObject::connect(&client, &R3Client::statusReceived, [btn](const QString &ctx, const QJsonValue &jsonValue){ + if (ctx != "action/yamahastereo/ircmd") + return; + if (!jsonValue.isObject()) + { + qWarning() << "json data is not an object"; + qWarning() << jsonValue; + return; + } + const auto object = jsonValue.toObject(); + if (!object.contains("Cmd")) + { + qWarning() << "json data does not contain Cmd"; + qWarning() << object; + return; + } + const auto cmdValue = object.value("Cmd"); + if (!cmdValue.isString()) + { + qWarning() << "json data Cmd is not a string"; + qWarning() << object; + return; + } + const auto cmd = cmdValue.toString(); + if (cmd == btn) + QCoreApplication::quit(); + }); + } + else + { + puts("Invalid cmd. Valid are log, script, sendYmhButton"); + return -2; + } + + client.open(); return a.exec(); } diff --git a/r3client.cpp b/r3client.cpp new file mode 100644 index 0000000..008679e --- /dev/null +++ b/r3client.cpp @@ -0,0 +1,101 @@ +#include "r3client.h" + +#include +#include + +R3Client::R3Client(QObject *parent) : + QObject{parent} +{ + connect(&m_webSocket, &QWebSocket::connected, this, &R3Client::connected); + connect(&m_webSocket, &QWebSocket::disconnected, this, &R3Client::disconnected); + connect(&m_webSocket, qOverload(&QWebSocket::error), this, &R3Client::error); + connect(&m_webSocket, &QWebSocket::textMessageReceived, this, &R3Client::textMessageReceived); + connect(&m_webSocket, &QWebSocket::binaryMessageReceived, this, &R3Client::binaryMessageReceived); +} + +void R3Client::open() +{ + m_webSocket.open(QUrl{"ws://licht.realraum.at/sock"}); +} + +void R3Client::close(QWebSocketProtocol::CloseCode closeCode, const QString &reason) +{ + m_webSocket.close(closeCode, reason); +} + +void R3Client::sendMQTT(const QString &ctx, const QJsonValue &data) +{ + const QJsonObject m{{"ctx", ctx}, {"data", data}}; + //qDebug() << m; + const auto text = QJsonDocument{m}.toJson(QJsonDocument::Compact); + //qDebug() << "sendTextMessage()" << text; + m_webSocket.sendTextMessage(text); +} + +void R3Client::sendYmhButton(const QString &btn) +{ + sendMQTT(mqtttopic_golightctrl(btn), QJsonObject{{"Action", "send"}}); +} + +QString R3Client::mqtttopic_golightctrl(const QString &lightname) +{ + return QString{"action/GoLightCtrl/%0"}.arg(lightname); +} + +void R3Client::textMessageReceived(const QString &message) +{ + QJsonParseError error; + QJsonDocument document = QJsonDocument::fromJson(message.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) + { + qWarning() << "could not parse JSON" << error.errorString(); + qDebug() << "textMessageReceived()" << message; + return; + } + + if (!document.isObject()) + { + qWarning() << "json is not an object"; + qDebug() << "textMessageReceived()" << message; + return; + } + + const auto obj = document.object(); + + if (!obj.contains("ctx")) + { + qWarning() << "json does not contain ctx"; + qDebug() << "textMessageReceived()" << message; + return; + } + + QString ctx; + + { + const auto ctxValue = obj.value("ctx"); + if (!ctxValue.isString()) + { + qWarning() << "ctx is not a string!"; + qDebug() << "textMessageReceived()" << message; + return; + } + + ctx = ctxValue.toString(); + } + + if (!obj.contains("data")) + { + qWarning() << "json does not contain data"; + qDebug() << "textMessageReceived()" << message; + return; + } + + const auto data = obj.value("data"); + + emit statusReceived(ctx, data); +} + +void R3Client::binaryMessageReceived(const QByteArray &message) +{ + qWarning() << "binaryMessageReceived()" << message; +} diff --git a/r3client.h b/r3client.h new file mode 100644 index 0000000..40d6814 --- /dev/null +++ b/r3client.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +class R3Client : public QObject +{ + Q_OBJECT + +public: + R3Client(QObject *parent = nullptr); + + void open(); + void close(QWebSocketProtocol::CloseCode closeCode = QWebSocketProtocol::CloseCodeNormal, const QString &reason = QString{}); + + void sendMQTT(const QString &ctx, const QJsonValue &data); + void sendYmhButton(const QString &btn); + static QString mqtttopic_golightctrl(const QString &lightname); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError error); + void statusReceived(const QString &ctx, const QJsonValue &jsonValue); + +private slots: + void textMessageReceived(const QString &message); + void binaryMessageReceived(const QByteArray &message); + +private: + QWebSocket m_webSocket; +}; diff --git a/r3ctl.pro b/r3ctl.pro index 2622e72..cb9415b 100644 --- a/r3ctl.pro +++ b/r3ctl.pro @@ -5,4 +5,8 @@ CONFIG += c++14 DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 SOURCES += \ - main.cpp + main.cpp \ + r3client.cpp + +HEADERS += \ + r3client.h