aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Collignon <loic.collignon@iot.bzh>2018-02-23 13:57:35 +0100
committerLoïc Collignon <loic.collignon@iot.bzh>2018-02-23 13:57:35 +0100
commitd2f071d5d8c5c21c4f438bf5a822fbaddb2d97ce (patch)
treee69b8367c41b2a23c107d86c2467977d7cb39d57
parent43dc156f29b89efd4055b8b412e888c0c3129e7b (diff)
hack: control master volume using 4a with the homescreen slidersandbox/ctxnop/4avolume
Change-Id: If527d160f8eacbcfed3df2ab744485995dbe1dc9 Signed-off-by: Loïc Collignon <loic.collignon@iot.bzh>
-rw-r--r--homescreen/homescreen.pro10
-rw-r--r--homescreen/qml/MediaAreaBlank.qml6
-rw-r--r--homescreen/src/main.cpp3
-rw-r--r--homescreen/src/mastervolume.cpp127
-rw-r--r--homescreen/src/mastervolume.h34
-rw-r--r--homescreen/src/qafbwsclient.cpp136
-rw-r--r--homescreen/src/qafbwsclient.h50
-rw-r--r--homescreen/src/qafbwsmsg.cpp44
-rw-r--r--homescreen/src/qafbwsmsg.h33
-rw-r--r--package/config.xml3
10 files changed, 423 insertions, 23 deletions
diff --git a/homescreen/homescreen.pro b/homescreen/homescreen.pro
index b72931b..2d20a03 100644
--- a/homescreen/homescreen.pro
+++ b/homescreen/homescreen.pro
@@ -15,7 +15,7 @@
TEMPLATE = app
TARGET = HomeScreen
-QT = qml quick dbus
+QT = qml quick dbus core websockets
CONFIG += c++11 link_pkgconfig
DESTDIR = $${OUT_PWD}/../package/root/bin
PKGCONFIG += qlibwindowmanager
@@ -32,7 +32,9 @@ SOURCES += \
src/statusbarserver.cpp \
src/applicationlauncher.cpp \
src/mastervolume.cpp \
- src/homescreenhandler.cpp
+ src/homescreenhandler.cpp \
+ src/qafbwsclient.cpp \
+ src/qafbwsmsg.cpp
HEADERS += \
src/statusbarmodel.h \
@@ -41,7 +43,9 @@ HEADERS += \
src/applicationmodel.h \
src/appinfo.h \
src/mastervolume.h \
- src/homescreenhandler.h
+ src/homescreenhandler.h \
+ src/qafbwsclient.h \
+ src/qafbwsmsg.h
OTHER_FILES += \
README.md
diff --git a/homescreen/qml/MediaAreaBlank.qml b/homescreen/qml/MediaAreaBlank.qml
index 2b888c8..6114b55 100644
--- a/homescreen/qml/MediaAreaBlank.qml
+++ b/homescreen/qml/MediaAreaBlank.qml
@@ -71,6 +71,7 @@ Image {
MasterVolume {
id: mv
objectName: "mv"
+ Component.onCompleted: mv.init(afbPort, afbToken)
onVolumeChanged: slider.value = volume
}
@@ -101,10 +102,11 @@ Image {
id: slider
Layout.fillWidth: true
from: 0
- to: 65536
- stepSize: 256
+ to: 100
+ stepSize: 1
snapMode: Slider.SnapOnRelease
onValueChanged: mv.volume = value
+ value: 0
Component.onCompleted: value = mv.volume
onPressedChanged: {
if (pressed) {volume_timer.stop()}
diff --git a/homescreen/src/main.cpp b/homescreen/src/main.cpp
index a7f435a..283c513 100644
--- a/homescreen/src/main.cpp
+++ b/homescreen/src/main.cpp
@@ -85,6 +85,7 @@ int main(int argc, char *argv[])
}
HMI_DEBUG("HomeScreen","port = %d, token = %s", port, token.toStdString().c_str());
+ qDebug() << "HomeScreen - port =" << port << ", token =" << token;
// import C++ class to QML
// qmlRegisterType<ApplicationLauncher>("HomeScreen", 1, 0, "ApplicationLauncher");
@@ -132,6 +133,8 @@ int main(int argc, char *argv[])
// mail.qml loading
QQmlApplicationEngine engine;
+ engine.rootContext()->setContextProperty("afbPort", port);
+ engine.rootContext()->setContextProperty("afbToken", token);
engine.rootContext()->setContextProperty("layoutHandler", layoutHandler);
engine.rootContext()->setContextProperty("homescreenHandler", homescreenHandler);
engine.rootContext()->setContextProperty("launcher", launcher);
diff --git a/homescreen/src/mastervolume.cpp b/homescreen/src/mastervolume.cpp
index 5ef78c5..a4f8d28 100644
--- a/homescreen/src/mastervolume.cpp
+++ b/homescreen/src/mastervolume.cpp
@@ -15,17 +15,132 @@
*/
#include "mastervolume.h"
+#include "qafbwsclient.h"
+#include "qafbwsmsg.h"
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QJsonArray>
+#include <QtDebug>
+/*!
+ * @brief Default constructor.
+ * @param parent Parent's object.
+ */
+MasterVolume::MasterVolume(QObject* parent)
+ : QObject{parent}
+ , m_volume(0)
+{
+ connect(&m_client, SIGNAL(connected()), this, SLOT(onAfbClientConnected()));
+ connect(&m_client, SIGNAL(disconnected()), this, SLOT(onAfbClientDisconnected()));
+}
+
+/*!
+ * @brief Destructor.
+ */
+MasterVolume::~MasterVolume()
+{
+}
+
+/*!
+ * @brief Initialize this instance.
+ * @param port AFB Port.
+ * @param token AFB Token.
+ */
+void MasterVolume::init(quint16 port, QString token)
+{
+ qDebug() << "MasterVolume::init(" << port << "," << token << ")";
+ m_client.open(QUrl(QString("ws://localhost:") + QString::number(port) + QString("/api?token=") + token));
+}
+
+/*!
+ * \brief Get the current volume.
+ * @return The volume.
+ */
+uint32_t MasterVolume::getVolume() const
+{
+ return m_volume;
+}
+
+/*!
+ * @brief Set volume.
+ * @param volume New desired value.
+ */
void MasterVolume::setVolume(uint32_t volume)
{
- int volume_delta = volume - m_volume;
- m_volume = volume;
- emit sliderVolumeChanged(volume_delta);
+ qDebug() << "MasterVolume::setVolume(" << volume << ")";
+ if (volume == m_volumePending) return; // Nothing to do
+ m_volumePending = volume;
+ QJsonObject value;
+ value["label"] = "Master_Playback_Volume";
+ value["val"] = static_cast<int>(volume);
+
+ // FIXME: card should be obtained!
+ if (!m_soundcard.size()) m_soundcard = "rsnddai0ak4613h";
+
+ m_call = m_client.call(m_soundcard, "ctlset", value);
+ connect(m_call.data(), SIGNAL(closed()), this, SLOT(onCallCtlSet()));
+}
+
+void MasterVolume::onAfbClientConnected()
+{
+ qDebug() << "MasterVolume::onAfbClientConnected()";
+ m_call = m_client.call("alsacore", "hallist");
+ connect(m_call.data(), SIGNAL(closed()), this, SLOT(onCallHalListClosed()));
+}
+
+void MasterVolume::onAfbClientDisconnected()
+{
+ qDebug() << "MasterVolume::onAfbClientDisconnected()";
+}
+
+void MasterVolume::onCallHalListClosed()
+{
+ qDebug() << "MasterVolume::onCallHalListClosed()";
+ disconnect(m_call.data(), SIGNAL(closed()), this, SLOT(onCallHalListClosed()));
+ if (m_call->messageType() == AfMsgType::RetOk && m_call->value().isObject())
+ {
+ m_soundcard = m_call->value().toObject()["response"].toArray()[0].toObject()["api"].toString();
+ qDebug() << "MasterVolume::onCallHalListClosed() - Found soundcard api:" << m_soundcard;
+ }
+ //m_call.clear();
+
+ // FIXME: card should be obtained!
+ if (!m_soundcard.size()) m_soundcard = "rsnddai0ak4613h";
+
+ // Get volume
+ if (m_soundcard.size())
+ {
+ QJsonObject arg;
+ arg["label"] = "Master_Playback_Volume";
+ m_call = m_client.call(m_soundcard, "ctlget", arg);
+ connect(m_call.data(), SIGNAL(closed()), this, SLOT(onCallCtlGet()));
+ }
+}
+
+void MasterVolume::onCallCtlGet()
+{
+ qDebug() << "MasterVolume::onCallCtlGet()";
+ disconnect(m_call.data(), SIGNAL(closed()), this, SLOT(onCallCtlGet()));
+ if (m_call->messageType() == AfMsgType::RetOk && m_call->value().isObject())
+ {
+ setVolume(m_call->value().toObject()["response"].toObject()["val"].toArray()[0].toInt());
+ }
+ //m_call.clear();
}
-void MasterVolume::changeExternalVolume(int volume)
+void MasterVolume::onCallCtlSet()
{
- m_volume = volume;
- emit volumeChanged();
+ qDebug() << "MasterVolume::onCallCtlSet()";
+ if (m_call.data())
+ {
+ disconnect(m_call.data(), SIGNAL(closed()), this, SLOT(onCallCtlSet()));
+ if (m_call->messageType() == AfMsgType::RetOk && m_call->value().isObject())
+ {
+ m_volume = m_volumePending;
+ emit volumeChanged(m_volume);
+ }
+ //m_call.clear();
+ }
}
diff --git a/homescreen/src/mastervolume.h b/homescreen/src/mastervolume.h
index 3536e58..402b2e5 100644
--- a/homescreen/src/mastervolume.h
+++ b/homescreen/src/mastervolume.h
@@ -16,6 +16,10 @@
#include <QtCore/QObject>
#include <QQmlEngine>
+#include <QSharedPointer>
+
+#include "qafbwsclient.h"
+#include "qafbwsmsg.h"
class MasterVolume
: public QObject
@@ -24,24 +28,30 @@ class MasterVolume
Q_PROPERTY (uint32_t volume READ getVolume WRITE setVolume NOTIFY volumeChanged)
public:
- MasterVolume(QObject *parent = 0)
- : QObject(parent), m_volume(32768)
- {
- }
+ explicit MasterVolume(QObject* parent = nullptr);
+ ~MasterVolume();
- ~MasterVolume() {}
+ Q_INVOKABLE void init(quint16 port, QString token);
- uint32_t getVolume() const { return m_volume; }
- void setVolume(uint32_t volume);
+ uint32_t getVolume() const;
+ signals:
+ void volumeChanged(uint32_t volume);
public slots:
- void changeExternalVolume(int volume);
+ void setVolume(uint32_t volume);
- signals:
- void volumeChanged(void);
- void sliderVolumeChanged(int volume_delta);
- void externalVolumeChanged(uint32_t volume);
+ private slots:
+ void onAfbClientConnected();
+ void onAfbClientDisconnected();
+ void onCallHalListClosed();
+ void onCallCtlGet();
+ void onCallCtlSet();
private:
uint32_t m_volume;
+ uint32_t m_volumePending;
+ QString m_soundcard;
+
+ QAfbWsClient m_client;
+ QSharedPointer<QAfbWsMsg> m_call;
};
diff --git a/homescreen/src/qafbwsclient.cpp b/homescreen/src/qafbwsclient.cpp
new file mode 100644
index 0000000..2816907
--- /dev/null
+++ b/homescreen/src/qafbwsclient.cpp
@@ -0,0 +1,136 @@
+#include "qafbwsclient.h"
+#include "qafbwsmsg.h"
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QtDebug>
+
+QAfbWsClient::QAfbWsClient(QObject* parent)
+ : QObject{parent}
+ , m_nextCallId{0}
+{
+ connect(&m_socket, SIGNAL(connected()), this, SLOT(onSocketConnected()));
+ connect(&m_socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
+ connect(&m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
+}
+
+QUrl QAfbWsClient::url() const
+{
+ return m_socket.requestUrl();
+}
+
+QSharedPointer<QAfbWsMsg> QAfbWsClient::call(const QString& api, const QString& verb, const QJsonValue& args)
+{
+ while(m_calls.constFind(m_nextCallId) != m_calls.cend()) m_nextCallId++; // Make sure that callId is not currently used
+
+ qDebug() << "QAfbWsClient::call(" << api << ", " << verb << ", " << args << ")";
+ QJsonArray arr;
+ arr.append(static_cast<int>(AfMsgType::Call));
+ arr.append(m_nextCallId);
+ arr.append(api + "/" + verb);
+ arr.append(args);
+
+ QJsonDocument doc;
+ doc.setArray(arr);
+
+ QSharedPointer<QAfbWsMsg> msg(new QAfbWsMsg(m_nextCallId, api, verb));
+ m_calls[m_nextCallId] = msg;
+
+ // TODO: handle failure of sendTextMessage
+ m_socket.sendTextMessage(doc.toJson(QJsonDocument::Compact));
+ qDebug() << "m_socket.sendTextMessage(" << doc.toJson(QJsonDocument::Compact) << ")";
+
+ m_nextCallId++;
+
+ return msg;
+}
+
+void QAfbWsClient::onSocketConnected()
+{
+ qDebug() << "QAfbWsClient::onSocketConnected()";
+ connect(&m_socket, SIGNAL(textMessageReceived(QString)), this, SLOT(onSocketTextMessageReceived(QString)));
+ emit connected();
+}
+
+void QAfbWsClient::onSocketDisconnected()
+{
+ qDebug() << "QAfbWsClient::onSocketDisconnected()";
+ m_nextCallId = 0;
+ disconnect(&m_socket, SIGNAL(textMessageReceived(QString)), this, SLOT(onSocketTextMessageReceived(QString)));
+ emit disconnected();
+}
+
+void QAfbWsClient::onSocketError(QAbstractSocket::SocketError err)
+{
+ qDebug() << "QAfbWsClient::onSocketError(" << err << ")";
+ emit error(err, m_socket.errorString());
+}
+
+void QAfbWsClient::onSocketTextMessageReceived(QString msg)
+{
+ qDebug() << "QAfbWsClient::onSocketTextMessageReceived(" << msg << ")";
+
+ if (msg.size() == 0)
+ {
+ qCritical() << "QAfbWsClient::onSocketTextMessageReceived: received an empty message.";
+ return;
+ }
+
+ QJsonDocument doc = QJsonDocument::fromJson(msg.toUtf8());
+ if (doc.isEmpty() || doc.isNull() || !doc.isArray())
+ {
+ qCritical() << "QAfbWsClient::onSocketTextMessageReceived: received an invalid message.";
+ return;
+ }
+
+ QJsonArray arr = doc.array();
+ AfMsgType msgType = static_cast<AfMsgType>(arr[0].toInt());
+ int callId = arr[1].isString() ? arr[1].toString().toInt() : arr[1].toInt();
+ QString api;
+ QJsonValue value;
+
+ switch(msgType)
+ {
+ case AfMsgType::Call:
+ qCritical() << "QAfbWsClient::onSocketTextMessageReceived: Client received a call, which should not happen.";
+ break;
+
+ case AfMsgType::Event:
+ qDebug() << "QAfbWsClient::onSocketTextMessageReceived: Client received an event.";
+ // TODO: handle events
+ value = arr[3];
+ break;
+
+ case AfMsgType::RetOk:
+ case AfMsgType::RetErr:
+ {
+ value = arr[2];
+ QMap<int, QSharedPointer<QAfbWsMsg>>::const_iterator it = m_calls.find(callId);
+ if (it == m_calls.end())
+ {
+ qCritical() << "QAfbWsClient::onSocketTextMessageReceived: received a response to a not found query.";
+ return;
+ }
+
+ QSharedPointer<QAfbWsMsg> m = *it;
+ m_calls.remove(callId);
+ m->close(msgType, value);
+ }
+ break;
+
+ default:
+ qCritical() << "QAfbWsClient::onSocketTextMessageReceived: Client received an unsupported message.";
+ break;
+ }
+}
+
+void QAfbWsClient::open(const QUrl& url)
+{
+ qDebug() << "QAfbWsClient::open(" << url << ")";
+ m_socket.open(url);
+}
+
+void QAfbWsClient::close()
+{
+ qDebug() << "QAfbWsClient::close()";
+ m_socket.close();
+}
diff --git a/homescreen/src/qafbwsclient.h b/homescreen/src/qafbwsclient.h
new file mode 100644
index 0000000..2576ecb
--- /dev/null
+++ b/homescreen/src/qafbwsclient.h
@@ -0,0 +1,50 @@
+#ifndef QAFBWSCLIENT_H
+#define QAFBWSCLIENT_H
+
+#include <QObject>
+#include <QMap>
+#include <QSharedPointer>
+#include <QWebSocket>
+#include <QJsonValue>
+
+enum class AfMsgType
+ : int
+{
+ Call = 2,
+ RetOk = 3,
+ RetErr = 4,
+ Event = 5
+};
+
+class QAfbWsMsg;
+
+class QAfbWsClient
+ : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QAfbWsClient(QObject* parent = nullptr);
+
+ void open(const QUrl& url);
+ void close();
+ QUrl url() const;
+ QSharedPointer<QAfbWsMsg> call(const QString &api, const QString &verb, const QJsonValue &args = QJsonValue());
+
+signals:
+ void connected();
+ void disconnected();
+ void error(QAbstractSocket::SocketError err, QString errStr);
+
+private slots:
+ void onSocketConnected();
+ void onSocketDisconnected();
+ void onSocketError(QAbstractSocket::SocketError err);
+ void onSocketTextMessageReceived(QString msg);
+
+private:
+ int m_nextCallId;
+ QWebSocket m_socket;
+ QMap<int, QSharedPointer<QAfbWsMsg>> m_calls;
+};
+
+#endif // QAFBWSCLIENT_H
diff --git a/homescreen/src/qafbwsmsg.cpp b/homescreen/src/qafbwsmsg.cpp
new file mode 100644
index 0000000..3f07cb0
--- /dev/null
+++ b/homescreen/src/qafbwsmsg.cpp
@@ -0,0 +1,44 @@
+#include "qafbwsmsg.h"
+
+QAfbWsMsg::QAfbWsMsg(int callId, const QString& api, const QString& verb, QObject* parent)
+ : QObject{parent}
+ , m_callId{callId}
+{
+ m_api = api;
+ m_verb = verb;
+}
+
+void QAfbWsMsg::close(AfMsgType type, const QJsonValue& result)
+{
+ m_type = type;
+ m_value = result;
+
+ qDebug() << "QAfbWsMsg::close: type=" << static_cast<int>(m_type) << ", api=" << m_api << ", verb=" << m_verb << ", value=" << m_value;
+
+ emit closed();
+}
+
+int QAfbWsMsg::callId() const
+{
+ return m_callId;
+}
+
+AfMsgType QAfbWsMsg::messageType() const
+{
+ return m_type;
+}
+
+QString QAfbWsMsg::api() const
+{
+ return m_api;
+}
+
+QString QAfbWsMsg::verb() const
+{
+ return m_verb;
+}
+
+QJsonValue QAfbWsMsg::value() const
+{
+ return m_value;
+}
diff --git a/homescreen/src/qafbwsmsg.h b/homescreen/src/qafbwsmsg.h
new file mode 100644
index 0000000..6acce59
--- /dev/null
+++ b/homescreen/src/qafbwsmsg.h
@@ -0,0 +1,33 @@
+#ifndef QAFBWSMSG_H
+#define QAFBWSMSG_H
+
+#include <QObject>
+#include <QJsonValue>
+#include "qafbwsclient.h"
+
+class QAfbWsMsg
+ : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QAfbWsMsg(int callId = 0, const QString& api = "", const QString& verb = "", QObject* parent = nullptr);
+ void close(AfMsgType type, const QJsonValue& result);
+
+ int callId() const;
+ AfMsgType messageType() const;
+ QString api() const;
+ QString verb() const;
+ QJsonValue value() const;
+
+signals:
+ void closed();
+
+private:
+ int m_callId;
+ AfMsgType m_type;
+ QString m_api;
+ QString m_verb;
+ QJsonValue m_value;
+};
+
+#endif // QAFBWSMSG_H
diff --git a/package/config.xml b/package/config.xml
index 885473c..c3f8846 100644
--- a/package/config.xml
+++ b/package/config.xml
@@ -9,6 +9,9 @@
<feature name="urn:AGL:widget:required-api">
<param name="homescreen" value="ws" />
<param name="windowmanager" value="ws" />
+ <param name="ahl-4a" value="ws" />
+ <param name="alsacore" value="ws" />
+ <param name="rsnddai0ak4613h" value="ws" />
</feature>
<feature name="urn:AGL:widget:required-permission">
<param name="urn:AGL:permission::public:no-htdocs" value="required" />