diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/Radio.qml | 65 | ||||
-rw-r--r-- | app/api/Binding.qml | 196 | ||||
-rw-r--r-- | app/main.cpp | 28 | ||||
-rw-r--r-- | app/radio.qrc | 1 |
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> |