diff --git a/idm/idm-registers.json b/idm/idm-registers.json new file mode 100644 index 0000000..790ea64 --- /dev/null +++ b/idm/idm-registers.json @@ -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" + } + ] +} diff --git a/idm/idm.cpp b/idm/idm.cpp deleted file mode 100644 index 9ea769e..0000000 --- a/idm/idm.cpp +++ /dev/null @@ -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 . -* -* 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 - -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 value = ModbusHelpers::convertFloatToRegister(targetTemperature); - return m_modbusMaster->writeHoldingRegisters(Idm::modbusUnitID, Idm::RegisterList::RoomTemperatureTargetHeatingEcoHKA, value); -} - -void Idm::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector &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; -} diff --git a/idm/idm.h b/idm/idm.h deleted file mode 100644 index 7c78d21..0000000 --- a/idm/idm.h +++ /dev/null @@ -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 . -* -* 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 - -#include - -#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 &value); -}; - -#endif // IDM_H diff --git a/idm/idm.pro b/idm/idm.pro index d8a1e12..c5c3cc4 100644 --- a/idm/idm.pro +++ b/idm/idm.pro @@ -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 diff --git a/idm/idminfo.h b/idm/idminfo.h deleted file mode 100644 index 6e8d7f1..0000000 --- a/idm/idminfo.h +++ /dev/null @@ -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 . -* -* 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 -#include - -/** 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 - diff --git a/idm/integrationpluginidm.cpp b/idm/integrationpluginidm.cpp index 8913958..b613895 100644 --- a/idm/integrationpluginidm.cpp +++ b/idm/integrationpluginidm.cpp @@ -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 +#include 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(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); - } -} + diff --git a/idm/integrationpluginidm.h b/idm/integrationpluginidm.h index 860196c..8671330 100644 --- a/idm/integrationpluginidm.h +++ b/idm/integrationpluginidm.h @@ -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 +#include #include -#include "idm.h" - -#include +#include "idmmodbustcpconnection.h" class IntegrationPluginIdm: public IntegrationPlugin { @@ -56,16 +55,10 @@ public: private: PluginTimer *m_refreshTimer = nullptr; - QHash m_idmConnections; - QHash m_asyncActions; - - void update(Thing *thing); - void onRefreshTimer(); - -private slots: - void onStatusUpdated(const IdmInfo &info); - void onWriteRequestExecuted(const QUuid &requestId, bool success); + QHash m_connections; + QHash m_monitors; + void setupConnection(ThingSetupInfo *info); }; #endif // INTEGRATIONPLUGINIDM_H diff --git a/idm/integrationpluginidm.json b/idm/integrationpluginidm.json index a17f4d4..3fb1bff 100644 --- a/idm/integrationpluginidm.json +++ b/idm/integrationpluginidm.json @@ -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 } ] } diff --git a/idm/modbushelpers.cpp b/idm/modbushelpers.cpp deleted file mode 100644 index 043b671..0000000 --- a/idm/modbushelpers.cpp +++ /dev/null @@ -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 . -* -* 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 ModbusHelpers::convertFloatToRegister(float value) -{ - quint32 tmp = 0; - memcpy((char *)&tmp, (char *)&value, sizeof(value)); - - QVector reg; - reg.append((quint16)(tmp)); - reg.append((quint16)((tmp & 0xFFFF0000) >> 16)); - return reg; -} - diff --git a/idm/modbushelpers.h b/idm/modbushelpers.h deleted file mode 100644 index bae33f3..0000000 --- a/idm/modbushelpers.h +++ /dev/null @@ -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 . -* -* 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 -#include - -class ModbusHelpers { -public: - static float convertRegisterToFloat(const quint16 *reg); - static QVector convertFloatToRegister(float value); -}; - -#endif - diff --git a/idm/translations/3968d86d-d51a-4ad1-a185-91faa017e38f-en_US.ts b/idm/translations/3968d86d-d51a-4ad1-a185-91faa017e38f-en_US.ts index 3511917..19fe9d3 100644 --- a/idm/translations/3968d86d-d51a-4ad1-a185-91faa017e38f-en_US.ts +++ b/idm/translations/3968d86d-d51a-4ad1-a185-91faa017e38f-en_US.ts @@ -2,198 +2,151 @@ - Idm + IntegrationPluginIdm - - + + The discovery is not available. + + + + + The MAC address is not known. Please reconfigure the thing. + + + + + The host address is not known yet. Trying later again. + + + + + Could not initialize the communication with the device. + + + + + idm + + Connected - 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 + The name of the StateType ({cfd71e64-b666-45ef-8db0-8213acd82c5f}) of ThingClass navigator2 - - Connected changed - The name of the EventType ({cfd71e64-b666-45ef-8db0-8213acd82c5f}) of ThingClass navigator2 + + Current power + The name of the StateType ({b98fb325-100d-4eae-bf8d-97e8f7e1eb00}) of ThingClass navigator2 - - - Current power consumption - 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 - - - - - Current power consumption heat pump changed - The name of the EventType ({b98fb325-100d-4eae-bf8d-97e8f7e1eb00}) of ThingClass navigator2 - - - - - + Error - The name of the ParamType (ThingClass: navigator2, EventType: error, ID: {49fd83ee-ddf3-4477-9ee4-e01c53283b43}) + The name of the StateType ({49fd83ee-ddf3-4477-9ee4-e01c53283b43}) of ThingClass navigator2 + + + + + + Heating + 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 +The name of the StateType ({22543424-6f4f-4200-9f90-111df28d50c9}) of ThingClass navigator2 - - Error changed - The name of the EventType ({49fd83ee-ddf3-4477-9ee4-e01c53283b43}) of ThingClass navigator2 + + MAC address + The name of the ParamType (ThingClass: navigator2, Type: thing, ID: {d178ca29-41a1-4f56-82ec-76a833c1de50}) - - IP address - The name of the ParamType (ThingClass: navigator2, Type: thing, ID: {05714e5c-d66a-4095-bbff-a0eb96fb035b}) - - - - - + Mode - 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 + The name of the StateType ({e539366b-44da-4119-b11b-497bcdb1f522}) of ThingClass navigator2 - - Mode changed - The name of the EventType ({e539366b-44da-4119-b11b-497bcdb1f522}) of ThingClass navigator2 - - - - + Navigator 2.0 The name of the ThingClass ({1c95ac91-4eca-4cbf-b0f4-d60d35d069ed}) - - - Outside air temperature - 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 + + Operation mode + The name of the StateType ({fb71c8f1-2b77-403d-b2ad-f3cba9baf237}) of ThingClass navigator2 - - Outside air temperature changed - The name of the EventType ({9f3462c2-7c42-4eeb-afc4-092e1e41a25d}) of ThingClass navigator2 + + Outside temperature + The name of the StateType ({9f3462c2-7c42-4eeb-afc4-092e1e41a25d}) of ThingClass navigator2 - - - Power - 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 - - - - - Power changed - The name of the EventType ({33c27167-8e24-4cc5-943c-d17cd03e0f68}) of ThingClass navigator2 - - - - - + Room temperature - 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 + The name of the StateType ({f0f596bf-7e45-43ea-b3d4-767b82dd422a}) of ThingClass navigator2 - - Room temperature changed - The name of the EventType ({f0f596bf-7e45-43ea-b3d4-767b82dd422a}) of ThingClass navigator2 - - - - + Set target room temperature The name of the ActionType ({efae7493-68c3-4cb9-853c-81011bdf09ca}) of ThingClass navigator2 - - - + + Target room temperature 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 - - Target room temperature changed - The name of the EventType ({efae7493-68c3-4cb9-853c-81011bdf09ca}) of ThingClass navigator2 - - - - - + Target water temperature - 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 + The name of the StateType ({746244d6-dd37-4af8-b2ae-a7d8463e51e2}) of ThingClass navigator2 - - Target water temperature changed - The name of the EventType ({746244d6-dd37-4af8-b2ae-a7d8463e51e2}) of ThingClass navigator2 + + Total cooling energy + The name of the StateType ({9bef3a6c-4a6b-4023-a82e-c5263765310d}) of ThingClass navigator2 - - + + Total energy consumed + The name of the StateType ({b457dc5a-920a-4c92-9956-a703e69f6084}) of ThingClass navigator2 + + + + + Total heating energy + The name of the StateType ({bfad9bbe-063d-497e-b13e-5de0f4738c91}) of ThingClass navigator2 + + + + + Total hot water energy + The name of the StateType ({0b5114ec-295f-42e6-a058-3e61f6da46b0}) of ThingClass navigator2 + + + + Water temperature - 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 + The name of the StateType ({fcf8e97f-a672-407f-94ae-30df15b310f4}) of ThingClass navigator2 - - Water temperature changed - The name of the EventType ({fcf8e97f-a672-407f-94ae-30df15b310f4}) of ThingClass navigator2 - - - - - + + iDM The name of the vendor ({6f54e4b0-1057-4004-87a9-97fdf4581625}) ---------- -The name of the plugin Idm ({3968d86d-d51a-4ad1-a185-91faa017e38f}) - - - - - IntegrationPluginIdm - - - No IP address given - - - - - IP address already in use +The name of the plugin idm ({3968d86d-d51a-4ad1-a185-91faa017e38f})