Finishing touches

This commit is contained in:
Michael Zanetti 2020-12-14 16:28:56 +01:00
parent 3c41a7090b
commit 82454fc554
33 changed files with 420 additions and 180 deletions

View File

@ -79,6 +79,8 @@ QVariant LogsModel::data(const QModelIndex &index, int role) const
return m_list.at(index.row())->source();
case RoleLoggingEventType:
return m_list.at(index.row())->loggingEventType();
case RoleErrorCode:
return m_list.at(index.row())->errorCode();
}
return QVariant();
}
@ -93,6 +95,7 @@ QHash<int, QByteArray> LogsModel::roleNames() const
roles.insert(RoleTypeId, "typeId");
roles.insert(RoleSource, "source");
roles.insert(RoleLoggingEventType, "loggingEventType");
roles.insert(RoleErrorCode, "errorCode");
return roles;
}
@ -229,7 +232,8 @@ void LogsModel::logsReply(int /*commandId*/, const QVariantMap &data)
QMetaEnum loggingEventTypeEnum = QMetaEnum::fromType<LogEntry::LoggingEventType>();
LogEntry::LoggingEventType loggingEventType = static_cast<LogEntry::LoggingEventType>(loggingEventTypeEnum.keyToValue(entryMap.value("eventType").toByteArray()));
QVariant value = loggingEventType == LogEntry::LoggingEventTypeActiveChange ? entryMap.value("active").toBool() : entryMap.value("value");
LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, this);
QString errorCode = entryMap.value("errorCode").toString();
LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, errorCode, this);
newBlock.append(entry);
}
@ -371,7 +375,7 @@ void LogsModel::newLogEntryReceived(const QVariantMap &data)
QMetaEnum loggingEventTypeEnum = QMetaEnum::fromType<LogEntry::LoggingEventType>();
LogEntry::LoggingEventType loggingEventType = static_cast<LogEntry::LoggingEventType>(loggingEventTypeEnum.keyToValue(entryMap.value("eventType").toByteArray()));
QVariant value = loggingEventType == LogEntry::LoggingEventTypeActiveChange ? entryMap.value("active").toBool() : entryMap.value("value");
LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, this);
LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, entryMap.value("errorCode").toString(), this);
m_list.prepend(entry);
endInsertRows();
emit countChanged();

View File

@ -61,7 +61,8 @@ public:
RoleDeviceId, // < JSONRPC 5.0
RoleTypeId,
RoleSource,
RoleLoggingEventType
RoleLoggingEventType,
RoleErrorCode
};
explicit LogsModel(QObject *parent = nullptr);

View File

