diff --git a/debian/control b/debian/control
index 8738471..0ab3eb2 100644
--- a/debian/control
+++ b/debian/control
@@ -122,6 +122,14 @@ Description: nymea integration plugin for M-TEC heat pumps
This package contains the nymea integration plugin for M-TEC heat pumps.
+Package: nymea-plugin-schrack
+Architecture: any
+Depends: ${shlibs:Depends},
+ ${misc:Depends},
+Description: nymea integration plugin for Schrack wallboxes
+ This package contains the nymea integration plugin for Schrack wallboxes.
+
+
Package: nymea-plugin-sunspec
Architecture: any
Depends: ${shlibs:Depends},
diff --git a/debian/nymea-plugin-schrack.install.in b/debian/nymea-plugin-schrack.install.in
new file mode 100644
index 0000000..e713ba5
--- /dev/null
+++ b/debian/nymea-plugin-schrack.install.in
@@ -0,0 +1,2 @@
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginschrack.so
+schrack/translations/*qm usr/share/nymea/translations/
diff --git a/nymea-plugins-modbus.pro b/nymea-plugins-modbus.pro
index 717131d..9f6bdcb 100644
--- a/nymea-plugins-modbus.pro
+++ b/nymea-plugins-modbus.pro
@@ -14,6 +14,7 @@ PLUGIN_DIRS = \
modbuscommander \
mtec \
mypv \
+ schrack \
sunspec \
unipi \
wallbe \
diff --git a/plugins.pri b/plugins.pri
index f180f53..ee37061 100644
--- a/plugins.pri
+++ b/plugins.pri
@@ -15,3 +15,5 @@ isEmpty(PLUGIN_PRI) {
# message("Using $$PLUGIN_PRI")
include($$PLUGIN_PRI)
}
+
+top_srcdir=$${PWD}
diff --git a/schrack/cion-registers.json b/schrack/cion-registers.json
new file mode 100644
index 0000000..66d9221
--- /dev/null
+++ b/schrack/cion-registers.json
@@ -0,0 +1,132 @@
+{
+ "protocol": "RTU",
+ "endianness": "BigEndian",
+ "blocks": [
+ {
+ "id": "e3",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "currentChargingCurrentE3",
+ "address": 126,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Current charging Ampere",
+ "unit": "A",
+ "defaultValue": 6,
+ "access": "R"
+ },
+ {
+ "id": "maxChargingCurrentE3",
+ "address": 127,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Maximum charging current of connected cable",
+ "unit": "A",
+ "defaultValue": 32,
+ "access": "R"
+ }
+ ]
+ }
+ ],
+ "registers": [
+ {
+ "id": "chargingEnabled",
+ "address": 100,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Charging enabled",
+ "defaultValue": 0,
+ "access": "RW"
+ },
+ {
+ "id": "chargingCurrentSetpoint",
+ "address": 101,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Allowed charging current",
+ "unit": "A",
+ "defaultValue": 6,
+ "access": "RW"
+ },
+ {
+ "id": "statusBits",
+ "address": 121,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Maximum charging current",
+ "defaultValue": 0,
+ "access": "R"
+ },
+ {
+ "id": "chargingDuration",
+ "address": 151,
+ "size": 2,
+ "type": "uint32",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Charging duration",
+ "unit": "ms",
+ "defaultValue": 6,
+ "access": "R"
+ },
+ {
+ "id": "pluggedInDuration",
+ "address": 153,
+ "size": 2,
+ "type": "uint32",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Plugged in duration",
+ "unit": "ms",
+ "defaultValue": 6,
+ "access": "R"
+ },
+ {
+ "id": "u1Voltage",
+ "address": 167,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "U1 voltage",
+ "unit": "V",
+ "defaultValue": 32,
+ "access": "R"
+ },
+ {
+ "id": "gridVoltage",
+ "address": 302,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Voltage of the power supply grid",
+ "unit": "V",
+ "defaultValue": 0,
+ "access": "R"
+ },
+ {
+ "id": "minChargingCurrent",
+ "address": 507,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Minimum charging current",
+ "unit": "A",
+ "defaultValue": 6,
+ "access": "R"
+ }
+ ]
+}
diff --git a/schrack/cion-registers.pdf b/schrack/cion-registers.pdf
new file mode 100644
index 0000000..0b42698
Binary files /dev/null and b/schrack/cion-registers.pdf differ
diff --git a/schrack/cionmodbusrtuconnection.cpp b/schrack/cionmodbusrtuconnection.cpp
new file mode 100644
index 0000000..2384519
--- /dev/null
+++ b/schrack/cionmodbusrtuconnection.cpp
@@ -0,0 +1,469 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2022, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This fileDescriptor is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* 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 "cionmodbusrtuconnection.h"
+#include "loggingcategories.h"
+
+NYMEA_LOGGING_CATEGORY(dcCionModbusRtuConnection, "CionModbusRtuConnection")
+
+CionModbusRtuConnection::CionModbusRtuConnection(ModbusRtuMaster *modbusRtuMaster, quint16 slaveId, QObject *parent) :
+ QObject(parent),
+ m_modbusRtuMaster(modbusRtuMaster),
+ m_slaveId(slaveId)
+{
+
+}
+
+ModbusRtuMaster *CionModbusRtuConnection::modbusRtuMaster() const
+{
+ return m_modbusRtuMaster;
+}
+quint16 CionModbusRtuConnection::slaveId() const
+{
+ return m_slaveId;
+}
+quint16 CionModbusRtuConnection::chargingEnabled() const
+{
+ return m_chargingEnabled;
+}
+
+ModbusRtuReply *CionModbusRtuConnection::setChargingEnabled(quint16 chargingEnabled)
+{
+ QVector values = ModbusDataUtils::convertFromUInt16(chargingEnabled);
+ qCDebug(dcCionModbusRtuConnection()) << "--> Write \"Charging enabled\" register:" << 100 << "size:" << 1 << values;
+ return m_modbusRtuMaster->writeHoldingRegisters(m_slaveId, 100, values);
+}
+
+quint16 CionModbusRtuConnection::chargingCurrentSetpoint() const
+{
+ return m_chargingCurrentSetpoint;
+}
+
+ModbusRtuReply *CionModbusRtuConnection::setChargingCurrentSetpoint(quint16 chargingCurrentSetpoint)
+{
+ QVector values = ModbusDataUtils::convertFromUInt16(chargingCurrentSetpoint);
+ qCDebug(dcCionModbusRtuConnection()) << "--> Write \"Allowed charging current\" register:" << 101 << "size:" << 1 << values;
+ return m_modbusRtuMaster->writeHoldingRegisters(m_slaveId, 101, values);
+}
+
+quint16 CionModbusRtuConnection::statusBits() const
+{
+ return m_statusBits;
+}
+
+quint32 CionModbusRtuConnection::chargingDuration() const
+{
+ return m_chargingDuration;
+}
+
+quint32 CionModbusRtuConnection::pluggedInDuration() const
+{
+ return m_pluggedInDuration;
+}
+
+quint16 CionModbusRtuConnection::u1Voltage() const
+{
+ return m_u1Voltage;
+}
+
+quint16 CionModbusRtuConnection::gridVoltage() const
+{
+ return m_gridVoltage;
+}
+
+quint16 CionModbusRtuConnection::minChargingCurrent() const
+{
+ return m_minChargingCurrent;
+}
+
+quint16 CionModbusRtuConnection::currentChargingCurrentE3() const
+{
+ return m_currentChargingCurrentE3;
+}
+
+quint16 CionModbusRtuConnection::maxChargingCurrentE3() const
+{
+ return m_maxChargingCurrentE3;
+}
+
+void CionModbusRtuConnection::initialize()
+{
+ // No init registers defined. Nothing to be done and we are finished.
+ emit initializationFinished();
+}
+
+void CionModbusRtuConnection::update()
+{
+ updateChargingEnabled();
+ updateChargingCurrentSetpoint();
+ updateStatusBits();
+ updateChargingDuration();
+ updatePluggedInDuration();
+ updateU1Voltage();
+ updateGridVoltage();
+ updateMinChargingCurrent();
+ updateE3Block();
+}
+
+void CionModbusRtuConnection::updateChargingEnabled()
+{
+ // Update registers from Charging enabled
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read \"Charging enabled\" register:" << 100 << "size:" << 1;
+ ModbusRtuReply *reply = readChargingEnabled();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector values = reply->result();
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from \"Charging enabled\" register" << 100 << "size:" << 1 << values;
+ quint16 receivedChargingEnabled = ModbusDataUtils::convertToUInt16(values);
+ if (m_chargingEnabled != receivedChargingEnabled) {
+ m_chargingEnabled = receivedChargingEnabled;
+ emit chargingEnabledChanged(m_chargingEnabled);
+ }
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating \"Charging enabled\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading \"Charging enabled\" registers";
+ }
+}
+
+void CionModbusRtuConnection::updateChargingCurrentSetpoint()
+{
+ // Update registers from Allowed charging current
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read \"Allowed charging current\" register:" << 101 << "size:" << 1;
+ ModbusRtuReply *reply = readChargingCurrentSetpoint();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector values = reply->result();
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from \"Allowed charging current\" register" << 101 << "size:" << 1 << values;
+ quint16 receivedChargingCurrentSetpoint = ModbusDataUtils::convertToUInt16(values);
+ if (m_chargingCurrentSetpoint != receivedChargingCurrentSetpoint) {
+ m_chargingCurrentSetpoint = receivedChargingCurrentSetpoint;
+ emit chargingCurrentSetpointChanged(m_chargingCurrentSetpoint);
+ }
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating \"Allowed charging current\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading \"Allowed charging current\" registers";
+ }
+}
+
+void CionModbusRtuConnection::updateStatusBits()
+{
+ // Update registers from Maximum charging current
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read \"Maximum charging current\" register:" << 121 << "size:" << 1;
+ ModbusRtuReply *reply = readStatusBits();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector values = reply->result();
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from \"Maximum charging current\" register" << 121 << "size:" << 1 << values;
+ quint16 receivedStatusBits = ModbusDataUtils::convertToUInt16(values);
+ if (m_statusBits != receivedStatusBits) {
+ m_statusBits = receivedStatusBits;
+ emit statusBitsChanged(m_statusBits);
+ }
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating \"Maximum charging current\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading \"Maximum charging current\" registers";
+ }
+}
+
+void CionModbusRtuConnection::updateChargingDuration()
+{
+ // Update registers from Charging duration
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read \"Charging duration\" register:" << 151 << "size:" << 2;
+ ModbusRtuReply *reply = readChargingDuration();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector values = reply->result();
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from \"Charging duration\" register" << 151 << "size:" << 2 << values;
+ quint32 receivedChargingDuration = ModbusDataUtils::convertToUInt32(values, ModbusDataUtils::ByteOrderBigEndian);
+ if (m_chargingDuration != receivedChargingDuration) {
+ m_chargingDuration = receivedChargingDuration;
+ emit chargingDurationChanged(m_chargingDuration);
+ }
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating \"Charging duration\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading \"Charging duration\" registers";
+ }
+}
+
+void CionModbusRtuConnection::updatePluggedInDuration()
+{
+ // Update registers from Plugged in duration
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read \"Plugged in duration\" register:" << 153 << "size:" << 2;
+ ModbusRtuReply *reply = readPluggedInDuration();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector values = reply->result();
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from \"Plugged in duration\" register" << 153 << "size:" << 2 << values;
+ quint32 receivedPluggedInDuration = ModbusDataUtils::convertToUInt32(values, ModbusDataUtils::ByteOrderBigEndian);
+ if (m_pluggedInDuration != receivedPluggedInDuration) {
+ m_pluggedInDuration = receivedPluggedInDuration;
+ emit pluggedInDurationChanged(m_pluggedInDuration);
+ }
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating \"Plugged in duration\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading \"Plugged in duration\" registers";
+ }
+}
+
+void CionModbusRtuConnection::updateU1Voltage()
+{
+ // Update registers from U1 voltage
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read \"U1 voltage\" register:" << 167 << "size:" << 1;
+ ModbusRtuReply *reply = readU1Voltage();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector values = reply->result();
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from \"U1 voltage\" register" << 167 << "size:" << 1 << values;
+ quint16 receivedU1Voltage = ModbusDataUtils::convertToUInt16(values);
+ if (m_u1Voltage != receivedU1Voltage) {
+ m_u1Voltage = receivedU1Voltage;
+ emit u1VoltageChanged(m_u1Voltage);
+ }
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating \"U1 voltage\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading \"U1 voltage\" registers";
+ }
+}
+
+void CionModbusRtuConnection::updateGridVoltage()
+{
+ // Update registers from Voltage of the power supply grid
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read \"Voltage of the power supply grid\" register:" << 302 << "size:" << 1;
+ ModbusRtuReply *reply = readGridVoltage();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector values = reply->result();
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from \"Voltage of the power supply grid\" register" << 302 << "size:" << 1 << values;
+ quint16 receivedGridVoltage = ModbusDataUtils::convertToUInt16(values);
+ if (m_gridVoltage != receivedGridVoltage) {
+ m_gridVoltage = receivedGridVoltage;
+ emit gridVoltageChanged(m_gridVoltage);
+ }
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating \"Voltage of the power supply grid\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading \"Voltage of the power supply grid\" registers";
+ }
+}
+
+void CionModbusRtuConnection::updateMinChargingCurrent()
+{
+ // Update registers from Minimum charging current
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read \"Minimum charging current\" register:" << 507 << "size:" << 1;
+ ModbusRtuReply *reply = readMinChargingCurrent();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector values = reply->result();
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from \"Minimum charging current\" register" << 507 << "size:" << 1 << values;
+ quint16 receivedMinChargingCurrent = ModbusDataUtils::convertToUInt16(values);
+ if (m_minChargingCurrent != receivedMinChargingCurrent) {
+ m_minChargingCurrent = receivedMinChargingCurrent;
+ emit minChargingCurrentChanged(m_minChargingCurrent);
+ }
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating \"Minimum charging current\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading \"Minimum charging current\" registers";
+ }
+}
+
+void CionModbusRtuConnection::updateE3Block()
+{
+ // Update register block "e3"
+ qCDebug(dcCionModbusRtuConnection()) << "--> Read block \"e3\" registers from:" << 126 << "size:" << 2;
+ ModbusRtuReply *reply = m_modbusRtuMaster->readHoldingRegister(m_slaveId, 126, 2);
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &ModbusRtuReply::finished, this, [this, reply](){
+ if (reply->error() == ModbusRtuReply::NoError) {
+ QVector blockValues = reply->result();
+ QVector values;
+ qCDebug(dcCionModbusRtuConnection()) << "<-- Response from reading block \"e3\" register" << 126 << "size:" << 2 << blockValues;
+ values = blockValues.mid(0, 1);
+ quint16 receivedCurrentChargingCurrentE3 = ModbusDataUtils::convertToUInt16(values);
+ if (m_currentChargingCurrentE3 != receivedCurrentChargingCurrentE3) {
+ m_currentChargingCurrentE3 = receivedCurrentChargingCurrentE3;
+ emit currentChargingCurrentE3Changed(m_currentChargingCurrentE3);
+ }
+
+ values = blockValues.mid(1, 1);
+ quint16 receivedMaxChargingCurrentE3 = ModbusDataUtils::convertToUInt16(values);
+ if (m_maxChargingCurrentE3 != receivedMaxChargingCurrentE3) {
+ m_maxChargingCurrentE3 = receivedMaxChargingCurrentE3;
+ emit maxChargingCurrentE3Changed(m_maxChargingCurrentE3);
+ }
+
+ }
+ });
+
+ connect(reply, &ModbusRtuReply::errorOccurred, this, [reply] (ModbusRtuReply::Error error){
+ qCWarning(dcCionModbusRtuConnection()) << "ModbusRtu reply error occurred while updating block \"e3\" registers" << error << reply->errorString();
+ emit reply->finished();
+ });
+ }
+ } else {
+ qCWarning(dcCionModbusRtuConnection()) << "Error occurred while reading block \"e3\" registers";
+ }
+}
+
+ModbusRtuReply *CionModbusRtuConnection::readChargingEnabled()
+{
+ return m_modbusRtuMaster->readHoldingRegister(m_slaveId, 100, 1);
+}
+
+ModbusRtuReply *CionModbusRtuConnection::readChargingCurrentSetpoint()
+{
+ return m_modbusRtuMaster->readHoldingRegister(m_slaveId, 101, 1);
+}
+
+ModbusRtuReply *CionModbusRtuConnection::readStatusBits()
+{
+ return m_modbusRtuMaster->readHoldingRegister(m_slaveId, 121, 1);
+}
+
+ModbusRtuReply *CionModbusRtuConnection::readChargingDuration()
+{
+ return m_modbusRtuMaster->readHoldingRegister(m_slaveId, 151, 2);
+}
+
+ModbusRtuReply *CionModbusRtuConnection::readPluggedInDuration()
+{
+ return m_modbusRtuMaster->readHoldingRegister(m_slaveId, 153, 2);
+}
+
+ModbusRtuReply *CionModbusRtuConnection::readU1Voltage()
+{
+ return m_modbusRtuMaster->readHoldingRegister(m_slaveId, 167, 1);
+}
+
+ModbusRtuReply *CionModbusRtuConnection::readGridVoltage()
+{
+ return m_modbusRtuMaster->readHoldingRegister(m_slaveId, 302, 1);
+}
+
+ModbusRtuReply *CionModbusRtuConnection::readMinChargingCurrent()
+{
+ return m_modbusRtuMaster->readHoldingRegister(m_slaveId, 507, 1);
+}
+
+void CionModbusRtuConnection::verifyInitFinished()
+{
+ if (m_pendingInitReplies.isEmpty()) {
+ qCDebug(dcCionModbusRtuConnection()) << "Initialization finished of CionModbusRtuConnection";
+ emit initializationFinished();
+ }
+}
+
+QDebug operator<<(QDebug debug, CionModbusRtuConnection *cionModbusRtuConnection)
+{
+ debug.nospace().noquote() << "CionModbusRtuConnection(" << cionModbusRtuConnection->modbusRtuMaster()->modbusUuid().toString() << ", " << cionModbusRtuConnection->modbusRtuMaster()->serialPort() << ", slave ID:" << cionModbusRtuConnection->slaveId() << ")" << "\n";
+ debug.nospace().noquote() << " - Charging enabled:" << cionModbusRtuConnection->chargingEnabled() << "\n";
+ debug.nospace().noquote() << " - Allowed charging current:" << cionModbusRtuConnection->chargingCurrentSetpoint() << " [A]" << "\n";
+ debug.nospace().noquote() << " - Maximum charging current:" << cionModbusRtuConnection->statusBits() << "\n";
+ debug.nospace().noquote() << " - Charging duration:" << cionModbusRtuConnection->chargingDuration() << " [ms]" << "\n";
+ debug.nospace().noquote() << " - Plugged in duration:" << cionModbusRtuConnection->pluggedInDuration() << " [ms]" << "\n";
+ debug.nospace().noquote() << " - U1 voltage:" << cionModbusRtuConnection->u1Voltage() << " [V]" << "\n";
+ debug.nospace().noquote() << " - Voltage of the power supply grid:" << cionModbusRtuConnection->gridVoltage() << " [V]" << "\n";
+ debug.nospace().noquote() << " - Minimum charging current:" << cionModbusRtuConnection->minChargingCurrent() << " [A]" << "\n";
+ debug.nospace().noquote() << " - Current charging Ampere:" << cionModbusRtuConnection->currentChargingCurrentE3() << " [A]" << "\n";
+ debug.nospace().noquote() << " - Maximum charging current of connected cable:" << cionModbusRtuConnection->maxChargingCurrentE3() << " [A]" << "\n";
+ return debug.quote().space();
+}
+
diff --git a/schrack/cionmodbusrtuconnection.h b/schrack/cionmodbusrtuconnection.h
new file mode 100644
index 0000000..5efab18
--- /dev/null
+++ b/schrack/cionmodbusrtuconnection.h
@@ -0,0 +1,160 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2022, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This fileDescriptor is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* 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 CIONMODBUSRTUCONNECTION_H
+#define CIONMODBUSRTUCONNECTION_H
+
+#include
+
+#include "../modbus/modbusdatautils.h"
+#include
+
+class CionModbusRtuConnection : public QObject
+{
+ Q_OBJECT
+public:
+ enum Registers {
+ RegisterChargingEnabled = 100,
+ RegisterChargingCurrentSetpoint = 101,
+ RegisterStatusBits = 121,
+ RegisterCurrentChargingCurrentE3 = 126,
+ RegisterMaxChargingCurrentE3 = 127,
+ RegisterChargingDuration = 151,
+ RegisterPluggedInDuration = 153,
+ RegisterU1Voltage = 167,
+ RegisterGridVoltage = 302,
+ RegisterMinChargingCurrent = 507
+ };
+ Q_ENUM(Registers)
+
+ explicit CionModbusRtuConnection(ModbusRtuMaster *modbusRtuMaster, quint16 slaveId, QObject *parent = nullptr);
+ ~CionModbusRtuConnection() = default;
+
+ ModbusRtuMaster *modbusRtuMaster() const;
+ quint16 slaveId() const;
+
+ /* Charging enabled - Address: 100, Size: 1 */
+ quint16 chargingEnabled() const;
+ ModbusRtuReply *setChargingEnabled(quint16 chargingEnabled);
+
+ /* Allowed charging current [A] - Address: 101, Size: 1 */
+ quint16 chargingCurrentSetpoint() const;
+ ModbusRtuReply *setChargingCurrentSetpoint(quint16 chargingCurrentSetpoint);
+
+ /* Maximum charging current - Address: 121, Size: 1 */
+ quint16 statusBits() const;
+
+ /* Charging duration [ms] - Address: 151, Size: 2 */
+ quint32 chargingDuration() const;
+
+ /* Plugged in duration [ms] - Address: 153, Size: 2 */
+ quint32 pluggedInDuration() const;
+
+ /* U1 voltage [V] - Address: 167, Size: 1 */
+ quint16 u1Voltage() const;
+
+ /* Voltage of the power supply grid [V] - Address: 302, Size: 1 */
+ quint16 gridVoltage() const;
+
+ /* Minimum charging current [A] - Address: 507, Size: 1 */
+ quint16 minChargingCurrent() const;
+
+ /* Current charging Ampere [A] - Address: 126, Size: 1 */
+ quint16 currentChargingCurrentE3() const;
+
+ /* Maximum charging current of connected cable [A] - Address: 127, Size: 1 */
+ quint16 maxChargingCurrentE3() const;
+
+ /* Read block from start addess 126 with size of 2 registers containing following 2 properties:
+ - Current charging Ampere [A] - Address: 126, Size: 1
+ - Maximum charging current of connected cable [A] - Address: 127, Size: 1
+ */
+ void updateE3Block();
+
+ void updateChargingEnabled();
+ void updateChargingCurrentSetpoint();
+ void updateStatusBits();
+ void updateChargingDuration();
+ void updatePluggedInDuration();
+ void updateU1Voltage();
+ void updateGridVoltage();
+ void updateMinChargingCurrent();
+
+ virtual void initialize();
+ virtual void update();
+
+signals:
+ void initializationFinished();
+
+ void chargingEnabledChanged(quint16 chargingEnabled);
+ void chargingCurrentSetpointChanged(quint16 chargingCurrentSetpoint);
+ void statusBitsChanged(quint16 statusBits);
+ void chargingDurationChanged(quint32 chargingDuration);
+ void pluggedInDurationChanged(quint32 pluggedInDuration);
+ void u1VoltageChanged(quint16 u1Voltage);
+ void gridVoltageChanged(quint16 gridVoltage);
+ void minChargingCurrentChanged(quint16 minChargingCurrent);
+ void currentChargingCurrentE3Changed(quint16 currentChargingCurrentE3);
+ void maxChargingCurrentE3Changed(quint16 maxChargingCurrentE3);
+
+protected:
+ ModbusRtuReply *readChargingEnabled();
+ ModbusRtuReply *readChargingCurrentSetpoint();
+ ModbusRtuReply *readStatusBits();
+ ModbusRtuReply *readChargingDuration();
+ ModbusRtuReply *readPluggedInDuration();
+ ModbusRtuReply *readU1Voltage();
+ ModbusRtuReply *readGridVoltage();
+ ModbusRtuReply *readMinChargingCurrent();
+
+ quint16 m_chargingEnabled = 0;
+ quint16 m_chargingCurrentSetpoint = 6;
+ quint16 m_statusBits = 0;
+ quint32 m_chargingDuration = 6;
+ quint32 m_pluggedInDuration = 6;
+ quint16 m_u1Voltage = 32;
+ quint16 m_gridVoltage = 0;
+ quint16 m_minChargingCurrent = 6;
+ quint16 m_currentChargingCurrentE3 = 6;
+ quint16 m_maxChargingCurrentE3 = 32;
+
+private:
+ ModbusRtuMaster *m_modbusRtuMaster = nullptr;
+ quint16 m_slaveId = 1;
+ QVector m_pendingInitReplies;
+
+ void verifyInitFinished();
+
+
+};
+
+QDebug operator<<(QDebug debug, CionModbusRtuConnection *cionModbusRtuConnection);
+
+#endif // CIONMODBUSRTUCONNECTION_H
diff --git a/schrack/integrationpluginschrack.cpp b/schrack/integrationpluginschrack.cpp
new file mode 100644
index 0000000..22096ad
--- /dev/null
+++ b/schrack/integrationpluginschrack.cpp
@@ -0,0 +1,291 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "integrationpluginschrack.h"
+#include "plugininfo.h"
+
+IntegrationPluginSchrack::IntegrationPluginSchrack()
+{
+}
+
+void IntegrationPluginSchrack::init()
+{
+// connect(hardwareManager()->modbusRtuResource(), &ModbusRtuHardwareResource::modbusRtuMasterRemoved, this, [=] (const QUuid &modbusUuid){
+// qCDebug(dcEnergyMeters()) << "Modbus RTU master has been removed" << modbusUuid.toString();
+
+// foreach (Thing *thing, myThings()) {
+// if (m_modbusUuidParamTypeIds.contains(thing->thingClassId())) {
+// if (thing->paramValue(m_modbusUuidParamTypeIds.value(thing->thingClassId())) == modbusUuid) {
+// qCWarning(dcEnergyMeters()) << "Modbus RTU hardware resource removed for" << thing << ". The thing will not be functional any more until a new resource has been configured for it.";
+// thing->setStateValue(m_connectionStateTypeIds[thing->thingClassId()], false);
+
+// if (thing->thingClassId() == sdm630ThingClassId) {
+// delete m_sdmConnections.take(thing);
+// } else if (thing->thingClassId() == pro380ThingClassId) {
+// delete m_ineproConnections.take(thing);
+// }
+// }
+// }
+// }
+// });
+}
+
+void IntegrationPluginSchrack::discoverThings(ThingDiscoveryInfo *info)
+{
+ qCDebug(dcSchrack()) << "Discovering modbus RTU resources...";
+ if (hardwareManager()->modbusRtuResource()->modbusRtuMasters().isEmpty()) {
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("No Modbus RTU interface available. Please set up a Modbus RTU interface first."));
+ return;
+ }
+
+ uint slaveAddress = info->params().paramValue(cionDiscoverySlaveAddressParamTypeId).toUInt();
+ if (slaveAddress > 254 || slaveAddress == 0) {
+ info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The Modbus slave address must be a value between 1 and 254."));
+ return;
+ }
+
+ foreach (ModbusRtuMaster *modbusMaster, hardwareManager()->modbusRtuResource()->modbusRtuMasters()) {
+ qCDebug(dcSchrack()) << "Found RTU master resource" << modbusMaster << "connected" << modbusMaster->connected();
+ if (!modbusMaster->connected())
+ continue;
+
+ ThingDescriptor descriptor(info->thingClassId(), "i-CHARGE CION", QString::number(slaveAddress) + " " + modbusMaster->serialPort());
+ ParamList params;
+ params << Param(cionThingSlaveAddressParamTypeId, slaveAddress);
+ params << Param(cionThingModbusMasterUuidParamTypeId, modbusMaster->modbusUuid());
+ descriptor.setParams(params);
+ info->addThingDescriptor(descriptor);
+ }
+
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginSchrack::setupThing(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ qCDebug(dcSchrack()) << "Setup thing" << thing << thing->params();
+
+ uint address = thing->paramValue(cionThingSlaveAddressParamTypeId).toUInt();
+ if (address > 254 || address == 0) {
+ qCWarning(dcSchrack()) << "Setup failed, slave address is not valid" << address;
+ info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus address not valid. It must be a value between 1 and 254."));
+ return;
+ }
+
+ QUuid uuid = thing->paramValue(cionThingModbusMasterUuidParamTypeId).toUuid();
+ if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
+ qCWarning(dcSchrack()) << "Setup failed, hardware manager not available";
+ info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU resource is not available."));
+ return;
+ }
+
+ if (m_cionConnections.contains(thing)) {
+ qCDebug(dcSchrack()) << "Already have a CION connection for this thing. Cleaning up old connection and initializing new one...";
+ delete m_cionConnections.take(thing);
+ }
+
+ CionModbusRtuConnection *cionConnection = new CionModbusRtuConnection(hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, this);
+ connect(cionConnection->modbusRtuMaster(), &ModbusRtuMaster::connectedChanged, thing, [=](bool connected){
+ if (connected) {
+ qCDebug(dcSchrack()) << "Modbus RTU resource connected" << thing << cionConnection->modbusRtuMaster()->serialPort();
+ } else {
+ qCWarning(dcSchrack()) << "Modbus RTU resource disconnected" << thing << cionConnection->modbusRtuMaster()->serialPort();
+ }
+ });
+
+ connect(cionConnection, &CionModbusRtuConnection::chargingEnabledChanged, thing, [=](quint16 charging){
+ qCDebug(dcSchrack()) << "Charging enabled changed:" << charging;
+ thing->setStateValue(cionPowerStateTypeId, charging == 1);
+ thing->setStateValue(cionConnectedStateTypeId, true);
+ finishAction(cionPowerStateTypeId);
+ });
+
+ // We can write chargingCurrentSetpoint to the preferred charging we want, and the wallbox will take it,
+ // however, it may not necessarily *do* it, but will give us the actual value it uses in currentChargingCurrentE3
+ // We'll use that for setting our state, just monitoring this one on the logs
+ connect(cionConnection, &CionModbusRtuConnection::chargingCurrentSetpointChanged, thing, [=](quint16 chargingCurrentSetpoint){
+ qCDebug(dcSchrack()) << "Charging current setpoint changed:" << chargingCurrentSetpoint;
+// thing->setStateValue(cionMaxChargingCurrentStateTypeId, chargingCurrentSetpoint);
+// thing->setStateValue(cionConnectedStateTypeId, true);
+// finishAction(cionMaxChargingCurrentStateTypeId);
+ });
+
+ //
+ connect(cionConnection, &CionModbusRtuConnection::currentChargingCurrentE3Changed, thing, [=](quint16 currentChargingCurrentE3){
+ qCDebug(dcSchrack()) << "Current charging current E3 current changed:" << currentChargingCurrentE3;
+ thing->setStateValue(cionMaxChargingCurrentStateTypeId, currentChargingCurrentE3);
+ thing->setStateValue(cionConnectedStateTypeId, true);
+ finishAction(cionMaxChargingCurrentStateTypeId);
+ });
+
+ // The maxChargingCurrentE3 takes into account the DIP switches and connected cable, so this is effectively
+ // our maximum. However, it will go to 0 when unplugged, which is odd, so we'll ignore 0 values.
+ connect(cionConnection, &CionModbusRtuConnection::maxChargingCurrentE3Changed, thing, [=](quint16 maxChargingCurrentE3){
+ qCDebug(dcSchrack()) << "Maximum charging current E3 current changed:" << maxChargingCurrentE3;
+ if (maxChargingCurrentE3 != 0) {
+ thing->setStateMaxValue(cionMaxChargingCurrentStateTypeId, maxChargingCurrentE3);
+ thing->setStateValue(cionConnectedStateTypeId, true);
+ }
+ });
+
+ connect(cionConnection, &CionModbusRtuConnection::statusBitsChanged, thing, [=](quint16 statusBits){
+ qCDebug(dcSchrack()) << "Status bits changed:" << statusBits;
+ thing->setStateValue(cionConnectedStateTypeId, true);
+ });
+
+ connect(cionConnection, &CionModbusRtuConnection::minChargingCurrentChanged, thing, [=](quint16 minChargingCurrent){
+ qCDebug(dcSchrack()) << "Minimum charging current changed:" << minChargingCurrent;
+ thing->setStateMinValue(cionMaxChargingCurrentStateTypeId, minChargingCurrent);
+ thing->setStateValue(cionConnectedStateTypeId, true);
+ });
+
+ connect(cionConnection, &CionModbusRtuConnection::gridVoltageChanged, thing, [=](quint16 gridVoltage){
+ qCDebug(dcSchrack()) << "Grid voltage changed:" << 1.0 * gridVoltage / 100;
+ thing->setStateValue(cionConnectedStateTypeId, true);
+// updateCurrentPower(thing);
+ });
+ connect(cionConnection, &CionModbusRtuConnection::u1VoltageChanged, thing, [=](quint16 gridVoltage){
+ qCDebug(dcSchrack()) << "U1 voltage changed:" << 1.0 * gridVoltage / 100;
+ thing->setStateValue(cionConnectedStateTypeId, true);
+ updateCurrentPower(thing);
+ });
+ connect(cionConnection, &CionModbusRtuConnection::pluggedInDurationChanged, thing, [=](quint32 pluggedInDuration){
+ qCDebug(dcSchrack()) << "Plugged in duration changed:" << pluggedInDuration;
+ thing->setStateValue(cionPluggedInStateTypeId, pluggedInDuration > 0);
+ thing->setStateValue(cionConnectedStateTypeId, true);
+ });
+ connect(cionConnection, &CionModbusRtuConnection::chargingDurationChanged, thing, [=](quint32 chargingDuration){
+ qCDebug(dcSchrack()) << "Charging duration changed:" << chargingDuration;
+ thing->setStateValue(cionChargingStateTypeId, chargingDuration > 0);
+ thing->setStateValue(cionConnectedStateTypeId, true);
+ });
+
+
+ cionConnection->update();
+
+ // Initialize min/max to their defaults. If both, nymea and the wallbox are restarted simultaneously, nymea would cache the min/max while
+ // the wallbox would revert to its defaults, and being the default, the modbusconnection also never emits "changed" signals for them.
+ // To prevent running out of sync we'll "uncache" min/max values too.
+ thing->setStateMinMaxValues(cionMaxChargingCurrentStateTypeId, 6, 32);
+
+ m_cionConnections.insert(thing, cionConnection);
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginSchrack::postSetupThing(Thing *thing)
+{
+ qCDebug(dcSchrack()) << "Post setup thing" << thing->name();
+ if (!m_refreshTimer) {
+ m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
+ connect(m_refreshTimer, &PluginTimer::timeout, this, [this] {
+ foreach (Thing *thing, myThings()) {
+
+ m_cionConnections.value(thing)->update();
+ }
+ });
+
+ qCDebug(dcSchrack()) << "Starting refresh timer...";
+ m_refreshTimer->start();
+ }
+}
+
+void IntegrationPluginSchrack::thingRemoved(Thing *thing)
+{
+ qCDebug(dcSchrack()) << "Thing removed" << thing->name();
+
+ if (m_cionConnections.contains(thing))
+ m_cionConnections.take(thing)->deleteLater();
+
+ if (myThings().isEmpty() && m_refreshTimer) {
+ qCDebug(dcSchrack()) << "Stopping reconnect timer";
+ hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
+ m_refreshTimer = nullptr;
+ }
+}
+
+void IntegrationPluginSchrack::executeAction(ThingActionInfo *info)
+{
+ CionModbusRtuConnection *cionConnection = m_cionConnections.value(info->thing());
+ if (info->action().actionTypeId() == cionPowerActionTypeId) {
+ ModbusRtuReply *reply = cionConnection->setChargingEnabled(info->action().paramValue(cionPowerActionPowerParamTypeId).toBool() ? 1 : 0);
+ waitForActionFinish(info, reply, cionPowerStateTypeId);
+ } else if (info->action().actionTypeId() == cionMaxChargingCurrentActionTypeId) {
+ ModbusRtuReply *reply = cionConnection->setChargingCurrentSetpoint(info->action().paramValue(cionMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt());
+ waitForActionFinish(info, reply, cionMaxChargingCurrentStateTypeId);
+ }
+
+
+ Q_ASSERT_X(false, "IntegrationPluginSchrack::executeAction", QString("Unhandled action: %1").arg(info->action().actionTypeId().toString()).toLocal8Bit());
+}
+
+void IntegrationPluginSchrack::waitForActionFinish(ThingActionInfo *info, ModbusRtuReply *reply, const StateTypeId &stateTypeId)
+{
+ m_pendingActions.insert(info, stateTypeId);
+ connect(info, &ThingActionInfo::destroyed, this, [=](){
+ m_pendingActions.remove(info);
+ });
+
+ connect(reply, &ModbusRtuReply::finished, info, [=](){
+ if (reply->error() != ModbusRtuReply::NoError) {
+ info->finish(Thing::ThingErrorHardwareFailure);
+ }
+ });
+}
+
+void IntegrationPluginSchrack::finishAction(const StateTypeId &stateTypeId)
+{
+ foreach (ThingActionInfo *info, m_pendingActions.keys(stateTypeId)) {
+ info->finish(Thing::ThingErrorNoError);
+ }
+}
+
+void IntegrationPluginSchrack::updateCurrentPower(Thing *thing)
+{
+ CionModbusRtuConnection *cionConnection = m_cionConnections.value(thing);
+
+ QDateTime lastUpdate = thing->property("lastUpdate").toDateTime();
+ QDateTime now = QDateTime::currentDateTime();
+ if (lastUpdate.isValid()) {
+ double lastCurrentPower = thing->stateValue(cionCurrentPowerStateTypeId).toDouble();
+ double lastTotal = thing->stateValue(cionTotalEnergyConsumedStateTypeId).toDouble();
+ qlonglong msecsSinceLast = lastUpdate.msecsTo(now);
+ double wattMs = lastCurrentPower * msecsSinceLast;
+ double kWh = wattMs / 60 / 60;
+ thing->setStateValue(cionTotalEnergyConsumedStateTypeId, lastTotal + kWh);
+ }
+ thing->setProperty("lastUpdate", now);
+
+ if (cionConnection->chargingDuration() > 0) {
+ thing->setStateValue(cionCurrentPowerStateTypeId, 1.0 * cionConnection->u1Voltage() / 100 * cionConnection->currentChargingCurrentE3());
+ } else {
+ thing->setStateValue(cionCurrentPowerStateTypeId, 0);
+ }
+}
+
diff --git a/schrack/integrationpluginschrack.h b/schrack/integrationpluginschrack.h
new file mode 100644
index 0000000..75ace86
--- /dev/null
+++ b/schrack/integrationpluginschrack.h
@@ -0,0 +1,73 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 INTEGRATIONPLUGINSCHRACK_H
+#define INTEGRATIONPLUGINSCHRACK_H
+
+#include
+#include
+#include
+
+#include "cionmodbusrtuconnection.h"
+
+#include "extern-plugininfo.h"
+
+#include
+#include
+
+class IntegrationPluginSchrack : public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginschrack.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ explicit IntegrationPluginSchrack();
+ void init() override;
+ void discoverThings(ThingDiscoveryInfo *info) override;
+ void setupThing(ThingSetupInfo *info) override;
+ void postSetupThing(Thing *thing) override;
+ void thingRemoved(Thing *thing) override;
+ void executeAction(ThingActionInfo *info) override;
+
+private:
+ void waitForActionFinish(ThingActionInfo *info, ModbusRtuReply *reply, const StateTypeId &stateTypeId);
+ void finishAction(const StateTypeId &stateTypeId);
+
+ void updateCurrentPower(Thing *thing);
+private:
+ PluginTimer *m_refreshTimer = nullptr;
+
+ QHash m_cionConnections;
+ QHash m_pendingActions;
+};
+
+#endif // INTEGRATIONPLUGINSCHRACK_H
diff --git a/schrack/integrationpluginschrack.json b/schrack/integrationpluginschrack.json
new file mode 100644
index 0000000..055a322
--- /dev/null
+++ b/schrack/integrationpluginschrack.json
@@ -0,0 +1,116 @@
+{
+ "name": "schrack",
+ "displayName": "Schrack",
+ "id": "600beeb5-5c34-49fc-b2af-8f83c1b11eab",
+ "paramTypes":[ ],
+ "vendors": [
+ {
+ "name": "schrack",
+ "displayName": "Schrack GmbH",
+ "id": "cadbbbc5-216f-4d25-a8ad-ccf653d1518f",
+ "thingClasses": [
+ {
+ "name": "cion",
+ "displayName": "i-CHARGE CION",
+ "id": "075d389d-3330-4d0b-9649-9f085120ca40",
+ "createMethods": ["discovery"],
+ "interfaces": ["evcharger", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "8004705f-0e13-4713-b75e-49d115cd9517",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "dac10e08-734c-4e71-a5d6-0d2a1f416ca6",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "636241a9-4838-48ae-bcc8-3427ac3fc102",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "b7aa8e49-a6c0-4b48-b65e-47259686185f",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "61aea53a-bf8d-4fe6-859a-f1687e15d190",
+ "name": "power",
+ "displayName": "Charging enabled",
+ "displayNameEvent": "Charging enabled or disabled",
+ "displayNameAction": "Enable or disable charging",
+ "type": "bool",
+ "defaultValue": false,
+ "writable": true
+ },
+ {
+ "id": "221af869-a796-46c2-a5e0-27c8972a0bf2",
+ "name": "maxChargingCurrent",
+ "displayName": "Maximum charging current",
+ "displayNameEvent": "Maximum charging current changed",
+ "displayNameAction": "Set maximum charging current",
+ "type": "uint",
+ "unit": "Ampere",
+ "defaultValue": 6,
+ "minValue": 1,
+ "maxValue": 32,
+ "writable": true
+ },
+ {
+ "id": "db5c951f-47e0-4cdc-8b8b-285f7ed95285",
+ "name": "currentPower",
+ "displayName": "Current power consumption",
+ "displayNameEvent": "Current power consumption changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "8390c005-a8ba-4db6-bb94-8f765ddcc784",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total consumed energy",
+ "displayNameEvent": "Total consumed energy changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "13423618-4314-49be-b48c-42d9415199a8",
+ "name": "pluggedIn",
+ "displayName": "Plugged in",
+ "displayNameEvent": "Plugged or unplugged",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "b59b4b4d-2cdb-4534-bf86-90123ae9bb1a",
+ "name": "charging",
+ "displayName": "Charging",
+ "displayNameEvent": "Charging started or stopped",
+ "type": "bool",
+ "defaultValue": false
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/schrack/meta.json b/schrack/meta.json
new file mode 100644
index 0000000..b402a34
--- /dev/null
+++ b/schrack/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "Schrack",
+ "tagline": "Integrate the Schrack i-CHARGE CION wallbox with nymea.",
+ "icon": "schrack.jpg",
+ "stability": "consumer",
+ "offline": true,
+ "technologies": [
+ "modbus"
+ ],
+ "categories": [
+ "energy"
+ ]
+}
diff --git a/schrack/schrack.jpg b/schrack/schrack.jpg
new file mode 100644
index 0000000..0b6b4d0
Binary files /dev/null and b/schrack/schrack.jpg differ
diff --git a/schrack/schrack.pro b/schrack/schrack.pro
new file mode 100644
index 0000000..88565a9
--- /dev/null
+++ b/schrack/schrack.pro
@@ -0,0 +1,20 @@
+include(../plugins.pri)
+
+QT += serialport serialbus
+
+SOURCES += \
+ integrationpluginschrack.cpp \
+ cionmodbusrtuconnection.cpp \
+ ../modbus/modbusdatautils.cpp
+
+HEADERS += \
+ integrationpluginschrack.h \
+ cionmodbusrtuconnection.h \
+ ../modbus/modbusdatautils.h
+
+OTHER_FILES += cion-registers.json
+
+modbusconnection.commands = python $${top_srcdir}/modbus/tools/generate-connection.py -j $${_PRO_FILE_PWD_}/cion-registers.json -o $${_PRO_FILE_PWD_} -c CionModbusRtuConnection
+QMAKE_EXTRA_TARGETS += modbusconnection
+
+#target.depends += modbusconnection
diff --git a/schrack/translations/600beeb5-5c34-49fc-b2af-8f83c1b11eab-en_US.ts b/schrack/translations/600beeb5-5c34-49fc-b2af-8f83c1b11eab-en_US.ts
new file mode 100644
index 0000000..da42dcd
--- /dev/null
+++ b/schrack/translations/600beeb5-5c34-49fc-b2af-8f83c1b11eab-en_US.ts
@@ -0,0 +1,189 @@
+
+
+
+
+ IntegrationPluginSchrack
+
+
+ No Modbus RTU interface available. Please set up a Modbus RTU interface first.
+
+
+
+
+ The Modbus slave address must be a value between 1 and 254.
+
+
+
+
+ The Modbus address not valid. It must be a value between 1 and 254.
+
+
+
+
+ The Modbus RTU resource is not available.
+
+
+
+
+ schrack
+
+
+
+ Charging
+ The name of the ParamType (ThingClass: cion, EventType: charging, ID: {b59b4b4d-2cdb-4534-bf86-90123ae9bb1a})
+----------
+The name of the StateType ({b59b4b4d-2cdb-4534-bf86-90123ae9bb1a}) of ThingClass cion
+
+
+
+
+
+
+ Charging enabled
+ The name of the ParamType (ThingClass: cion, ActionType: power, ID: {61aea53a-bf8d-4fe6-859a-f1687e15d190})
+----------
+The name of the ParamType (ThingClass: cion, EventType: power, ID: {61aea53a-bf8d-4fe6-859a-f1687e15d190})
+----------
+The name of the StateType ({61aea53a-bf8d-4fe6-859a-f1687e15d190}) of ThingClass cion
+
+
+
+
+ Charging enabled or disabled
+ The name of the EventType ({61aea53a-bf8d-4fe6-859a-f1687e15d190}) of ThingClass cion
+
+
+
+
+ Charging started or stopped
+ The name of the EventType ({b59b4b4d-2cdb-4534-bf86-90123ae9bb1a}) of ThingClass cion
+
+
+
+
+
+ Connected
+ The name of the ParamType (ThingClass: cion, EventType: connected, ID: {b7aa8e49-a6c0-4b48-b65e-47259686185f})
+----------
+The name of the StateType ({b7aa8e49-a6c0-4b48-b65e-47259686185f}) of ThingClass cion
+
+
+
+
+ Connected changed
+ The name of the EventType ({b7aa8e49-a6c0-4b48-b65e-47259686185f}) of ThingClass cion
+
+
+
+
+
+ Current power consumption
+ The name of the ParamType (ThingClass: cion, EventType: currentPower, ID: {db5c951f-47e0-4cdc-8b8b-285f7ed95285})
+----------
+The name of the StateType ({db5c951f-47e0-4cdc-8b8b-285f7ed95285}) of ThingClass cion
+
+
+
+
+ Current power consumption changed
+ The name of the EventType ({db5c951f-47e0-4cdc-8b8b-285f7ed95285}) of ThingClass cion
+
+
+
+
+ Enable or disable charging
+ The name of the ActionType ({61aea53a-bf8d-4fe6-859a-f1687e15d190}) of ThingClass cion
+
+
+
+
+
+
+ Maximum charging current
+ The name of the ParamType (ThingClass: cion, ActionType: maxChargingCurrent, ID: {221af869-a796-46c2-a5e0-27c8972a0bf2})
+----------
+The name of the ParamType (ThingClass: cion, EventType: maxChargingCurrent, ID: {221af869-a796-46c2-a5e0-27c8972a0bf2})
+----------
+The name of the StateType ({221af869-a796-46c2-a5e0-27c8972a0bf2}) of ThingClass cion
+
+
+
+
+ Maximum charging current changed
+ The name of the EventType ({221af869-a796-46c2-a5e0-27c8972a0bf2}) of ThingClass cion
+
+
+
+
+ Modbus RTU master
+ The name of the ParamType (ThingClass: cion, Type: thing, ID: {636241a9-4838-48ae-bcc8-3427ac3fc102})
+
+
+
+
+ Modbus slave address
+ The name of the ParamType (ThingClass: cion, Type: thing, ID: {dac10e08-734c-4e71-a5d6-0d2a1f416ca6})
+
+
+
+
+
+ Plugged in
+ The name of the ParamType (ThingClass: cion, EventType: pluggedIn, ID: {13423618-4314-49be-b48c-42d9415199a8})
+----------
+The name of the StateType ({13423618-4314-49be-b48c-42d9415199a8}) of ThingClass cion
+
+
+
+
+ Plugged or unplugged
+ The name of the EventType ({13423618-4314-49be-b48c-42d9415199a8}) of ThingClass cion
+
+
+
+
+ Schrack
+ The name of the plugin schrack ({600beeb5-5c34-49fc-b2af-8f83c1b11eab})
+
+
+
+
+ Schrack GmbH
+ The name of the vendor ({cadbbbc5-216f-4d25-a8ad-ccf653d1518f})
+
+
+
+
+ Set maximum charging current
+ The name of the ActionType ({221af869-a796-46c2-a5e0-27c8972a0bf2}) of ThingClass cion
+
+
+
+
+ Slave address
+ The name of the ParamType (ThingClass: cion, Type: discovery, ID: {8004705f-0e13-4713-b75e-49d115cd9517})
+
+
+
+
+
+ Total consumed energy
+ The name of the ParamType (ThingClass: cion, EventType: totalEnergyConsumed, ID: {8390c005-a8ba-4db6-bb94-8f765ddcc784})
+----------
+The name of the StateType ({8390c005-a8ba-4db6-bb94-8f765ddcc784}) of ThingClass cion
+
+
+
+
+ Total consumed energy changed
+ The name of the EventType ({8390c005-a8ba-4db6-bb94-8f765ddcc784}) of ThingClass cion
+
+
+
+
+ i-CHARGE CION
+ The name of the ThingClass ({075d389d-3330-4d0b-9649-9f085120ca40})
+
+
+
+