Merge PR #98: iDM: Update to modbus tools and latest interfaces

This commit is contained in:
jenkins 2022-12-14 23:59:31 +01:00
commit 4a353ddf66
11 changed files with 650 additions and 864 deletions

247
idm/idm-registers.json Normal file
View File

@ -0,0 +1,247 @@
{
"className": "Idm",
"protocol": "TCP",
"endianness": "LittleEndian",
"errorLimitUntilNotReachable": 20,
"checkReachableRegister": "outdoorTemperature",
"enums": [
{
"name": "SystemOperationMode",
"values": [
{
"key": "Standby",
"value": 0
},
{
"key": "Automatic",
"value": 1
},
{
"key": "Absent",
"value": 2
},
{
"key": "WarmWaterOnly",
"value": 4
},
{
"key": "HeatingCoolingOnly",
"value": 5
}
]
},
{
"name": "HeatPumpOperationMode",
"values": [
{
"key": "Off",
"value": 0
},
{
"key": "Heating",
"value": 1
},
{
"key": "Cooling",
"value": 2
},
{
"key": "HotWater",
"value": 4
},
{
"key": "Defrost",
"value": 8
}
]
}
],
"blocks": [
{
"id": "status",
"readSchedule": "update",
"registers": [
{
"id": "currentFaultNumber",
"address": 1004,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"description": "Current fault number",
"defaultValue": "0",
"access": "RO"
},
{
"id": "systemOperationMode",
"address": 1005,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"description": "Operation mode",
"enum": "SystemOperationMode",
"defaultValue": "SystemOperationModeStandby",
"access": "RO"
},
{
"id": "smartGridStatus",
"address": 1006,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"description": "Smart grid status",
"defaultValue": "0",
"access": "RO"
}
]
},
{
"id": "energyProduced",
"readSchedule": "update",
"registers": [
{
"id": "energyHeating",
"address": 1750,
"size": 2,
"type": "float",
"registerType": "holdingRegister",
"description": "Energy heating",
"unit": "kWh",
"defaultValue": "0",
"access": "RO"
},
{
"id": "energyCooling",
"address": 1752,
"size": 2,
"type": "float",
"registerType": "holdingRegister",
"description": "Energy cooling",
"unit": "kWh",
"defaultValue": "0",
"access": "RO"
},
{
"id": "energyHotWater",
"address": 1754,
"size": 2,
"type": "float",
"registerType": "holdingRegister",
"description": "Energy hot water",
"unit": "kWh",
"defaultValue": "0",
"access": "RO"
}
]
}
],
"registers": [
{
"id": "currentPvSurplus",
"address": 74,
"size": 2,
"type": "float",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Current PV surplus ",
"unit": "kW",
"defaultValue": "0",
"access": "RW"
},
{
"id": "currentPvProduction",
"address": 78,
"size": 2,
"type": "float",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Current PV production ",
"unit": "kW",
"defaultValue": "0",
"access": "RW"
},
{
"id": "outdoorTemperature",
"address": 1000,
"size": 2,
"type": "float",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Outdoor temperature",
"unit": "°C",
"defaultValue": "0",
"access": "RO"
},
{
"id": "heatStorageTemperature",
"address": 1008,
"size": 2,
"type": "float",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Heat storage temperature",
"defaultValue": "0",
"unit": "°C",
"access": "RO"
},
{
"id": "targetHotWaterTemperature",
"address": 1032,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Target hot water temperature",
"defaultValue": "0",
"unit": "°C",
"access": "RO"
},
{
"id": "roomTemperature",
"address": 1364,
"size": 2,
"type": "float",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Room temperature",
"defaultValue": "0",
"unit": "°C",
"access": "RO"
},
{
"id": "roomTemperatureTargetHeatingEco",
"address": 1415,
"size": 2,
"type": "float",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Room target temperature",
"defaultValue": "0",
"unit": "°C",
"access": "RW"
},
{
"id": "heatPumpOperatingMode",
"address": 1090,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Heat pump operation mode",
"enum": "HeatPumpOperationMode",
"defaultValue": "HeatPumpOperationModeOff",
"access": "RO"
},
{
"id": "currentPowerConsumption",
"address": 4122,
"size": 2,
"type": "float",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Current power consumption",
"unit": "kW",
"defaultValue": "0",
"access": "RO"
}
]
}

View File

