Repository: kpetremann/mqtt-exporter

Issue:
Doesn't support Shelly 3EM format very well
Hello there, 

my understanding from trying to use this to push 6 different power measurements from 2 Shelly 3EM devices, is that there's no way to either sum, or differentiate the three phases' measurements.

This is what one Shelly 3EM sends as MQTT topics:
```
shellies/shelly-deviceid1234/emeter/0/power
shellies/shelly-deviceid1234/emeter/0/pf
shellies/shelly-deviceid1234/emeter/0/current
shellies/shelly-deviceid1234/emeter/0/voltage
shellies/shelly-deviceid1234/emeter/0/total
shellies/shelly-deviceid1234/emeter/0/total_returned
shellies/shelly-deviceid1234/emeter/1/power
shellies/shelly-deviceid1234/emeter/1/pf
shellies/shelly-deviceid1234/emeter/1/current
shellies/shelly-deviceid1234/emeter/1/voltage
shellies/shelly-deviceid1234/emeter/1/total
shellies/shelly-deviceid1234/emeter/1/total_returned
shellies/shelly-deviceid1234/emeter/2/power
shellies/shelly-deviceid1234/emeter/2/pf
shellies/shelly-deviceid1234/emeter/2/current
shellies/shelly-deviceid1234/emeter/2/voltage
shellies/shelly-deviceid1234/emeter/2/total
shellies/shelly-deviceid1234/emeter/2/total_returned
```

As you can see, it reports 3 different e(nergy)meters, each with separate power, power factor (pf), current, voltage, and total energy, and total energy returned.

I haven't found a way to report on those separately, and as of now, only get a single `mqtt_total` that only contains the latter value.

Is there a way to configure `mqtt-explorer` to report on these separately? Or could this be allowed with a new configuration setting?

Discussion:

Patch:
diff --git a/README.md b/README.md
index 4458dce..f132d1f 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,7 @@ The exporter is tested with:
   * Aqara/Xiaomi sensors (WSDCGQ11LM and VOCKQJK11LM)
   * SONOFF sensors (SNZB-02)
   * Shelly sensors (H&T wifi)
+  * Shelly power sensors (3EM - only with `KEEP_FULL_TOPIC` enabled)
 
 ### Metrics conversion example
 ```
@@ -83,6 +84,7 @@ To setup this, you need to specify the topic prefix used by Zwavejs2Mqtt in `ZWA
 Parameters are passed using environment variables.
 
 The list of parameters are:
+  * `KEEP_FULL_TOPIC`: Keep entire topic instead of the first two elements only. Usecase: Shelly 3EM (default: False)
   * `LOG_LEVEL`: Logging level (default: INFO)
   * `LOG_MQTT_MESSAGE`: Log MQTT original message, only if LOG_LEVEL is set to DEBUG (default: False)
   * `MQTT_IGNORED_TOPICS`: Comma-separated lists of topics to ignore. Accepts wildcards. (default: None)
diff --git a/mqtt_exporter/main.py b/mqtt_exporter/main.py
index 6b10962..839e913 100644
--- a/mqtt_exporter/main.py
+++ b/mqtt_exporter/main.py
@@ -162,7 +162,11 @@ def _normalize_name_in_topic_msg(topic, payload):
 
     # Shellies format
     try:
-        topic = f"{info[0]}/{info[1]}".lower()
+        if settings.KEEP_FULL_TOPIC:  # options instead of hardcoded length
+            topic = "/".join(info[:-1]).lower()
+        else:
+            topic = f"{info[0]}/{info[1]}".lower()
+
         payload_dict = {info[-1]: payload}  # usually the last element is the type of sensor
     except IndexError:
         pass
diff --git a/mqtt_exporter/settings.py b/mqtt_exporter/settings.py
index fac98bf..0b21f74 100644
--- a/mqtt_exporter/settings.py
+++ b/mqtt_exporter/settings.py
@@ -19,3 +19,5 @@ MQTT_V5_PROTOCOL = os.getenv("MQTT_V5_PROTOCOL", "False") == "True"
 MQTT_CLIENT_ID = os.getenv("MQTT_CLIENT_ID", "")
 MQTT_EXPOSE_CLIENT_ID = os.getenv("MQTT_EXPOSE_CLIENT_ID", "False") == "True"
 PROMETHEUS_PORT = int(os.getenv("PROMETHEUS_PORT", "9000"))
+
+KEEP_FULL_TOPIC = os.getenv("KEEP_FULL_TOPIC", "False") == "True"


Test patch:
diff --git a/tests/functional/test_parse_message.py b/tests/functional/test_parse_message.py
index e3f4d47..91ce2cb 100644
--- a/tests/functional/test_parse_message.py
+++ b/tests/functional/test_parse_message.py
@@ -1,4 +1,5 @@
 """Functional tests of MQTT message parsing."""
+from mqtt_exporter import settings
 from mqtt_exporter.main import _parse_message
 
 
@@ -16,8 +17,8 @@ def test__parse_message__aqara_style():
     assert parsed_payload == {"temperature": 26.24, "humidity": 45.37}
 
 
-def test__parse_message__shelly_style():
-    """Test message parsing with shelly style."""
+def test__parse_message__shelly_style_ht():
+    """Test message parsing with shelly style H&T."""
     topic = "shellies/room/sensor/temperature"
     payload = b"20.00"
 
@@ -27,6 +28,19 @@ def test__parse_message__shelly_style():
     assert parsed_payload == {"temperature": 20.00}
 
 
+def test__parse_message__shelly_style_3em():
+    """Test message parsing with shelly style (3EM)."""
+    topic = "shellies/room/emeter/0/power"
+    payload = b"1"
+
+    settings.KEEP_FULL_TOPIC = True
+    parsed_topic, parsed_payload = _parse_message(topic, payload)
+    settings.KEEP_FULL_TOPIC = False
+
+    assert parsed_topic == "shellies_room_emeter_0"
+    assert parsed_payload == {"power": 1}
+
+
 def test__parse_message__generic_single_value_style():
     """Test message parsing when payload is a single value (same as Shelly but more custom).