1+ # config.py
2+
3+ # Standard Libraries
4+ import os
5+ from datetime import datetime , time
6+ from enum import IntEnum
7+ from typing import Final
8+
9+ # Third-Party Libraries
10+ from dotenv import load_dotenv
11+
12+
13+ load_dotenv ()
14+
15+
16+ class EnergyMode (IntEnum ):
17+ """
18+ Inverter energy mode codes.
19+
20+ Replace the codes below with the actual codes used by the inverter.
21+ """
22+
23+ # 1 = SBU, 2 = SUB, 3 = UTI, 4 = SOL
24+ SBU = 1
25+ SUB = 2
26+ UTI = 3
27+ SOL = 4
28+
29+ def get_env_bool (variable_name : str , default : bool = False ) -> bool :
30+ """
31+ Read and convert a boolean environment variable.
32+ """
33+ value = os .getenv (variable_name )
34+
35+ if value is None :
36+ return default
37+
38+ normalized_value = value .strip ().lower ()
39+
40+ if normalized_value in {"true" , "1" , "yes" , "on" }:
41+ return True
42+
43+ if normalized_value in {"false" , "0" , "no" , "off" }:
44+ return False
45+
46+ raise ValueError (
47+ f"Environment variable { variable_name } must contain true or false"
48+ )
49+
50+
51+ def get_env_int (
52+ variable_name : str ,
53+ default : int ,
54+ min_value : int | None = None ,
55+ max_value : int | None = None ,
56+ ) -> int :
57+ """
58+ Read an integer environment variable.
59+
60+ Returns the default value if the variable is missing
61+ or contains an invalid value.
62+
63+ Optionally limits the value to a minimum and maximum.
64+ """
65+ raw_value = os .getenv (variable_name )
66+
67+ if raw_value is None :
68+ return default
69+
70+ try :
71+ value = int (raw_value .strip ())
72+ except (TypeError , ValueError ):
73+ print (
74+ f"Invalid value for { variable_name } : { raw_value !r} . "
75+ f"Using default value: { default } ."
76+ )
77+ return default
78+
79+ if min_value is not None and value < min_value :
80+ print (
81+ f"{ variable_name } is below the minimum value "
82+ f"of { min_value } . Using { min_value } ."
83+ )
84+ return min_value
85+
86+ if max_value is not None and value > max_value :
87+ print (
88+ f"{ variable_name } is above the maximum value "
89+ f"of { max_value } . Using { max_value } ."
90+ )
91+ return max_value
92+
93+ return value
94+
95+
96+ def get_env_time (variable_name : str , default : str ) -> time :
97+ """
98+ Read an environment variable in HH:MM format and convert it to time.
99+ """
100+ value = os .getenv (variable_name , default )
101+
102+ try :
103+ return datetime .strptime (value , "%H:%M" ).time ()
104+ except ValueError as error :
105+ raise ValueError (
106+ f"Environment variable { variable_name } "
107+ f"must use the HH:MM format"
108+ ) from error
109+
110+
111+ def get_env_energy_mode (
112+ variable_name : str ,
113+ default : EnergyMode ,
114+ ) -> EnergyMode :
115+ """
116+ Read an energy mode name and convert it to EnergyMode.
117+ """
118+ value = os .getenv (variable_name , default .name ).strip ().upper ()
119+
120+ try :
121+ return EnergyMode [value ]
122+ except KeyError as error :
123+ allowed_modes = ", " .join (mode .name for mode in EnergyMode )
124+
125+ raise ValueError (
126+ f"Environment variable { variable_name } contains "
127+ f"unsupported mode { value } . Allowed modes: { allowed_modes } "
128+ ) from error
129+
130+
131+ MUST_PORT : Final [str ] = os .getenv ("MUST_PORT" , "COM3" )
132+
133+ DATA_GATHER_INTERVAL_SECONDS : Final [int ] = get_env_int (
134+ variable_name = "DATA_GATHER_INTERVAL_SECONDS" ,
135+ default = 60 ,
136+ min_value = 10 ,
137+ max_value = 3600 ,
138+ )
139+
140+ ENABLE_AUTO_SWITCH : Final [bool ] = get_env_bool (
141+ variable_name = "ENABLE_AUTO_SWITCH" ,
142+ default = False ,
143+ )
144+
145+ AUTO_SWITCH_TARGET_TIME : Final [time ] = get_env_time (
146+ variable_name = "AUTO_SWITCH_TARGET_TIME" ,
147+ default = "20:00" ,
148+ )
149+
150+ AUTO_SWITCH_TARGET_MODE : Final [EnergyMode ] = get_env_energy_mode (
151+ variable_name = "AUTO_SWITCH_TARGET_MODE" ,
152+ default = EnergyMode .SUB ,
153+ )
154+
155+ # Grid outage automatic mode switch
156+ ENABLE_GRID_OUTAGE_AUTO_SWITCH : Final [bool ] = (
157+ get_env_bool (
158+ variable_name = "ENABLE_GRID_OUTAGE_AUTO_SWITCH" ,
159+ default = False ,
160+ )
161+ )
162+
163+ GRID_OUTAGE_TARGET_MODE : Final [EnergyMode ] = (
164+ get_env_energy_mode (
165+ variable_name = "GRID_OUTAGE_TARGET_MODE" ,
166+ default = EnergyMode .SUB ,
167+ )
168+ )
169+
170+
171+ if __name__ == '__main__' :
172+ print ("debugging config.py" )
173+
174+ # if (
175+ # ENABLE_AUTO_SWITCH
176+ # and is_time_reached(AUTO_SWITCH_TARGET_TIME)
177+ # and current_energy_mode != AUTO_SWITCH_TARGET_MODE.value
178+ # ):
179+ # switch_energy_mode(AUTO_SWITCH_TARGET_MODE.value)
180+
181+ print ("Enable auto switch:" , ENABLE_AUTO_SWITCH )
182+ print ("Object Type:" , type (ENABLE_AUTO_SWITCH ))
183+
184+ auto_switch_target_mode_name = AUTO_SWITCH_TARGET_MODE .name
185+ print ("Auto switch target mode name:" , auto_switch_target_mode_name )
186+ print ("Object Type:" , type (auto_switch_target_mode_name ))
187+
188+ auto_switch_target_mode_code = AUTO_SWITCH_TARGET_MODE .value
189+ print ("Auto switch target mode code:" , auto_switch_target_mode_code )
190+ print ("Object Type:" , type (auto_switch_target_mode_code ))
191+
192+ grid_outage_auto_switch = ENABLE_GRID_OUTAGE_AUTO_SWITCH
193+ print ("Grid outage auto switch:" , grid_outage_auto_switch )
194+ print ("Object Type:" , type (grid_outage_auto_switch ))
195+
196+ grid_outage_target_mode_name = GRID_OUTAGE_TARGET_MODE .name
197+ grid_outage_target_mode_code = GRID_OUTAGE_TARGET_MODE .value
198+ print ("Grid outage target mode:" , grid_outage_target_mode_name )
199+ print ("Object Type:" , type (grid_outage_target_mode_name ))
200+ print ("Grid outage target mode code:" , grid_outage_target_mode_code )
201+ print ("Object Type:" , type (grid_outage_target_mode_code ))
0 commit comments