@ -1,194 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "idm.h"
#include "extern-plugininfo.h"
#include "modbushelpers.h"
#include <QTimer>
Idm::Idm(const QHostAddress &address, QObject *parent) :
QObject(parent),
m_hostAddress(address)
{
qCDebug(dcIdm()) << "iDM: Creating iDM connection" << m_hostAddress.toString();
m_modbusMaster = new ModbusTCPMaster(address, 502, this);
connect(m_modbusMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &Idm::onReceivedHoldingRegister);
connect(m_modbusMaster, &ModbusTCPMaster::readRequestError, this, &Idm::onModbusError);
connect(m_modbusMaster, &ModbusTCPMaster::writeRequestError, this, &Idm::onModbusError);
connect(m_modbusMaster, &ModbusTCPMaster::writeRequestExecuted, this, &Idm::writeRequestExecuted);
}
Idm::~Idm()
{
qCDebug(dcIdm()) << "iDM: Deleting iDM connection" << m_hostAddress.toString();
}
bool Idm::connectDevice()
{
qCDebug(dcIdm()) << "iDM: Connecting device";
return m_modbusMaster->connectDevice();
}
QHostAddress Idm::address() const
{
return m_hostAddress;
}
void Idm::getStatus()
{
// This request starts an update cycle
m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::OutsideTemperature, 2);
}
QUuid Idm::setTargetTemperature(double targetTemperature)
{
QVector<uint16_t> value = ModbusHelpers::convertFloatToRegister(targetTemperature);
return m_modbusMaster->writeHoldingRegisters(Idm::modbusUnitID, Idm::RegisterList::RoomTemperatureTargetHeatingEcoHKA, value);
}
void Idm::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector<quint16> &value)
{
Q_UNUSED(slaveAddress);
/* qCDebug(dcIdm()) << "receivedHoldingRegister " << modbusRegister << "(length: " << value.length() << ")"; */
switch (modbusRegister) {
case Idm::OutsideTemperature:
/* qCDebug(dcIdm()) << "received outside temperature"; */
if (value.length() == 2) {
m_idmInfo.outsideTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::OutsideTemperature - modbusRegister]);
}
QTimer::singleShot(200, this, [this] {
m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::CurrentFaultNumber, 1);
});
break;
case Idm::CurrentFaultNumber:
/* qCDebug(dcIdm()) << "current fault number"; */
if (value.length() == 1) {
if (value[0] > 0) {
m_idmInfo.error = true;
} else {
m_idmInfo.error = false;
}
}
QTimer::singleShot(200, this, [this] {
m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::HeatStorageTemperature, 2);
});
break;
case Idm::HeatStorageTemperature:
/* qCDebug(dcIdm()) << "received storage temperature"; */
if (value.length() == 2) {
m_idmInfo.waterTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::HeatStorageTemperature - modbusRegister]);
}
QTimer::singleShot(200, this, [this] {
m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::TargetHotWaterTemperature, 1);
});
break;
case Idm::TargetHotWaterTemperature:
/* qCDebug(dcIdm()) << "received target hot water temperature"; */
if (value.length() == 1) {
/* The hot water target temperature is stored as UCHAR (manual p. 13) */
m_idmInfo.targetWaterTemperature = (double)value[RegisterList::TargetHotWaterTemperature - modbusRegister];
}
QTimer::singleShot(200, this, [this] {
m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::HeatPumpOperatingMode, 1);
});
break;
case Idm::HeatPumpOperatingMode:
/* qCDebug(dcIdm()) << "received heat pump operating mode"; */
if (value.length() == 1) {
m_idmInfo.mode = heatPumpOperationModeToString((Idm::IdmHeatPumpMode)value[RegisterList::HeatPumpOperatingMode-modbusRegister]);
}
QTimer::singleShot(200, this, [this] {
m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::RoomTemperatureHKA, 2);
});
break;
case Idm::RoomTemperatureHKA:
/* qCDebug(dcIdm()) << "received room temperature hka"; */
if (value.length() == 2) {
m_idmInfo.roomTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::RoomTemperatureHKA - modbusRegister]);
}
QTimer::singleShot(200, this, [this] {
m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::RoomTemperatureTargetHeatingEcoHKA, 2);
});
break;
case Idm::RoomTemperatureTargetHeatingEcoHKA:
/* qCDebug(dcIdm()) << "received room temprature hka eco"; */
if (value.length() == 2) {
m_idmInfo.targetRoomTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::RoomTemperatureTargetHeatingEcoHKA - modbusRegister]);
}
QTimer::singleShot(200, this, [this] {
m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::CurrentPowerConsumptionHeatPump, 2);
});
break;
case Idm::CurrentPowerConsumptionHeatPump:
/* qCDebug(dcIdm()) << "received power consumption heat pump"; */
if (value.length() == 2) {
m_idmInfo.powerConsumptionHeatPump = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::CurrentPowerConsumptionHeatPump - modbusRegister]);
}
/* Everything read without an error
* -> set connected to true */
m_idmInfo.connected = true;
emit statusUpdated(m_idmInfo);
break;
}
}
void Idm::onModbusError()
{
qCDebug(dcIdm()) << "iDM: Received modbus error" << m_modbusMaster->errorString();
m_idmInfo.connected = false;
emit statusUpdated(m_idmInfo);
}
QString Idm::heatPumpOperationModeToString(IdmHeatPumpMode mode)
{
QString result{};
/* Operation modes according to table of manual p. 14 */
switch (mode) {
case IdmHeatPumpModeOff:
result = "Off";
break;
case IdmHeatPumpModeHeating:
result = "Heating";
break;
case IdmHeatPumpModeCooling:
result = "Cooling";
break;
case IdmHeatPumpModeHotWater:
result = "Hot water";
break;
case IdmHeatPumpModeDefrost:
result = "Defrost";
break;
}
return result;
}

183
idm/idm.h
View File

