growatt 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env.template +4 -4
- data/.gitignore +47 -47
- data/CHANGELOG.md +16 -14
- data/Gemfile +6 -6
- data/README.md +114 -83
- data/Rakefile +22 -19
- data/growatt.gemspec +37 -37
- data/lib/growatt/api.rb +59 -35
- data/lib/growatt/authorization.rb +91 -36
- data/lib/growatt/client.rb +219 -160
- data/lib/growatt/connection.rb +45 -26
- data/lib/growatt/const.rb +47 -29
- data/lib/growatt/error.rb +24 -12
- data/lib/growatt/pagination.rb +32 -26
- data/lib/growatt/version.rb +5 -5
- data/lib/growatt.rb +62 -27
- metadata +4 -14
- data/test/auth_test.rb +0 -20
- data/test/client_test.rb +0 -69
- data/test/test_helper.rb +0 -23
@@ -1,36 +1,91 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
require File.expand_path('error', __dir__)
|
5
|
+
|
6
|
+
module Growatt
|
7
|
+
# Handles authentication flow and stores session data in the global configuration.
|
8
|
+
#
|
9
|
+
# This module provides methods for logging into the Growatt portal, hashing passwords,
|
10
|
+
# and validating credentials.
|
11
|
+
#
|
12
|
+
module Authentication
|
13
|
+
# Logs in to the Growatt portal using the stored credentials.
|
14
|
+
#
|
15
|
+
# This method:
|
16
|
+
# - Validates that the username and password are set.
|
17
|
+
# - Hashes the password using MD5.
|
18
|
+
# - Sends a login request with the credentials.
|
19
|
+
# - Processes the server response.
|
20
|
+
#
|
21
|
+
# @raise [ConfigurationError] If username or password is missing.
|
22
|
+
# @raise [AuthenticationError] If authentication fails.
|
23
|
+
# @return [Hash] The login response data if successful.
|
24
|
+
#
|
25
|
+
# @example Logging in to Growatt:
|
26
|
+
# client.login
|
27
|
+
#
|
28
|
+
def login
|
29
|
+
validate_credentials
|
30
|
+
_password = hash_password(self.password) # Hash password before sending
|
31
|
+
|
32
|
+
_format = self.format
|
33
|
+
self.format = 'x-www-form-urlencoded'
|
34
|
+
response = post('newTwoLoginAPI.do', { 'userName' => self.username, 'password' => _password })
|
35
|
+
self.format = _format
|
36
|
+
process_response(response.body['back'])
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Hashes the given password using MD5.
|
42
|
+
#
|
43
|
+
# This method generates an MD5 hash of the password and modifies it by replacing
|
44
|
+
# every occurrence of '0' at even indices with 'c'.
|
45
|
+
#
|
46
|
+
# @param password [String] The plain-text password.
|
47
|
+
# @return [String] The modified MD5-hashed password.
|
48
|
+
#
|
49
|
+
# @example Hashing a password:
|
50
|
+
# hash_password("mypassword") # => "5f4dcc3bcfcd204e074324a5e7565eaf"
|
51
|
+
#
|
52
|
+
def hash_password(password)
|
53
|
+
password_md5 = Digest::MD5.hexdigest(password.encode('utf-8'))
|
54
|
+
(0...password_md5.length).step(2) do |i|
|
55
|
+
password_md5[i] = 'c' if password_md5[i] == '0'
|
56
|
+
end
|
57
|
+
password_md5
|
58
|
+
end
|
59
|
+
|
60
|
+
# Validates that the username and password are set.
|
61
|
+
#
|
62
|
+
# @raise [ConfigurationError] If either credential is missing.
|
63
|
+
#
|
64
|
+
# @example Checking credentials before login:
|
65
|
+
# validate_credentials # Raises ConfigurationError if missing
|
66
|
+
#
|
67
|
+
def validate_credentials
|
68
|
+
raise ConfigurationError, "Username/password not set" unless username && password
|
69
|
+
end
|
70
|
+
|
71
|
+
# Processes the authentication response.
|
72
|
+
#
|
73
|
+
# If authentication is successful, stores the login data. Otherwise, raises an error.
|
74
|
+
#
|
75
|
+
# @param data [Hash] The response data from the Growatt portal.
|
76
|
+
# @raise [AuthenticationError] If authentication fails.
|
77
|
+
# @return [Hash] The login data if authentication succeeds.
|
78
|
+
#
|
79
|
+
# @example Handling a successful login:
|
80
|
+
# process_response({ "success" => true }) # Returns login data
|
81
|
+
#
|
82
|
+
def process_response(data)
|
83
|
+
if data && data['success']
|
84
|
+
@login_data = data
|
85
|
+
data
|
86
|
+
else
|
87
|
+
raise AuthenticationError.new(data['error'])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/growatt/client.rb
CHANGED
@@ -1,160 +1,219 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require File.expand_path('
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
#
|
48
|
-
def
|
49
|
-
|
50
|
-
'
|
51
|
-
'
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
def
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('api', __dir__)
|
4
|
+
require File.expand_path('const', __dir__)
|
5
|
+
require File.expand_path('error', __dir__)
|
6
|
+
|
7
|
+
module Growatt
|
8
|
+
# Wrapper for the Growatt REST API
|
9
|
+
#
|
10
|
+
# This class provides methods to interact with the Growatt API, including:
|
11
|
+
# - Retrieving plant and inverter data
|
12
|
+
# - Managing inverters (turning them on/off, updating settings)
|
13
|
+
# - Handling authentication and session data
|
14
|
+
#
|
15
|
+
# @note No official API documentation is available; this was reverse-engineered.
|
16
|
+
#
|
17
|
+
# @see Growatt::Authentication For authentication-related logic.
|
18
|
+
class Client < API
|
19
|
+
# Initializes the Growatt API client.
|
20
|
+
#
|
21
|
+
# @param options [Hash] Additional options for the API client.
|
22
|
+
def initialize(options = {})
|
23
|
+
super(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Retrieves login session data.
|
27
|
+
#
|
28
|
+
# @return [Hash, nil] The login data stored in @login_data.
|
29
|
+
def login_info
|
30
|
+
@login_data
|
31
|
+
end
|
32
|
+
|
33
|
+
# Retrieves a list of plants associated with a user.
|
34
|
+
#
|
35
|
+
# @param user_id [String, nil] The user ID; if nil, it defaults to the logged-in user's ID.
|
36
|
+
# @return [Hash] The list of plants.
|
37
|
+
def plant_list(user_id = nil)
|
38
|
+
user_id ||= login_info['user']['id']
|
39
|
+
_plant_list({ 'userId': user_id })
|
40
|
+
end
|
41
|
+
|
42
|
+
# Retrieves detailed information about a plant.
|
43
|
+
#
|
44
|
+
# @param plant_id [String] The plant ID.
|
45
|
+
# @param type [String] The timespan type (default: `Timespan::DAY`).
|
46
|
+
# @param date [Time] The date for the requested timespan (default: `Time.now`).
|
47
|
+
# @return [Hash] The plant details.
|
48
|
+
def plant_detail(plant_id, type = Timespan::DAY, date = Time.now)
|
49
|
+
_plant_detail({
|
50
|
+
'plantId': plant_id,
|
51
|
+
'type': type,
|
52
|
+
'date': timespan_date(type, date)
|
53
|
+
})
|
54
|
+
end
|
55
|
+
|
56
|
+
# Retrieves plant information.
|
57
|
+
#
|
58
|
+
# @param plant_id [String] The plant ID.
|
59
|
+
# @return [Hash] Plant information, including available devices.
|
60
|
+
def plant_info(plant_id)
|
61
|
+
_plant_info({
|
62
|
+
'op': 'getAllDeviceList',
|
63
|
+
'plantId': plant_id,
|
64
|
+
'pageNum': 1,
|
65
|
+
'pageSize': 1
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
# Retrieves a list of devices in a plant.
|
70
|
+
#
|
71
|
+
# @param plant_id [String] The plant ID.
|
72
|
+
# @return [Array] A list of devices.
|
73
|
+
def device_list(plant_id)
|
74
|
+
plant_info(plant_id).deviceList
|
75
|
+
end
|
76
|
+
|
77
|
+
# Retrieves a list of inverters in a plant.
|
78
|
+
#
|
79
|
+
# @param plant_id [String] The plant ID.
|
80
|
+
# @return [Array] A list of inverters.
|
81
|
+
def inverter_list(plant_id)
|
82
|
+
devices = device_list(plant_id)
|
83
|
+
devices.select { |device| 'inverter'.eql? device.deviceType }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Retrieves data for inverter control.
|
87
|
+
#
|
88
|
+
# @param inverter_id [String] The inverter's serial number.
|
89
|
+
# @return [Hash] The inverter's control data.
|
90
|
+
def inverter_control_data(inverter_id)
|
91
|
+
_inverter_api({
|
92
|
+
'op': 'getMaxSetData',
|
93
|
+
'serialNum': inverter_id
|
94
|
+
}).obj.maxSetBean
|
95
|
+
end
|
96
|
+
|
97
|
+
# Updates an inverter's setting.
|
98
|
+
#
|
99
|
+
# @param serial_number [String] The inverter's serial number.
|
100
|
+
# @param command [String] The command to execute.
|
101
|
+
# @param param_id [String] The parameter ID.
|
102
|
+
# @param parameters [Array, Hash] The parameters to send.
|
103
|
+
# @return [Boolean] `true` if the update was successful, `false` otherwise.
|
104
|
+
def update_inverter_setting(serial_number, command, param_id, parameters)
|
105
|
+
command_parameters = {
|
106
|
+
'op': command,
|
107
|
+
'serialNum': serial_number,
|
108
|
+
'paramId': param_id
|
109
|
+
}
|
110
|
+
|
111
|
+
parameters = parameters.map.with_index { |value, index| ["param#{index + 1}", value] }.to_h if parameters.is_a? Array
|
112
|
+
self.format = 'x-www-form-urlencoded'
|
113
|
+
data = JSON.parse(post('newTcpsetAPI.do', command_parameters.merge(parameters)).body)
|
114
|
+
self.format = :json
|
115
|
+
data['success']
|
116
|
+
end
|
117
|
+
|
118
|
+
# Turns an inverter on or off.
|
119
|
+
#
|
120
|
+
# @param serial_number [String] The inverter's serial number.
|
121
|
+
# @param on [Boolean] `true` to turn on, `false` to turn off.
|
122
|
+
# @return [Boolean] `true` if the operation was successful.
|
123
|
+
def turn_inverter(serial_number, on = true)
|
124
|
+
onoff = (on ? Inverter::ON : Inverter::OFF)
|
125
|
+
update_inverter_setting(serial_number, 'maxSetApi', 'max_cmd_on_off', [onoff])
|
126
|
+
end
|
127
|
+
|
128
|
+
# Checks if an inverter is turned on.
|
129
|
+
#
|
130
|
+
# @param serial_number [String] The inverter's serial number.
|
131
|
+
# @return [Boolean] `true` if the inverter is on, `false` otherwise.
|
132
|
+
def inverter_on?(serial_number)
|
133
|
+
status = inverter_control_data(serial_number)
|
134
|
+
Inverter::ON.eql? status.max_cmd_on_off
|
135
|
+
end
|
136
|
+
|
137
|
+
# Sets export limit for an inverter.
|
138
|
+
#
|
139
|
+
# @param serial_number [String] The inverter's serial number.
|
140
|
+
# @param enable [String] `ExportLimit::DISABLE`, `ExportLimit::WATT`, or `ExportLimit::PERCENTAGE`.
|
141
|
+
# @param value [Numeric, nil] The export limit value (required unless disabled).
|
142
|
+
# @return [Boolean] `true` if the setting update was successful.
|
143
|
+
def export_limit(serial_number, enable, value = nil)
|
144
|
+
if ExportLimit::DISABLE.eql? enable
|
145
|
+
params = [0]
|
146
|
+
else
|
147
|
+
validate_export_parameters(enable, value)
|
148
|
+
params = [1, value, enable]
|
149
|
+
end
|
150
|
+
update_inverter_setting(serial_number, 'maxSetApi', 'backflow_setting', params)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Utility function to get a formatted date based on timespan.
|
154
|
+
#
|
155
|
+
# @param timespan [String] The timespan type (`Timespan::DAY`, `Timespan::MONTH`, `Timespan::YEAR`).
|
156
|
+
# @param date [Time] The date (default: `Time.now`).
|
157
|
+
# @return [String] The formatted date.
|
158
|
+
def timespan_date(timespan = Timespan::DAY, date = Time.now)
|
159
|
+
case timespan
|
160
|
+
when Timespan::YEAR
|
161
|
+
date.strftime("%Y")
|
162
|
+
when Timespan::MONTH
|
163
|
+
date.strftime("%Y-%m")
|
164
|
+
when Timespan::DAY
|
165
|
+
date.strftime("%Y-%m-%d")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Retrieves inverter data based on timespan.
|
170
|
+
#
|
171
|
+
# @param inverter_id [String] The inverter's ID.
|
172
|
+
# @param type [String] The timespan type.
|
173
|
+
# @param date [Time] The date (default: `Time.now`).
|
174
|
+
# @return [Hash] The inverter data.
|
175
|
+
def inverter_data(inverter_id, type = Timespan::DAY, date = Time.now)
|
176
|
+
operator =
|
177
|
+
case type
|
178
|
+
when Timespan::DAY then 'getInverterData_max'
|
179
|
+
when Timespan::MONTH then 'getMaxMonthPac'
|
180
|
+
when Timespan::YEAR then 'getMaxYearPac'
|
181
|
+
end
|
182
|
+
|
183
|
+
_inverter_api({
|
184
|
+
'op': operator,
|
185
|
+
'id': inverter_id,
|
186
|
+
'type': 1,
|
187
|
+
'date': timespan_date(type, date)
|
188
|
+
})
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
|
193
|
+
# Defines API endpoints dynamically.
|
194
|
+
#
|
195
|
+
# @param method [Symbol] The method name.
|
196
|
+
# @param path [String] The API endpoint.
|
197
|
+
def self.api_endpoint(method, path)
|
198
|
+
define_method(method) do |params = {}|
|
199
|
+
get(path, params) do |request|
|
200
|
+
request.headers['Accept'] = "application/#{format}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
api_endpoint :_plant_list, 'PlantListAPI.do'
|
206
|
+
api_endpoint :_plant_detail, 'PlantDetailAPI.do'
|
207
|
+
api_endpoint :_inverter_api, 'newInverterAPI.do'
|
208
|
+
api_endpoint :_plant_info, 'newTwoPlantAPI.do'
|
209
|
+
|
210
|
+
# Validates export limitation parameters.
|
211
|
+
def validate_export_parameters(enable, value)
|
212
|
+
unless [ExportLimit::WATT, ExportLimit::PERCENTAGE].include?(enable)
|
213
|
+
raise ArgumentError, "Export limitation must be ExportLimit::WATT or ExportLimit::PERCENTAGE"
|
214
|
+
end
|
215
|
+
raise ArgumentError, "Value is required" unless value
|
216
|
+
raise ArgumentError, "Value must be numeric" unless value.is_a? Numeric
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
data/lib/growatt/connection.rb
CHANGED
@@ -1,26 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday-cookie_jar'
|
5
|
+
|
6
|
+
module Growatt
|
7
|
+
# Handles HTTP connection setup and authentication management.
|
8
|
+
#
|
9
|
+
# This module establishes a Faraday connection to interact with the Growatt API,
|
10
|
+
# ensuring proper authentication via cookies and setting up required headers.
|
11
|
+
module Connection
|
12
|
+
# Establishes a Faraday connection with appropriate middleware and settings.
|
13
|
+
#
|
14
|
+
# @return [Faraday::Connection] The configured Faraday connection instance.
|
15
|
+
# @raise [ConfigurationError] If the API endpoint is not defined.
|
16
|
+
def connection
|
17
|
+
raise ConfigurationError, "Option for endpoint is not defined" unless endpoint
|
18
|
+
|
19
|
+
options = setup_options
|
20
|
+
@connection ||= Faraday::Connection.new(options) do |connection|
|
21
|
+
# Enable cookie-based authentication
|
22
|
+
connection.use :cookie_jar
|
23
|
+
|
24
|
+
# Handle HTTP response errors
|
25
|
+
connection.use Faraday::Response::RaiseError
|
26
|
+
|
27
|
+
# Set up default Faraday adapter
|
28
|
+
connection.adapter Faraday.default_adapter
|
29
|
+
|
30
|
+
# Configure authentication and request headers
|
31
|
+
setup_authorization(connection)
|
32
|
+
setup_headers(connection)
|
33
|
+
|
34
|
+
# Parse JSON responses automatically
|
35
|
+
connection.response :json, content_type: /\bjson$/
|
36
|
+
|
37
|
+
# Ensure requests are URL-encoded
|
38
|
+
connection.use Faraday::Request::UrlEncoded
|
39
|
+
|
40
|
+
# Configure logging if a logger is present
|
41
|
+
setup_logger_filtering(connection, logger) if logger
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/growatt/const.rb
CHANGED
@@ -1,29 +1,47 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Growatt
|
4
|
+
# A base class for defining enumerations using constants.
|
5
|
+
#
|
6
|
+
# This class dynamically defines constants from an array of strings.
|
7
|
+
class Enum
|
8
|
+
# Defines constants based on the provided array.
|
9
|
+
#
|
10
|
+
# @param array [Array<String>] The array of strings to be converted into constants.
|
11
|
+
def self.enum(array)
|
12
|
+
array.each do |c|
|
13
|
+
const_set c, c
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Represents different timespan options for data retrieval.
|
19
|
+
class Timespan
|
20
|
+
# Hourly data timespan
|
21
|
+
HOUR = 0
|
22
|
+
# Daily data timespan
|
23
|
+
DAY = 1
|
24
|
+
# Monthly data timespan
|
25
|
+
MONTH = 2
|
26
|
+
# Yearly data timespan
|
27
|
+
YEAR = 3
|
28
|
+
end
|
29
|
+
|
30
|
+
# Represents possible states for an inverter.
|
31
|
+
class Inverter
|
32
|
+
# Inverter is turned on
|
33
|
+
ON = "0101"
|
34
|
+
# Inverter is turned off
|
35
|
+
OFF = "0000"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Represents export limit settings for an inverter.
|
39
|
+
class ExportLimit
|
40
|
+
# Disables export limitation
|
41
|
+
DISABLE = -1
|
42
|
+
# Export limit is set in watts
|
43
|
+
WATT = 1
|
44
|
+
# Export limit is set in percentage
|
45
|
+
PERCENTAGE = 0
|
46
|
+
end
|
47
|
+
end
|