Skip to content

Commit 1ce4d6e

Browse files
committed
Shelly Pro 3EM support for dbus-modbus-client 1.58+
1 parent c2fbd6f commit 1ce4d6e

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

dbus-modbus-client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import ev_charger
3535
import smappee
3636
import victron_em
37+
import shelly
3738

3839
import logging
3940
log = logging.getLogger()

shelly.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import device
2+
import probe
3+
from register import *
4+
5+
# This Python script adds support for Modbus enabled Shelly devices in dbus-modbus-client version 1.58 and later.
6+
#
7+
8+
# This is just a helper class to make life a bit easier with the Modbus registers.
9+
#
10+
class Reg_shelly(Reg_f32l):
11+
def __init__(self, base, name=None, scale=1, text=None, write=False, invalid=[], **kwargs):
12+
super().__init__(base - 30000, name, scale, text, write, invalid, **kwargs)
13+
14+
# Common base class for Shelly EMs but currently there's only one implementation.
15+
#
16+
class Shelly_Meter(device.CustomName, device.EnergyMeter):
17+
vendor_id = 'shelly'
18+
vendor_name = 'Shelly'
19+
min_timeout = 0.5
20+
21+
# Shelly uses input registers!
22+
default_access = 'input'
23+
24+
def device_init(self):
25+
self.info_regs = [
26+
Reg_text(0, 6, '/Serial', little=True),
27+
]
28+
29+
30+
# Shelly Pro 3EM
31+
# https://shelly-api-docs.shelly.cloud/gen2/Devices/Gen2/ShellyPro3EM
32+
#
33+
# Triphase (defualt)
34+
# https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/EM/
35+
# https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/EMData/
36+
#
37+
# Monophase
38+
# https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/EM1/
39+
# https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/EM1Data/
40+
#
41+
# Modbus
42+
# https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/Modbus
43+
#
44+
class Shelly_Pro_3EM(Shelly_Meter):
45+
productname = 'Shelly Pro 3EM'
46+
productmodel = 'SPEM-003CEBEU'
47+
48+
# Shelly doesn't have a purely numerical Product ID and it's unclear what could be a good placeholder for it.
49+
# Using the Base16 encoded value of 'PEM3'.
50+
productid = 0x50454D33
51+
52+
def device_init(self):
53+
super().device_init()
54+
55+
if self.monophase():
56+
# As far as I can tell there is no elegant way of mapping Shelly Pro 3EM's Monophase profile. Shelly
57+
# has modeled the Monophase profile as three virtual devices under the same IP:Port whereas
58+
# dbus-modbus-client makes the strong assumption that an IP:Port is an unique identifier for a single
59+
# device. Our only option is to go truly Monophase and disregard the Phase B and C meters. *shrug*
60+
#
61+
nr_phases = 1
62+
63+
self.data_regs += [
64+
Reg_shelly(32302, '/Ac/Energy/Forward', 1000, '%.1f kWh'),
65+
Reg_shelly(32304, '/Ac/Energy/Reverse', 1000, '%.1f kWh'),
66+
Reg_shelly(32007, '/Ac/Power', 1, '%.1f W'),
67+
68+
Reg_shelly(32003, '/Ac/L1/Voltage', 1, '%.1f V'),
69+
Reg_shelly(32005, '/Ac/L1/Current', 1, '%.1f A'),
70+
Reg_shelly(32310, '/Ac/L1/Energy/Forward', 1000, '%.1f kWh'),
71+
Reg_shelly(32312, '/Ac/L1/Energy/Reverse', 1000, '%.1f kWh'),
72+
Reg_shelly(32007, '/Ac/L1/Power', 1, '%.1f W'),
73+
]
74+
75+
else:
76+
# Triphase (default)
77+
#
78+
nr_phases = 3
79+
80+
self.data_regs += [
81+
Reg_shelly(31162, '/Ac/Energy/Forward', 1000, '%.1f kWh'),
82+
Reg_shelly(31164, '/Ac/Energy/Reverse', 1000, '%.1f kWh'),
83+
Reg_shelly(31013, '/Ac/Power', 1, '%.1f W'),
84+
85+
Reg_shelly(31020, '/Ac/L1/Voltage', 1, '%.1f V'),
86+
Reg_shelly(31022, '/Ac/L1/Current', 1, '%.1f A'),
87+
Reg_shelly(31182, '/Ac/L1/Energy/Forward', 1000, '%.1f kWh'),
88+
Reg_shelly(31184, '/Ac/L1/Energy/Reverse', 1000, '%.1f kWh'),
89+
Reg_shelly(31024, '/Ac/L1/Power', 1, '%.1f W'),
90+
91+
Reg_shelly(31040, '/Ac/L2/Voltage', 1, '%.1f V'),
92+
Reg_shelly(31042, '/Ac/L2/Current', 1, '%.1f A'),
93+
Reg_shelly(31202, '/Ac/L2/Energy/Forward', 1000, '%.1f kWh'),
94+
Reg_shelly(31204, '/Ac/L2/Energy/Reverse', 1000, '%.1f kWh'),
95+
Reg_shelly(31044, '/Ac/L2/Power', 1, '%.1f W'),
96+
97+
Reg_shelly(31060, '/Ac/L3/Voltage', 1, '%.1f V'),
98+
Reg_shelly(31062, '/Ac/L3/Current', 1, '%.1f A'),
99+
Reg_shelly(31222, '/Ac/L3/Energy/Forward', 1000, '%.1f kWh'),
100+
Reg_shelly(31224, '/Ac/L3/Energy/Reverse', 1000, '%.1f kWh'),
101+
Reg_shelly(31064, '/Ac/L3/Power', 1, '%.1f W'),
102+
]
103+
104+
#
105+
# Returns true if the Shelly Pro 3EM appears to run in the Monophase profile.
106+
#
107+
def monophase(self):
108+
try:
109+
self.read_register(Reg_shelly(32000))
110+
return True
111+
except Exception as err:
112+
self.log.info("The device appears to be in Triphase (default) mode.", err)
113+
return False
114+
115+
models = {
116+
Shelly_Pro_3EM.productmodel: {
117+
'model': Shelly_Pro_3EM.productmodel,
118+
'handler': Shelly_Pro_3EM,
119+
},
120+
}
121+
122+
probe.add_handler(probe.ModelRegister(Reg_text(6, 10, 'model', little=True), models,
123+
methods=['tcp'],
124+
units=[1]))

0 commit comments

Comments
 (0)