@ -1,183 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef IDM_H
#define IDM_H
#include <QObject>
#include <modbustcpmaster.h>
#include "idminfo.h"
/*
* Functionality:
* The current version allows read access to selected
* modbus registers:
* + Room temperature (HK A)
* + Water temperature
* + Outside air temperature
* + Target room temperature eco mode (HK A)
* + Target water temperature
* + Current power consumption
* + Operation mode
* + Error
*
* At present there is no write access for target
* room temperature and target water temperature. These
* result in an "invalid data access" error from the
* device.
*/
/* Note: It would be desirable to read the modbus registers
* of the Idm heat pump in groups to minimize the number
* of read requests. However, a maximum of 6 registers
* can be read simultaneously. With the given set of
* addresses it is not possible to reasonably group the
* registers, therefore they are read individually.
*/
class Idm : public QObject
{
Q_OBJECT
public:
explicit Idm(const QHostAddress &address, QObject *parent = nullptr);
~Idm();
bool connectDevice();
QHostAddress address() const;
QUuid setTargetTemperature(double targetTemperature);
void getStatus();
private:
/** Modbus Unit ID of Idm device */
static const quint16 modbusUnitID = 1;
enum IscModus {
KeineAbwarme = 0,
Heizung = 1,
Warmwasser = 4,
Warmequelle = 8,
};
/** System operation modes according to manual p. 13 */
enum IdmSysMode {
IdmSysModeStandby = 0,
IdmSysModeAutomatic = 1,
IdmSysModeAway = 2,
IdmSysModeOnlyHotwater = 4,
IdmSysModeOnlyRoomHeating = 5
};
/** Heat pump operation modes according to manual p. 14 */
enum IdmHeatPumpMode {
IdmHeatPumpModeOff = 0,
IdmHeatPumpModeHeating = 1,
IdmHeatPumpModeCooling = 2,
IdmHeatPumpModeHotWater = 4,
IdmHeatPumpModeDefrost = 8
};
/** The following modbus addresses are according to the manual
* Modbus TCP Navigatorregelung 2.0 pages 13-31.
* Comments at the end of each line give their original name
* in the German manual. */
enum RegisterList {
OutsideTemperature = 1000, // Außentemperatur (B31)
MeanOutsideTemperature = 1002, // Gemittelte Außentemperature
CurrentFaultNumber = 1004, // Aktuelle Störungsnummer
OperationModeSystem = 1005, // Betriebsart System
SmartGridStatus = 1006, // Smart Grid Status
HeatStorageTemperature = 1008, // Wärmespeichertemperatur (B38)
ColdStorageTemperature = 1010, // Kältespeichertemperatur (B40)
DrinkingWaterHeaterTempBottom = 1012, // Trinkwassererwärmertmp. unten (B41)
DrinkingWaterHeaterTempTop = 1014, // Trinkwassererwärmertmp. oben (B48)
HotWaterTapTemperature = 1030, // Warmwasserzapftemperatur (B42)
TargetHotWaterTemperature = 1032, // Warmwasser-Solltemperatur
HeatPumpOperatingMode = 1090, // Betriebsart Wärmepumpe
SummationFaultHeatPump = 1099, // Summenstörung Wärepumpe
RoomTemperatureHKA = 1364, // Heizkreis A Raumtemperature (B61)
HumiditySensor = 1392, // Feuchtesensor
RoomTemperatureTargetHeatingHKA = 1401, // Raumsolltemperatur Heizen Normal HK A
RoomTemperatureTargetHeatingEcoHKA = 1415, // Raumsolltemperatur Heizen Eco HK A
ExternalOutsideTemperature = 1690, // Externe Außentemperatur
ExternalHumidity = 1692, // Externe Feuchte
ExternalRequestTemperatureHeating = 1694, // Externe Anforderungstemperatur Heizen
ExternalRequestTemperatureCooling = 1695, // Externe Anforderungstemperatur Kühlen
HeatingRequirement = 1710, // Anforderung Heizen
CoolingRequirement = 1711, // Anforderung Kühlen
HotWaterChargingRequirement = 1712, // Anforderung Warmwasserladung
HeatQuantityHeating = 1750, // Wärmemenge Heizen
HeatQuantityCooling = 1752, // Wärmemenge Kühlen
HeatQuantityHotWater = 1754, // Wärmemenge Warmwasser
HeatQuantityDefrosting = 1756, // Wärmemenge Abtauung
HeatQuantityPassiveCooling = 1758, // Wärmemenge Passive Kühlung,
HeatQuantityPhotovolatics = 1760, // Wärmemenge Solar
HeatQuantityHeatingElemetn = 1762, // Wärmemenge Elektroheizeinsatz,
CurrentPower = 1790, // Momentanleistung
CurrentPowerSolar = 1792, // MomentanleistungSolar
SolarCollectorTemperature = 1850, // SolarKollektortemperatur (B73)
SolarCollectorReturnTemperature = 1852, // SolarKollektorruecklauftemperatur (B75)
SolarChargeTemperature = 1854, // SolarLadetemperatur (B74)
SolarOperatingMode = 1856, // Betriebsart Solar
ISCMode = 1875, // ISCModus
AcknowledgeFaultMessages = 1999, // Störmeldungen quittieren
TargetRoomTemperatureZ1R1 = 2004, // Zonenmodul 1 Raumsolltemperatur Raum 1
CurrentPhotovoltaicsSurplus = 74, // Aktueller PV-Überschuss
CurrentPhotovoltaicsProduction = 78, // Aktueller PV Produktion
CurrentPowerConsumptionHeatPump = 4122, // Aktuelle Leistungsaufnahme Wärmepumpe
};
/* Note: This class only requires one IP address and one
* TCP Modbus connection. Multiple devices are managed
* within the IntegrationPluginIdm class. */
QHostAddress m_hostAddress;
/** Pointer to ModbusTCPMaster object, responsible for low-level communicaiton */
ModbusTCPMaster *m_modbusMaster = nullptr;
/** This structure is allocated within onRequestStatus and filled
* by the receivedStatusGroupx functions */
IdmInfo m_idmInfo;
/** Converts a heat pump operation mode code to a string (according to manual p. 14) */
QString heatPumpOperationModeToString(IdmHeatPumpMode mode);
signals:
void statusUpdated(const IdmInfo &info);
void targetRoomTemperatureChanged();
void writeRequestExecuted(const QUuid &requestId, bool success);
private slots:
void onModbusError();
void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector<quint16> &value);
};
#endif // IDM_H

View File

@ -1,14 +1,12 @@
include(../plugins.pri)
# Generate modbus connection
MODBUS_CONNECTIONS += idm-registers.json
#MODBUS_TOOLS_CONFIG += VERBOSE
include(../modbus.pri)
SOURCES += \
idm.cpp \
integrationpluginidm.cpp \
modbushelpers.cpp
integrationpluginidm.cpp
HEADERS += \
idm.h \
idminfo.h \
integrationpluginidm.h \
modbushelpers.h
integrationpluginidm.h

View File

