added my-PV
This commit is contained in:
parent
59a9927f2c
commit
344239fe0d
16
debian/control
vendored
16
debian/control
vendored
@ -33,6 +33,22 @@ Description: nymea.io plugin for Drexel & Weiss heat pumps
|
||||
This package will install the nymea.io plugin for Drexel & Weiss heat pumps
|
||||
|
||||
|
||||
Package: nymea-plugin-mypv
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
nymea-plugins-translations,
|
||||
libmodbus,
|
||||
Description: nymea.io plugin for my-pv heating rods
|
||||
The nymea daemon is a plugin based IoT (Internet of Things) server. The
|
||||
server works like a translator for devices, things and services and
|
||||
allows them to interact.
|
||||
With the powerful rule engine you are able to connect any device available
|
||||
in the system and create individual scenes and behaviors for your environment.
|
||||
.
|
||||
This package will install the nymea.io plugin for my-pv
|
||||
|
||||
|
||||
Package: nymea-plugin-wallbe
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
|
||||
1
debian/nymea-plugin-mypv.install.in
vendored
Normal file
1
debian/nymea-plugin-mypv.install.in
vendored
Normal file
@ -0,0 +1 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginmypv.so
|
||||
@ -171,12 +171,10 @@ void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info)
|
||||
discoverModbusSlaves(modbus, slave);
|
||||
info->finish(Device::DeviceErrorNoError);
|
||||
return;
|
||||
} else {
|
||||
info->finish(Device::DeviceErrorActionTypeNotFound);
|
||||
}
|
||||
info->finish(Device::DeviceErrorActionTypeNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == x2luDeviceClassId) {
|
||||
} else if (device->deviceClassId() == x2luDeviceClassId) {
|
||||
Device *parentDevice = myDevices().findById(device->parentId());
|
||||
if (!parentDevice) {
|
||||
qWarning(dcDrexelUndWeiss()) << "Could not find the parent device";
|
||||
@ -210,12 +208,10 @@ void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info)
|
||||
}
|
||||
m_pendingActions.insert(modbus->writeHoldingRegister(slave, ModbusRegisterX2::Betriebsart, data), info);
|
||||
return;
|
||||
} else {
|
||||
info->finish(Device::DeviceErrorActionTypeNotFound);
|
||||
}
|
||||
info->finish(Device::DeviceErrorActionTypeNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == x2wpDeviceClassId) {
|
||||
} else if (device->deviceClassId() == x2wpDeviceClassId) {
|
||||
Device *parentDevice = myDevices().findById(device->parentId());
|
||||
if (!parentDevice) {
|
||||
qWarning(dcDrexelUndWeiss()) << "Could not find modbus interface";
|
||||
@ -235,18 +231,17 @@ void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info)
|
||||
int data = static_cast<int>(qRound(targetTemp * 1000));
|
||||
m_pendingActions.insert(modbus->writeHoldingRegister(slave,ModbusRegisterX2::RaumSoll, data), info);
|
||||
return;
|
||||
}
|
||||
if (action.actionTypeId() == x2wpTargetWaterTemperatureActionTypeId) {
|
||||
} else if (action.actionTypeId() == x2wpTargetWaterTemperatureActionTypeId) {
|
||||
qreal targetWaterTemp = action.param(x2wpTargetWaterTemperatureActionTargetWaterTemperatureParamTypeId).value().toDouble();
|
||||
int data = static_cast<int>(qRound(targetWaterTemp * 1000));
|
||||
m_pendingActions.insert(modbus->writeHoldingRegister(slave, ModbusRegisterX2::BrauchwasserSolltermperatur, data), info);
|
||||
return;
|
||||
} else {
|
||||
info->finish(Device::DeviceErrorActionTypeNotFound);
|
||||
}
|
||||
info->finish(Device::DeviceErrorActionTypeNotFound);
|
||||
return;
|
||||
} else {
|
||||
info->finish(Device::DeviceErrorDeviceClassNotFound);
|
||||
}
|
||||
info->finish(Device::DeviceErrorDeviceClassNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -51,8 +51,6 @@ public:
|
||||
void setupDevice(DeviceSetupInfo *info) override;
|
||||
void postSetupDevice(Device *device) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
|
||||
public slots:
|
||||
void executeAction(DeviceActionInfo *info) override;
|
||||
|
||||
private:
|
||||
|
||||
5
mypv/README.md
Normal file
5
mypv/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# mypv
|
||||
--------------------------------
|
||||
|
||||
Description of the plugin...
|
||||
|
||||
261
mypv/devicepluginmypv.cpp
Normal file
261
mypv/devicepluginmypv.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "plugininfo.h"
|
||||
#include "devicepluginmypv.h"
|
||||
|
||||
#include <QUdpSocket>
|
||||
#include <QHostAddress>
|
||||
|
||||
DevicePluginMyPv::DevicePluginMyPv()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DevicePluginMyPv::discoverDevices(DeviceDiscoveryInfo *info)
|
||||
{
|
||||
QUdpSocket *searchSocket = new QUdpSocket(this);
|
||||
|
||||
// Note: This will fail, and it's not a problem, but it is required to force the socket to stick to IPv4...
|
||||
searchSocket->bind(QHostAddress::AnyIPv4, 16124);
|
||||
|
||||
QByteArray discoveryString;
|
||||
discoveryString.resize(19);
|
||||
discoveryString.fill(0);
|
||||
discoveryString.insert(0, QByteArray::fromHex("86d93efc"));
|
||||
|
||||
discoveryString.insert(4, "AC ELWA-E");
|
||||
qCDebug(dcMypv()) << "Send datagram:" << discoveryString << "length: " << discoveryString.length();
|
||||
qint64 len = searchSocket->writeDatagram(discoveryString, QHostAddress("255.255.255.255"), 16124);
|
||||
if (len != discoveryString.length()) {
|
||||
searchSocket->deleteLater();
|
||||
qCWarning(dcMypv()) << "Error sending discovery";
|
||||
return;
|
||||
}
|
||||
|
||||
QTimer::singleShot(2000, this, [this, searchSocket, info](){
|
||||
QList<DeviceDescriptor> descriptorList;
|
||||
while(searchSocket->hasPendingDatagrams()) {
|
||||
char buffer[1024];
|
||||
QHostAddress senderAddress;
|
||||
int len = searchSocket->readDatagram(buffer, 1024, &senderAddress);
|
||||
QByteArray data = QByteArray::fromRawData(buffer, len);
|
||||
qCDebug(dcMypv()) << "Have datagram:" << data;
|
||||
if (data.length() < 64) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Device Id AC•THOR = 0x4e84
|
||||
//Device Id Power = 0x4e8e
|
||||
//Device Id AC ELWA-E = 0x3efc
|
||||
qCDebug(dcMypv()) << "device Id:" << data.mid(2, 2);
|
||||
if (data.mid(2, 2) == QByteArray::fromHex("3efc")) {
|
||||
qCDebug(dcMypv()) << "Found Device: AC ElWA-E";
|
||||
} else if (data.mid(2, 2) == QByteArray::fromHex("0x4e8e")) {
|
||||
qCDebug(dcMypv()) << "Found Device: Powermeter";
|
||||
} else if (data.mid(2, 2) == QByteArray::fromHex("0x4e84")) {
|
||||
qCDebug(dcMypv()) << "Found Device: AC Thor";
|
||||
} else {
|
||||
qCDebug(dcMypv()) << "Failed to parse discovery datagram from" << senderAddress << data;
|
||||
continue;
|
||||
}
|
||||
|
||||
quint32 uiAddress = 0;
|
||||
for (int i=0; i<4; i++) {
|
||||
uiAddress |= data.at(7-i) << (i*8);
|
||||
}
|
||||
QHostAddress address(uiAddress);
|
||||
QByteArray serialNumber = data.mid(8, 16);
|
||||
|
||||
bool existing = false;
|
||||
foreach (Device *existingDev, myDevices()) {
|
||||
if (existingDev->deviceClassId() == info->deviceClassId() && existingDev->paramValue(elwaDeviceIpAddressParamTypeId).toString() == address.toString()) {
|
||||
existing = true;
|
||||
}
|
||||
}
|
||||
if (existing) {
|
||||
qCDebug(dcMypv()) << "Already have device" << senderAddress << "in configured devices. Skipping...";
|
||||
continue;
|
||||
}
|
||||
DeviceDescriptor deviceDescriptors(info->deviceClassId(), "AC ELWA-E", address.toString());
|
||||
ParamList params;
|
||||
params << Param(elwaDeviceIpAddressParamTypeId, address.toString());
|
||||
params << Param(elwaDeviceSerialNumberParamTypeId, serialNumber);
|
||||
deviceDescriptors.setParams(params);
|
||||
descriptorList << deviceDescriptors;
|
||||
}
|
||||
info->addDeviceDescriptors(descriptorList);;
|
||||
searchSocket->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void DevicePluginMyPv::setupDevice(DeviceSetupInfo *info)
|
||||
{
|
||||
Device *device = info->device();
|
||||
|
||||
if(device->deviceClassId() == elwaDeviceClassId) {
|
||||
QHostAddress address = QHostAddress(device->paramValue(elwaDeviceIpAddressParamTypeId).toString());
|
||||
ModbusTCPMaster *modbusTcpMaster = new ModbusTCPMaster(address, 502, this);
|
||||
|
||||
if (!modbusTcpMaster->createInterface()) {
|
||||
modbusTcpMaster->deleteLater();
|
||||
|
||||
return;
|
||||
}
|
||||
m_modbusTcpMasters.insert(device, modbusTcpMaster);
|
||||
} else {
|
||||
Q_ASSERT_X(false, "setupDevice", QString("Unhandled deviceClassId: %1").arg(device->deviceClassId().toString()).toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginMyPv::postSetupDevice(Device *device)
|
||||
{
|
||||
if (device->deviceClassId() == elwaDeviceClassId) {
|
||||
update(device);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginMyPv::deviceRemoved(Device *device)
|
||||
{
|
||||
if (device->deviceClassId() == elwaDeviceClassId) {
|
||||
ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.take(device);
|
||||
modbusTCPMaster->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginMyPv::executeAction(DeviceActionInfo *info)
|
||||
{
|
||||
Device *device = info->device();
|
||||
Action action = info->action();
|
||||
|
||||
if (device->deviceClassId() == elwaDeviceClassId) {
|
||||
|
||||
ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(device);
|
||||
if (action.actionTypeId() == elwaHeatingPowerActionTypeId) {
|
||||
int heatingPower = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toInt();
|
||||
|
||||
if(!modbusTCPMaster->setRegister(0xff, ElwaModbusRegisters::Power, heatingPower)){
|
||||
return info->finish(Device::DeviceErrorHardwareFailure);
|
||||
}
|
||||
return;
|
||||
} else if (action.actionTypeId() == elwaPowerActionTypeId) {
|
||||
bool power = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toBool();
|
||||
if(power) {
|
||||
if(!modbusTCPMaster->setRegister(0xff, ElwaModbusRegisters::ManuelStart, 1)){
|
||||
return info->finish(Device::DeviceErrorHardwareFailure);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
|
||||
}
|
||||
} else {
|
||||
Q_ASSERT_X(false, "executeAction", QString("Unhandled deviceClassId: %1").arg(device->deviceClassId().toString()).toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginMyPv::onRefreshTimer(){
|
||||
|
||||
foreach (Device *device, myDevices()) {
|
||||
update(device);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginMyPv::update(Device *device) {
|
||||
if (device->deviceClassId() == elwaDeviceClassId)
|
||||
{
|
||||
ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(device);
|
||||
|
||||
int data;
|
||||
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::Status, &data)) {
|
||||
switch (data) {
|
||||
case Heating: {
|
||||
device->setStateValue(elwaStatusStateTypeId, "Heating");
|
||||
device->setStateValue(elwaPowerStateTypeId, true);
|
||||
break;
|
||||
}
|
||||
case Standby:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Standby");
|
||||
device->setStateValue(elwaPowerStateTypeId, false);
|
||||
break;
|
||||
}
|
||||
case Boosted:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Boosted");
|
||||
device->setStateValue(elwaPowerStateTypeId, true);
|
||||
break;
|
||||
}
|
||||
case HeatFinished:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Heat finished");
|
||||
device->setStateValue(elwaPowerStateTypeId, false);
|
||||
break;
|
||||
}
|
||||
case Setup:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Setup");
|
||||
device->setStateValue(elwaPowerStateTypeId, false);
|
||||
break;
|
||||
}
|
||||
case ErrorOvertempFuseBlown:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Error Overtemp Fuse blown");
|
||||
break;
|
||||
}
|
||||
case ErrorOvertempMeasured:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Error Overtemp measured");
|
||||
break;
|
||||
}
|
||||
case ErrorOvertempElectronics:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Error Overtemp Electronics");
|
||||
break;
|
||||
}
|
||||
case ErrorHardwareFault:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Error Hardware Fault");
|
||||
break;
|
||||
}
|
||||
case ErrorTempSensor:{
|
||||
device->setStateValue(elwaStatusStateTypeId, "Error Temp Sensor");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
device->setStateValue(elwaStatusStateTypeId, "Unknown");
|
||||
}
|
||||
|
||||
device->setStateValue(elwaConnectedStateTypeId, true);
|
||||
} else {
|
||||
device->setStateValue(elwaConnectedStateTypeId, false);
|
||||
}
|
||||
|
||||
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::WaterTemperature, &data)) {
|
||||
device->setStateValue(elwaTemperatureStateTypeId, data/10.00);
|
||||
}
|
||||
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::TargetWaterTemperature, &data)) {
|
||||
device->setStateValue(elwaTargetWaterTemperatureStateTypeId, data/10.00);
|
||||
}
|
||||
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::Power, &data)) {
|
||||
device->setStateValue(elwaHeatingPowerStateTypeId, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
87
mypv/devicepluginmypv.h
Normal file
87
mypv/devicepluginmypv.h
Normal file
@ -0,0 +1,87 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 DEVICEPLUGINMYPV_H
|
||||
#define DEVICEPLUGINMYPV_H
|
||||
|
||||
#include "devices/deviceplugin.h"
|
||||
#include "plugintimer.h"
|
||||
#include "modbustcpmaster.h"
|
||||
|
||||
#include <QUdpSocket>
|
||||
|
||||
class DevicePluginMyPv: public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginmypv.json")
|
||||
Q_INTERFACES(DevicePlugin)
|
||||
|
||||
|
||||
public:
|
||||
explicit DevicePluginMyPv();
|
||||
|
||||
void discoverDevices(DeviceDiscoveryInfo *info) override;
|
||||
void setupDevice(DeviceSetupInfo *info) override;
|
||||
void postSetupDevice(Device *device) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
void executeAction(DeviceActionInfo *info) override;
|
||||
|
||||
private:
|
||||
|
||||
enum ElwaModbusRegisters {
|
||||
Power= 1000,
|
||||
WaterTemperature = 1001,
|
||||
TargetWaterTemperature = 1002,
|
||||
Status = 1003,
|
||||
ManuelStart = 1012
|
||||
};
|
||||
|
||||
enum ElwaStatus {
|
||||
Heating = 2,
|
||||
Standby = 3,
|
||||
Boosted = 4,
|
||||
HeatFinished = 5,
|
||||
Setup = 9,
|
||||
ErrorOvertempFuseBlown = 201,
|
||||
ErrorOvertempMeasured = 202,
|
||||
ErrorOvertempElectronics = 203,
|
||||
ErrorHardwareFault = 204,
|
||||
ErrorTempSensor = 205
|
||||
};
|
||||
|
||||
PluginTimer *m_refreshTimer = nullptr;
|
||||
QHash<Device *, ModbusTCPMaster *> m_modbusTcpMasters;
|
||||
|
||||
void update(Device *device);
|
||||
|
||||
private slots:
|
||||
void onRefreshTimer();
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINMYPV_H
|
||||
|
||||
112
mypv/devicepluginmypv.json
Normal file
112
mypv/devicepluginmypv.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"name": "Mypv",
|
||||
"displayName": "Mypv",
|
||||
"id": "73c7efcc-80d5-4166-ad97-2cbbeb129d91",
|
||||
"vendors": [
|
||||
{
|
||||
"name": "myPV",
|
||||
"displayName": "my-PV",
|
||||
"id": "1f17597f-e0d0-459b-858d-ec9cbcd10b2c",
|
||||
"deviceClasses": [
|
||||
{
|
||||
"name": "elwa",
|
||||
"displayName": "AC Elwa E",
|
||||
"id": "19ac4c7c-9c0a-4998-a8f0-c77d940cbb08",
|
||||
"createMethods": ["Discovery"],
|
||||
"interfaces": ["connectable", "temperaturesensor", "heating"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "ae66596f-f6c7-4d9c-9eee-b9190616a9e1",
|
||||
"name":"ipAddress",
|
||||
"displayName": "IP address",
|
||||
"type": "QString"
|
||||
},
|
||||
{
|
||||
"id": "b31a263a-2fdc-4a88-88ec-9e182025da8f",
|
||||
"name":"serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type": "QString"
|
||||
}
|
||||
],
|
||||
"stateTypes":[
|
||||
{
|
||||
"id": "a5afaad5-78bf-4cac-b98d-7eae31aac518",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "e6b0260b-f255-4f17-8ac1-bc87a950f449",
|
||||
"name": "power",
|
||||
"displayName": "Power",
|
||||
"displayNameEvent": "Power changed",
|
||||
"displayNameAction": "Change power",
|
||||
"type": "bool",
|
||||
"defaultValue": 0,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "2eb3c40c-1b43-4b64-82e2-6558f0b8817e",
|
||||
"name": "heatingPower",
|
||||
"displayName": "Power consumption",
|
||||
"displayNameEvent": "Power consumption changed",
|
||||
"displayNameAction": "Change power consumption",
|
||||
"type": "int",
|
||||
"unit": "Watt",
|
||||
"minValue": 0,
|
||||
"maxValue": 3600,
|
||||
"defaultValue": 0,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "60006f93-8852-433b-bbc0-f10cc3939eeb",
|
||||
"name": "temperature",
|
||||
"displayName": "Water temperature",
|
||||
"displayNameEvent": "Water temperature changed",
|
||||
"type": "double",
|
||||
"unit": "DegreeCelsius",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "2b089c93-6411-41f7-96aa-f78d5cf910cb",
|
||||
"name": "targetWaterTemperature",
|
||||
"displayName": "Target water temperature",
|
||||
"displayNameEvent": "Target water temperature changed",
|
||||
"type": "double",
|
||||
"unit": "DegreeCelsius",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "d0a7065e-7773-47d7-b474-ce8d21d55aa7",
|
||||
"name": "status",
|
||||
"displayName": "Status",
|
||||
"displayNameEvent": "Status changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "Unknown",
|
||||
"possibleValues": [
|
||||
"Unknown",
|
||||
"Heat",
|
||||
"Standby",
|
||||
"Boost heat",
|
||||
"Heat finished",
|
||||
"Setup",
|
||||
"Error Overtemp Fuse blown",
|
||||
"Error Overtemp measured",
|
||||
"Error Overtemp Electronics",
|
||||
"Error Hardware Fault",
|
||||
"Error Temp Sensor"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
181
mypv/modbustcpmaster.cpp
Normal file
181
mypv/modbustcpmaster.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "modbustcpmaster.h"
|
||||
#include "extern-plugininfo.h"
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcModbus)
|
||||
Q_LOGGING_CATEGORY(dcModbus, "Modbus")
|
||||
|
||||
ModbusTCPMaster::ModbusTCPMaster(QHostAddress IPv4Address, int port, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_IPv4Address(IPv4Address),
|
||||
m_port(port)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ModbusTCPMaster::~ModbusTCPMaster()
|
||||
{
|
||||
if (m_mb != NULL) {
|
||||
modbus_close(m_mb);
|
||||
}
|
||||
modbus_free(m_mb);
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::createInterface() {
|
||||
// TCP connction to target device
|
||||
qCDebug(dcModbus()) << "Setting up TCP connecion" << m_IPv4Address.toString() << "port:" << m_port;
|
||||
const char *address = m_IPv4Address.toString().toLatin1().data();
|
||||
m_mb = modbus_new_tcp(address, m_port);
|
||||
|
||||
if(m_mb == nullptr){
|
||||
qCWarning(dcMypv()) << "Error modbus TCP: " << modbus_strerror(errno) ;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extend the timeout to 3 seconds
|
||||
//struct timeval response_timeout;
|
||||
//response_timeout.tv_sec = 3;
|
||||
//response_timeout.tv_usec = 0;
|
||||
//modbus_set_response_timeout(m_mb, &response_timeout);
|
||||
|
||||
if(modbus_connect(m_mb) == -1){
|
||||
qCWarning(dcMypv()) << "Error connecting modbus:" << modbus_strerror(errno) ;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int ModbusTCPMaster::port()
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::setIPv4Address(QHostAddress ipv4Address)
|
||||
{
|
||||
m_IPv4Address = ipv4Address;
|
||||
if (!createInterface()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::setPort(int port)
|
||||
{
|
||||
m_port = port;
|
||||
if (!createInterface()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QHostAddress ModbusTCPMaster::ipv4Address()
|
||||
{
|
||||
return m_IPv4Address;
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::setCoil(int slaveAddress, int coilAddress, bool status)
|
||||
{
|
||||
if (m_mb == nullptr) {
|
||||
if (!createInterface())
|
||||
return false;
|
||||
}
|
||||
|
||||
if(modbus_set_slave(m_mb, slaveAddress) == -1){
|
||||
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modbus_write_bit(m_mb, coilAddress, status) == -1) {
|
||||
qCWarning(dcMypv()) << "Could not write Coil" << coilAddress << "Reason:" << modbus_strerror(errno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::setRegister(int slaveAddress, int registerAddress, int data)
|
||||
{
|
||||
if (m_mb == nullptr) {
|
||||
if (!createInterface())
|
||||
return false;
|
||||
}
|
||||
if(modbus_set_slave(m_mb, slaveAddress) == -1){
|
||||
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modbus_write_register(m_mb, registerAddress, data) == -1) {
|
||||
qCWarning(dcMypv()) << "Could not write Register" << registerAddress << "Reason:" << modbus_strerror(errno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::getCoil(int slaveAddress, int coilAddress, bool *result)
|
||||
{
|
||||
if (m_mb == nullptr) {
|
||||
if (!createInterface())
|
||||
return false;
|
||||
}
|
||||
|
||||
if(modbus_set_slave(m_mb, slaveAddress) == -1){
|
||||
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t status;
|
||||
if (modbus_read_bits(m_mb, coilAddress, 1, &status) == -1){
|
||||
qCWarning(dcMypv()) << "Could not read bits" << coilAddress << "Reason:"<< modbus_strerror(errno);
|
||||
return false;
|
||||
}
|
||||
*result = (bool)status;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::getRegister(int slaveAddress, int registerAddress, int *result)
|
||||
{
|
||||
uint16_t data;
|
||||
|
||||
if (m_mb == nullptr) {
|
||||
if (!createInterface())
|
||||
return false;
|
||||
}
|
||||
|
||||
if(modbus_set_slave(m_mb, slaveAddress) == -1){
|
||||
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modbus_read_registers(m_mb, registerAddress, 1, &data) == -1){
|
||||
qCWarning(dcMypv()) << "Could not read register" << registerAddress << "Reason:" << modbus_strerror(errno);
|
||||
return false;
|
||||
}
|
||||
*result = data;
|
||||
return true;
|
||||
}
|
||||
63
mypv/modbustcpmaster.h
Normal file
63
mypv/modbustcpmaster.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 MODBUSTCPMASTER_H
|
||||
#define MODBUSTCPMASTER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
#include <modbus/modbus.h>
|
||||
|
||||
class ModbusTCPMaster : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModbusTCPMaster(QHostAddress IPv4Address, int port, QObject *parent = 0);
|
||||
~ModbusTCPMaster();
|
||||
bool createInterface();
|
||||
|
||||
bool getCoil(int slaveAddress, int coilAddress, bool *result);
|
||||
bool getRegister(int slaveAddress, int registerAddress, int *result);
|
||||
bool setCoil(int slaveAddress, int coilAddress, bool status);
|
||||
bool setRegister(int slaveAddress, int registerAddress, int data);
|
||||
QHostAddress ipv4Address();
|
||||
int port();
|
||||
bool setIPv4Address(QHostAddress IPv4Address);
|
||||
bool setPort(int port);
|
||||
|
||||
private:
|
||||
modbus_t *m_mb;
|
||||
QHostAddress m_IPv4Address;
|
||||
int m_port;
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // MODBUSTCPMASTER_H
|
||||
19
mypv/mypv.pro
Normal file
19
mypv/mypv.pro
Normal file
@ -0,0 +1,19 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
TARGET = $$qtLibraryTarget(nymea_devicepluginmypv)
|
||||
|
||||
QT += \
|
||||
network \
|
||||
serialbus \
|
||||
|
||||
LIBS += -lmodbus
|
||||
|
||||
SOURCES += \
|
||||
devicepluginmypv.cpp \
|
||||
modbustcpmaster.cpp \
|
||||
|
||||
HEADERS += \
|
||||
devicepluginmypv.h \
|
||||
modbustcpmaster.h \
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ TEMPLATE = subdirs
|
||||
|
||||
PLUGIN_DIRS = \
|
||||
drexelundweiss \
|
||||
mypv \
|
||||
wallbe \
|
||||
|
||||
message(============================================)
|
||||
|
||||
Reference in New Issue
Block a user