You may find examples in this forum, looking for MqttMapper.
I may also help
Moderator: leecollings
Perhaps someone can gather all shelly MQTTMapper configs and put it in a topic or github repository.FlyingDomotic wrote: Friday 07 November 2025 10:50 Some people here did manage connecting Shellies to Domoticz through MqttMapper plug-in.
You may find examples in this forum, looking for MqttMapper.
I may also help![]()
oli80330 wrote: Saturday 25 October 2025 2:33 Json for sdhelly 2.5 roller works fine.
several JSON examples for shelly (1/2.5/EM Gen1) :
1 - For a Shelly 1 (GEN1) / domoticz device on/off :
"SDB Ventil": {
"topic": "shellies/SDB Ventil/relay/0",
"type": "244", "subtype": "73",
"switchtype": "0",
"mapping": {"item": "", "default": "0", "values": { "off": "0", "on": "100", "false": "0", "true": "100" }},
"commands": {
"On": {"topic": "shellies/SDB Ventil/relay/0/command", "payload": "on"},
"Off": {"topic": "shellies/SDB Ventil/relay/0/command", "payload": "off"}
}
2- For a Shelly 2.5 mode relay (not roller mode) (GEN1) / domoticz on/off :
"CE Mode PAC": {
"topic": "shellies/CE Thermo Solaire/relay/0",
"type": "244", "subtype": "73",
"switchtype": "0",
"mapping": {"item": "", "default": "0", "values": { "off": "0", "on": "100", "false": "0", "true": "100" }},
"commands": {
"On": {"topic": "shellies/CE Thermo Solaire/relay/0", "payload": "on"},
"Off": {"topic": "shellies/CE Thermo Solaire/relay/0", "payload": "off"}
}
}
3 -For a Shelly 2.5 mode roller (not relay mode) (GEN1) / domoticz blinds on/off/stop/position :
"ShellyMapper - Buro Volet": {
"topic": "shellies/Buro Volet/roller/0/pos",
"type": "244",
"subtype": "73",
"switchtype": "21",
"mapping": {"item": ""},
"commands": {
"Open": { "topic": "shellies/Buro Volet/roller/0/command", "payload": "open" },
"Close": { "topic": "shellies/Buro Volet/roller/0/command", "payload": "close" },
"Stop": { "topic": "shellies/Buro Volet/roller/0/command", "payload": "stop" },
"Set Level": { "topic": "shellies/Buro Volet/roller/0/command/pos", "payload": "<level>", "retain": false }
}
}
4 - For a Shelly EM or Shelly 2.5 (Gen1 conso) / domoticz General, kWh :
"Clim Salon": {
"topic": "shellies/Clim Salon/relay/0/power",
"type": "243", "subtype": "29", "switchtype": "0",
"options": {"EnergyMeterMode":"0"},
"mapping": {"item":"~*;~0"}
}
Thank you very much for your plugin, very fast for feedback in MQTT !
The CPU consumption of the RPi is very low.
A good plugin for shelly device, better than shellyteacher or AutoDiscovery, very slow in feedback.
Code: Select all
// Send Plug's Status & Power to Domoticz
let CONFIG = {
outputId: 0,
httpServer: 'http://1.8.1.1:8080', //Domoticz Server IP
httpState: '/json.htm?type=command¶m=switchlight&idx=6536&switchcmd=', // Domoticz Dummy Switch
httpPower: '/json.htm?type=command¶m=udevice&idx=6535&nvalue=0&svalue=' // Domoticz Dummy Power Device
};
function notifyState(OnOff) {
Shelly.call(
'HTTP.GET',
{url:CONFIG.httpServer+CONFIG.httpState+OnOff}
)
}
function notifyPower(Power, Energy) {
Shelly.call(
'HTTP.GET',
{url:CONFIG.httpServer+CONFIG.httpPower+Power+';'+Energy}
)
}
let outputStringId = "switch:" + JSON.stringify(CONFIG.outputId);
print(outputStringId);
Shelly.addStatusHandler(function (statusNtf) {
if (statusNtf.component !== outputStringId) return;
let sysStatus = Shelly.getComponentStatus('Switch:0')
if (sysStatus.output === true) notifyState('On');
else notifyState('Off');
notifyPower(sysStatus.apower, sysStatus.aenergy.total)
print(sysStatus.apower)
print(sysStatus.aenergy.total)
});Code: Select all
http://<Domoticz IPaddress>:<port>/json.htm?type=command¶m=switchlight&idx=<door contact idx>&switchcmd=OnReturning to this post by "Itschi,"Itschi wrote: Monday 17 November 2025 17:06 The most elegant way to integrate Shelly devices in Domoticz from my perspective is to make use of their integrated scripting function. Just add the respective dummy devices in Domoticz and a script in the Shelly device to call the Domoticz JSON interface. For a Plug S this looks like:
Finally you add in the Domoticz switch the on and off actions where 1.8.1.17 is the Shelly IP in this example:Code: Select all
// Send Plug's Status & Power to Domoticz let CONFIG = { outputId: 0, httpServer: 'http://1.8.1.1:8080', //Domoticz Server IP httpState: '/json.htm?type=command¶m=switchlight&idx=6536&switchcmd=', // Domoticz Dummy Switch httpPower: '/json.htm?type=command¶m=udevice&idx=6535&nvalue=0&svalue=' // Domoticz Dummy Power Device }; function notifyState(OnOff) { Shelly.call( 'HTTP.GET', {url:CONFIG.httpServer+CONFIG.httpState+OnOff} ) } function notifyPower(Power, Energy) { Shelly.call( 'HTTP.GET', {url:CONFIG.httpServer+CONFIG.httpPower+Power+';'+Energy} ) } let outputStringId = "switch:" + JSON.stringify(CONFIG.outputId); print(outputStringId); Shelly.addStatusHandler(function (statusNtf) { if (statusNtf.component !== outputStringId) return; let sysStatus = Shelly.getComponentStatus('Switch:0') if (sysStatus.output === true) notifyState('On'); else notifyState('Off'); notifyPower(sysStatus.apower, sysStatus.aenergy.total) print(sysStatus.apower) print(sysStatus.aenergy.total) });
"http://1.8.1.17/relay/0?turn=on"
"http://1.8.1.17/relay/0?turn=off"
That's it. Now you can control and monitor the plug from any endpoint (Domoticz, Shelly-App and even from the device switch).
Code: Select all
let DOMOTICZ_IP = "192.168.2.2";
let DOMOTICZ_PORT = "8080";
let IDX = 38;
function sendEnergy() {
let pm = Shelly.getComponentStatus("pm1:0");
if (!pm || !pm.aenergy) {
print("Energy data not ready");
return;
}
let power = pm.apower; // Watts
let energy_kwh = pm.aenergy.total / 1000; // Wh → kWh
let url = "http://" + DOMOTICZ_IP + ":" + DOMOTICZ_PORT +
"/json.htm?type=command¶m=udevice" +
"&idx=" + IDX +
"&nvalue=0&svalue=" + power + ";" + energy_kwh;
print("Sending:", power, "W,", energy_kwh, "kWh");
Shelly.call("HTTP.GET", { url: url }, function (res, err_code, err_msg) {
if (err_code !== 0) {
print("HTTP error:", err_msg);
} else {
print("OK");
}
});
}
// every 30 seconds
Timer.set(30000, true, sendEnergy);Code: Select all
{
"name": "Shelly Fase A Actief Vermogen",
"unique_id": "shelly_<id>_a_act_power",
"state_topic": "shelly<id>/status/em:0",
"value_template": "{{ value_json.a_act_power }}",
"device_class": "power",
"state_class": "measurement",
"unit_of_measurement": "W",
"device": {
"identifiers": ["shelly_<id>"],
"name": "Shelly Pro 3EM",
"model": "Pro 3EM",
"manufacturer": "Shelly"
}
}That would be highly appreciated by me!jorknu wrote: Wednesday 02 September 2026 21:57 I will post a link here once I have something that is 'workable'
did you test it?imautohuttraeger wrote: Wednesday 05 November 2025 13:30 Super! Will test it maybe next week and give feedback if it works.
Unfortunately not. I was quite busy after that post and I have to admit that I forgot it.lemassykoi wrote: Thursday 03 September 2026 19:04did you test it?imautohuttraeger wrote: Wednesday 05 November 2025 13:30 Super! Will test it maybe next week and give feedback if it works.
Code: Select all
# SHELLY
#
# Description: Shelly Gen2 / Gen3 power meter. Reads power, voltage, current,
# energy and temperature over the local HTTP RPC API.
# Supports Switch.GetStatus (1PM, Plus 1PM, 1PM Mini Gen3, ...)
# and PM1.GetStatus (Plus PM Mini, PM Mini Gen3, ...).
#
# History:
# 1.0.0 Initial version
# 1.0.1 Use urllib for the request (Domoticz HTTP protocol returned an empty body)
# 1.1.0 Added "Shelly type" option: Switch, PM1 or Auto-detect
"""
<plugin key="Shelly_PM" name="Shelly power meter (Gen2 / Gen3)" author="" version="1.1.0" externallink="https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/Switch">
<description>
<h2>Shelly power meter (Gen2 / Gen3)</h2><br/>
Reads the live values of a Shelly with power measurement through the local API:
<i>/rpc/Switch.GetStatus?id=0</i> (1PM, Plus 1PM, 1PM Mini Gen3, ...) or
<i>/rpc/PM1.GetStatus?id=0</i> (Plus PM Mini, PM Mini Gen3, ...).<br/><br/>
Creates the devices: Power + energy (kWh), Voltage, Current and Temperature
(temperature only if the Shelly reports it, PM1 models do not).<br/>
Authentication on the Shelly is not supported, so leave it switched off.
</description>
<params>
<param field="Address" label="IP Address" width="200px" required="true" default="192.168.2.0"/>
<param field="Port" label="Port" width="40px" required="true" default="80"/>
<param field="Mode1" label="Switch / channel id" width="40px" required="true" default="0"/>
<param field="Mode2" label="Shelly type" width="200px">
<options>
<option label="Auto-detect" value="auto" default="true"/>
<option label="Switch (1PM, Plus 1PM, ...)" value="switch"/>
<option label="PM1 (Plus PM Mini, PM Mini Gen3, ...)" value="pm1"/>
</options>
</param>
<param field="Mode3" label="Poll interval" width="100px">
<options>
<option label="10 seconds" value="10"/>
<option label="20 seconds" value="20"/>
<option label="30 seconds" value="30" default="true"/>
<option label="60 seconds" value="60"/>
</options>
</param>
<param field="Mode4" label="Show values as positive" width="100px">
<options>
<option label="Yes" value="1" default="true"/>
<option label="No" value="0"/>
</options>
</param>
<param field="Mode6" label="Debug" width="100px">
<options>
<option label="Off" value="0" default="true"/>
<option label="On" value="1"/>
</options>
</param>
</params>
</plugin>
"""
import Domoticz
import json
import urllib.request
# Device units
UNIT_POWER = 1 # power (W) + energy counter (Wh)
UNIT_VOLTAGE = 2 # V
UNIT_CURRENT = 3 # A
UNIT_TEMP = 4 # degrees C
REQUEST_TIMEOUT = 5 # seconds
# RPC methods per Shelly type
METHODS = {
"switch": "Switch.GetStatus",
"pm1": "PM1.GetStatus",
}
class BasePlugin:
switchId = 0
shellyType = "auto" # auto / switch / pm1
detected = None # RPC method that worked (auto-detect)
absolute = True # show negative values as positive
pollTicks = 3 # heartbeat is 10 s, so 3 ticks = 30 s
ticks = 0
errorReported = False
def onStart(self):
if Parameters["Mode6"] == "1":
Domoticz.Debugging(1)
self.absolute = (Parameters["Mode4"] == "1")
self.shellyType = Parameters["Mode2"] if Parameters["Mode2"] in ("auto", "switch", "pm1") else "auto"
self.detected = None
try:
self.switchId = int(Parameters["Mode1"])
except ValueError:
Domoticz.Error("Switch id '" + Parameters["Mode1"] + "' is not an integer, using 0")
self.switchId = 0
try:
interval = int(Parameters["Mode3"])
except ValueError:
interval = 30
self.pollTicks = max(1, interval // 10)
Domoticz.Heartbeat(10)
self.ticks = self.pollTicks # poll at the first heartbeat
self.createDevices()
def onStop(self):
Domoticz.Debug("onStop called")
def onConnect(self, Connection, Status, Description):
pass
def onMessage(self, Connection, Data):
pass
def onCommand(self, Unit, Command, Level, Hue):
Domoticz.Debug("onCommand called for Unit " + str(Unit) + ": " + str(Command))
def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
pass
def onDisconnect(self, Connection):
pass
def onHeartbeat(self):
self.ticks += 1
if self.ticks < self.pollTicks:
return
self.ticks = 0
self.poll()
####################### Plugin specific functions #######################
def createDevices(self):
if UNIT_POWER not in Devices:
Domoticz.Device(Name="Power", Unit=UNIT_POWER, Type=243, Subtype=29, Switchtype=4, Used=1).Create()
if UNIT_VOLTAGE not in Devices:
Domoticz.Device(Name="Voltage", Unit=UNIT_VOLTAGE, Type=243, Subtype=8, Used=1).Create()
if UNIT_CURRENT not in Devices:
Domoticz.Device(Name="Current", Unit=UNIT_CURRENT, Type=243, Subtype=23, Used=1).Create()
if UNIT_TEMP not in Devices:
Domoticz.Device(Name="Temperature", Unit=UNIT_TEMP, Type=80, Subtype=5, Used=1).Create()
def methodsToTry(self):
if self.shellyType in METHODS:
return [METHODS[self.shellyType]]
# auto-detect: reuse the method that worked, otherwise try both
if self.detected:
return [self.detected]
return [METHODS["switch"], METHODS["pm1"]]
def fetch(self, url):
Domoticz.Debug("GET " + url)
with urllib.request.urlopen(url, timeout=REQUEST_TIMEOUT) as response:
raw = response.read().decode("utf-8", "ignore")
Domoticz.Debug("Shelly reply: " + raw)
start = raw.find("{")
if start < 0:
raise ValueError("no JSON in reply: '" + raw[:80] + "'")
return json.loads(raw[start:])
def poll(self):
errors = []
for method in self.methodsToTry():
url = "http://%s:%s/rpc/%s?id=%d" % (Parameters["Address"], Parameters["Port"], method, self.switchId)
try:
status = self.fetch(url)
except Exception as e:
errors.append(url + ": " + str(e))
continue
if self.shellyType == "auto" and self.detected != method:
self.detected = method
Domoticz.Log("Shelly type detected, using " + method)
self.errorReported = False
self.updateFromStatus(status)
return
# Every method failed; forget the detected one so auto-detect retries both
if self.shellyType == "auto":
self.detected = None
self.fail("Could not read the Shelly: " + " | ".join(errors))
def fail(self, message):
# Report and mark the devices only once per outage
if self.errorReported:
return
self.errorReported = True
Domoticz.Error(message)
for unit in Devices:
Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
def updateFromStatus(self, st):
power = self.number(st.get("apower"))
voltage = self.number(st.get("voltage"))
current = self.number(st.get("current"))
if self.absolute:
power = abs(power)
current = abs(current)
# Energy counter in Wh. When absolute values are wanted, the returned
# energy (negative direction) is added to the forward energy.
energy = 0.0
aenergy = st.get("aenergy")
if isinstance(aenergy, dict) and aenergy.get("total") is not None:
energy = self.number(aenergy.get("total"))
if self.absolute:
ret = st.get("ret_aenergy")
if isinstance(ret, dict) and ret.get("total") is not None:
energy += self.number(ret.get("total"))
self.updateDevice(UNIT_POWER, "%.1f;%.2f" % (power, energy))
self.updateDevice(UNIT_VOLTAGE, "%.1f" % voltage)
self.updateDevice(UNIT_CURRENT, "%.3f" % current)
# Only Switch models report a temperature; PM1 does not
temperature = st.get("temperature")
if isinstance(temperature, dict) and temperature.get("tC") is not None:
self.updateDevice(UNIT_TEMP, "%.1f" % self.number(temperature.get("tC")))
def number(self, value):
try:
return float(value)
except (TypeError, ValueError):
return 0.0
def updateDevice(self, unit, sValue):
if unit in Devices:
Devices[unit].Update(nValue=0, sValue=sValue, TimedOut=0)
####################### Global functions for plugin #######################
global _plugin
_plugin = BasePlugin()
def onStart():
global _plugin
_plugin.onStart()
def onStop():
global _plugin
_plugin.onStop()
def onConnect(Connection, Status, Description):
global _plugin
_plugin.onConnect(Connection, Status, Description)
def onMessage(Connection, Data):
global _plugin
_plugin.onMessage(Connection, Data)
def onCommand(Unit, Command, Level, Hue):
global _plugin
_plugin.onCommand(Unit, Command, Level, Hue)
def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
global _plugin
_plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile)
def onDisconnect(Connection):
global _plugin
_plugin.onDisconnect(Connection)
def onHeartbeat():
global _plugin
_plugin.onHeartbeat()