@ -1,79 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef IDMINFO_H
#define IDMINFO_H
#include <QMetaType>
#include <QString>
/** This struct holds the status information that is read from the IDM device
* and passed to the nymea framework within this plugin.
*/
struct IdmInfo {
/** Set to true, if register values can be read,
* false in case of communication problems */
bool connected;
bool power;
/** RegisterList::OutsideTemperature */
double roomTemperature;
/** RegisterList::ExternalOutsideTemperature */
double outsideTemperature;
/** RegisterList::HeatStorageTemperature */
double waterTemperature;
/** RegisterList::TargetRoomTemperatureZ1R1 (zone 1, room 1) */
double targetRoomTemperature;
/** RegisterList::TargetHotWaterTemperature */
double targetWaterTemperature;
/** RegisterList::HumiditySensor */
double humidity;
/** RegisterList::CurrentPowerConsumptionHeatPump */
double powerConsumptionHeatPump;
/** RegisterList::OperationModeSystem */
QString mode;
/** True if there is an error code set
* (RegisterList::CurrentFaultNumber != 0) */
bool error;
};
Q_DECLARE_METATYPE(IdmInfo);
#endif

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -32,6 +32,7 @@
#include "plugininfo.h"
#include <network/networkdevicediscovery.h>
#include <hardwaremanager.h>
IntegrationPluginIdm::IntegrationPluginIdm()
{
@ -79,7 +80,6 @@ void IntegrationPluginIdm::discoverThings(ThingDiscoveryInfo *info)
ParamList params;
params << Param(navigator2ThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
params << Param(navigator2ThingIpAddressParamTypeId, networkDeviceInfo.address().toString());
descriptor.setParams(params);
info->addThingDescriptor(descriptor);
}
@ -90,77 +90,80 @@ void IntegrationPluginIdm::discoverThings(ThingDiscoveryInfo *info)
void IntegrationPluginIdm::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
qCDebug(dcIdm()) << "setupThing called" << thing->name();
qCDebug(dcIdm()) << "Setup" << thing << thing->params();
// Inverter (connection)
if (thing->thingClassId() == navigator2ThingClassId) {
QHostAddress hostAddress = QHostAddress(thing->paramValue(navigator2ThingIpAddressParamTypeId).toString());
if (hostAddress.isNull()) {
qCWarning(dcIdm()) << "Setup failed, IP address not valid";
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("No IP address given"));
return;
}
if (m_idmConnections.contains(thing)) {
qCDebug(dcIdm()) << "Cleaning up after reconfiguration";
m_idmConnections.take(thing)->deleteLater();
}
// Handle reconfigure
if (m_connections.contains(thing)) {
qCDebug(dcIdm()) << "Reconfiguring existing thing" << thing->name();
m_connections.take(thing)->deleteLater();
qCDebug(dcIdm()) << "User entered address: " << hostAddress.toString();
/* Check, if address is already in use for another device */
foreach (Idm *idm, m_idmConnections) {
if (hostAddress.isEqual(idm->address())) {
qCWarning(dcIdm()) << "Address already in use";
info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("IP address already in use"));
return;
if (m_monitors.contains(thing)) {
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
}
qCDebug(dcIdm()) << "Creating Idm object";
/* Create new Idm object and store it in hash table */
Idm *idm = new Idm(hostAddress, this);
if (idm->connectDevice()) {
qCWarning(dcIdm()) << "Could not connect to thing";
info->finish(Thing::ThingErrorHardwareNotAvailable);
MacAddress macAddress = MacAddress(thing->paramValue(navigator2ThingMacAddressParamTypeId).toString());
if (!macAddress.isValid()) {
qCWarning(dcIdm()) << "The configured mac address is not valid" << thing->params();
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not known. Please reconfigure the thing."));
return;
}
connect(idm, &Idm::statusUpdated, info, [info, thing, idm, this] (const IdmInfo &idmInfo) {
if (idmInfo.connected) {
m_idmConnections.insert(thing, idm);
connect(idm, &Idm::statusUpdated, this, &IntegrationPluginIdm::onStatusUpdated);
connect(idm, &Idm::writeRequestExecuted, this, &IntegrationPluginIdm::onWriteRequestExecuted);
info->finish(Thing::ThingErrorNoError);
// Create the monitor
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
m_monitors.insert(thing, monitor);
QHostAddress address = monitor->networkDeviceInfo().address();
if (address.isNull()) {
qCWarning(dcIdm()) << "Cannot set up thing. The host address is not known yet. Maybe it will be available in the next run...";
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The host address is not known yet. Trying later again."));
return;
}
// Clean up in case the setup gets aborted
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
if (m_monitors.contains(thing)) {
qCDebug(dcIdm()) << "Unregister monitor because setup has been aborted.";
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
});
connect(idm, &Idm::destroyed, this, [thing, this] {m_idmConnections.remove(thing);});
connect(info, &ThingSetupInfo::aborted, idm, &Idm::deleteLater);
// Wait for the monitor to be ready
if (monitor->reachable()) {
// Thing already reachable...let's continue with the setup
setupConnection(info);
} else {
qCDebug(dcIdm()) << "Waiting for the network monitor to get reachable before continue to set up the connection" << thing->name() << address.toString() << "...";
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
if (reachable) {
qCDebug(dcIdm()) << "The monitor for thing setup" << thing->name() << "is now reachable. Continue setup...";
setupConnection(info);
}
});
}
} else {
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
return;
}
}
void IntegrationPluginIdm::postSetupThing(Thing *thing)
{
qCDebug(dcIdm()) << "postSetupThing called" << thing->name();
Q_UNUSED(thing)
if (!m_refreshTimer) {
qCDebug(dcIdm()) << "Starting refresh timer";
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginIdm::onRefreshTimer);
}
connect(m_refreshTimer, &PluginTimer::timeout, this, [this](){
foreach (IdmModbusTcpConnection *connection, m_connections) {
connection->update();
}
});
if (thing->thingClassId() == navigator2ThingClassId) {
Idm *idm = m_idmConnections.value(thing);
if (!idm) {
qCWarning(dcIdm()) << "Could not find any iDM connection for" << thing->name();
return;
}
thing->setStateValue(navigator2ConnectedStateTypeId, true);
update(thing);
m_refreshTimer->start();
}
}
@ -169,11 +172,15 @@ void IntegrationPluginIdm::thingRemoved(Thing *thing)
qCDebug(dcIdm()) << "thingRemoved called" << thing->name();
if (thing->thingClassId() == navigator2ThingClassId) {
if (m_idmConnections.contains(thing)) {
m_idmConnections.take(thing)->deleteLater();
if (m_connections.contains(thing)) {
m_connections.take(thing)->deleteLater();
}
}
// Unregister related hardware resources
if (m_monitors.contains(thing))
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
if (myThings().isEmpty()) {
qCDebug(dcIdm()) << "Stopping refresh timer";
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
@ -187,15 +194,37 @@ void IntegrationPluginIdm::executeAction(ThingActionInfo *info)
Action action = info->action();
if (thing->thingClassId() == navigator2ThingClassId) {
Idm *idm = m_idmConnections.value(thing);
if (!idm) {
return info->finish(Thing::ThingErrorHardwareFailure);
IdmModbusTcpConnection *connection = m_connections.value(thing);
if (!connection) {
qCWarning(dcIdm()) << "Failed to execute action. Could not find connection for" << thing;
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
if (!connection->connected())
info->finish(Thing::ThingErrorHardwareNotAvailable);
if (action.actionTypeId() == navigator2TargetTemperatureActionTypeId) {
double targetTemperature = thing->stateValue(navigator2TargetTemperatureStateTypeId).toDouble();
QUuid requestId = idm->setTargetTemperature(targetTemperature);
m_asyncActions.insert(requestId, info);
connect(info, &ThingActionInfo::aborted, [requestId, this] (){ m_asyncActions.remove(requestId); });
float targetTemperature = action.paramValue(navigator2TargetTemperatureActionTargetTemperatureParamTypeId).toDouble();
qCDebug(dcIdm()) << "Setting room target temperature to" << targetTemperature << "°C";
QModbusReply *reply = connection->setRoomTemperatureTargetHeatingEco(targetTemperature);
if (!reply) {
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, info, [info, reply, thing, targetTemperature]{
if (reply->error() != QModbusDevice::NoError) {
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
qCDebug(dcIdm()) << "Room target temperature set successfully to" << targetTemperature << "°C";
thing->setStateValue(navigator2TargetTemperatureStateTypeId, targetTemperature);
info->finish(Thing::ThingErrorNoError);
});
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8());
}
@ -204,55 +233,144 @@ void IntegrationPluginIdm::executeAction(ThingActionInfo *info)
}
}
void IntegrationPluginIdm::update(Thing *thing)
void IntegrationPluginIdm::setupConnection(ThingSetupInfo *info)
{
if (thing->thingClassId() == navigator2ThingClassId) {
qCDebug(dcIdm()) << "Updating thing" << thing->name();
Idm *idm = m_idmConnections.value(thing);
if (!idm) { return; };
idm->getStatus();
}
}
Thing *thing = info->thing();
void IntegrationPluginIdm::onStatusUpdated(const IdmInfo &info)
{
qCDebug(dcIdm()) << "Received status from heat pump";
Idm *idm = qobject_cast<Idm *>(sender());
Thing *thing = m_idmConnections.key(idm);
QHostAddress address = m_monitors.value(thing)->networkDeviceInfo().address();
if (!thing)
return;
qCDebug(dcIdm()) << "Setting up IDM on" << address.toString() << 502 << "unit ID:" << 1;
IdmModbusTcpConnection *connection = new IdmModbusTcpConnection(address, 502, 1, this);
connect(info, &ThingSetupInfo::aborted, connection, &IdmModbusTcpConnection::deleteLater);
/* Received a structure holding the status info of the
* heat pump. Update the thing states with the individual fields. */
thing->setStateValue(navigator2ConnectedStateTypeId, info.connected);
thing->setStateValue(navigator2PowerStateTypeId, info.power);
thing->setStateValue(navigator2TemperatureStateTypeId, info.roomTemperature);
thing->setStateValue(navigator2OutsideAirTemperatureStateTypeId, info.outsideTemperature);
thing->setStateValue(navigator2WaterTemperatureStateTypeId, info.waterTemperature);
thing->setStateValue(navigator2TargetTemperatureStateTypeId, info.targetRoomTemperature);
thing->setStateValue(navigator2TargetWaterTemperatureStateTypeId, info.targetWaterTemperature);
thing->setStateValue(navigator2CurrentPowerConsumptionHeatPumpStateTypeId, info.powerConsumptionHeatPump);
thing->setStateValue(navigator2ModeStateTypeId, info.mode);
thing->setStateValue(navigator2ErrorStateTypeId, info.error);
}
// Reconnect on monitor reachable changed
NetworkDeviceMonitor *monitor = m_monitors.value(thing);
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){
qCDebug(dcIdm()) << "Network device monitor reachable changed for" << thing->name() << reachable;
if (!thing->setupComplete())
return;
void IntegrationPluginIdm::onWriteRequestExecuted(const QUuid &requestId, bool success)
{
if (m_asyncActions.contains(requestId)) {
ThingActionInfo *info = m_asyncActions.value(requestId);
if (success) {
info->finish(Thing::ThingErrorNoError);
} else {
info->finish(Thing::ThingErrorHardwareNotAvailable);
if (reachable && !thing->stateValue("connected").toBool()) {
connection->setHostAddress(monitor->networkDeviceInfo().address());
connection->connectDevice();
} else if (!reachable) {
// Note: We disable autoreconnect explicitly and we will
// connect the device once the monitor says it is reachable again
connection->disconnectDevice();
}
}
});
connect(connection, &IdmModbusTcpConnection::reachableChanged, thing, [this, thing, connection](bool reachable){
qCDebug(dcIdm()) << "Reachable changed to" << reachable << "for" << thing;
if (reachable) {
// Connected true will be set after successfull init
connection->initialize();
} else {
thing->setStateValue("connected", false);
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
childThing->setStateValue("connected", false);
}
}
});
connect(connection, &IdmModbusTcpConnection::initializationFinished, thing, [=](bool success){
if (!thing->setupComplete())
return;
thing->setStateValue("connected", success);
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
childThing->setStateValue("connected", success);
}
if (!success) {
// Try once to reconnect the device
connection->reconnectDevice();
}
});
connect(connection, &IdmModbusTcpConnection::initializationFinished, info, [=](bool success){
if (!success) {
qCWarning(dcIdm()) << "Connection init finished with errors" << thing->name() << connection->hostAddress().toString();
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
connection->deleteLater();
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Could not initialize the communication with the device."));
return;
}
qCDebug(dcIdm()) << "Connection init finished successfully" << connection;
m_connections.insert(thing, connection);
info->finish(Thing::ThingErrorNoError);
// Set connected true
thing->setStateValue("connected", true);
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
childThing->setStateValue("connected", true);
}
connect(connection, &IdmModbusTcpConnection::updateFinished, thing, [=](){
qCDebug(dcIdm()) << "Updated" << connection;
thing->setStateValue(navigator2ConnectedStateTypeId, connection->reachable());
thing->setStateValue(navigator2TemperatureStateTypeId, connection->roomTemperature());
thing->setStateValue(navigator2OutsideTemperatureStateTypeId, connection->outdoorTemperature());
thing->setStateValue(navigator2WaterTemperatureStateTypeId, connection->heatStorageTemperature());
thing->setStateValue(navigator2TargetTemperatureStateTypeId, connection->roomTemperatureTargetHeatingEco());
thing->setStateValue(navigator2TargetWaterTemperatureStateTypeId, connection->targetHotWaterTemperature());
thing->setStateValue(navigator2CurrentPowerStateTypeId, connection->currentPowerConsumption() * 1000.0);
thing->setStateValue(navigator2EnergyProducedHeatingStateTypeId, connection->energyHeating());
thing->setStateValue(navigator2EnergyProducedCoolingStateTypeId, connection->energyCooling());
thing->setStateValue(navigator2EnergyProducedHotWaterStateTypeId, connection->energyHotWater());
switch (connection->heatPumpOperatingMode()) {
case IdmModbusTcpConnection::HeatPumpOperationModeOff:
thing->setStateValue(navigator2ModeStateTypeId, "Off");
break;
case IdmModbusTcpConnection::HeatPumpOperationModeHeating:
thing->setStateValue(navigator2ModeStateTypeId, "Heating");
break;
case IdmModbusTcpConnection::HeatPumpOperationModeCooling:
thing->setStateValue(navigator2ModeStateTypeId, "Cooling");
break;
case IdmModbusTcpConnection::HeatPumpOperationModeHotWater:
thing->setStateValue(navigator2ModeStateTypeId, "Hot water");
break;
case IdmModbusTcpConnection::HeatPumpOperationModeDefrost:
thing->setStateValue(navigator2ModeStateTypeId, "Defrost");
break;
}
thing->setStateValue(navigator2HeatingOnStateTypeId, connection->heatPumpOperatingMode() == IdmModbusTcpConnection::HeatPumpOperationModeHeating);
thing->setStateValue(navigator2CoolingOnStateTypeId, connection->heatPumpOperatingMode() == IdmModbusTcpConnection::HeatPumpOperationModeCooling);
switch (connection->systemOperationMode()) {
case IdmModbusTcpConnection::SystemOperationModeStandby:
thing->setStateValue(navigator2OperationModeStateTypeId, "Standby");
break;
case IdmModbusTcpConnection::SystemOperationModeAutomatic:
thing->setStateValue(navigator2OperationModeStateTypeId, "Automatic");
break;
case IdmModbusTcpConnection::SystemOperationModeAbsent:
thing->setStateValue(navigator2OperationModeStateTypeId, "Absent");
break;
case IdmModbusTcpConnection::SystemOperationModeWarmWaterOnly:
thing->setStateValue(navigator2OperationModeStateTypeId, "Hot water only");
break;
case IdmModbusTcpConnection::SystemOperationModeHeatingCoolingOnly:
thing->setStateValue(navigator2OperationModeStateTypeId, "Heating cooling only");
break;
}
thing->setStateValue(navigator2ErrorStateTypeId, connection->currentFaultNumber());
});
// Update registers
connection->update();
});
connection->connectDevice();
}
void IntegrationPluginIdm::onRefreshTimer()
{
foreach (Thing *thing, myThings().filterByThingClassId(navigator2ThingClassId)) {
update(thing);
}
}

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -32,11 +32,10 @@
#define INTEGRATIONPLUGINIDM_H
#include <integrations/integrationplugin.h>
#include <network/networkdevicemonitor.h>
#include <plugintimer.h>
#include "idm.h"
#include <QUuid>
#include "idmmodbustcpconnection.h"
class IntegrationPluginIdm: public IntegrationPlugin
{
@ -56,16 +55,10 @@ public:
private:
PluginTimer *m_refreshTimer = nullptr;
QHash<Thing *, Idm *> m_idmConnections;
QHash<QUuid, ThingActionInfo *> m_asyncActions;
void update(Thing *thing);
void onRefreshTimer();
private slots:
void onStatusUpdated(const IdmInfo &info);
void onWriteRequestExecuted(const QUuid &requestId, bool success);
QHash<Thing *, IdmModbusTcpConnection *> m_connections;
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
void setupConnection(ThingSetupInfo *info);
};
#endif // INTEGRATIONPLUGINIDM_H

