powersync-plugins/espsomfyrts/espsomfyrts.cpp

255 lines
8.1 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "espsomfyrts.h"
#include "extern-plugininfo.h"
#include <network/networkdevicemonitor.h>
#include <QJsonDocument>
#include <QJsonParseError>
EspSomfyRts::EspSomfyRts(NetworkDeviceMonitor *monitor, QObject *parent)
: QObject{parent},
m_monitor{monitor}
{
m_websocketUrl.setScheme("ws");
m_websocketUrl.setHost("127.0.0.1");
m_websocketUrl.setPort(8080);
m_webSocket = new QWebSocket("nymea", QWebSocketProtocol::Version13, this);
connect(m_webSocket, &QWebSocket::textMessageReceived, this, &EspSomfyRts::onWebSocketTextMessageReceived);
connect(m_webSocket, &QWebSocket::connected, this, [this](){
qCDebug(dcESPSomfyRTS()) << "Websocket connected";
m_connected = true;
emit connectedChanged(m_connected);
});
connect(m_webSocket, &QWebSocket::disconnected, this, [this](){
qCDebug(dcESPSomfyRTS()) << "Websocket disconnected";
m_connected = false;
emit connectedChanged(m_connected);
m_reconnectTimer.start();
});
if (m_monitor) {
qCDebug(dcESPSomfyRTS()) << "Setting up ESP Somfy using the network device monitor on" << m_monitor->macAddress();
connect(m_monitor, &NetworkDeviceMonitor::reachableChanged, this, &EspSomfyRts::onMonitorReachableChanged);
// Init connection based on the monitor
onMonitorReachableChanged(m_monitor->reachable());
}
// Websocket reconnect mechanism
m_reconnectTimer.setInterval(5);
m_reconnectTimer.setSingleShot(false);
connect(&m_reconnectTimer, &QTimer::timeout, this, [this](){
if (m_webSocket->state() == QAbstractSocket::UnconnectedState && m_monitor->reachable()) {
m_websocketUrl.setHost(m_monitor->networkDeviceInfo().address().toString());
qCDebug(dcESPSomfyRTS()) << "Trying to connect to" << m_websocketUrl;
m_webSocket->open(m_websocketUrl);
}
});
}
NetworkDeviceMonitor *EspSomfyRts::monitor() const
{
return m_monitor;
}
QHostAddress EspSomfyRts::address() const
{
return QHostAddress(m_websocketUrl.host());
}
bool EspSomfyRts::connected() const
{
return m_connected;
}
QString EspSomfyRts::firmwareVersion() const
{
return m_firmwareVersion;
}
QUrl EspSomfyRts::shadesUrl()
{
return buildUrl("shades");
}
QUrl EspSomfyRts::shadeCommandUrl()
{
return buildUrl("shadeCommand");
}
QUrl EspSomfyRts::tiltCommandUrl()
{
return buildUrl("tiltCommand");
}
QString EspSomfyRts::getShadeCommandString(ShadeCommand shadeCommand)
{
QString shadeCommandString;
switch(shadeCommand) {
case ShadeCommandMy:
shadeCommandString = "m";
break;
case ShadeCommandUp:
shadeCommandString = "u";
break;
case ShadeCommandDown:
shadeCommandString = "d";
break;
case ShadeCommandMyUp:
shadeCommandString = "mu";
break;
case ShadeCommandMyDown:
shadeCommandString = "md";
break;
case ShadeCommandUpDown:
shadeCommandString = "ud";
break;
case ShadeCommandMyUpDown:
shadeCommandString = "mud";
break;
case ShadeCommandProg:
shadeCommandString = "p";
break;
case ShadeCommandSunFlag:
shadeCommandString = "s";
break;
case ShadeCommandFlag:
shadeCommandString = "f";
break;
case ShadeCommandStepUp:
shadeCommandString = "su";
break;
case ShadeCommandStepDown:
shadeCommandString = "sd";
break;
case ShadeCommandFavorite:
shadeCommandString = "fav";
break;
case ShadeCommandStop:
shadeCommandString = "stop";
break;
}
return shadeCommandString;
}
void EspSomfyRts::onMonitorReachableChanged(bool reachable)
{
qCDebug(dcESPSomfyRTS()) << "Network device of" << m_websocketUrl.host() << "is" << (reachable ? "now reachable" : "not reachable any more");
if (reachable) {
if (m_webSocket->state() == QAbstractSocket::ConnectedState)
return;
m_websocketUrl.setHost(m_monitor->networkDeviceInfo().address().toString());
qCDebug(dcESPSomfyRTS()) << "Connecting to" << m_websocketUrl.toString();
m_webSocket->open(m_websocketUrl);
}
}
void EspSomfyRts::onWebSocketTextMessageReceived(const QString &message)
{
//qCDebug(dcESPSomfyRTS()) << "Websocket message received:" << message;
if (message.startsWith("42")) {
QJsonParseError jsonError;
QByteArray rawMessage = message.mid(3, message.size() - 4).toUtf8();
// Make parsing easier
int index = rawMessage.indexOf(',');
if (index < 0) {
qCWarning(dcESPSomfyRTS()) << "Could not parse notification from data" << rawMessage;
return;
}
QString notification = rawMessage.left(index);
QByteArray rawPayload = rawMessage.right(rawMessage.size() - index - 1);
QJsonDocument jsonDoc = QJsonDocument::fromJson(rawPayload, &jsonError);
if (jsonError.error != QJsonParseError::NoError) {
qCWarning(dcESPSomfyRTS()) << "Json error parsing the data" << rawPayload << jsonError.error << jsonError.errorString();
return;
}
QVariantMap payload = jsonDoc.toVariant().toMap();
if (notification == "wifiStrength") {
uint signalStrength = 0;
int dbm = payload.value("strength").toInt();
if (dbm > -90)
signalStrength += 20;
if (dbm > -80)
signalStrength += 20;
if (dbm > -70)
signalStrength += 20;
if (dbm > -67)
signalStrength += 20;
if (dbm > -30)
signalStrength += 20;
if (m_signalStrength != signalStrength) {
m_signalStrength = signalStrength;
emit signalStrengthChanged(m_signalStrength);
}
} else if (notification == "fwStatus") {
QString firmwareVersion = payload.value("fwVersion").toMap().value("name").toString();
if (m_firmwareVersion != firmwareVersion) {
m_firmwareVersion = firmwareVersion;
emit firmwareVersionChanged(m_firmwareVersion);
}
// TODO. firmware update
} else if (notification == "shadeState") {
emit shadeStateReceived(payload);
} else if (notification == "memStatus") {
// We are not interested in this, filter it out
} else {
qCDebug(dcESPSomfyRTS()) << "Notification" << notification << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
}
}
}
QUrl EspSomfyRts::buildUrl(const QString &path)
{
return QUrl(QString("http://%1/%2").arg(m_websocketUrl.host()).arg(path));
}