diff --git a/abbb2x/2CMC485003M0201_en_B23_B24_User_Manual.pdf b/abbb2x/2CMC485003M0201_en_B23_B24_User_Manual.pdf
new file mode 100644
index 0000000..757a2c0
Binary files /dev/null and b/abbb2x/2CMC485003M0201_en_B23_B24_User_Manual.pdf differ
diff --git a/abbb2x/2CMC485004M0201_en_D_B21_User_Manual.pdf.pdf b/abbb2x/2CMC485004M0201_en_D_B21_User_Manual.pdf.pdf
new file mode 100644
index 0000000..f96109e
Binary files /dev/null and b/abbb2x/2CMC485004M0201_en_D_B21_User_Manual.pdf.pdf differ
diff --git a/abbb2x/abb.png b/abbb2x/abb.png
new file mode 100644
index 0000000..7f54801
Binary files /dev/null and b/abbb2x/abb.png differ
diff --git a/abbb2x/abbb2x-registers.json b/abbb2x/abbb2x-registers.json
new file mode 100644
index 0000000..dbcec7d
--- /dev/null
+++ b/abbb2x/abbb2x-registers.json
@@ -0,0 +1,164 @@
+{
+ "className": "AbbB2x",
+ "protocol": "BOTH",
+ "endianness": "BigEndian",
+ "errorLimitUntilNotReachable": 15,
+ "checkReachableRegister": "voltagePhaseA",
+ "blocks": [
+ {
+ "id": "instantaneousValues",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "voltagePhaseA",
+ "address": 23296,
+ "size": 2,
+ "type": "uint32",
+ "unit": "0.1V",
+ "registerType": "holdingRegister",
+ "description": "Voltage L1-N",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "voltagePhaseB",
+ "address": 23298,
+ "size": 2,
+ "type": "uint32",
+ "unit": "0.1V",
+ "registerType": "holdingRegister",
+ "description": "Voltage L2-N",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "voltagePhaseC",
+ "address": 23300,
+ "size": 2,
+ "type": "uint32",
+ "unit": "0.1V",
+ "registerType": "holdingRegister",
+ "description": "Voltage L3-N",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseA",
+ "address": 23308,
+ "size": 2,
+ "type": "uint32",
+ "unit": "0.01A",
+ "registerType": "holdingRegister",
+ "description": "Current L1",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseB",
+ "address": 23310,
+ "size": 2,
+ "type": "uint32",
+ "unit": "0.01A",
+ "registerType": "holdingRegister",
+ "description": "Current L2",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseC",
+ "address": 23312,
+ "size": 2,
+ "type": "uint32",
+ "unit": "0.01A",
+ "registerType": "holdingRegister",
+ "description": "Current L3",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "activePowerTotal",
+ "address": 23316,
+ "size": 2,
+ "type": "int32",
+ "unit": "0.01W",
+ "registerType": "holdingRegister",
+ "description": "Active power Total (signed: + import / - export)",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "activePowerPhaseA",
+ "address": 23318,
+ "size": 2,
+ "type": "int32",
+ "unit": "0.01W",
+ "registerType": "holdingRegister",
+ "description": "Active power L1 (signed)",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "activePowerPhaseB",
+ "address": 23320,
+ "size": 2,
+ "type": "int32",
+ "unit": "0.01W",
+ "registerType": "holdingRegister",
+ "description": "Active power L2 (signed)",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "activePowerPhaseC",
+ "address": 23322,
+ "size": 2,
+ "type": "int32",
+ "unit": "0.01W",
+ "registerType": "holdingRegister",
+ "description": "Active power L3 (signed)",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "frequency",
+ "address": 23340,
+ "size": 1,
+ "type": "uint16",
+ "unit": "0.01Hz",
+ "registerType": "holdingRegister",
+ "description": "Frequency",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ },
+ {
+ "id": "energyAccumulators",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "totalEnergyConsumed",
+ "address": 20480,
+ "size": 4,
+ "type": "uint64",
+ "unit": "0.01kWh",
+ "registerType": "holdingRegister",
+ "description": "Active import (total consumed energy)",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "totalEnergyProduced",
+ "address": 20484,
+ "size": 4,
+ "type": "uint64",
+ "unit": "0.01kWh",
+ "registerType": "holdingRegister",
+ "description": "Active export (total produced energy)",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ }
+ ]
+}
diff --git a/abbb2x/abbb2x.pro b/abbb2x/abbb2x.pro
new file mode 100644
index 0000000..b1f5e7e
--- /dev/null
+++ b/abbb2x/abbb2x.pro
@@ -0,0 +1,12 @@
+include(../plugins.pri)
+
+# Generate modbus connections
+MODBUS_CONNECTIONS += abbb2x-registers.json
+#MODBUS_TOOLS_CONFIG += VERBOSE
+include(../modbus.pri)
+
+HEADERS += \
+ integrationpluginabbb2x.h
+
+SOURCES += \
+ integrationpluginabbb2x.cpp
diff --git a/abbb2x/integrationpluginabbb2x.cpp b/abbb2x/integrationpluginabbb2x.cpp
new file mode 100644
index 0000000..17b1754
--- /dev/null
+++ b/abbb2x/integrationpluginabbb2x.cpp
@@ -0,0 +1,209 @@
+// 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";
+ }
+}
diff --git a/abbb2x/integrationpluginabbb2x.h b/abbb2x/integrationpluginabbb2x.h
new file mode 100644
index 0000000..a8ec198
--- /dev/null
+++ b/abbb2x/integrationpluginabbb2x.h
@@ -0,0 +1,59 @@
+// 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 .
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef INTEGRATIONPLUGINABBB2X_H
+#define INTEGRATIONPLUGINABBB2X_H
+
+#include
+#include
+#include
+
+#include "abbb2xmodbusrtuconnection.h"
+
+#include "extern-plugininfo.h"
+
+#include
+#include
+
+class IntegrationPluginAbbB2x: public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginabbb2x.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ explicit IntegrationPluginAbbB2x();
+ void init() override;
+ void discoverThings(ThingDiscoveryInfo *info) override;
+ void setupThing(ThingSetupInfo *info) override;
+ void postSetupThing(Thing *thing) override;
+ void thingRemoved(Thing *thing) override;
+
+private:
+ PluginTimer *m_refreshTimer = nullptr;
+ QHash m_connections;
+};
+
+#endif // INTEGRATIONPLUGINABBB2X_H
diff --git a/abbb2x/integrationpluginabbb2x.json b/abbb2x/integrationpluginabbb2x.json
new file mode 100644
index 0000000..a02c758
--- /dev/null
+++ b/abbb2x/integrationpluginabbb2x.json
@@ -0,0 +1,181 @@
+{
+ "id": "0c5c64dd-52b4-4afe-8438-83997f3485f9",
+ "name": "AbbB2x",
+ "displayName": "ABB B2x",
+ "vendors": [
+ {
+ "id": "c112c5ba-6680-400a-9e67-3333ba6e3bd2",
+ "name": "abb",
+ "displayName": "ABB",
+ "thingClasses": [
+ {
+ "id": "bd391ff1-ce19-48ba-8080-d74b8b88dfa2",
+ "name": "abbB2x",
+ "displayName": "ABB B2x energy meter",
+ "createMethods": ["discovery", "user"],
+ "interfaces": ["energymeter", "connectable"],
+ "providedInterfaces": [],
+ "discoveryParamTypes": [
+ {
+ "id": "4d716b9c-3dab-4827-abdf-2bd394dabe6d",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "minValue": 1,
+ "maxValue": 254,
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "54faf3d6-b243-4111-ace8-18eef6192e14",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "minValue": 1,
+ "maxValue": 254,
+ "defaultValue": 1,
+ "readOnly": true
+ },
+ {
+ "id": "3c10aac2-e125-40d8-8a7e-23185d3a7667",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QString",
+ "inputType": "TextLine",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "0ef1e287-b5c0-4011-9887-eb549a9d0e19",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "01fd9397-5a66-4a67-8144-ebad166ce926",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "aa41495d-f4b9-44e3-a98c-1e6b83cb57d6",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "975a11da-40f0-4b7a-af2b-b96b2dba6eb4",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "d7f3752b-b653-42c7-89aa-cf20cb06e69c",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ },
+ {
+ "id": "a1a68363-072b-497b-bbb0-6f946198de26",
+ "name": "voltagePhaseA",
+ "displayName": "Voltage phase A",
+ "displayNameEvent": "Voltage phase A changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "69c25429-c4cf-4eb8-ace4-b43b9bb42777",
+ "name": "voltagePhaseB",
+ "displayName": "Voltage phase B",
+ "displayNameEvent": "Voltage phase B changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "c15752c6-7321-4c74-a65e-18ee3eb0cd13",
+ "name": "voltagePhaseC",
+ "displayName": "Voltage phase C",
+ "displayNameEvent": "Voltage phase C changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "78ce9dad-6432-4c53-b9bf-0a20ffaf8203",
+ "name": "currentPhaseA",
+ "displayName": "Current phase A",
+ "displayNameEvent": "Current phase A changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "13cd360e-3f25-4c73-b5fe-72aa9ec96b9a",
+ "name": "currentPhaseB",
+ "displayName": "Current phase B",
+ "displayNameEvent": "Current phase B changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "542b90dd-b5c7-4b9e-ba3a-c7416386f9a1",
+ "name": "currentPhaseC",
+ "displayName": "Current phase C",
+ "displayNameEvent": "Current phase C changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "ec52d2f1-af43-4268-8d9e-16bfbe089259",
+ "name": "currentPowerPhaseA",
+ "displayName": "Current power phase A",
+ "displayNameEvent": "Current power phase A changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "0e9a9a3d-4df1-4531-aff3-63b21e560f40",
+ "name": "currentPowerPhaseB",
+ "displayName": "Current power phase B",
+ "displayNameEvent": "Current power phase B changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "93c9329d-fbda-4d79-bc24-c133b3763b9b",
+ "name": "currentPowerPhaseC",
+ "displayName": "Current power phase C",
+ "displayNameEvent": "Current power phase C changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/abbb2x/meta.json b/abbb2x/meta.json
new file mode 100644
index 0000000..b373dfb
--- /dev/null
+++ b/abbb2x/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "ABB B2x",
+ "tagline": "Connect ABB B21 / B23 / B24 energy meters via Modbus RTU.",
+ "icon": "abb.png",
+ "stability": "consumer",
+ "offline": true,
+ "technologies": [
+ "modbus"
+ ],
+ "categories": [
+ "energy"
+ ]
+}