View File

@ -13,16 +13,8 @@
"displayName": "Navigator 2.0",
"id": "1c95ac91-4eca-4cbf-b0f4-d60d35d069ed",
"createMethods": ["user", "discovery"],
"interfaces": ["thermostat", "connectable"],
"interfaces": ["thermostat", "smartmeterconsumer", "connectable" ],
"paramTypes": [
{
"id": "05714e5c-d66a-4095-bbff-a0eb96fb035b",
"name":"ipAddress",
"displayName": "IP address",
"inputType": "IPv4Address",
"type": "QString",
"defaultValue": "0.0.0.0"
},
{
"id": "d178ca29-41a1-4f56-82ec-76a833c1de50",
"name": "macAddress",
@ -36,42 +28,14 @@
"id": "cfd71e64-b666-45ef-8db0-8213acd82c5f",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "33c27167-8e24-4cc5-943c-d17cd03e0f68",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"type": "bool",
"defaultValue": false
},
{
"id": "f0f596bf-7e45-43ea-b3d4-767b82dd422a",
"name": "temperature",
"displayName": "Room temperature",
"displayNameEvent": "Room temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "fcf8e97f-a672-407f-94ae-30df15b310f4",
"name": "waterTemperature",
"displayName": "Water temperature",
"displayNameEvent": "Water temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "9f3462c2-7c42-4eeb-afc4-092e1e41a25d",
"name": "outsideAirTemperature",
"displayName": "Outside air temperature",
"displayNameEvent": "Outside air temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
@ -81,39 +45,99 @@
"name": "targetTemperature",
"displayName": "Target room temperature",
"displayNameAction": "Set target room temperature",
"displayNameEvent": "Target room temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"minValue": "18",
"maxValue": "28",
"defaultValue": 22.00,
"defaultValue": 21.00,
"writable": true
},
{
"id": "fcf8e97f-a672-407f-94ae-30df15b310f4",
"name": "waterTemperature",
"displayName": "Water temperature",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "22543424-6f4f-4200-9f90-111df28d50c9",
"name": "heatingOn",
"displayName": "Heating",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "5719b9f6-6581-4ea8-b000-4ae6a852bb2d",
"name": "coolingOn",
"displayName": "Heating",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "9f3462c2-7c42-4eeb-afc4-092e1e41a25d",
"name": "outsideTemperature",
"displayName": "Outside temperature",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0,
"cached": false
},
{
"id": "746244d6-dd37-4af8-b2ae-a7d8463e51e2",
"name": "targetWaterTemperature",
"displayName": "Target water temperature",
"displayNameEvent": "Target water temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0.00
},
{
"id": "b98fb325-100d-4eae-bf8d-97e8f7e1eb00",
"name": "currentPowerConsumptionHeatPump",
"displayName": "Current power consumption",
"displayNameEvent": "Current power consumption heat pump changed",
"displayNameAction": "Change current power consumption het pump",
"name": "currentPower",
"displayName": "Current power",
"type": "double",
"unit": "KiloWatt",
"unit": "Watt",
"defaultValue": 0.00,
"cached": false
},
{
"id": "b457dc5a-920a-4c92-9956-a703e69f6084",
"name": "totalEnergyConsumed",
"displayName": "Total energy consumed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.00
},
{
"id": "bfad9bbe-063d-497e-b13e-5de0f4738c91",
"name": "energyProducedHeating",
"displayName": "Total heating energy",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.00
},
{
"id": "9bef3a6c-4a6b-4023-a82e-c5263765310d",
"name": "energyProducedCooling",
"displayName": "Total cooling energy",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.00
},
{
"id": "0b5114ec-295f-42e6-a058-3e61f6da46b0",
"name": "energyProducedHotWater",
"displayName": "Total hot water energy",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.00
},
{
"id": "e539366b-44da-4119-b11b-497bcdb1f522",
"name": "mode",
"displayName": "Mode",
"displayNameEvent": "Mode changed",
"type": "QString",
"defaultValue": "Off",
"possibleValues": [
@ -124,13 +148,27 @@
"Defrost"
]
},
{
"id": "fb71c8f1-2b77-403d-b2ad-f3cba9baf237",
"name": "operationMode",
"displayName": "Operation mode",
"type": "QString",
"defaultValue": "Automatic",
"possibleValues": [
"Standby",
"Automatic",
"Absent",
"Hot water only",
"Heating cooling only"
],
"cached": false
},
{
"id": "49fd83ee-ddf3-4477-9ee4-e01c53283b43",
"name": "error",
"displayName": "Error",
"displayNameEvent": "Error changed",
"type": "bool",
"defaultValue": false
"type": "int",
"defaultValue": 0
}
]
}

