diff --git a/idm/idm.cpp b/idm/idm.cpp index 2fcbc81..2acb9ee 100644 --- a/idm/idm.cpp +++ b/idm/idm.cpp @@ -34,10 +34,45 @@ Idm::Idm(const QHostAddress &address, QObject *parent) : QObject(parent), m_hostAddress(address) { + /* qCDebug(dcIdm()) << "Creating Idm with addr: " << address; */ + m_modbusMaster = new ModbusTCPMaster(address, 502, this); + + connect(m_modbusMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &Idm::onReceivedHoldingRegister); } -void Idm::getOutsideTemperature() +Idm::~Idm() { - m_modbusMaster-> + if (m_modbusMaster) { + delete m_modbusMaster; + } } + +double Idm::getOutsideTemperature() +{ + //m_modbusMaster-> + return 0.0; +} + +void Idm::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector &value) +{ + /* qCDebug(dcIdm()) << "Idm::onReceivedHoldingRegister"; */ + + Q_UNUSED(slaveAddress); + Q_UNUSED(modbusRegister); + Q_UNUSED(value); + + IdmInfo *info = new IdmInfo; + info->m_outsideTemperature = 24.3; + + emit statusUpdated(info); +} + +void Idm::onRequestStatus() +{ + /* Reading a total of 16 bytes, starting from address 1000. + * This covers the following parameters: + * */ + m_modbusMaster->readHoldingRegister(0xff, RegisterList::OutsideTemperature, 16); +} + diff --git a/idm/idm.h b/idm/idm.h index 4c6fbd2..0792fff 100644 --- a/idm/idm.h +++ b/idm/idm.h @@ -35,13 +35,16 @@ #include "../modbus/modbustcpmaster.h" +#include "idminfo.h" + class Idm : public QObject { Q_OBJECT public: explicit Idm(const QHostAddress &address, QObject *parent = nullptr); - void getOutsideTemperature(); - void getCurrentFaultNumber(); + ~Idm(); + double getOutsideTemperature(); + //void getCurrentFaultNumber(); private: @@ -54,51 +57,66 @@ private: }; enum RegisterList { - OutsideTemperature = 1000, - MeanOutsideTemperature = 1002, - CurrentFaultNumber = 1004, - OperationModeSystem = 1005, - SmartGridStatus = 1006, - HeatStorageTemperature = 1008, - ColdStorageTemperature = 1010, - DrinkingWaterHeaterTempBelow = 1012, - DrinkingWaterHeaterTempAbove = 1014, - HotWaterTapTemperature = 1030, - TargetHotWaterTemperature = 1032, - HeatPumpOperatingMode = 1090, - SummationFaultHeatPump = 1099, - Humiditysensor = 1392, - ExternalOutsideTemperature = 1690, - ExternalHumidity = 1692, - ExternalRequestTemperatureHeating = 1694, //Externe Anforderungstemperatur Heizen + /* 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. */ + 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 + Humiditysensor = 1392, // Feuchtesensor + ExternalOutsideTemperature = 1690, // Externe Außentemperatur + ExternalHumidity = 1692, // Externe Feuchte + ExternalRequestTemperatureHeating = 1694, // Externe Anforderungstemperatur Heizen ExternalRequestTemperatureCooling = 1695, // Externe Anforderungstemperatur Kühlen - HeatingRequirement = 1710 - CoolingRequirement = 1711, + HeatingRequirement = 1710, // Anforderung Heizen + CoolingRequirement = 1711, // Anforderung Kühlen HotWaterChargingRequirement = 1712, // Anforderung Warmwasserladung - //Wärmemenge Heizen - //Wärmemenge Kühlen, - //Wärmemenge Warmwasser, - //Wärmemenge Abtauung, - //Wärmemenge Passive Kühlung, - //Wärmemenge Solar, - //Wärmemenge Elektroheizeinsatz, - Momentanleistung - SolarKollektortemperatur - SolarKollektorrücklauftemperatur - SolarLadetemperatur - MomentanleistungSolar, - SolarOperatingMode = - ISCModus = 1874, - AcknowledgeFaultMessages = 1999, // Störmeldungen quittieren - Aktueller PV-Überschuss - Aktueller PV Produktion - Aktuelle Leistungsaufnahme Wärmepumpe + 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 + CurrentPhotovoltaicsSurplus = 74, // Aktueller PV-Überschuss + CurrentPhotovoltaicsProduction = 78, // Aktueller PV Produktion + CurrentPowerConsumption = 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; ModbusTCPMaster *m_modbusMaster = nullptr; signals: + void statusUpdated(IdmInfo *info); + +private slots: + void onRequestStatus(); + +// only public for debugging! +public slots: + void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector &value); }; diff --git a/idm/idm.pro b/idm/idm.pro index bc0cf39..63867ed 100644 --- a/idm/idm.pro +++ b/idm/idm.pro @@ -11,5 +11,6 @@ SOURCES += \ HEADERS += \ idm.h \ + idminfo.h \ integrationpluginidm.h \ ../modbus/modbustcpmaster.h \ diff --git a/idm/idminfo.h b/idm/idminfo.h new file mode 100644 index 0000000..115c49b --- /dev/null +++ b/idm/idminfo.h @@ -0,0 +1,49 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* 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 + +struct IdmInfo { + bool m_connected; + bool m_power; + double m_roomTemperature; + double m_outsideTemperature; + double m_waterTemperature; + double m_targetRoomTemperature; + double m_targetWaterTemperature; + QString m_mode; + bool m_error; +}; + +#endif + diff --git a/idm/integrationpluginidm.cpp b/idm/integrationpluginidm.cpp index af78481..cd3e113 100644 --- a/idm/integrationpluginidm.cpp +++ b/idm/integrationpluginidm.cpp @@ -31,10 +31,24 @@ #include "integrationpluginidm.h" #include "plugininfo.h" +IntegrationPluginIdm::IntegrationPluginIdm() +{ + +} + void IntegrationPluginIdm::discoverThings(ThingDiscoveryInfo *info) { if (info->thingClassId() == navigator2ThingClassId) { - //TODO add discovery method + // TODO Is a discovery method actually needed? + // The plugin has a parameter for the IP address + + QString description = "Navigator 2"; + ThingDescriptor descriptor(info->thingClassId(), "", description); + info->addThingDescriptor(descriptor); + + // Just report no error for now, until the above question + // is clarified + info->finish(Thing::ThingErrorNoError); } } @@ -46,22 +60,38 @@ void IntegrationPluginIdm::setupThing(ThingSetupInfo *info) QHostAddress hostAddress = QHostAddress(thing->paramValue(navigator2ThingIpAddressParamTypeId).toString()); Idm *idm = new Idm(hostAddress, this); m_idmConnections.insert(thing, idm); + m_idmInfos.insert(thing, info); + + info->finish(Thing::ThingErrorNoError); } } void IntegrationPluginIdm::postSetupThing(Thing *thing) { + if (!m_refreshTimer) { + m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); + connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginIdm::onRefreshTimer); + } + if (thing->thingClassId() == navigator2ThingClassId) { Idm *idm = m_idmConnections.value(thing); + connect(idm, &Idm::statusUpdated, this, &IntegrationPluginIdm::onStatusUpdated); + + qCDebug(dcIdm()) << "Thing set up, calling update"; + update(thing); + + thing->setStateValue(navigator2ConnectedStateTypeId, true); } } void IntegrationPluginIdm::thingRemoved(Thing *thing) { - if (m_idmConnections.contains(thing)) + if (m_idmConnections.contains(thing)) { m_idmConnections.take(thing)->deleteLater(); + m_idmInfos.take(thing)->deleteLater(); + } } void IntegrationPluginIdm::executeAction(ThingActionInfo *info) @@ -78,3 +108,58 @@ void IntegrationPluginIdm::executeAction(ThingActionInfo *info) Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } } + +void IntegrationPluginIdm::update(Thing *thing) +{ + if (thing->thingClassId() == navigator2ThingClassId) { + qCDebug(dcIdm()) << "Updating thing"; + + Idm *idm = m_idmConnections.value(thing); + Q_UNUSED(idm); + + QVector val{}; + + idm->onReceivedHoldingRegister(0, 1021, val); + //idm->onRequestStatus(); + + //idm->readHoldingRegister(0xff, RegisterList::OutsideTemperature); + if (m_idmInfos.contains(thing)) { + ThingSetupInfo *info = m_idmInfos.take(thing); + info->finish(Thing::ThingErrorNoError); + } + } +} + +void IntegrationPluginIdm::onStatusUpdated(IdmInfo *info) +{ + if (!info) + return; + + qCDebug(dcIdm()) << "Received status from heat pump"; + + Idm *idm = static_cast(sender()); + Thing *thing = m_idmConnections.key(idm); + + if (!thing) + return; + + /* Received a structure holding the status info of the + * heat pump. Update the thing states with the individual fields. */ + thing->setStateValue(navigator2ConnectedStateTypeId, info->m_connected); + thing->setStateValue(navigator2PowerStateTypeId, info->m_power); + thing->setStateValue(navigator2TemperatureStateTypeId, info->m_roomTemperature); + thing->setStateValue(navigator2OutsideAirTemperatureStateTypeId, info->m_outsideTemperature); + thing->setStateValue(navigator2WaterTemperatureStateTypeId, info->m_waterTemperature); + thing->setStateValue(navigator2TargetTemperatureStateTypeId, info->m_targetRoomTemperature); + thing->setStateValue(navigator2TargetWaterTemperatureStateTypeId, info->m_targetWaterTemperature); + thing->setStateValue(navigator2ModeStateTypeId, info->m_mode); + thing->setStateValue(navigator2ErrorStateTypeId, info->m_error); +} + +void IntegrationPluginIdm::onRefreshTimer() +{ + foreach (Thing *thing, myThings().filterByThingClassId(navigator2ThingClassId)) { + update(thing); + } +} + diff --git a/idm/integrationpluginidm.h b/idm/integrationpluginidm.h index 58d2910..3da5ef6 100644 --- a/idm/integrationpluginidm.h +++ b/idm/integrationpluginidm.h @@ -38,6 +38,7 @@ #include + class IntegrationPluginIdm: public IntegrationPlugin { Q_OBJECT @@ -54,6 +55,7 @@ public: void postSetupThing(Thing *thing) override; void thingRemoved(Thing *thing) override; void executeAction(ThingActionInfo *info) override; + void update(Thing *thing); private: @@ -87,10 +89,13 @@ private: PluginTimer *m_refreshTimer = nullptr; QHash m_idmConnections; + QHash m_idmInfos; QHash m_asyncActions; -private slots: void onRefreshTimer(); + +private slots: + void onStatusUpdated(IdmInfo *info); }; #endif // INTEGRATIONPLUGINIDM_H diff --git a/idm/integrationpluginidm.json b/idm/integrationpluginidm.json index 7c6593b..edc139d 100644 --- a/idm/integrationpluginidm.json +++ b/idm/integrationpluginidm.json @@ -12,7 +12,7 @@ "name": "navigator2", "displayName": "Navigator 2.0", "id": "1c95ac91-4eca-4cbf-b0f4-d60d35d069ed", - "createMethods": ["Discovery"], + "createMethods": ["User","Discovery"], "interfaces": ["heating", "temperaturesensor", "connectable"], "paramTypes": [ {