// 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 <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "integrationplugineastron.h"
#include "plugininfo.h"

IntegrationPluginEastron::IntegrationPluginEastron()
{
}

void IntegrationPluginEastron::init()
{
    connect(hardwareManager()->modbusRtuResource(), &ModbusRtuHardwareResource::modbusRtuMasterRemoved,
            this, [=](const QUuid &modbusUuid) {
        qCDebug(dcEastron()) << "Modbus RTU master removed:" << modbusUuid.toString();

        foreach (Thing *thing, myThings()) {
            ThingClassId classId = thing->thingClassId();

            if (classId == sdm630ThingClassId || classId == sdm630ConsumerThingClassId || classId == sdm630ProducerThingClassId) {
                if (thing->paramValue(sdm630ThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm630ConnectedStateTypeId, false);
                    delete m_sdm630Connections.take(thing);
                }
            } else if (classId == sdm630ConsumerThingClassId) {
                if (thing->paramValue(sdm630ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm630ConsumerConnectedStateTypeId, false);
                    delete m_sdm630Connections.take(thing);
                }
            } else if (classId == sdm630ProducerThingClassId) {
                if (thing->paramValue(sdm630ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm630ProducerConnectedStateTypeId, false);
                    delete m_sdm630Connections.take(thing);
                }
            } else if (classId == sdm72ThingClassId) {
                if (thing->paramValue(sdm72ThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm72ConnectedStateTypeId, false);
                    delete m_sdm72Connections.take(thing);
                }
            } else if (classId == sdm72ConsumerThingClassId) {
                if (thing->paramValue(sdm72ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm72ConsumerConnectedStateTypeId, false);
                    delete m_sdm72Connections.take(thing);
                }
            } else if (classId == sdm72ProducerThingClassId) {
                if (thing->paramValue(sdm72ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm72ProducerConnectedStateTypeId, false);
                    delete m_sdm72Connections.take(thing);
                }
            } else if (classId == sdm120ThingClassId) {
                if (thing->paramValue(sdm120ThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm120ConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            } else if (classId == sdm120ConsumerThingClassId) {
                if (thing->paramValue(sdm120ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm120ConsumerConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            } else if (classId == sdm120ProducerThingClassId) {
                if (thing->paramValue(sdm120ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm120ProducerConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            } else if (classId == sdm220ThingClassId) {
                if (thing->paramValue(sdm220ThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm220ConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            } else if (classId == sdm220ConsumerThingClassId) {
                if (thing->paramValue(sdm220ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm220ConsumerConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            } else if (classId == sdm220ProducerThingClassId) {
                if (thing->paramValue(sdm220ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm220ProducerConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            } else if (classId == sdm230ThingClassId) {
                if (thing->paramValue(sdm230ThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm230ConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            } else if (classId == sdm230ConsumerThingClassId) {
                if (thing->paramValue(sdm230ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm230ConsumerConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            } else if (classId == sdm230ProducerThingClassId) {
                if (thing->paramValue(sdm230ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
                    thing->setStateValue(sdm230ProducerConnectedStateTypeId, false);
                    delete m_sdm120Connections.take(thing);
                }
            }
        }
    });
}

void IntegrationPluginEastron::discoverThings(ThingDiscoveryInfo *info)
{
    ThingClassId classId = info->thingClassId();
    QString modelName;
    ParamTypeId discoverySlaveParamTypeId;
    ParamTypeId thingSlaveParamTypeId;
    ParamTypeId thingMasterUuidParamTypeId;

    if (classId == sdm630ThingClassId) {
        modelName = "SDM630";
        discoverySlaveParamTypeId = sdm630DiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm630ThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm630ThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm630ConsumerThingClassId) {
        modelName = "SDM630";
        discoverySlaveParamTypeId = sdm630ConsumerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm630ConsumerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm630ConsumerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm630ProducerThingClassId) {
        modelName = "SDM630";
        discoverySlaveParamTypeId = sdm630ProducerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm630ProducerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm630ProducerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm72ThingClassId) {
        modelName = "SDM72";
        discoverySlaveParamTypeId = sdm72DiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm72ThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm72ThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm72ConsumerThingClassId) {
        modelName = "SDM72";
        discoverySlaveParamTypeId = sdm72ConsumerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm72ConsumerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm72ConsumerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm72ProducerThingClassId) {
        modelName = "SDM72";
        discoverySlaveParamTypeId = sdm72ProducerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm72ProducerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm72ProducerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm120ThingClassId) {
        modelName = "SDM120";
        discoverySlaveParamTypeId = sdm120DiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm120ThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm120ThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm120ConsumerThingClassId) {
        modelName = "SDM120";
        discoverySlaveParamTypeId = sdm120ConsumerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm120ConsumerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm120ConsumerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm120ProducerThingClassId) {
        modelName = "SDM120";
        discoverySlaveParamTypeId = sdm120ProducerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm120ProducerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm120ProducerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm220ThingClassId) {
        modelName = "SDM220";
        discoverySlaveParamTypeId = sdm220DiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm220ThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm220ThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm220ConsumerThingClassId) {
        modelName = "SDM220";
        discoverySlaveParamTypeId = sdm220ConsumerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm220ConsumerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm220ConsumerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm220ProducerThingClassId) {
        modelName = "SDM220";
        discoverySlaveParamTypeId = sdm220ProducerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm220ProducerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm220ProducerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm230ThingClassId) {
        modelName = "SDM230";
        discoverySlaveParamTypeId = sdm230DiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm230ThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm230ThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm230ConsumerThingClassId) {
        modelName = "SDM230";
        discoverySlaveParamTypeId = sdm230ConsumerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm230ConsumerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm230ConsumerThingModbusMasterUuidParamTypeId;
    } else if (classId == sdm230ProducerThingClassId) {
        modelName = "SDM230";
        discoverySlaveParamTypeId = sdm230ProducerDiscoverySlaveAddressParamTypeId;
        thingSlaveParamTypeId = sdm230ProducerThingSlaveAddressParamTypeId;
        thingMasterUuidParamTypeId = sdm230ProducerThingModbusMasterUuidParamTypeId;
    } else {
        info->finish(Thing::ThingErrorThingClassNotFound);
        return;
    }

    discoverRtuDevices(info, modelName, discoverySlaveParamTypeId, thingSlaveParamTypeId, thingMasterUuidParamTypeId);
}

void IntegrationPluginEastron::discoverRtuDevices(ThingDiscoveryInfo *info, const QString &modelName,
                                                   const ParamTypeId &discoverySlaveParamTypeId,
                                                   const ParamTypeId &thingSlaveParamTypeId,
                                                   const ParamTypeId &thingMasterUuidParamTypeId)
{
    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(discoverySlaveParamTypeId).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(dcEastron()) << "Found RTU master" << modbusMaster << "connected:" << modbusMaster->connected();
        if (!modbusMaster->connected())
            continue;

        ThingDescriptor descriptor(info->thingClassId(), modelName,
                                   QString::number(slaveAddress) + " " + modbusMaster->serialPort());
        ParamList params;
        params << Param(thingSlaveParamTypeId, slaveAddress);
        params << Param(thingMasterUuidParamTypeId, modbusMaster->modbusUuid());
        descriptor.setParams(params);
        info->addThingDescriptor(descriptor);
    }

    info->finish(Thing::ThingErrorNoError);
}

void IntegrationPluginEastron::setupThing(ThingSetupInfo *info)
{
    ThingClassId classId = info->thing()->thingClassId();

    if (classId == sdm630ThingClassId || classId == sdm630ConsumerThingClassId || classId == sdm630ProducerThingClassId) {
        setupSdm630(info);
    } else if (classId == sdm72ThingClassId || classId == sdm72ConsumerThingClassId || classId == sdm72ProducerThingClassId) {
        setupSdm72(info);
    } else if (classId == sdm120ThingClassId || classId == sdm120ConsumerThingClassId || classId == sdm120ProducerThingClassId
               || classId == sdm220ThingClassId || classId == sdm220ConsumerThingClassId || classId == sdm220ProducerThingClassId
               || classId == sdm230ThingClassId || classId == sdm230ConsumerThingClassId || classId == sdm230ProducerThingClassId) {
        setupSdm120(info);
    } else {
        info->finish(Thing::ThingErrorThingClassNotFound);
    }
}

void IntegrationPluginEastron::setupSdm630(ThingSetupInfo *info)
{
    Thing *thing = info->thing();
    ThingClassId classId = thing->thingClassId();

    ParamTypeId slaveParamTypeId;
    ParamTypeId masterUuidParamTypeId;
    StateTypeId connectedStateTypeId;

    if (classId == sdm630ThingClassId) {
        slaveParamTypeId = sdm630ThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm630ThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm630ConnectedStateTypeId;
    } else if (classId == sdm630ConsumerThingClassId) {
        slaveParamTypeId = sdm630ConsumerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm630ConsumerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm630ConsumerConnectedStateTypeId;
    } else {
        slaveParamTypeId = sdm630ProducerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm630ProducerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm630ProducerConnectedStateTypeId;
    }

    uint address = thing->paramValue(slaveParamTypeId).toUInt();
    if (address == 0 || address > 254) {
        qCWarning(dcEastron()) << "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(masterUuidParamTypeId).toUuid();
    if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
        qCWarning(dcEastron()) << "Setup failed, Modbus RTU master not available";
        info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU interface not available."));
        return;
    }

    if (m_sdm630Connections.contains(thing))
        m_sdm630Connections.take(thing)->deleteLater();

    Sdm630ModbusRtuConnection *connection = new Sdm630ModbusRtuConnection(
        hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, this);

    connect(connection, &Sdm630ModbusRtuConnection::reachableChanged, this, [=](bool reachable) {
        thing->setStateValue(connectedStateTypeId, reachable);
    });

    if (classId == sdm630ThingClassId) {
        connect(connection, &Sdm630ModbusRtuConnection::voltagePhaseAChanged, this, [=](float v) {
            thing->setStateValue(sdm630VoltagePhaseAStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::voltagePhaseBChanged, this, [=](float v) {
            thing->setStateValue(sdm630VoltagePhaseBStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::voltagePhaseCChanged, this, [=](float v) {
            thing->setStateValue(sdm630VoltagePhaseCStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::currentPhaseAChanged, this, [=](float v) {
            thing->setStateValue(sdm630CurrentPhaseAStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::currentPhaseBChanged, this, [=](float v) {
            thing->setStateValue(sdm630CurrentPhaseBStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::currentPhaseCChanged, this, [=](float v) {
            thing->setStateValue(sdm630CurrentPhaseCStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
            thing->setStateValue(sdm630CurrentPowerStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::powerPhaseAChanged, this, [=](float v) {
            thing->setStateValue(sdm630CurrentPowerPhaseAStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::powerPhaseBChanged, this, [=](float v) {
            thing->setStateValue(sdm630CurrentPowerPhaseBStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::powerPhaseCChanged, this, [=](float v) {
            thing->setStateValue(sdm630CurrentPowerPhaseCStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::frequencyChanged, this, [=](float v) {
            thing->setStateValue(sdm630FrequencyStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
            thing->setStateValue(sdm630TotalEnergyConsumedStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
            thing->setStateValue(sdm630TotalEnergyProducedStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::energyConsumedPhaseAChanged, this, [=](float v) {
            thing->setStateValue(sdm630EnergyConsumedPhaseAStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::energyConsumedPhaseBChanged, this, [=](float v) {
            thing->setStateValue(sdm630EnergyConsumedPhaseBStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::energyConsumedPhaseCChanged, this, [=](float v) {
            thing->setStateValue(sdm630EnergyConsumedPhaseCStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::energyProducedPhaseAChanged, this, [=](float v) {
            thing->setStateValue(sdm630EnergyProducedPhaseAStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::energyProducedPhaseBChanged, this, [=](float v) {
            thing->setStateValue(sdm630EnergyProducedPhaseBStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::energyProducedPhaseCChanged, this, [=](float v) {
            thing->setStateValue(sdm630EnergyProducedPhaseCStateTypeId, v);
        });

    } else if (classId == sdm630ConsumerThingClassId) {
        connect(connection, &Sdm630ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
            thing->setStateValue(sdm630ConsumerCurrentPowerStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
            thing->setStateValue(sdm630ConsumerTotalEnergyConsumedStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::frequencyChanged, this, [=](float v) {
            thing->setStateValue(sdm630ConsumerFrequencyStateTypeId, v);
        });

    } else { // sdm630Producer
        connect(connection, &Sdm630ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
            thing->setStateValue(sdm630ProducerCurrentPowerStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
            thing->setStateValue(sdm630ProducerTotalEnergyProducedStateTypeId, v);
        });
        connect(connection, &Sdm630ModbusRtuConnection::frequencyChanged, this, [=](float v) {
            thing->setStateValue(sdm630ProducerFrequencyStateTypeId, v);
        });
    }

    m_sdm630Connections.insert(thing, connection);
    info->finish(Thing::ThingErrorNoError);
}

void IntegrationPluginEastron::setupSdm72(ThingSetupInfo *info)
{
    Thing *thing = info->thing();
    ThingClassId classId = thing->thingClassId();

    ParamTypeId slaveParamTypeId;
    ParamTypeId masterUuidParamTypeId;
    StateTypeId connectedStateTypeId;

    if (classId == sdm72ThingClassId) {
        slaveParamTypeId = sdm72ThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm72ThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm72ConnectedStateTypeId;
    } else if (classId == sdm72ConsumerThingClassId) {
        slaveParamTypeId = sdm72ConsumerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm72ConsumerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm72ConsumerConnectedStateTypeId;
    } else {
        slaveParamTypeId = sdm72ProducerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm72ProducerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm72ProducerConnectedStateTypeId;
    }

    uint address = thing->paramValue(slaveParamTypeId).toUInt();
    if (address == 0 || address > 254) {
        qCWarning(dcEastron()) << "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(masterUuidParamTypeId).toUuid();
    if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
        qCWarning(dcEastron()) << "Setup failed, Modbus RTU master not available";
        info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU interface not available."));
        return;
    }

    if (m_sdm72Connections.contains(thing))
        m_sdm72Connections.take(thing)->deleteLater();

    Sdm72ModbusRtuConnection *connection = new Sdm72ModbusRtuConnection(
        hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, this);

    connect(connection, &Sdm72ModbusRtuConnection::reachableChanged, this, [=](bool reachable) {
        thing->setStateValue(connectedStateTypeId, reachable);
    });

    if (classId == sdm72ThingClassId) {
        connect(connection, &Sdm72ModbusRtuConnection::voltagePhaseAChanged, this, [=](float v) {
            thing->setStateValue(sdm72VoltagePhaseAStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::voltagePhaseBChanged, this, [=](float v) {
            thing->setStateValue(sdm72VoltagePhaseBStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::voltagePhaseCChanged, this, [=](float v) {
            thing->setStateValue(sdm72VoltagePhaseCStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::currentPhaseAChanged, this, [=](float v) {
            thing->setStateValue(sdm72CurrentPhaseAStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::currentPhaseBChanged, this, [=](float v) {
            thing->setStateValue(sdm72CurrentPhaseBStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::currentPhaseCChanged, this, [=](float v) {
            thing->setStateValue(sdm72CurrentPhaseCStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
            thing->setStateValue(sdm72CurrentPowerStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::powerPhaseAChanged, this, [=](float v) {
            thing->setStateValue(sdm72CurrentPowerPhaseAStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::powerPhaseBChanged, this, [=](float v) {
            thing->setStateValue(sdm72CurrentPowerPhaseBStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::powerPhaseCChanged, this, [=](float v) {
            thing->setStateValue(sdm72CurrentPowerPhaseCStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::frequencyChanged, this, [=](float v) {
            thing->setStateValue(sdm72FrequencyStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
            thing->setStateValue(sdm72TotalEnergyConsumedStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
            thing->setStateValue(sdm72TotalEnergyProducedStateTypeId, v);
        });

    } else if (classId == sdm72ConsumerThingClassId) {
        connect(connection, &Sdm72ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
            thing->setStateValue(sdm72ConsumerCurrentPowerStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
            thing->setStateValue(sdm72ConsumerTotalEnergyConsumedStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::frequencyChanged, this, [=](float v) {
            thing->setStateValue(sdm72ConsumerFrequencyStateTypeId, v);
        });

    } else { // sdm72Producer
        connect(connection, &Sdm72ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
            thing->setStateValue(sdm72ProducerCurrentPowerStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
            thing->setStateValue(sdm72ProducerTotalEnergyProducedStateTypeId, v);
        });
        connect(connection, &Sdm72ModbusRtuConnection::frequencyChanged, this, [=](float v) {
            thing->setStateValue(sdm72ProducerFrequencyStateTypeId, v);
        });
    }

    m_sdm72Connections.insert(thing, connection);
    info->finish(Thing::ThingErrorNoError);
}

void IntegrationPluginEastron::setupSdm120(ThingSetupInfo *info)
{
    Thing *thing = info->thing();
    ThingClassId classId = thing->thingClassId();

    ParamTypeId slaveParamTypeId;
    ParamTypeId masterUuidParamTypeId;
    StateTypeId connectedStateTypeId;
    StateTypeId voltageStateTypeId;
    StateTypeId currentStateTypeId;
    StateTypeId powerStateTypeId;
    StateTypeId frequencyStateTypeId;
    StateTypeId totalEnergyConsumedStateTypeId;
    StateTypeId totalEnergyProducedStateTypeId;
    bool isEnergyMeter = false;

    if (classId == sdm120ThingClassId) {
        slaveParamTypeId = sdm120ThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm120ThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm120ConnectedStateTypeId;
        voltageStateTypeId = sdm120VoltagePhaseAStateTypeId;
        currentStateTypeId = sdm120CurrentPhaseAStateTypeId;
        powerStateTypeId = sdm120CurrentPowerStateTypeId;
        frequencyStateTypeId = sdm120FrequencyStateTypeId;
        totalEnergyConsumedStateTypeId = sdm120TotalEnergyConsumedStateTypeId;
        totalEnergyProducedStateTypeId = sdm120TotalEnergyProducedStateTypeId;
        isEnergyMeter = true;
    } else if (classId == sdm120ConsumerThingClassId) {
        slaveParamTypeId = sdm120ConsumerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm120ConsumerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm120ConsumerConnectedStateTypeId;
        powerStateTypeId = sdm120ConsumerCurrentPowerStateTypeId;
        frequencyStateTypeId = sdm120ConsumerFrequencyStateTypeId;
        totalEnergyConsumedStateTypeId = sdm120ConsumerTotalEnergyConsumedStateTypeId;
    } else if (classId == sdm120ProducerThingClassId) {
        slaveParamTypeId = sdm120ProducerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm120ProducerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm120ProducerConnectedStateTypeId;
        powerStateTypeId = sdm120ProducerCurrentPowerStateTypeId;
        frequencyStateTypeId = sdm120ProducerFrequencyStateTypeId;
        totalEnergyProducedStateTypeId = sdm120ProducerTotalEnergyProducedStateTypeId;
    } else if (classId == sdm220ThingClassId) {
        slaveParamTypeId = sdm220ThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm220ThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm220ConnectedStateTypeId;
        voltageStateTypeId = sdm220VoltagePhaseAStateTypeId;
        currentStateTypeId = sdm220CurrentPhaseAStateTypeId;
        powerStateTypeId = sdm220CurrentPowerStateTypeId;
        frequencyStateTypeId = sdm220FrequencyStateTypeId;
        totalEnergyConsumedStateTypeId = sdm220TotalEnergyConsumedStateTypeId;
        totalEnergyProducedStateTypeId = sdm220TotalEnergyProducedStateTypeId;
        isEnergyMeter = true;
    } else if (classId == sdm220ConsumerThingClassId) {
        slaveParamTypeId = sdm220ConsumerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm220ConsumerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm220ConsumerConnectedStateTypeId;
        powerStateTypeId = sdm220ConsumerCurrentPowerStateTypeId;
        frequencyStateTypeId = sdm220ConsumerFrequencyStateTypeId;
        totalEnergyConsumedStateTypeId = sdm220ConsumerTotalEnergyConsumedStateTypeId;
    } else if (classId == sdm220ProducerThingClassId) {
        slaveParamTypeId = sdm220ProducerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm220ProducerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm220ProducerConnectedStateTypeId;
        powerStateTypeId = sdm220ProducerCurrentPowerStateTypeId;
        frequencyStateTypeId = sdm220ProducerFrequencyStateTypeId;
        totalEnergyProducedStateTypeId = sdm220ProducerTotalEnergyProducedStateTypeId;
    } else if (classId == sdm230ThingClassId) {
        slaveParamTypeId = sdm230ThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm230ThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm230ConnectedStateTypeId;
        voltageStateTypeId = sdm230VoltagePhaseAStateTypeId;
        currentStateTypeId = sdm230CurrentPhaseAStateTypeId;
        powerStateTypeId = sdm230CurrentPowerStateTypeId;
        frequencyStateTypeId = sdm230FrequencyStateTypeId;
        totalEnergyConsumedStateTypeId = sdm230TotalEnergyConsumedStateTypeId;
        totalEnergyProducedStateTypeId = sdm230TotalEnergyProducedStateTypeId;
        isEnergyMeter = true;
    } else if (classId == sdm230ConsumerThingClassId) {
        slaveParamTypeId = sdm230ConsumerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm230ConsumerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm230ConsumerConnectedStateTypeId;
        powerStateTypeId = sdm230ConsumerCurrentPowerStateTypeId;
        frequencyStateTypeId = sdm230ConsumerFrequencyStateTypeId;
        totalEnergyConsumedStateTypeId = sdm230ConsumerTotalEnergyConsumedStateTypeId;
    } else { // sdm230Producer
        slaveParamTypeId = sdm230ProducerThingSlaveAddressParamTypeId;
        masterUuidParamTypeId = sdm230ProducerThingModbusMasterUuidParamTypeId;
        connectedStateTypeId = sdm230ProducerConnectedStateTypeId;
        powerStateTypeId = sdm230ProducerCurrentPowerStateTypeId;
        frequencyStateTypeId = sdm230ProducerFrequencyStateTypeId;
        totalEnergyProducedStateTypeId = sdm230ProducerTotalEnergyProducedStateTypeId;
    }

    uint address = thing->paramValue(slaveParamTypeId).toUInt();
    if (address == 0 || address > 254) {
        qCWarning(dcEastron()) << "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(masterUuidParamTypeId).toUuid();
    if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
        qCWarning(dcEastron()) << "Setup failed, Modbus RTU master not available";
        info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU interface not available."));
        return;
    }

    if (m_sdm120Connections.contains(thing))
        m_sdm120Connections.take(thing)->deleteLater();

    Sdm120ModbusRtuConnection *connection = new Sdm120ModbusRtuConnection(
        hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, this);

    connect(connection, &Sdm120ModbusRtuConnection::reachableChanged, this, [=](bool reachable) {
        thing->setStateValue(connectedStateTypeId, reachable);
    });

    connect(connection, &Sdm120ModbusRtuConnection::activePowerChanged, this, [=](float v) {
        thing->setStateValue(powerStateTypeId, v);
    });
    connect(connection, &Sdm120ModbusRtuConnection::frequencyChanged, this, [=](float v) {
        thing->setStateValue(frequencyStateTypeId, v);
    });

    if (isEnergyMeter) {
        connect(connection, &Sdm120ModbusRtuConnection::voltageChanged, this, [=](float v) {
            thing->setStateValue(voltageStateTypeId, v);
        });
        connect(connection, &Sdm120ModbusRtuConnection::currentChanged, this, [=](float v) {
            thing->setStateValue(currentStateTypeId, v);
        });
        connect(connection, &Sdm120ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
            thing->setStateValue(totalEnergyConsumedStateTypeId, v);
        });
        connect(connection, &Sdm120ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
            thing->setStateValue(totalEnergyProducedStateTypeId, v);
        });
    } else {
        // Consumer or producer: connect only the relevant energy direction
        bool isProducer = (classId == sdm120ProducerThingClassId
                           || classId == sdm220ProducerThingClassId
                           || classId == sdm230ProducerThingClassId);
        if (isProducer) {
            connect(connection, &Sdm120ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
                thing->setStateValue(totalEnergyProducedStateTypeId, v);
            });
        } else {
            connect(connection, &Sdm120ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
                thing->setStateValue(totalEnergyConsumedStateTypeId, v);
            });
        }
    }

    m_sdm120Connections.insert(thing, connection);
    info->finish(Thing::ThingErrorNoError);
}

void IntegrationPluginEastron::postSetupThing(Thing *thing)
{
    qCDebug(dcEastron()) << "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_sdm630Connections.contains(thing))
                    m_sdm630Connections.value(thing)->update();
                else if (m_sdm72Connections.contains(thing))
                    m_sdm72Connections.value(thing)->update();
                else if (m_sdm120Connections.contains(thing))
                    m_sdm120Connections.value(thing)->update();
            }
        });
        qCDebug(dcEastron()) << "Refresh timer started";
        m_refreshTimer->start();
    }
}

void IntegrationPluginEastron::thingRemoved(Thing *thing)
{
    qCDebug(dcEastron()) << "Thing removed" << thing->name();

    if (m_sdm630Connections.contains(thing))
        m_sdm630Connections.take(thing)->deleteLater();
    else if (m_sdm72Connections.contains(thing))
        m_sdm72Connections.take(thing)->deleteLater();
    else if (m_sdm120Connections.contains(thing))
        m_sdm120Connections.take(thing)->deleteLater();

    if (myThings().isEmpty() && m_refreshTimer) {
        hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
        m_refreshTimer = nullptr;
        qCDebug(dcEastron()) << "Refresh timer stopped";
    }
}
