@@ -125,10 +125,91 @@ class Em511:
125125 INPUT_MAX_VALUE_32 = 0x7FFFFFFF
126126 INPUT_MAX_VALUE_16 = 0x7FFF
127127
128+ EM511_REGISTER_RESET_TOT_ENERGY_AND_RUN_HOUR_COUNTER = 0x4003
129+ EM511_REGISTER_RESET_PARTIAL_ENERGY_AND_HOUR_COUNTER = 0x4004
130+ EM511_REGISTER_RESET_DMD_AND_DMD_MAX = 0x4005
131+ EM511_REGISTER_RESET_TO_FACTORY_SETTINGS = 0x4020
132+ EM511_REGISTER_FIRMWARE_AND_REVISION = 0x0302
133+
128134 _register_specs : Final [dict [str , RegisterSpec ]] = {
129- "V" : RegisterSpec (address = 0x0000 , count = 2 , decimals = 1 , scale = 10 ),
130- "A" : RegisterSpec (address = 0x0002 , count = 2 , decimals = 3 , scale = 1000 ),
131- "W" : RegisterSpec (address = 0x0004 , count = 2 , decimals = 1 , scale = 10 ),
135+ "V" : RegisterSpec (
136+ address = 0x0000 ,
137+ count = 2 ,
138+ decimals = 1 ,
139+ scale = 10 ,
140+ ),
141+ "A" : RegisterSpec (
142+ address = 0x0002 ,
143+ count = 2 ,
144+ decimals = 3 ,
145+ scale = 1000 ,
146+ ),
147+ "A_dmd" : RegisterSpec (
148+ address = 0x003A ,
149+ count = 2 ,
150+ decimals = 3 ,
151+ scale = 1000 ,
152+ ),
153+ "A_dmd_peak" : RegisterSpec (
154+ address = 0x003C ,
155+ count = 2 ,
156+ decimals = 3 ,
157+ scale = 1000 ,
158+ ),
159+ "W" : RegisterSpec (
160+ address = 0x0004 ,
161+ count = 2 ,
162+ decimals = 1 ,
163+ scale = 10 ,
164+ ),
165+ "W_dmd" : RegisterSpec (
166+ address = 0x000A ,
167+ count = 2 ,
168+ decimals = 1 ,
169+ scale = 10 ,
170+ ),
171+ "W_dmd_peak" : RegisterSpec (
172+ address = 0x000C ,
173+ count = 2 ,
174+ decimals = 1 ,
175+ scale = 10 ,
176+ ),
177+ "Hz" : RegisterSpec (
178+ address = 0x000F ,
179+ count = 1 ,
180+ decimals = 1 ,
181+ scale = 10 ,
182+ ),
183+ "kwh_tot" : RegisterSpec (
184+ address = 0x0010 ,
185+ count = 2 ,
186+ decimals = 1 ,
187+ scale = 10 ,
188+ ),
189+ "kwh_partial" : RegisterSpec (
190+ address = 0x0014 ,
191+ count = 2 ,
192+ decimals = 1 ,
193+ scale = 10 ,
194+ ),
195+ "hour_counter" : RegisterSpec (
196+ address = 0x002C ,
197+ count = 2 ,
198+ decimals = 2 ,
199+ scale = 100 ,
200+ ),
201+ "lifetime_counter" : RegisterSpec (
202+ address = 0x0030 ,
203+ count = 2 ,
204+ decimals = 2 ,
205+ scale = 100 ,
206+ ),
207+ "hour_counter_part" : RegisterSpec (
208+ address = 0x0036 ,
209+ count = 2 ,
210+ decimals = 2 ,
211+ scale = 100 ,
212+ ),
132213 "password" : RegisterSpec (
133214 address = 0x1000 ,
134215 count = 1 ,
@@ -138,7 +219,102 @@ class Em511:
138219 return_type = int ,
139220 writable = True ,
140221 ),
141- "alarm_status" : RegisterSpec (address = 0x0307 , count = 1 , return_type = int ),
222+ "alarm_status" : RegisterSpec (
223+ address = 0x0306 ,
224+ count = 1 ,
225+ range = True ,
226+ min = 0 ,
227+ max = 1 ,
228+ return_type = int ,
229+ ),
230+ "alarm_mode" : RegisterSpec (
231+ address = 0x1015 ,
232+ count = 1 ,
233+ range = True ,
234+ min = 1 ,
235+ max = 6 ,
236+ return_type = int ,
237+ writable = True ,
238+ ),
239+ "alarm_delay" : RegisterSpec (
240+ address = 0x101A ,
241+ count = 1 ,
242+ range = True ,
243+ min = 0 ,
244+ max = 3600 ,
245+ return_type = int ,
246+ writable = True ,
247+ ),
248+ "dmd_integration_time" : RegisterSpec (
249+ address = 0x1010 ,
250+ count = 2 ,
251+ range = True ,
252+ min = 0 ,
253+ max = 6 ,
254+ return_type = int ,
255+ writable = True ,
256+ ),
257+ "device_id" : RegisterSpec (
258+ address = 0x2000 ,
259+ count = 1 ,
260+ range = True ,
261+ min = 1 ,
262+ max = 247 ,
263+ return_type = int ,
264+ writable = True ,
265+ ),
266+ "baud_rate" : RegisterSpec (
267+ address = 0x2001 ,
268+ count = 1 ,
269+ range = True ,
270+ min = 1 ,
271+ max = 5 ,
272+ return_type = int ,
273+ writable = True ,
274+ ),
275+ "parity" : RegisterSpec (
276+ address = 0x2002 ,
277+ count = 1 ,
278+ range = True ,
279+ min = 1 ,
280+ max = 2 ,
281+ return_type = int ,
282+ writable = True ,
283+ ),
284+ "stop_bit" : RegisterSpec (
285+ address = 0x2003 ,
286+ count = 1 ,
287+ range = True ,
288+ min = 0 ,
289+ max = 1 ,
290+ return_type = int ,
291+ writable = True ,
292+ ),
293+ "reply_delay" : RegisterSpec (
294+ address = 0x2004 ,
295+ count = 1 ,
296+ range = True ,
297+ min = 0 ,
298+ max = 1000 ,
299+ return_type = int ,
300+ writable = True ,
301+ ),
302+ "identification_code" : RegisterSpec (
303+ address = 0x000B ,
304+ count = 1 ,
305+ range = True ,
306+ min = 1792 ,
307+ max = 1795 ,
308+ return_type = int ,
309+ ),
310+ "measure_mode" : RegisterSpec (
311+ address = 0x1103 ,
312+ count = 1 ,
313+ range = True ,
314+ min = 0 ,
315+ max = 1 ,
316+ return_type = int ,
317+ ),
142318 }
143319
144320 def __init__ (self , device_address : int , client : ModbusSerialClient ) -> None :
@@ -211,6 +387,10 @@ def _write_register(self, address: int, value: int) -> None:
211387 )
212388 raise ModbusException (msg )
213389
390+ def write_register (self , address : int , value : int ) -> None :
391+ """Public wrapper for `_write_register`."""
392+ self ._write_register (address , value )
393+
214394 def _unpack (self , regs : list [int ], address : int ) -> int :
215395 """Unpack raw Modbus register data into an integer value.
216396
@@ -244,3 +424,62 @@ def _unpack(self, regs: list[int], address: int) -> int:
244424
245425 msg = f"Unexpected register count: { len (regs )} for address={ address } "
246426 raise ValueError (msg )
427+
428+ @property
429+ def firmware_and_revision_code (self ) -> str :
430+ """Read firmware version and revision code.
431+
432+ This property reads a 16-bit Modbus register that holds firmware version
433+ and revision information.
434+
435+ Returns:
436+ str: A string formatted as "<major>.<minor>,<revision>", for example "4.3,67".
437+
438+ Raises:
439+ ValueError: If the raw register value is outside the valid 16-bit range
440+ or cannot be decoded properly.
441+ """
442+ regs = self ._read_input_registers (self .EM511_REGISTER_FIRMWARE_AND_REVISION , self .INT16_REG_COUNT )
443+ value = regs [0 ]
444+
445+ msb = (value >> 8 ) & 0xFF
446+ revision = value & 0xFF
447+ minor = msb & 0x0F
448+ major = (msb >> 4 ) & 0x0F
449+
450+ return f"{ major } .{ minor } .{ revision } "
451+
452+ def reset_tot_energy_and_run_hour_counter (self ) -> None :
453+ """Reset total energy + total run hour counters (excluding lifetime).
454+
455+ Raises:
456+ ModbusException: If failed to write to single register.
457+ """
458+ self .write_register (self .EM511_REGISTER_RESET_TOT_ENERGY_AND_RUN_HOUR_COUNTER , 1 )
459+
460+ def reset_partial_energy_and_hour_counter (self ) -> None :
461+ """Reset partial energy + partial run hour counters.
462+
463+ Raises:
464+ ModbusException: If failed to write to single register.
465+ """
466+ self .write_register (self .EM511_REGISTER_RESET_PARTIAL_ENERGY_AND_HOUR_COUNTER , 1 )
467+
468+ def reset_dmd_and_dmd_max (self ) -> None :
469+ """Reset DMD and DMD max values.
470+
471+ Raises:
472+ ModbusException: If failed to write to single register.
473+ """
474+ self .write_register (self .EM511_REGISTER_RESET_DMD_AND_DMD_MAX , 1 )
475+
476+ def reset_to_factory_settings (self ) -> None :
477+ """Factory Restore (Default settings).
478+
479+ Write 0x0A0A=2570, then within 1s write 0xC1A0=49568 to trigger reset.
480+
481+ Raises:
482+ ModbusException: If failed to write to single register.
483+ """
484+ self .write_register (self .EM511_REGISTER_RESET_TO_FACTORY_SETTINGS , 0x0A0A )
485+ self .write_register (self .EM511_REGISTER_RESET_TO_FACTORY_SETTINGS , 0xC1A0 )
0 commit comments