summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Radio.qml65
-rw-r--r--app/api/Binding.qml196
-rw-r--r--app/main.cpp28
-rw-r--r--app/radio.qrc1
4 files changed, 270 insertions, 20 deletions
diff --git a/app/Radio.qml b/app/Radio.qml
index 8bcd56e..f812af1 100644
--- a/app/Radio.qml
+++ b/app/Radio.qml
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (C) 2017 Konsulko Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,29 +18,23 @@
import QtQuick 2.6
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
-import QtMultimedia 5.5
import AGL.Demo.Controls 1.0
+import 'api' as API
ApplicationWindow {
id: root
- Radio {
+ API.Binding {
id: radio
+
property string title
+
onBandChanged: frequency = minimumFrequency
onStationFound: title = stationId
onFrequencyChanged: {
title = ''
slider.value = frequency
}
-
- function freq2str(freq) {
- if (freq > 5000000) {
- return '%1 MHz'.arg((freq / 1000000).toFixed(1))
- } else {
- return '%1 kHz'.arg((freq / 1000).toFixed(0))
- }
- }
}
ColumnLayout {
@@ -82,7 +77,7 @@ ApplicationWindow {
// offImage: './images/FM_Icons_FM.svg'
// onImage: './images/FM_Icons_AM.svg'
// onCheckedChanged: {
-// radio.band = checked ? Radio.AM : Radio.FM
+// radio.band = checked ? radio.amBand : radio.fmBand
// radio.frequency = radio.minimumFrequency
// }
// }
@@ -129,7 +124,11 @@ ApplicationWindow {
}
RowLayout {
Layout.fillHeight: true
- Item { Layout.fillWidth: true }
+
+ Label {
+ text: 'TUNE'
+ }
+
ImageButton {
offImage: './images/AGL_MediaPlayer_BackArrow.svg'
Timer {
@@ -140,13 +139,27 @@ ApplicationWindow {
onTriggered: radio.tuneDown()
}
}
+
+ ImageButton {
+ offImage: './images/AGL_MediaPlayer_ForwardArrow.svg'
+ Timer {
+ running: parent.pressed
+ triggeredOnStart: true
+ interval: 100
+ repeat: true
+ onTriggered: radio.tuneUp()
+ }
+ }
+
+ Item { Layout.fillWidth: true }
+
ImageButton {
id: play
offImage: './images/AGL_MediaPlayer_Player_Play.svg'
onClicked: radio.start()
states: [
State {
- when: radio.state === Radio.ActiveState
+ when: radio.state === radio.activeState
PropertyChanges {
target: play
offImage: './images/AGL_MediaPlayer_Player_Pause.svg'
@@ -155,6 +168,25 @@ ApplicationWindow {
}
]
}
+
+ Item { Layout.fillWidth: true }
+
+ Label {
+ //Layout.fillWidth: true
+ text: 'SCAN'
+ }
+
+ ImageButton {
+ offImage: './images/AGL_MediaPlayer_BackArrow.svg'
+ Timer {
+ running: parent.pressed
+ triggeredOnStart: true
+ interval: 100
+ repeat: true
+ onTriggered: radio.scanDown()
+ }
+ }
+
ImageButton {
offImage: './images/AGL_MediaPlayer_ForwardArrow.svg'
Timer {
@@ -162,11 +194,10 @@ ApplicationWindow {
triggeredOnStart: true
interval: 100
repeat: true
- onTriggered: radio.tuneUp()
+ onTriggered: radio.scanUp()
}
}
- Item { Layout.fillWidth: true }
}
}
}
@@ -214,9 +245,9 @@ ApplicationWindow {
Image {
source: {
switch (model.modelData.band) {
- case Radio.FM:
+ case radio.fmBand:
return './images/FM_Icons_FM.svg'
- case Radio.AM:
+ case radio.amBand:
return './images/FM_Icons_AM.svg'
}
return null
diff --git a/app/api/Binding.qml b/app/api/Binding.qml
new file mode 100644
index 0000000..3b43510
--- /dev/null
+++ b/app/api/Binding.qml
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (C) 2017 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.
+ */
+
+import QtQuick 2.6
+import QtWebSockets 1.0
+
+WebSocket {
+ id: root
+ active: true
+ url: bindingAddress
+
+ property string apiString: "radio"
+ property var verbs: []
+ property string payloadLength: "9999"
+
+ readonly property var msgid: {
+ "call": 2,
+ "retok": 3,
+ "reterr": 4,
+ "event": 5
+ }
+
+ readonly property int amBand: 0
+ readonly property int fmBand: 1
+
+ readonly property int stoppedState: 0
+ readonly property int activeState: 1
+
+ property int band: fmBand
+ property int frequency
+ property int frequencyStep
+ property int minimumFrequency
+ property int maximumFrequency
+ property int state: stoppedState
+ property int scanningState: stoppedState
+ property bool scanningFreqUpdate: false
+ property string stationId: ""
+
+ signal stationFound
+
+ property Connections c : Connections {
+ target: root
+
+ onFrequencyChanged: {
+ if(scanningState != activeState) {
+ // Not scanning, push update
+ sendSocketMessage("frequency", { value: frequency })
+ } else if(!scanningFreqUpdate) {
+ // External change, stop scanning
+ sendSocketMessage("scan_stop", 'None')
+ scanningState = stoppedState
+ sendSocketMessage("frequency", { value: frequency })
+ } else {
+ // This update was from scanning, clear state
+ scanningFreqUpdate = false
+ }
+ }
+
+ onBandChanged: {
+ sendSocketMessage("band", { value: band })
+ updateFrequencyRange(band)
+ updateFrequencyStep(band)
+ frequency = minimumFrequency
+ }
+ }
+
+ onTextMessageReceived: {
+ var json = JSON.parse(message)
+ //console.debug("Raw response: " + message)
+ var request = json[2].request
+ var response = json[2].response
+
+ switch (json[0]) {
+ case msgid.call:
+ break
+ case msgid.retok:
+ var verb = verbs.shift()
+ if (verb == "frequency_range") {
+ minimumFrequency = response.min
+ maximumFrequency = response.max
+ } else if (verb == "frequency_step") {
+ frequencyStep = response.step
+ }
+ break
+ case msgid.event:
+ var event = JSON.parse(JSON.stringify(json[2]))
+ if (event.event === "radio/frequency") {
+ if(scanningState == activeState) {
+ scanningFreqUpdate = true
+ frequency = event.data.value
+ }
+ } else if (event.event === "radio/station_found") {
+ if(scanningState == activeState) {
+ scanningState = stoppedState
+ stationId = freq2str(event.data.value)
+ root.stationFound()
+ }
+ }
+ break
+ case msg.reterr:
+ console.debug("Bad return value, binding probably not installed")
+ break
+ case MessageId.event:
+ break
+ }
+ }
+
+ onStatusChanged: {
+ switch (status) {
+ case WebSocket.Open:
+ // Initialize band values now that we're connected to the
+ // binding
+ updateFrequencyRange(band)
+ updateFrequencyStep(band)
+ frequency = minimumFrequency
+ sendSocketMessage("subscribe", { value: "frequency" })
+ sendSocketMessage("subscribe", { value: "station_found" })
+ break
+ case WebSocket.Error:
+ console.debug("WebSocket error: " + root.errorString)
+ break
+ }
+ }
+
+ function freq2str(freq) {
+ if (freq > 5000000) {
+ return '%1 MHz'.arg((freq / 1000000).toFixed(1))
+ } else {
+ return '%1 kHz'.arg((freq / 1000).toFixed(0))
+ }
+ }
+
+ function sendSocketMessage(verb, parameter) {
+ var requestJson = [ msgid.call, payloadLength, apiString + '/'
+ + verb, parameter ]
+ //console.debug("sendSocketMessage: " + JSON.stringify(requestJson))
+ verbs.push(verb)
+ sendTextMessage(JSON.stringify(requestJson))
+ }
+
+ function start() {
+ sendSocketMessage("start", 'None')
+ state = activeState
+ }
+
+ function stop() {
+ sendSocketMessage("stop", 'None')
+ state = stoppedState
+ }
+
+ function tuneUp() {
+ frequency += frequencyStep
+ if(frequency > maximumFrequency) {
+ frequency = minimumFrequency
+ }
+ }
+
+ function tuneDown() {
+ frequency -= frequencyStep
+ if(frequency < minimumFrequency) {
+ frequency = maximumFrequency
+ }
+ }
+
+ function scanUp() {
+ scanningState = activeState
+ sendSocketMessage("scan_start", { direction: "forward" })
+ }
+
+ function scanDown() {
+ scanningState = activeState
+ sendSocketMessage("scan_start", { direction: "backward" })
+ }
+
+ function updateFrequencyRange(band) {
+ sendSocketMessage("frequency_range", { band: band })
+ }
+
+ function updateFrequencyStep(band) {
+ sendSocketMessage("frequency_step", { band: band })
+ }
+}
diff --git a/app/main.cpp b/app/main.cpp
index 9d2785e..e430099 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016 The Qt Company Ltd.
- * Copyright (C) 2016 Scott Murray <scott.murray@konsulko.com>
+ * Copyright (C) 2016, 2017 Konsulko Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,13 @@
*/
#include <QtCore/QDebug>
+#include <QtCore/QCommandLineParser>
+#include <QtCore/QUrlQuery>
#include <QtCore/QSettings>
#include <QtGui/QGuiApplication>
#include <QtQml/QQmlApplicationEngine>
#include <QtQml/QQmlContext>
#include <QtQuickControls2/QQuickStyle>
-#include <QtMultimedia/QRadioTunerControl>
#include <stdlib.h>
#include "PresetDataObject.h"
@@ -45,6 +46,14 @@ int main(int argc, char *argv[])
QQuickStyle::setStyle("AGL");
+ QCommandLineParser parser;
+ parser.addPositionalArgument("port", app.translate("main", "port for binding"));
+ parser.addPositionalArgument("secret", app.translate("main", "secret for binding"));
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.process(app);
+ QStringList positionalArguments = parser.positionalArguments();
+
// Read presets from configuration file
//
// If HOME is set, use $HOME/app-data/radio/presets.conf, else fall back
@@ -70,13 +79,26 @@ int main(int argc, char *argv[])
pSettings->setArrayIndex(i);
presetDataList.append(new PresetDataObject(pSettings->value("title").toString(),
pSettings->value("frequency").toInt(),
- QRadioTuner::FM));
+ 1));
}
pSettings->endArray();
QQmlApplicationEngine engine;
QQmlContext *context = engine.rootContext();
context->setContextProperty("presetModel", QVariant::fromValue(presetDataList));
+ if (positionalArguments.length() == 2) {
+ int port = positionalArguments.takeFirst().toInt();
+ QString secret = positionalArguments.takeFirst();
+ QUrl bindingAddress;
+ bindingAddress.setScheme(QStringLiteral("ws"));
+ bindingAddress.setHost(QStringLiteral("localhost"));
+ bindingAddress.setPort(port);
+ bindingAddress.setPath(QStringLiteral("/api"));
+ QUrlQuery query;
+ query.addQueryItem(QStringLiteral("token"), secret);
+ bindingAddress.setQuery(query);
+ context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress);
+ }
engine.load(QUrl(QStringLiteral("qrc:/Radio.qml")));
return app.exec();
diff --git a/app/radio.qrc b/app/radio.qrc
index 347894c..38ce4f8 100644
--- a/app/radio.qrc
+++ b/app/radio.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>Radio.qml</file>
+ <file>api/Binding.qml</file>
</qresource>
</RCC>