Add initial plugin structure for stiebel eltron

This commit is contained in:
Simon Stürz 2022-02-02 11:11:49 +01:00
parent e4a4281710
commit eb56d176d3
8 changed files with 559 additions and 0 deletions

View File

@ -15,6 +15,7 @@ PLUGIN_DIRS = \
mtec \
mypv \
schrack \
stiebeleltron \
sunspec \
unipi \
wallbe \

View File

@ -0,0 +1,154 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, 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 "integrationpluginstiebeleltron.h"
#include "network/networkdevicediscovery.h"
#include "hardwaremanager.h"
#include "plugininfo.h"
IntegrationPluginStiebelEltron::IntegrationPluginStiebelEltron()
{
}
void IntegrationPluginStiebelEltron::discoverThings(ThingDiscoveryInfo *info)
{
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
qCWarning(dcStiebelEltron()) << "The network discovery is not available on this platform.";
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
return;
}
NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover();
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
qCDebug(dcStiebelEltron()) << "Found" << networkDeviceInfo;
QString title;
if (networkDeviceInfo.hostName().isEmpty()) {
title = networkDeviceInfo.address().toString();
} else {
title = networkDeviceInfo.hostName() + " (" + networkDeviceInfo.address().toString() + ")";
}
QString description;
if (networkDeviceInfo.macAddressManufacturer().isEmpty()) {
description = networkDeviceInfo.macAddress();
} else {
description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")";
}
ThingDescriptor descriptor(stiebelEltronThingClassId, title, description);
ParamList params;
params << Param(stiebelEltronThingIpAddressParamTypeId, networkDeviceInfo.address().toString());
params << Param(stiebelEltronThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
descriptor.setParams(params);
// Check if we already have set up this device
Things existingThings = myThings().filterByParam(stiebelEltronThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
if (existingThings.count() == 1) {
qCDebug(dcStiebelEltron()) << "This connection already exists in the system:" << networkDeviceInfo;
descriptor.setThingId(existingThings.first()->id());
}
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
});
}
void IntegrationPluginStiebelEltron::startMonitoringAutoThings()
{
}
void IntegrationPluginStiebelEltron::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
qCDebug(dcStiebelEltron()) << "Setup" << thing << thing->params();
if (thing->thingClassId() == stiebelEltronThingClassId) {
QHostAddress address(thing->paramValue(stiebelEltronThingIpAddressParamTypeId).toString());
quint16 port = thing->paramValue(stiebelEltronThingPortParamTypeId).toUInt();
quint16 slaveId = thing->paramValue(stiebelEltronThingSlaveIdParamTypeId).toUInt();
StiebelEltronModbusConnection *connection = new StiebelEltronModbusConnection(address, port, slaveId, this);
connection->connectDevice();
m_connections.insert(thing, connection);
info->finish(Thing::ThingErrorNoError);
}
}
void IntegrationPluginStiebelEltron::postSetupThing(Thing *thing)
{
if (thing->thingClassId() == stiebelEltronThingClassId) {
if (!m_pluginTimer) {
qCDebug(dcStiebelEltron()) << "Starting plugin timer...";
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
foreach (StiebelEltronModbusConnection *connection, m_connections) {
if (connection->connected()) {
connection->update();
}
}
});
m_pluginTimer->start();
}
}
}
void IntegrationPluginStiebelEltron::thingRemoved(Thing *thing)
{
if (thing->thingClassId() == stiebelEltronThingClassId && m_connections.contains(thing)) {
m_connections.take(thing)->deleteLater();
}
if (myThings().isEmpty() && m_pluginTimer) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
}
}
void IntegrationPluginStiebelEltron::executeAction(ThingActionInfo *info)
{
info->finish(Thing::ThingErrorNoError);
}

View File

@ -0,0 +1,65 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef INTEGRATIONPLUGINSTIEBELELTRON_H
#define INTEGRATIONPLUGINSTIEBELELTRON_H
#include "plugintimer.h"
#include "integrations/integrationplugin.h"
#include "stiebeleltronmodbusconnection.h"
class IntegrationPluginStiebelEltron: public IntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginstiebeleltron.json")
Q_INTERFACES(IntegrationPlugin)
public:
explicit IntegrationPluginStiebelEltron();
void discoverThings(ThingDiscoveryInfo *info) override;
void startMonitoringAutoThings() override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void thingRemoved(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
private:
PluginTimer *m_pluginTimer = nullptr;
QHash<Thing *, StiebelEltronModbusConnection *> m_connections;
};
#endif // INTEGRATIONPLUGINSTIEBELELTRON_H