View File

@ -1,61 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "modbushelpers.h"
float ModbusHelpers::convertRegisterToFloat(const quint16 *reg) {
float result = 0.0;
if (reg != nullptr) {
/* low-order byte is sent first, so swap order */
quint32 tmp = 0;
tmp |= ((quint32)(reg[1]) << 16) & 0xFFFF0000;
tmp |= reg[0];
/* copy value over to float variable without any conversion */
/* needs to be done with char * to avoid pedantic compiler errors */
memcpy((char *)&result, (char *)&tmp, sizeof(result));
}
return result;
}
QVector<quint16> ModbusHelpers::convertFloatToRegister(float value)
{
quint32 tmp = 0;
memcpy((char *)&tmp, (char *)&value, sizeof(value));
QVector<quint16> reg;
reg.append((quint16)(tmp));
reg.append((quint16)((tmp & 0xFFFF0000) >> 16));
return reg;
}

View File

@ -1,44 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MODBUSHELPERS_H
#define MODBUSHELPERS_H
#include <QtGlobal>
#include <QVector>
class ModbusHelpers {
public:
static float convertRegisterToFloat(const quint16 *reg);
static QVector<quint16> convertFloatToRegister(float value);
};
#endif

View File

@ -2,198 +2,151 @@
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>Idm</name>
<name>IntegrationPluginIdm</name>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="61"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="64"/>
<location filename="../integrationpluginidm.cpp" line="46"/>
<source>The discovery is not available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginidm.cpp" line="111"/>
<source>The MAC address is not known. Please reconfigure the thing.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginidm.cpp" line="123"/>
<source>The host address is not known yet. Trying later again.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginidm.cpp" line="296"/>
<source>Could not initialize the communication with the device.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>idm</name>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="47"/>
<source>Connected</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: connected, ID: {cfd71e64-b666-45ef-8db0-8213acd82c5f})
----------
The name of the StateType ({cfd71e64-b666-45ef-8db0-8213acd82c5f}) of ThingClass navigator2</extracomment>
<extracomment>The name of the StateType ({cfd71e64-b666-45ef-8db0-8213acd82c5f}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="67"/>
<source>Connected changed</source>
<extracomment>The name of the EventType ({cfd71e64-b666-45ef-8db0-8213acd82c5f}) of ThingClass navigator2</extracomment>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="50"/>
<source>Current power</source>
<extracomment>The name of the StateType ({b98fb325-100d-4eae-bf8d-97e8f7e1eb00}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="70"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="73"/>
<source>Current power consumption</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: currentPowerConsumptionHeatPump, ID: {b98fb325-100d-4eae-bf8d-97e8f7e1eb00})
----------
The name of the StateType ({b98fb325-100d-4eae-bf8d-97e8f7e1eb00}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="76"/>
<source>Current power consumption heat pump changed</source>
<extracomment>The name of the EventType ({b98fb325-100d-4eae-bf8d-97e8f7e1eb00}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="79"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="82"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="53"/>
<source>Error</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: error, ID: {49fd83ee-ddf3-4477-9ee4-e01c53283b43})
<extracomment>The name of the StateType ({49fd83ee-ddf3-4477-9ee4-e01c53283b43}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="56"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="59"/>
<source>Heating</source>
<extracomment>The name of the StateType ({5719b9f6-6581-4ea8-b000-4ae6a852bb2d}) of ThingClass navigator2
----------
The name of the StateType ({49fd83ee-ddf3-4477-9ee4-e01c53283b43}) of ThingClass navigator2</extracomment>
The name of the StateType ({22543424-6f4f-4200-9f90-111df28d50c9}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="85"/>
<source>Error changed</source>
<extracomment>The name of the EventType ({49fd83ee-ddf3-4477-9ee4-e01c53283b43}) of ThingClass navigator2</extracomment>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="62"/>
<source>MAC address</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, Type: thing, ID: {d178ca29-41a1-4f56-82ec-76a833c1de50})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="88"/>
<source>IP address</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, Type: thing, ID: {05714e5c-d66a-4095-bbff-a0eb96fb035b})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="91"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="94"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="65"/>
<source>Mode</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: mode, ID: {e539366b-44da-4119-b11b-497bcdb1f522})
----------
The name of the StateType ({e539366b-44da-4119-b11b-497bcdb1f522}) of ThingClass navigator2</extracomment>
<extracomment>The name of the StateType ({e539366b-44da-4119-b11b-497bcdb1f522}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="97"/>
<source>Mode changed</source>
<extracomment>The name of the EventType ({e539366b-44da-4119-b11b-497bcdb1f522}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="100"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="68"/>
<source>Navigator 2.0</source>
<extracomment>The name of the ThingClass ({1c95ac91-4eca-4cbf-b0f4-d60d35d069ed})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="103"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="106"/>
<source>Outside air temperature</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: outsideAirTemperature, ID: {9f3462c2-7c42-4eeb-afc4-092e1e41a25d})
----------
The name of the StateType ({9f3462c2-7c42-4eeb-afc4-092e1e41a25d}) of ThingClass navigator2</extracomment>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="71"/>
<source>Operation mode</source>
<extracomment>The name of the StateType ({fb71c8f1-2b77-403d-b2ad-f3cba9baf237}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="109"/>
<source>Outside air temperature changed</source>
<extracomment>The name of the EventType ({9f3462c2-7c42-4eeb-afc4-092e1e41a25d}) of ThingClass navigator2</extracomment>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="74"/>
<source>Outside temperature</source>
<extracomment>The name of the StateType ({9f3462c2-7c42-4eeb-afc4-092e1e41a25d}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="112"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="115"/>
<source>Power</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: power, ID: {33c27167-8e24-4cc5-943c-d17cd03e0f68})
----------
The name of the StateType ({33c27167-8e24-4cc5-943c-d17cd03e0f68}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="118"/>
<source>Power changed</source>
<extracomment>The name of the EventType ({33c27167-8e24-4cc5-943c-d17cd03e0f68}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="121"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="124"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="77"/>
<source>Room temperature</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: temperature, ID: {f0f596bf-7e45-43ea-b3d4-767b82dd422a})
----------
The name of the StateType ({f0f596bf-7e45-43ea-b3d4-767b82dd422a}) of ThingClass navigator2</extracomment>
<extracomment>The name of the StateType ({f0f596bf-7e45-43ea-b3d4-767b82dd422a}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="127"/>
<source>Room temperature changed</source>
<extracomment>The name of the EventType ({f0f596bf-7e45-43ea-b3d4-767b82dd422a}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="130"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="80"/>
<source>Set target room temperature</source>
<extracomment>The name of the ActionType ({efae7493-68c3-4cb9-853c-81011bdf09ca}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="133"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="136"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="139"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="83"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="86"/>
<source>Target room temperature</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, ActionType: targetTemperature, ID: {efae7493-68c3-4cb9-853c-81011bdf09ca})
----------
The name of the ParamType (ThingClass: navigator2, EventType: targetTemperature, ID: {efae7493-68c3-4cb9-853c-81011bdf09ca})
----------
The name of the StateType ({efae7493-68c3-4cb9-853c-81011bdf09ca}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="142"/>
<source>Target room temperature changed</source>
<extracomment>The name of the EventType ({efae7493-68c3-4cb9-853c-81011bdf09ca}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="145"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="148"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="89"/>
<source>Target water temperature</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: targetWaterTemperature, ID: {746244d6-dd37-4af8-b2ae-a7d8463e51e2})
----------
The name of the StateType ({746244d6-dd37-4af8-b2ae-a7d8463e51e2}) of ThingClass navigator2</extracomment>
<extracomment>The name of the StateType ({746244d6-dd37-4af8-b2ae-a7d8463e51e2}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="151"/>
<source>Target water temperature changed</source>
<extracomment>The name of the EventType ({746244d6-dd37-4af8-b2ae-a7d8463e51e2}) of ThingClass navigator2</extracomment>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="92"/>
<source>Total cooling energy</source>
<extracomment>The name of the StateType ({9bef3a6c-4a6b-4023-a82e-c5263765310d}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="154"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="157"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="95"/>
<source>Total energy consumed</source>
<extracomment>The name of the StateType ({b457dc5a-920a-4c92-9956-a703e69f6084}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="98"/>
<source>Total heating energy</source>
<extracomment>The name of the StateType ({bfad9bbe-063d-497e-b13e-5de0f4738c91}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="101"/>
<source>Total hot water energy</source>
<extracomment>The name of the StateType ({0b5114ec-295f-42e6-a058-3e61f6da46b0}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="104"/>
<source>Water temperature</source>
<extracomment>The name of the ParamType (ThingClass: navigator2, EventType: waterTemperature, ID: {fcf8e97f-a672-407f-94ae-30df15b310f4})
----------
The name of the StateType ({fcf8e97f-a672-407f-94ae-30df15b310f4}) of ThingClass navigator2</extracomment>
<extracomment>The name of the StateType ({fcf8e97f-a672-407f-94ae-30df15b310f4}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="160"/>
<source>Water temperature changed</source>
<extracomment>The name of the EventType ({fcf8e97f-a672-407f-94ae-30df15b310f4}) of ThingClass navigator2</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="163"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="166"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="107"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/idm/plugininfo.h" line="110"/>
<source>iDM</source>
<extracomment>The name of the vendor ({6f54e4b0-1057-4004-87a9-97fdf4581625})
----------
The name of the plugin Idm ({3968d86d-d51a-4ad1-a185-91faa017e38f})</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>IntegrationPluginIdm</name>
<message>
<location filename="../integrationpluginidm.cpp" line="50"/>
<source>No IP address given</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginidm.cpp" line="65"/>
<source>IP address already in use</source>
The name of the plugin idm ({3968d86d-d51a-4ad1-a185-91faa017e38f})</extracomment>
<translation type="unfinished"></translation>
</message>
</context>