// SPDX-License-Identifier: GPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2025, ETM-Schurig SARL * * This file is part of nymea-plugins-modbus. * * nymea-plugins-modbus is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * nymea-plugins-modbus 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with nymea-plugins-modbus. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "integrationpluginabbb2x.h" #include "plugininfo.h" // Facteurs d'echelle ABB B2x (cf. registers.json / manuel B2x) : // tension : 0,1 V -> /10 // courant : 0,01 A -> /100 // puissance : 0,01 W -> /100 (registre SIGNED : + import / - export) // frequence : 0,01 Hz -> /100 // energie cumulee : 0,01 kWh -> /100 // NB : le generateur renvoie la valeur BRUTE du registre ; le scaling se fait ici // (meme convention que le plugin abbterra). IntegrationPluginAbbB2x::IntegrationPluginAbbB2x() { } void IntegrationPluginAbbB2x::init() { connect(hardwareManager()->modbusRtuResource(), &ModbusRtuHardwareResource::modbusRtuMasterRemoved, this, [=](const QUuid &modbusUuid) { qCDebug(dcAbbB2x()) << "Modbus RTU master removed:" << modbusUuid.toString(); foreach (Thing *thing, myThings()) { if (thing->paramValue(abbB2xThingModbusMasterUuidParamTypeId) == modbusUuid) { thing->setStateValue(abbB2xConnectedStateTypeId, false); if (m_connections.contains(thing)) delete m_connections.take(thing); } } }); } void IntegrationPluginAbbB2x::discoverThings(ThingDiscoveryInfo *info) { if (info->thingClassId() != abbB2xThingClassId) { info->finish(Thing::ThingErrorThingClassNotFound); return; } if (hardwareManager()->modbusRtuResource()->modbusRtuMasters().isEmpty()) { info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("No Modbus RTU interface available. Please set up the Modbus RTU interface first.")); return; } uint slaveAddress = info->params().paramValue(abbB2xDiscoverySlaveAddressParamTypeId).toUInt(); if (slaveAddress == 0 || slaveAddress > 254) { 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(dcAbbB2x()) << "Found RTU master" << modbusMaster << "connected:" << modbusMaster->connected(); if (!modbusMaster->connected()) continue; ThingDescriptor descriptor(info->thingClassId(), "ABB B2x", QString::number(slaveAddress) + " " + modbusMaster->serialPort()); ParamList params; params << Param(abbB2xThingSlaveAddressParamTypeId, slaveAddress); params << Param(abbB2xThingModbusMasterUuidParamTypeId, modbusMaster->modbusUuid()); descriptor.setParams(params); info->addThingDescriptor(descriptor); } info->finish(Thing::ThingErrorNoError); } void IntegrationPluginAbbB2x::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); if (thing->thingClassId() != abbB2xThingClassId) { info->finish(Thing::ThingErrorThingClassNotFound); return; } uint address = thing->paramValue(abbB2xThingSlaveAddressParamTypeId).toUInt(); if (address == 0 || address > 254) { qCWarning(dcAbbB2x()) << "Setup failed, invalid slave address" << 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(abbB2xThingModbusMasterUuidParamTypeId).toUuid(); if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) { qCWarning(dcAbbB2x()) << "Setup failed, Modbus RTU master not available"; info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU interface not available.")); return; } if (m_connections.contains(thing)) m_connections.take(thing)->deleteLater(); AbbB2xModbusRtuConnection *connection = new AbbB2xModbusRtuConnection( hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, this); connect(connection, &AbbB2xModbusRtuConnection::reachableChanged, this, [=](bool reachable) { thing->setStateValue(abbB2xConnectedStateTypeId, reachable); }); // Tensions (0,1 V) connect(connection, &AbbB2xModbusRtuConnection::voltagePhaseAChanged, this, [=](quint32 v) { thing->setStateValue(abbB2xVoltagePhaseAStateTypeId, v / 10.0); }); connect(connection, &AbbB2xModbusRtuConnection::voltagePhaseBChanged, this, [=](quint32 v) { thing->setStateValue(abbB2xVoltagePhaseBStateTypeId, v / 10.0); }); connect(connection, &AbbB2xModbusRtuConnection::voltagePhaseCChanged, this, [=](quint32 v) { thing->setStateValue(abbB2xVoltagePhaseCStateTypeId, v / 10.0); }); // Courants (0,01 A) connect(connection, &AbbB2xModbusRtuConnection::currentPhaseAChanged, this, [=](quint32 v) { thing->setStateValue(abbB2xCurrentPhaseAStateTypeId, v / 100.0); }); connect(connection, &AbbB2xModbusRtuConnection::currentPhaseBChanged, this, [=](quint32 v) { thing->setStateValue(abbB2xCurrentPhaseBStateTypeId, v / 100.0); }); connect(connection, &AbbB2xModbusRtuConnection::currentPhaseCChanged, this, [=](quint32 v) { thing->setStateValue(abbB2xCurrentPhaseCStateTypeId, v / 100.0); }); // Puissances (0,01 W, SIGNED : + import / - export) connect(connection, &AbbB2xModbusRtuConnection::activePowerTotalChanged, this, [=](qint32 v) { thing->setStateValue(abbB2xCurrentPowerStateTypeId, v / 100.0); }); connect(connection, &AbbB2xModbusRtuConnection::activePowerPhaseAChanged, this, [=](qint32 v) { thing->setStateValue(abbB2xCurrentPowerPhaseAStateTypeId, v / 100.0); }); connect(connection, &AbbB2xModbusRtuConnection::activePowerPhaseBChanged, this, [=](qint32 v) { thing->setStateValue(abbB2xCurrentPowerPhaseBStateTypeId, v / 100.0); }); connect(connection, &AbbB2xModbusRtuConnection::activePowerPhaseCChanged, this, [=](qint32 v) { thing->setStateValue(abbB2xCurrentPowerPhaseCStateTypeId, v / 100.0); }); // Frequence (0,01 Hz) connect(connection, &AbbB2xModbusRtuConnection::frequencyChanged, this, [=](quint16 v) { thing->setStateValue(abbB2xFrequencyStateTypeId, v / 100.0); }); // Energie cumulee (0,01 kWh) connect(connection, &AbbB2xModbusRtuConnection::totalEnergyConsumedChanged, this, [=](quint64 v) { thing->setStateValue(abbB2xTotalEnergyConsumedStateTypeId, v / 100.0); }); connect(connection, &AbbB2xModbusRtuConnection::totalEnergyProducedChanged, this, [=](quint64 v) { thing->setStateValue(abbB2xTotalEnergyProducedStateTypeId, v / 100.0); }); m_connections.insert(thing, connection); info->finish(Thing::ThingErrorNoError); } void IntegrationPluginAbbB2x::postSetupThing(Thing *thing) { qCDebug(dcAbbB2x()) << "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()) { if (m_connections.contains(thing)) m_connections.value(thing)->update(); } }); qCDebug(dcAbbB2x()) << "Refresh timer started"; m_refreshTimer->start(); } } void IntegrationPluginAbbB2x::thingRemoved(Thing *thing) { qCDebug(dcAbbB2x()) << "Thing removed" << thing->name(); if (m_connections.contains(thing)) m_connections.take(thing)->deleteLater(); if (myThings().isEmpty() && m_refreshTimer) { hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer); m_refreshTimer = nullptr; qCDebug(dcAbbB2x()) << "Refresh timer stopped"; } }