@@ -72,11 +72,27 @@ class GrowattData:
7272 pv3_power : float = 0.0 # W
7373 pv_total_power : float = 0.0 # W
7474
75- # AC Output
75+ # AC Output (generic - usually Phase R for 3-phase)
7676 ac_voltage : float = 0.0 # V
7777 ac_current : float = 0.0 # A
7878 ac_power : float = 0.0 # W
7979 ac_frequency : float = 0.0 # Hz
80+
81+ # Three-Phase AC Output (individual phases)
82+ ac_voltage_r : float = 0.0 # V (Phase R/L1)
83+ ac_current_r : float = 0.0 # A (Phase R/L1)
84+ ac_power_r : float = 0.0 # W (Phase R/L1)
85+ ac_voltage_s : float = 0.0 # V (Phase S/L2)
86+ ac_current_s : float = 0.0 # A (Phase S/L2)
87+ ac_power_s : float = 0.0 # W (Phase S/L2)
88+ ac_voltage_t : float = 0.0 # V (Phase T/L3)
89+ ac_current_t : float = 0.0 # A (Phase T/L3)
90+ ac_power_t : float = 0.0 # W (Phase T/L3)
91+
92+ # Line-to-Line Voltages (3-phase only)
93+ ac_voltage_rs : float = 0.0 # V
94+ ac_voltage_st : float = 0.0 # V
95+ ac_voltage_tr : float = 0.0 # V
8096
8197 # Power Flow (storage/hybrid models)
8298 power_to_user : float = 0.0 # W
@@ -438,8 +454,9 @@ def read_all_data(self) -> Optional[GrowattData]:
438454
439455 # Read 3000 range if needed - MIN/MOD models
440456 if has_3000_range :
441- logger .debug ("Reading 3000 range (3000-3110)" )
442- registers = self .read_input_registers (3000 , 111 )
457+ logger .debug ("Reading 3000 range (3000-3250)" )
458+ # Extended range to cover MOD battery registers up to 3250
459+ registers = self .read_input_registers (3000 , 251 )
443460 if registers is None :
444461 logger .error ("Failed to read main input register block" )
445462 return None
@@ -448,12 +465,12 @@ def read_all_data(self) -> Optional[GrowattData]:
448465 for i , value in enumerate (registers ):
449466 self ._register_cache [3000 + i ] = value
450467
451- # Read 31000 range if needed - MOD battery/BMS data
468+ # Read 31000 range if needed - MOD extended battery/BMS range
452469 if has_31000_range :
453- logger .debug ("Reading 31000 range (31100-31299 )" )
454- registers = self .read_input_registers (31100 , 200 )
470+ logger .debug ("Reading 31000 range (31100-31300 )" )
471+ registers = self .read_input_registers (31100 , 201 )
455472 if registers is None :
456- logger .warning ("Failed to read 31000 register block (MOD battery data may be unavailable )" )
473+ logger .warning ("Failed to read extended battery register block (31100+ )" )
457474 # Don't return None - continue with what we have
458475 else :
459476 # Populate cache
@@ -509,12 +526,12 @@ def read_all_data(self) -> Optional[GrowattData]:
509526 # Calculate from strings if not available
510527 data .pv_total_power = data .pv1_power + data .pv2_power + data .pv3_power
511528
512- # AC Output
529+ # AC Output (generic - will use Phase R via alias for 3-phase)
513530 ac_voltage_addr = self ._find_register_by_name ('ac_voltage' )
514531 ac_current_addr = self ._find_register_by_name ('ac_current' )
515532 ac_power_addr = self ._find_register_by_name ('ac_power_low' )
516533 ac_freq_addr = self ._find_register_by_name ('ac_frequency' )
517-
534+
518535 if ac_voltage_addr :
519536 data .ac_voltage = self ._get_register_value (ac_voltage_addr ) or 0.0
520537 if ac_current_addr :
@@ -523,6 +540,51 @@ def read_all_data(self) -> Optional[GrowattData]:
523540 data .ac_power = self ._get_register_value (ac_power_addr ) or 0.0
524541 if ac_freq_addr :
525542 data .ac_frequency = self ._get_register_value (ac_freq_addr ) or 0.0
543+
544+ # Three-Phase AC Output (individual phases)
545+ # Phase R
546+ ac_voltage_r_addr = self ._find_register_by_name ('ac_voltage_r' )
547+ ac_current_r_addr = self ._find_register_by_name ('ac_current_r' )
548+ ac_power_r_addr = self ._find_register_by_name ('ac_power_r_low' )
549+ if ac_voltage_r_addr :
550+ data .ac_voltage_r = self ._get_register_value (ac_voltage_r_addr ) or 0.0
551+ if ac_current_r_addr :
552+ data .ac_current_r = self ._get_register_value (ac_current_r_addr ) or 0.0
553+ if ac_power_r_addr :
554+ data .ac_power_r = self ._get_register_value (ac_power_r_addr ) or 0.0
555+
556+ # Phase S
557+ ac_voltage_s_addr = self ._find_register_by_name ('ac_voltage_s' )
558+ ac_current_s_addr = self ._find_register_by_name ('ac_current_s' )
559+ ac_power_s_addr = self ._find_register_by_name ('ac_power_s_low' )
560+ if ac_voltage_s_addr :
561+ data .ac_voltage_s = self ._get_register_value (ac_voltage_s_addr ) or 0.0
562+ if ac_current_s_addr :
563+ data .ac_current_s = self ._get_register_value (ac_current_s_addr ) or 0.0
564+ if ac_power_s_addr :
565+ data .ac_power_s = self ._get_register_value (ac_power_s_addr ) or 0.0
566+
567+ # Phase T
568+ ac_voltage_t_addr = self ._find_register_by_name ('ac_voltage_t' )
569+ ac_current_t_addr = self ._find_register_by_name ('ac_current_t' )
570+ ac_power_t_addr = self ._find_register_by_name ('ac_power_t_low' )
571+ if ac_voltage_t_addr :
572+ data .ac_voltage_t = self ._get_register_value (ac_voltage_t_addr ) or 0.0
573+ if ac_current_t_addr :
574+ data .ac_current_t = self ._get_register_value (ac_current_t_addr ) or 0.0
575+ if ac_power_t_addr :
576+ data .ac_power_t = self ._get_register_value (ac_power_t_addr ) or 0.0
577+
578+ # Line-to-Line Voltages
579+ ac_voltage_rs_addr = self ._find_register_by_name ('line_voltage_rs' )
580+ ac_voltage_st_addr = self ._find_register_by_name ('line_voltage_st' )
581+ ac_voltage_tr_addr = self ._find_register_by_name ('line_voltage_tr' )
582+ if ac_voltage_rs_addr :
583+ data .ac_voltage_rs = self ._get_register_value (ac_voltage_rs_addr ) or 0.0
584+ if ac_voltage_st_addr :
585+ data .ac_voltage_st = self ._get_register_value (ac_voltage_st_addr ) or 0.0
586+ if ac_voltage_tr_addr :
587+ data .ac_voltage_tr = self ._get_register_value (ac_voltage_tr_addr ) or 0.0
526588
527589 # Power Flow (if available - storage/hybrid models)
528590 power_to_user_addr = self ._find_register_by_name ('power_to_user_low' )
@@ -624,11 +686,15 @@ def write_register(self, register: int, value: int) -> bool:
624686
625687
626688 def _find_register_by_name (self , name : str ) -> Optional [int ]:
627- """Find register address by its name"""
689+ """Find register address by its name or alias """
628690 input_regs = self .register_map ['input_registers' ]
629691 for addr , reg_info in input_regs .items ():
692+ # Check exact name match
630693 if reg_info ['name' ] == name :
631694 return addr
695+ # Check alias match (for 3-phase compatibility)
696+ if reg_info .get ('alias' ) == name :
697+ return addr
632698 return None
633699
634700 def _read_energy_breakdown (self , data : GrowattData ) -> None :
@@ -691,31 +757,53 @@ def _read_battery_data(self, data: GrowattData) -> None:
691757 data .battery_temp = self ._get_register_value (addr ) or 0.0
692758 logger .debug (f"Battery temp from reg { addr } : { data .battery_temp } °C" )
693759
694- # Charge power (try to read from registers first)
695- addr = self ._find_register_by_name ('charge_power_low' )
696- if addr :
697- raw_low = self ._register_cache .get (addr , 0 )
698- pair_addr = self ._find_register_by_name ('charge_power_high' )
699- raw_high = self ._register_cache .get (pair_addr , 0 ) if pair_addr else 0
700- data .charge_power = self ._get_register_value (addr ) or 0.0
701- logger .debug (f"Charge power: HIGH={ raw_high } (reg { pair_addr } ), LOW={ raw_low } (reg { addr } ) → { data .charge_power } W" )
702- elif data .battery_voltage > 0 and data .battery_current < 0 :
703- # Fallback: Calculate from V×I when charging (negative current)
704- data .charge_power = data .battery_voltage * abs (data .battery_current )
705- logger .debug (f"Charge power (calculated): { data .battery_voltage } V × { abs (data .battery_current )} A = { data .charge_power } W" )
706-
707- # Discharge power (try to read from registers first)
708- addr = self ._find_register_by_name ('discharge_power_low' )
760+ # Battery power (signed: positive=charging, negative=discharging)
761+ # Try new signed battery_power register first (MOD series @ 31126)
762+ addr = self ._find_register_by_name ('battery_power_low' )
709763 if addr :
710764 raw_low = self ._register_cache .get (addr , 0 )
711- pair_addr = self ._find_register_by_name ('discharge_power_high ' )
765+ pair_addr = self ._find_register_by_name ('battery_power_high ' )
712766 raw_high = self ._register_cache .get (pair_addr , 0 ) if pair_addr else 0
713- data .discharge_power = self ._get_register_value (addr ) or 0.0
714- logger .debug (f"Discharge power: HIGH={ raw_high } (reg { pair_addr } ), LOW={ raw_low } (reg { addr } ) → { data .discharge_power } W" )
715- elif data .battery_voltage > 0 and data .battery_current > 0 :
716- # Fallback: Calculate from V×I when discharging (positive current)
717- data .discharge_power = data .battery_voltage * data .battery_current
718- logger .debug (f"Discharge power (calculated): { data .battery_voltage } V × { data .battery_current } A = { data .discharge_power } W" )
767+ battery_power = self ._get_register_value (addr ) or 0.0
768+ logger .debug (f"Battery power (signed): HIGH={ raw_high } (reg { pair_addr } ), LOW={ raw_low } (reg { addr } ) → { battery_power } W" )
769+
770+ # Split into charge/discharge based on sign
771+ if battery_power > 0 :
772+ data .charge_power = battery_power
773+ data .discharge_power = 0.0
774+ logger .debug (f" → Charging: { data .charge_power } W" )
775+ elif battery_power < 0 :
776+ data .charge_power = 0.0
777+ data .discharge_power = abs (battery_power )
778+ logger .debug (f" → Discharging: { data .discharge_power } W" )
779+ else :
780+ data .charge_power = 0.0
781+ data .discharge_power = 0.0
782+ else :
783+ # Fallback: Try old separate charge/discharge registers (SPH series)
784+ addr = self ._find_register_by_name ('charge_power_low' )
785+ if addr :
786+ raw_low = self ._register_cache .get (addr , 0 )
787+ pair_addr = self ._find_register_by_name ('charge_power_high' )
788+ raw_high = self ._register_cache .get (pair_addr , 0 ) if pair_addr else 0
789+ data .charge_power = self ._get_register_value (addr ) or 0.0
790+ logger .debug (f"Charge power: HIGH={ raw_high } (reg { pair_addr } ), LOW={ raw_low } (reg { addr } ) → { data .charge_power } W" )
791+ elif data .battery_voltage > 0 and data .battery_current < 0 :
792+ # Fallback: Calculate from V×I when charging (negative current)
793+ data .charge_power = data .battery_voltage * abs (data .battery_current )
794+ logger .debug (f"Charge power (calculated): { data .battery_voltage } V × { abs (data .battery_current )} A = { data .charge_power } W" )
795+
796+ addr = self ._find_register_by_name ('discharge_power_low' )
797+ if addr :
798+ raw_low = self ._register_cache .get (addr , 0 )
799+ pair_addr = self ._find_register_by_name ('discharge_power_high' )
800+ raw_high = self ._register_cache .get (pair_addr , 0 ) if pair_addr else 0
801+ data .discharge_power = self ._get_register_value (addr ) or 0.0
802+ logger .debug (f"Discharge power: HIGH={ raw_high } (reg { pair_addr } ), LOW={ raw_low } (reg { addr } ) → { data .discharge_power } W" )
803+ elif data .battery_voltage > 0 and data .battery_current > 0 :
804+ # Fallback: Calculate from V×I when discharging (positive current)
805+ data .discharge_power = data .battery_voltage * data .battery_current
806+ logger .debug (f"Discharge power (calculated): { data .battery_voltage } V × { data .battery_current } A = { data .discharge_power } W" )
719807
720808 # Charge energy today
721809 addr = self ._find_register_by_name ('charge_energy_today_low' )
0 commit comments