@ -261,7 +261,7 @@ void LogsModelNg::logsReply(int commandId, const QVariantMap &data)
QMetaEnum loggingEventTypeEnum = QMetaEnum::fromType<LogEntry::LoggingEventType>();
LogEntry::LoggingEventType loggingEventType = static_cast<LogEntry::LoggingEventType>(loggingEventTypeEnum.keyToValue(entryMap.value("eventType").toByteArray()));
QVariant value = loggingEventType == LogEntry::LoggingEventTypeActiveChange ? entryMap.value("active").toBool() : entryMap.value("value");
LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, this);
LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, entryMap.value("errorCode").toString(), this);
newBlock.append(entry);
}
@ -461,7 +461,7 @@ void LogsModelNg::newLogEntryReceived(const QVariantMap &data)
QMetaEnum loggingEventTypeEnum = QMetaEnum::fromType<LogEntry::LoggingEventType>();
LogEntry::LoggingEventType loggingEventType = static_cast<LogEntry::LoggingEventType>(loggingEventTypeEnum.keyToValue(entryMap.value("eventType").toByteArray()));
QVariant value = loggingEventType == LogEntry::LoggingEventTypeActiveChange ? entryMap.value("active").toBool() : entryMap.value("value");
LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, this);
LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, entryMap.value("errorCode").toString(), this);
m_list.prepend(entry);
if (m_graphSeries) {

View File

@ -167,12 +167,26 @@ State *Device::stateByName(const QString &stateName) const
return m_states->getState(st->id());
}
Param *Device::param(const QUuid &paramTypeId) const
{
return m_params->getParam(paramTypeId);
}
Param *Device::paramByName(const QString &paramName) const
{
ParamType *paramType = m_thingClass->paramTypes()->findByName(paramName);
if (!paramType) {
return nullptr;
}
return m_params->getParam(paramType->id());
}
DeviceClass *Device::thingClass() const
{
return m_thingClass;
}
bool Device::hasState(const QUuid &stateTypeId)
bool Device::hasState(const QUuid &stateTypeId) const
{
foreach (State *state, states()->states()) {
if (state->stateTypeId() == stateTypeId) {
@ -182,7 +196,7 @@ bool Device::hasState(const QUuid &stateTypeId)
return false;
}
QVariant Device::stateValue(const QUuid &stateTypeId)
QVariant Device::stateValue(const QUuid &stateTypeId) const
{
foreach (State *state, states()->states()) {
if (state->stateTypeId() == stateTypeId) {

View File

@ -92,15 +92,17 @@ public:
States *states() const;
void setStates(States *states);
void setStateValue(const QUuid &stateTypeId, const QVariant &value);
DeviceClass *thingClass() const;
Q_INVOKABLE bool hasState(const QUuid &stateTypeId);
Q_INVOKABLE bool hasState(const QUuid &stateTypeId) const;
Q_INVOKABLE State *state(const QUuid &stateTypeId) const;
Q_INVOKABLE State *stateByName(const QString &stateName) const;
Q_INVOKABLE QVariant stateValue(const QUuid &stateTypeId) const;
Q_INVOKABLE QVariant stateValue(const QUuid &stateTypeId);
void setStateValue(const QUuid &stateTypeId, const QVariant &value);
Q_INVOKABLE Param *param(const QUuid &paramTypeId) const;
Q_INVOKABLE Param *paramByName(const QString &paramName) const;
Q_INVOKABLE virtual int executeAction(const QString &actionName, const QVariantList &params);
@ -112,8 +114,6 @@ signals:
void statesChanged();
void eventTriggered(const QUuid &eventTypeId, const QVariantMap &params);
private:
protected:
DeviceManager *m_thingManager = nullptr;
QString m_name;

View File

@ -32,14 +32,15 @@
#include <QDateTime>
LogEntry::LogEntry(const QDateTime &timestamp, const QVariant &value, const QUuid &thingId, const QUuid &typeId, LoggingSource source, LoggingEventType loggingEventType, QObject *parent):
LogEntry::LogEntry(const QDateTime &timestamp, const QVariant &value, const QUuid &thingId, const QUuid &typeId, LoggingSource source, LoggingEventType loggingEventType, const QString &errorCode, QObject *parent):
QObject(parent),
m_value(value),
m_timeStamp(timestamp),
m_thingId(thingId),
m_typeId(typeId),
m_source(source),
m_loggingEventType(loggingEventType)
m_loggingEventType(loggingEventType),
m_errorCode(errorCode)
{
}
@ -104,3 +105,8 @@ QString LogEntry::dateString() const
{
return m_timeStamp.date().toString("dd.MM.");
}
QString LogEntry::errorCode() const
{
return m_errorCode;
}

View File

@ -50,6 +50,7 @@ class LogEntry : public QObject
Q_PROPERTY(QString timeString READ timeString CONSTANT)
Q_PROPERTY(QString dayString READ dayString CONSTANT)
Q_PROPERTY(QString dateString READ dateString CONSTANT)
Q_PROPERTY(QString errorCode READ errorCode CONSTANT)
public:
enum LoggingSource {
@ -71,7 +72,7 @@ public:
};
Q_ENUM(LoggingEventType)
explicit LogEntry(const QDateTime &timestamp, const QVariant &value, const QUuid &thingId = QUuid(), const QUuid &typeId = QUuid(), LoggingSource source = LoggingSourceSystem, LoggingEventType loggingEventType = LoggingEventTypeTrigger, QObject *parent = nullptr);
explicit LogEntry(const QDateTime &timestamp, const QVariant &value, const QUuid &thingId = QUuid(), const QUuid &typeId = QUuid(), LoggingSource source = LoggingSourceSystem, LoggingEventType loggingEventType = LoggingEventTypeTrigger, const QString &errorCode = QString(), QObject *parent = nullptr);
QVariant value() const;
QDateTime timestamp() const;
@ -83,6 +84,7 @@ public:
QString timeString() const;
QString dayString() const;
QString dateString() const;
QString errorCode() const;
private:
QVariant m_value;
@ -91,6 +93,7 @@ private:
QUuid m_typeId;
LoggingSource m_source;
LoggingEventType m_loggingEventType;
QString m_errorCode;
};
#endif // LOGENTRY_H

View File

@ -56,10 +56,10 @@ Param *Params::get(int index) const
return m_params.at(index);
}
Param *Params::getParam(QString paramTypeId) const
Param *Params::getParam(const QUuid &paramTypeId) const
{
foreach (Param *param, m_params) {
if (QUuid(param->paramTypeId()) == QUuid(paramTypeId)) {
if (param->paramTypeId() == paramTypeId) {
return param;
}
}

View File

@ -51,7 +51,7 @@ public:
Q_INVOKABLE int count() const;
Q_INVOKABLE Param *get(int index) const;
Q_INVOKABLE Param *getParam(QString paramTypeId) const;
Q_INVOKABLE Param *getParam(const QUuid &paramTypeId) const;
Q_INVOKABLE int paramCount() const;

View File

@ -37,16 +37,6 @@
#include <QCommandLineParser>
#include <QCommandLineOption>
#ifdef Q_OS_ANDROID
#include <QtAndroidExtras/QtAndroid>
#include "platformintegration/android/platformhelperandroid.h"
#elif defined(Q_OS_IOS)
#include "platformintegration/ios/platformhelperios.h"
#elif defined UBPORTS
#include "platformintegration/ubports/platformhelperubports.h"
#else
#include "platformintegration/generic/platformhelpergeneric.h"
#endif
#include "libnymea-app-core.h"
@ -56,21 +46,8 @@
#include "ruletemplates/messages.h"
#include "nfchelper.h"
#include "nfcthingactionwriter.h"
#include "platformhelper.h"
QObject *platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
#ifdef Q_OS_ANDROID
return new PlatformHelperAndroid();
#elif defined(Q_OS_IOS)
return new PlatformHelperIOS();
#elif defined UBPORTS
return new PlatformHelperUBPorts();
#else
return new PlatformHelperGeneric();
#endif
}
int main(int argc, char *argv[])
{
@ -159,7 +136,7 @@ int main(int argc, char *argv[])
engine->rootContext()->setContextProperty("styleController", &styleController);
qmlRegisterSingletonType<PlatformHelper>("Nymea", 1, 0, "PlatformHelper", platformHelperProvider);
qmlRegisterSingletonType<PlatformHelper>("Nymea", 1, 0, "PlatformHelper", PlatformHelper::platformHelperProvider);
qmlRegisterSingletonType<NfcHelper>("Nymea", 1, 0, "NfcHelper", NfcHelper::nfcHelperProvider);
qmlRegisterType<NfcThingActionWriter>("Nymea", 1, 0, "NfcThingActionWriter");

View File

@ -33,11 +33,40 @@
#include <QApplication>
#include <QClipboard>
#if defined Q_OS_ANDROID
#include <QtAndroidExtras/QtAndroid>
#include "platformintegration/android/platformhelperandroid.h"
#elif defined Q_OS_IOS
#include "platformintegration/ios/platformhelperios.h"
#elif defined UBPORTS
#include "platformintegration/ubports/platformhelperubports.h"
#else
#include "platformintegration/generic/platformhelpergeneric.h"
#endif
PlatformHelper* PlatformHelper::s_instance = nullptr;
PlatformHelper::PlatformHelper(QObject *parent) : QObject(parent)
{
}
PlatformHelper *PlatformHelper::instance()
{
if (!s_instance) {
#ifdef Q_OS_ANDROID
s_instance = new PlatformHelperAndroid();
#elif defined(Q_OS_IOS)
s_instance = new PlatformHelperIOS();
#elif defined UBPORTS
s_instance = new PlatformHelperUBPorts();
#else
s_instance = new PlatformHelperGeneric();
#endif
}
return s_instance;
}
bool PlatformHelper::hasPermissions() const
{
return true;
@ -153,3 +182,10 @@ QString PlatformHelper::fromClipBoard()
{
return QApplication::clipboard()->text();
}
QObject *PlatformHelper::platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return instance();
}

View File

@ -34,6 +34,9 @@
#include <QObject>
#include <QColor>
class QQmlEngine;
class QJSEngine;
class PlatformHelper : public QObject
{
Q_OBJECT
@ -58,7 +61,7 @@ public:
};
Q_ENUM(HapticsFeedback)
explicit PlatformHelper(QObject *parent = nullptr);
static PlatformHelper* instance();
virtual ~PlatformHelper() = default;
virtual bool hasPermissions() const;
@ -88,6 +91,7 @@ public:
Q_INVOKABLE virtual void toClipBoard(const QString &text);
Q_INVOKABLE virtual QString fromClipBoard();
static QObject *platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
signals:
void permissionsRequestFinished();
void screenTimeoutChanged();
@ -95,7 +99,12 @@ signals:
void topPanelColorChanged();
void bottomPanelColorChanged();
protected:
explicit PlatformHelper(QObject *parent = nullptr);
private:
static PlatformHelper *s_instance;
QColor m_topPanelColor = QColor("black");
QColor m_bottomPanelColor = QColor("black");
};

View File

@ -1,5 +1,8 @@
#include "platformhelperubports.h"
#include <QSettings>
#include <QUuid>
PlatformHelperUBPorts::PlatformHelperUBPorts(QObject *parent) : PlatformHelper(parent)
{
@ -9,3 +12,9 @@ QString PlatformHelperUBPorts::platform() const
{
return "ubports";
}
QString PlatformHelperUBPorts::deviceSerial() const
{
QSettings s;
return s.value("deviceSerial", QUuid::createUuid()).toString();
}

View File

@ -12,6 +12,7 @@ public:
explicit PlatformHelperUBPorts(QObject *parent = nullptr);
QString platform() const override;
QString deviceSerial() const override;
signals:

View File

@ -29,6 +29,7 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "pushnotifications.h"
#include "platformhelper.h"
#include <QDebug>
@ -87,6 +88,27 @@ PushNotifications *PushNotifications::instance()
return pushNotifications;
}
QString PushNotifications::service() const
{
#if defined Q_OS_ANDROID
return "FB-GCM";
#elif defined Q_OS_IOS
return "FB-APNs";
#elif defined UBPORTS
return "ubports";
#endif
return QString();
}
QString PushNotifications::clientId() const
{
QString branding;
#if defined BRANDING
branding = "-" + BRANDING;
#endif
return PlatformHelper::instance()->deviceSerial() + "+io.guh.nymeaapp" + branding;
}
QString PushNotifications::token() const
{
return m_token;

View File

@ -51,6 +51,8 @@ class PushNotifications : public QObject
#endif
{
Q_OBJECT
Q_PROPERTY(QString service READ service CONSTANT)
Q_PROPERTY(QString clientId READ clientId CONSTANT)
Q_PROPERTY(QString token READ token NOTIFY tokenChanged)
public:
@ -60,6 +62,8 @@ public:
static QObject* pushNotificationsProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
static PushNotifications* instance();
QString service() const;
QString clientId() const;
QString token() const;
// Called by Objective-C++

View File

@ -232,5 +232,6 @@
<file>ui/StyleBase.qml</file>
<file>ui/customviews/ThermostatController.qml</file>
<file>ui/devicepages/ThermostatDevicePage.qml</file>
<file>ui/components/BigThingTile.qml</file>
</qresource>
</RCC>

View File

@ -32,8 +32,8 @@
]
},
{
"description": "Notify me when something runs dry",
"ruleNameTemplate": "Notify %1 when %0 runs dry",
"description": "Notify me when something dries ou",
"ruleNameTemplate": "Notify %1 when %0 dries out",
"stateEvaluatorTemplate": {
"stateDescriptorTemplate": {
"interfaceName": "moisturesensor",

View File

@ -262,6 +262,7 @@ Item {
return false;
}
// Old nymea:cloud based push notifications...
function setupPushNotifications(askForPermissions) {
if (askForPermissions === undefined) {
askForPermissions = true;
@ -296,6 +297,46 @@ Item {
}
}
// New, nymea thing based push notifactions
function updatePushNotificationThings() {
if (PushNotifications.service == "") {
print("This platform does not support push notifications")
return;
}
if (!PushNotifications.token) {
print("No push notification token available at this time. Not updating...");
return;
}
print("Updating push notifications")
print("Own push service:", PushNotifications.service);
print("Own client ID:", PushNotifications.clientId);
print("Current token:", PushNotifications.token);
for (var i = 0; i < engine.thingManager.things.count; i++) {
var thing = engine.thingManager.things.get(i);
if (thing.thingClass.id.toString().match(/\{?f0dd4c03-0aca-42cc-8f34-9902457b05de\}?/)) {
var serviceParam = thing.paramByName("service");
var clientIdParam = thing.paramByName("clientId")
var tokenParam = thing.paramByName("token")
print("Found a push notification thing for client id:", clientIdParam.value)
if (clientIdParam.value === PushNotifications.clientId) {
if (tokenParam.value !== PushNotifications.token) {
var params = [
{ "paramTypeId": serviceParam.paramTypeId, "value": PushNotifications.service },
{ "paramTypeId": clientIdParam.paramTypeId, "value": PushNotifications.clientId },
{ "paramTypeId": tokenParam.paramTypeId, "value": PushNotifications.token }
];
print("Reconfiguring PushNotifications for", thing.name)
engine.thingManager.reconfigureDevice(thing.id, params);
} else {
print("Push notifications don't need to be updated. Token is valid.")
}
}
}
}
}
Connections {
target: engine.jsonRpcClient
onCurrentHostChanged: {
@ -370,13 +411,22 @@ Item {
}
}
Connections {
target: engine.thingManager
onFetchingDataChanged: {
if (!engine.thingManager.fetchingData) {
updatePushNotificationThings()
}
}
}
Component {
id: invalidVersionComponent
Popup {
id: popup
property string actualVersion: "0.0"
property string minimumVersion: "1.0"
property string minimumVersion: "1.10"
width: app.width * .8
height: col.childrenRect.height + app.margins * 2

View File

@ -0,0 +1,36 @@
import QtQuick 2.9
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.1
import Nymea 1.0
BigTile {
id: root
property Thing thing: null
readonly property State connectedState: thing.stateByName("connected")
readonly property bool isConnected: connectedState === null || connectedState.value === true
readonly property bool isEnabled: thing.setupStatus == Thing.ThingSetupStatusComplete && isConnected
onPressAndHold: {
var contextMenuComponent = Qt.createComponent("../components/ThingContextMenu.qml");
var contextMenu = contextMenuComponent.createObject(root, { thing: root.thing })
contextMenu.x = Qt.binding(function() { return (root.width - contextMenu.width) / 2 })
contextMenu.open()
}
header: RowLayout {
id: headerRow
visible: root.showHeader
width: parent.width
Layout.margins: app.margins / 2
Label {
Layout.fillWidth: true
text: root.thing.name
elide: Text.ElideRight
}
ThingStatusIcons {
thing: root.thing
}
}
}

View File

@ -4,25 +4,20 @@ import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import Nymea 1.0
Item {
Item {
id: root
implicitHeight: layout.implicitHeight + app.margins
property Thing thing: null
property bool showHeader: true
property alias header: headerContainer.children
property alias contentItem: content.contentItem
property alias showHeader: headerContainer.visible
property alias leftPadding: content.leftPadding
property alias rightPadding: content.rightPadding
property alias topPadding: content.topPadding
property alias bottomPadding: content.bottomPadding
readonly property State connectedState: thing.stateByName("connected")
readonly property bool isConnected: connectedState === null || connectedState.value === true
readonly property bool isEnabled: thing.setupStatus == Thing.ThingSetupStatusComplete && isConnected
signal clicked();
signal pressAndHold();
@ -73,12 +68,12 @@ import Nymea 1.0
gradient: Gradient {
GradientStop {
position: (headerRow.height + app.margins) / background.height
position: (headerContainer.height + app.margins) / background.height
color: Style.tileBackgroundColor
}
GradientStop {
position: (headerRow.height + app.margins) / background.height
color:root.showHeader ?
position: (headerContainer.height + app.margins) / background.height
color: headerContainer.visible ?
Style.tileOverlayColor
: Style.tileBackgroundColor
}
@ -90,20 +85,12 @@ import Nymea 1.0
spacing: 0
anchors { left: parent.left; top: parent.top; right: parent.right; margins: app.margins / 2 }
RowLayout {
id: headerRow
visible: root.showHeader
Item {
id: headerContainer
Layout.fillWidth: true
Layout.margins: app.margins / 2
Label {
Layout.fillWidth: true
text: root.thing.name
elide: Text.ElideRight
color: Style.tileOverlayForegroundColor
}
ThingStatusIcons {
thing: root.thing
// color: Style.tileOverlayForegroundColor
}
visible: children.length > 0
height: childrenRect.height
}
ItemDelegate {

View File

@ -98,7 +98,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)

View File

@ -62,7 +62,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)

View File

@ -68,7 +68,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)

View File

@ -87,7 +87,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
@ -97,6 +97,7 @@ DeviceListPageBase {
leftPadding: 0
rightPadding: 0
property State powerState: thing.stateByName("power")
property State brightnessState: thing.stateByName("brightness")
property State colorState: thing.stateByName("color")

View File

@ -59,7 +59,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: thingsProxy.getThing(model.id)

View File

@ -62,7 +62,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)

View File

@ -59,7 +59,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)

View File

@ -59,7 +59,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)

View File

@ -60,7 +60,7 @@ DeviceListPageBase {
Repeater {
model: root.thingsProxy
delegate: BigTile {
delegate: BigThingTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.get(model.id)

View File

@ -28,8 +28,8 @@
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.1
import Nymea 1.0
import "../components"
@ -52,19 +52,92 @@ DevicePageBase {
}
}
function sendMessage(title, text) {
print("sending message", title, text)
var actionType = root.deviceClass.actionTypes.findByName("notify")
var params = []
var titleParam = {}
titleParam["paramTypeId"] = actionType.paramTypes.findByName("title").id
titleParam["value"] = title
params.push(titleParam)
var bodyParam = {}
bodyParam["paramTypeId"] = actionType.paramTypes.findByName("body").id
bodyParam["value"] = text
params.push(bodyParam)
d.pendingAction = engine.deviceManager.executeAction(root.device.id, actionType.id, params)
titleTextField.clear();
bodyTextField.clear();
}
ColumnLayout {
id: content
anchors.fill: parent
Label {
RowLayout {
id: inputPane
Layout.fillWidth: true
Layout.margins: app.margins
wrapMode: Text.WordWrap
text: qsTr("Sent notifications:")
visible: logsModel.count > 0 && !logsModel.busy
spacing: app.margins
ColumnLayout {
id: inputColumn
TextField {
id: titleTextField
Layout.fillWidth: true
placeholderText: qsTr("Title")
}
ScrollView {
Layout.preferredWidth: inputPane.width - app.iconSize - inputPane.spacing
Layout.maximumHeight: content.height - y - app.margins
contentWidth: width
TextArea {
id: bodyTextField
placeholderText: qsTr("Text")
wrapMode: TextArea.WrapAtWordBoundaryOrAnywhere
}
}
}
Item {
id: sendButton
Layout.preferredWidth: app.iconSize
Layout.preferredHeight: inputColumn.height
ColorIcon {
anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; margins: app.margins }
height: app.iconSize
width: app.iconSize
name: "../images/send.svg"
color: titleTextField.displayText.length > 0 ? Style.accentColor : Style.iconColor
visible: d.pendingAction == -1
}
BusyIndicator {
anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; margins: app.margins }
visible: d.pendingAction != -1
running: visible
}
MouseArea {
anchors.fill: parent
onClicked: {
print("clicked!")
bodyTextField.focus = false
if (titleTextField.displayText.length > 0) {
root.sendMessage(titleTextField.displayText, bodyTextField.text)
titleTextField.clear();
bodyTextField.clear();
}
}
}
}
}
GenericTypeLogView {
id: logView
Layout.fillHeight: true
Layout.fillWidth: true
@ -76,16 +149,65 @@ DevicePageBase {
typeIds: [root.deviceClass.actionTypes.findByName("notify").id];
}
delegate: NymeaSwipeDelegate {
width: parent.width
iconName: app.interfaceToIcon("notifications")
text: model.value.trim()
subText: Qt.formatDateTime(model.timestamp)
progressive: false
delegate: BigTile {
id: itemDelegate
showHeader: false
width: logView.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
// Note: This will go wrong if the title contains ", ". Known shortcoming of the log db
readonly property string title: model.value.trim().replace(/, ?.*/, "")
readonly property string text: model.value.trim().replace(/.*, ?/, "")
contentItem: RowLayout {
ColumnLayout {
Label {
Layout.fillWidth: true
text: itemDelegate.title
elide: Text.ElideRight
}
GridLayout {
Layout.fillWidth: true
columns: textLabel.implicitWidth + dateLayout.implicitWidth < width ? 2 : 1
Label {
id: textLabel
Layout.fillWidth: true
text: itemDelegate.text
font.pixelSize: app.smallFont
wrapMode: Text.WordWrap
}
RowLayout {
id: dateLayout
Layout.fillWidth: true
spacing: app.margins / 2
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
text: Qt.formatDateTime(model.timestamp)
font.pixelSize: app.extraSmallFont
}
ColorIcon {
Layout.preferredWidth: app.smallIconSize
Layout.preferredHeight: app.smallIconSize
name: "../images/dialog-warning-symbolic.svg"
color: "red"
visible: model.errorCode !== ""
}
}
}
}
}
onClicked: {
var parts = model.value.trim().split(', ')
var popup = detailsPopup.createObject(root, {timestamp: model.timestamp, notificationTitle: parts[0], notificationBody: parts[1]});
var popup = detailsPopup.createObject(root,
{
timestamp: model.timestamp,
notificationTitle: itemDelegate.title,
notificationBody: itemDelegate.text,
errorCode: model.errorCode
});
popup.open();
}
}
@ -100,73 +222,6 @@ DevicePageBase {
visible: logsModel.count == 0 && !logsModel.busy
}
}
ThinDivider {}
RowLayout {
Layout.fillWidth: true
Layout.margins: app.margins
spacing: app.margins
ColumnLayout {
id: inputColumn
anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
TextField {
id: titleTextField
Layout.fillWidth: true
placeholderText: qsTr("Title")
}
TextArea {
id: bodyTextField
Layout.fillWidth: true
placeholderText: qsTr("Text")
wrapMode: Text.WordWrap
}
}
Item {
Layout.preferredWidth: app.iconSize
Layout.preferredHeight: inputColumn.height
ColorIcon {
anchors.centerIn: parent
height: app.iconSize
width: app.iconSize
name: "../images/send.svg"
color: titleTextField.displayText.length > 0 ? Style.accentColor : Style.iconColor
visible: d.pendingAction == -1
}
BusyIndicator {
anchors.centerIn: parent
visible: d.pendingAction != -1
running: visible
}
MouseArea {
anchors.fill: parent
onClicked: {
print("clicked!")
if (titleTextField.displayText.length > 0) {
var actionType = root.deviceClass.actionTypes.findByName("notify")
var params = []
var titleParam = {}
titleParam["paramTypeId"] = actionType.paramTypes.findByName("title").id
titleParam["value"] = titleTextField.displayText
params.push(titleParam)
var bodyParam = {}
bodyParam["paramTypeId"] = actionType.paramTypes.findByName("body").id
bodyParam["value"] = bodyTextField.text
params.push(bodyParam)
d.pendingAction = engine.deviceManager.executeAction(root.device.id, actionType.id, params)
titleTextField.clear();
bodyTextField.clear();
}
}
}
}
}
}
BusyIndicator {
@ -177,22 +232,38 @@ DevicePageBase {
Component {
id: detailsPopup
MeaDialog {
id: detailsDialog
standardButtons: Dialog.NoButton
property string timestamp
property string notificationTitle
property string notificationBody
property string errorCode
title: qsTr("Notification details")
Label {
Layout.fillWidth: true
text: qsTr("Date sent")
font.bold: true
headerIcon: "../images/messaging-app-symbolic.svg"
RowLayout {
ColumnLayout {
Label {
Layout.fillWidth: true
text: detailsDialog.errorCode == "" ? qsTr("Date sent") : qsTr("Sending failed")
font.bold: true
}
Label {
Layout.fillWidth: true
text: Qt.formatDateTime(detailsDialog.timestamp)
}
}
ColorIcon {
Layout.preferredWidth: app.largeIconSize
Layout.preferredHeight: app.largeIconSize
name: "../images/dialog-warning-symbolic.svg"
color: "red"
visible: detailsDialog.errorCode !== ""
}
}
Label {
Layout.fillWidth: true
text: Qt.formatDateTime(detailsDialog.timestamp)
}
Label {
Layout.topMargin: app.margins
Layout.fillWidth: true
@ -217,6 +288,20 @@ DevicePageBase {
text: detailsDialog.notificationBody
wrapMode: Text.WordWrap
}
RowLayout {
Item {
Layout.fillWidth: true
}
Button {
text: qsTr("Resend")
onClicked: root.sendMessage(detailsDialog.notificationTitle, detailsDialog.notificationBody)
}
Button {
text: qsTr("Close")
onClicked: detailsDialog.close()
}
}
}
}
}

View File

@ -401,21 +401,13 @@ Page {
print("Setting up params for thing class:", root.thingClass.id, root.thingClass.name)
if (root.thingClass.id.toString().match(/\{?f0dd4c03-0aca-42cc-8f34-9902457b05de\}?/)) {
if (paramType.id.toString().match(/\{?3cb8e30e-2ec5-4b4b-8c8c-03eaf7876839\}?/)) {
if (PlatformHelper.platform === "android") {
return "FB-GCM";
} else if (PlatformHelper.platform === "ios") {
return "FB-APNs"
} else if (PlatformHelper.platform === "ubports") {
return "UBPorts";
} else {
print("Unsupported platform for push notifications:", PlatformHelper.platform)
}
return PushNotifications.service;
}
if (paramType.id.toString().match(/\{?12ec06b2-44e7-486a-9169-31c684b91c8f\}?/)) {
return PushNotifications.token;
}
if (paramType.id.toString().match(/\{?d76da367-64e3-4b7d-aa84-c96b3acfb65e\}?/)) {
return PlatformHelper.deviceSerial + "+io.guh.nymeaapp" + (appBranding.length > 0 ? "-" + appBranding : "");
return PushNotifications.clientId;
}
}

View File

@ -1,5 +1,6 @@
pragma Singleton
import QtQuick 2.9
import Nymea 1.0
Item {
id: root
@ -81,4 +82,5 @@ Item {
return ((r * 299 + g * 587 + b * 114) / 1000) < 128
}
}