View File

@ -0,0 +1,112 @@
{
"name": "StiebelEltron",
"displayName": "Stiebel Eltron",
"id": "956c848b-b538-4b8f-8cdb-7bbecfc9d361",
"vendors": [
{
"name": "stiebelEltron",
"displayName": "Stiebel Eltron",
"id": "c8607f85-a81e-40e0-bc95-1b7199cd2d99",
"thingClasses": [
{
"name": "stiebelEltron",
"displayName": "Stiebel Eltron Heatpump",
"id": "e02ecf61-7d28-43c2-b87e-e7e98a48fbfd",
"createMethods": ["discovery", "user"],
"interfaces": ["smartgridheatpump", "connectable"],
"paramTypes": [
{
"id": "47d221fa-f6d2-400e-b80f-bb90abccb72c",
"name": "ipAddress",
"displayName": "IP address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": "127.0.0.1"
},
{
"id": "05cd59b8-3068-460f-b0d2-6d49f27458df",
"name":"macAddress",
"displayName": "MAC address",
"type": "QString",
"inputType": "MacAddress",
"defaultValue": ""
},
{
"id": "6842321f-1f1a-47e2-b12d-59ee322eb8a6",
"name":"port",
"displayName": "Port",
"type": "int",
"defaultValue": 502
},
{
"id": "732de6da-bd0a-4215-b320-602117ebc75c",
"name":"slaveId",
"displayName": "Modbus slave ID",
"type": "int",
"defaultValue": 1
}
],
"stateTypes": [
{
"id": "8d952a5e-87bd-492e-a213-277948521652",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "d6475acb-3a15-401b-8bad-8610eb056bf7",
"name": "flowTemperature",
"displayName": "Flow temperature",
"displayNameEvent": "Flow temperature changed",
"unit": "DegreeCelsius",
"type": "double",
"defaultValue": 0,
"suggestLogging": true
},
{
"id": "ce25e3fd-6544-40e9-bd39-032306553e32",
"name": "returnTemperature",
"displayName": "Return temperature",
"displayNameEvent": "Return temperature changed",
"unit": "DegreeCelsius",
"type": "double",
"defaultValue": 0,
"suggestLogging": true
},
{
"id": "7d474fb5-aa37-4f21-8166-b20f5bf84fb4",
"name": "sgReadyMode",
"displayName": "Smart grid mode",
"displayNameEvent": "Smart grid mode changed",
"displayNameAction": "Set smart grid mode",
"type": "QString",
"possibleValues": [
"Off",
"Low",
"Standard",
"High"
],
"writable": true,
"defaultValue": "Standard",
"suggestLogging": true
},
{
"id": "f4abbd8d-14d6-4294-9b63-411a9721f946",
"name": "totalEnergy",
"displayName": "Total energy",
"displayNameEvent": "Total energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0,
"suggestLogging": true
}
],
"actionTypes": [ ]
}
]
}
]
}

View File

@ -0,0 +1,19 @@
{
"protocol": "TCP",
"endianness": "BigEndian",
"registers": [
{
"id": "outdoorTemperature",
"address": 507,
"size": 1,
"type": "int16",
"registerType": "inputRegister",
"readSchedule": "update",
"description": "Outdoor temperature",
"staticScaleFactor": -1,
"defaultValue": "0",
"unit": "°C",
"access": "RO"
}
]
}

View File

@ -0,0 +1,16 @@
include(../plugins.pri)
QT += network serialbus
HEADERS += \
integrationpluginstiebeleltron.h \
stiebeleltronmodbusconnection.h \
../modbus/modbustcpmaster.h \
../modbus/modbusdatautils.h
SOURCES += \
integrationpluginstiebeleltron.cpp \
stiebeleltronmodbusconnection.cpp \
../modbus/modbustcpmaster.cpp \
../modbus/modbusdatautils.cpp

View File

@ -0,0 +1,113 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This fileDescriptor 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 "stiebeleltronmodbusconnection.h"
#include "loggingcategories.h"
NYMEA_LOGGING_CATEGORY(dcStiebelEltronModbusConnection, "StiebelEltronModbusConnection")
StiebelEltronModbusConnection::StiebelEltronModbusConnection(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) :
ModbusTCPMaster(hostAddress, port, parent),
m_slaveId(slaveId)
{
}
float StiebelEltronModbusConnection::outdoorTemperature() const
{
return m_outdoorTemperature;
}
void StiebelEltronModbusConnection::initialize()
{
// No init registers defined. Nothing to be done and we are finished.
emit initializationFinished();
}
void StiebelEltronModbusConnection::update()
{
updateOutdoorTemperature();
}
void StiebelEltronModbusConnection::updateOutdoorTemperature()
{
// Update registers from Flow
qCDebug(dcStiebelEltronModbusConnection()) << "--> Read \"Flow\" register:" << 507 << "size:" << 1;
QModbusReply *reply = readOutdoorTemperature();
if (reply) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
const QVector<quint16> values = unit.values();
qCDebug(dcStiebelEltronModbusConnection()) << "<-- Response from \"Flow\" register" << 507 << "size:" << 1 << values;
float receivedOutdoorTemperature = ModbusDataUtils::convertToInt16(values) * 1.0 * pow(10, -1);
if (m_outdoorTemperature != receivedOutdoorTemperature) {
m_outdoorTemperature = receivedOutdoorTemperature;
emit outdoorTemperatureChanged(m_outdoorTemperature);
}
}
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
qCWarning(dcStiebelEltronModbusConnection()) << "Modbus reply error occurred while updating \"Flow\" registers from" << hostAddress().toString() << error << reply->errorString();
emit reply->finished(); // To make sure it will be deleted
});
} else {
delete reply; // Broadcast reply returns immediatly
}
} else {
qCWarning(dcStiebelEltronModbusConnection()) << "Error occurred while reading \"Flow\" registers from" << hostAddress().toString() << errorString();
}
}
QModbusReply *StiebelEltronModbusConnection::readOutdoorTemperature()
{
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 507, 1);
return sendReadRequest(request, m_slaveId);
}
void StiebelEltronModbusConnection::verifyInitFinished()
{
if (m_pendingInitReplies.isEmpty()) {
qCDebug(dcStiebelEltronModbusConnection()) << "Initialization finished of StiebelEltronModbusConnection" << hostAddress().toString();
emit initializationFinished();
}
}
QDebug operator<<(QDebug debug, StiebelEltronModbusConnection *stiebelEltronModbusConnection)
{
debug.nospace().noquote() << "StiebelEltronModbusConnection(" << stiebelEltronModbusConnection->hostAddress().toString() << ":" << stiebelEltronModbusConnection->port() << ")" << "\n";
debug.nospace().noquote() << " - Flow:" << stiebelEltronModbusConnection->outdoorTemperature() << " [°C]" << "\n";
return debug.quote().space();
}

View File

@ -0,0 +1,79 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This fileDescriptor 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef STIEBELELTRONMODBUSCONNECTION_H
#define STIEBELELTRONMODBUSCONNECTION_H
#include <QObject>
#include "../modbus/modbusdatautils.h"
#include "../modbus/modbustcpmaster.h"
class StiebelEltronModbusConnection : public ModbusTCPMaster
{
Q_OBJECT
public:
enum Registers {
RegisterOutdoorTemperature = 507
};
Q_ENUM(Registers)
explicit StiebelEltronModbusConnection(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);
~StiebelEltronModbusConnection() = default;
/* Flow [°C] - Address: 507, Size: 1 */
float outdoorTemperature() const;
virtual void initialize();
virtual void update();
void updateOutdoorTemperature();
signals:
void initializationFinished();
void outdoorTemperatureChanged(float outdoorTemperature);
protected:
QModbusReply *readOutdoorTemperature();
float m_outdoorTemperature = 0;
private:
quint16 m_slaveId = 1;
QVector<QModbusReply *> m_pendingInitReplies;
void verifyInitFinished();
};
QDebug operator<<(QDebug debug, StiebelEltronModbusConnection *stiebelEltronModbusConnection);
#endif // STIEBELELTRONMODBUSCONNECTION_H