senec 0.18.0 → 0.20.0
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/.DS_Store +0 -0
- data/.env +9 -0
- data/.env.test +1 -1
- data/.gitignore +13 -0
- data/.rspec_status +64 -0
- data/.rubocop.yml +1 -1
- data/.ruby-lsp/.gitignore +1 -0
- data/.ruby-lsp/Gemfile +6 -0
- data/.ruby-lsp/Gemfile.lock +191 -0
- data/.ruby-lsp/last_updated +1 -0
- data/.ruby-lsp/main_lockfile_hash +1 -0
- data/.ruby-lsp/needs_update +0 -0
- data/README.md +13 -85
- data/coverage/.last_run.json +5 -0
- data/coverage/.resultset.json +1376 -0
- data/coverage/.resultset.json.lock +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.13.2/application.css +1 -0
- data/coverage/assets/0.13.2/application.js +7 -0
- data/coverage/assets/0.13.2/colorbox/border.png +0 -0
- data/coverage/assets/0.13.2/colorbox/controls.png +0 -0
- data/coverage/assets/0.13.2/colorbox/loading.gif +0 -0
- data/coverage/assets/0.13.2/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.13.2/favicon_green.png +0 -0
- data/coverage/assets/0.13.2/favicon_red.png +0 -0
- data/coverage/assets/0.13.2/favicon_yellow.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.13.2/loading.gif +0 -0
- data/coverage/assets/0.13.2/magnify.png +0 -0
- data/coverage/coverage.json +1377 -0
- data/coverage/index.html +14996 -0
- data/lib/.DS_Store +0 -0
- data/lib/senec/cloud/connection.rb +156 -36
- data/lib/senec/cloud/error.rb +0 -6
- data/lib/senec/local/connection.rb +1 -0
- data/lib/senec/local/request.rb +1 -1
- data/lib/senec/local/state.rb +1 -0
- data/lib/senec/version.rb +1 -1
- data/lib/senec.rb +1 -2
- data/pkg/senec-0.1.0.gem +0 -0
- data/pkg/senec-0.10.0.gem +0 -0
- data/pkg/senec-0.11.0.gem +0 -0
- data/pkg/senec-0.12.0.gem +0 -0
- data/pkg/senec-0.13.0.gem +0 -0
- data/pkg/senec-0.14.0.gem +0 -0
- data/pkg/senec-0.15.0.gem +0 -0
- data/pkg/senec-0.17.0.gem +0 -0
- data/pkg/senec-0.17.1.gem +0 -0
- data/pkg/senec-0.17.2.gem +0 -0
- data/pkg/senec-0.18.0.gem +0 -0
- data/pkg/senec-0.19.0.gem +0 -0
- data/pkg/senec-0.2.0.gem +0 -0
- data/pkg/senec-0.3.0.gem +0 -0
- data/pkg/senec-0.4.0.gem +0 -0
- data/pkg/senec-0.5.0.gem +0 -0
- data/pkg/senec-0.6.0.gem +0 -0
- data/pkg/senec-0.6.1.gem +0 -0
- data/pkg/senec-0.6.2.gem +0 -0
- data/pkg/senec-0.7.0.gem +0 -0
- data/pkg/senec-0.7.1.gem +0 -0
- data/pkg/senec-0.7.2.gem +0 -0
- data/pkg/senec-0.8.0.gem +0 -0
- data/pkg/senec-0.9.0.gem +0 -0
- metadata +86 -5
- data/lib/senec/cloud/dashboard.rb +0 -80
- data/lib/senec/cloud/technical_data.rb +0 -69
data/lib/.DS_Store
ADDED
Binary file
|
@@ -1,72 +1,192 @@
|
|
1
|
+
require 'oauth2'
|
2
|
+
|
1
3
|
module Senec
|
2
4
|
module Cloud
|
5
|
+
CONFIG_URL =
|
6
|
+
'https://sso.senec.com/realms/senec/.well-known/openid-configuration'.freeze
|
7
|
+
|
8
|
+
CLIENT_ID = 'endcustomer-app-frontend'.freeze
|
9
|
+
REDIRECT_URI = 'senec-app-auth://keycloak.prod'.freeze
|
10
|
+
SCOPE = 'roles meinsenec'.freeze
|
11
|
+
|
12
|
+
SYSTEMS_HOST = 'https://senec-app-systems-proxy.prod.senec.dev'.freeze
|
13
|
+
MEASUREMENTS_HOST = 'https://senec-app-measurements-proxy.prod.senec.dev'.freeze
|
14
|
+
WALLBOX_HOST = 'https://senec-app-wallbox-proxy.prod.senec.dev'.freeze
|
15
|
+
|
3
16
|
class Connection
|
4
|
-
|
17
|
+
DEFAULT_USER_AGENT = "ruby-senec/#{Senec::VERSION} (+https://github.com/solectrus/senec)".freeze
|
18
|
+
|
19
|
+
def initialize(username:, password:, user_agent: DEFAULT_USER_AGENT)
|
5
20
|
@username = username
|
6
21
|
@password = password
|
7
|
-
@
|
22
|
+
@user_agent = user_agent
|
8
23
|
end
|
9
24
|
|
10
|
-
attr_reader :username, :password
|
25
|
+
attr_reader :username, :password, :user_agent
|
26
|
+
|
27
|
+
def authenticate!
|
28
|
+
code_verifier = SecureRandom.alphanumeric(43)
|
29
|
+
digest = Digest::SHA256.digest(code_verifier)
|
30
|
+
code_challenge = Base64.urlsafe_encode64(digest).delete('=')
|
31
|
+
|
32
|
+
auth_url =
|
33
|
+
oauth_client.auth_code.authorize_url(
|
34
|
+
redirect_uri: REDIRECT_URI,
|
35
|
+
scope: SCOPE,
|
36
|
+
code_challenge:,
|
37
|
+
code_challenge_method: 'S256',
|
38
|
+
)
|
39
|
+
|
40
|
+
# Manual HTTP needed for Keycloak cross-domain form handling
|
41
|
+
login_form_url = fetch_login_form_url(auth_url)
|
42
|
+
redirect_url = submit_credentials(login_form_url)
|
43
|
+
authorization_code = extract_authorization_code(redirect_url)
|
44
|
+
|
45
|
+
self.oauth_token =
|
46
|
+
oauth_client.auth_code.get_token(
|
47
|
+
authorization_code,
|
48
|
+
redirect_uri: REDIRECT_URI,
|
49
|
+
code_verifier:,
|
50
|
+
)
|
51
|
+
end
|
11
52
|
|
12
53
|
def authenticated?
|
13
|
-
|
54
|
+
!!oauth_token
|
14
55
|
end
|
15
56
|
|
16
57
|
def systems
|
17
|
-
|
58
|
+
fetch_payload "#{SYSTEMS_HOST}/v1/systems"
|
18
59
|
end
|
19
60
|
|
20
|
-
def
|
21
|
-
|
61
|
+
def system_details(system_id)
|
62
|
+
fetch_payload "#{SYSTEMS_HOST}/systems/#{system_id}/details"
|
63
|
+
end
|
22
64
|
|
23
|
-
|
65
|
+
def dashboard(system_id)
|
66
|
+
fetch_payload "#{MEASUREMENTS_HOST}/v1/systems/#{system_id}/dashboard"
|
24
67
|
end
|
25
68
|
|
26
|
-
def
|
27
|
-
|
28
|
-
connection.get(path, params, { authorization: token })
|
29
|
-
end
|
69
|
+
def wallbox(system_id, wallbox_id)
|
70
|
+
fetch_payload "#{WALLBOX_HOST}/v1/systems/#{system_id}/wallboxes/#{wallbox_id}"
|
30
71
|
end
|
31
72
|
|
32
|
-
|
33
|
-
|
34
|
-
|
73
|
+
private
|
74
|
+
|
75
|
+
attr_accessor :oauth_token
|
76
|
+
|
77
|
+
def fetch_login_form_url(auth_url)
|
78
|
+
response = http_request(:get, auth_url)
|
79
|
+
store_cookies(response) # Required for Keycloak CSRF protection
|
80
|
+
extract_form_action_url(response.body)
|
81
|
+
end
|
82
|
+
|
83
|
+
def extract_form_action_url(html)
|
84
|
+
forms = html.scan(%r{<form[^>]*action="([^"]+)"[^>]*>(.*?)</form>}mi)
|
85
|
+
|
86
|
+
forms.each do |action_url, form_content|
|
87
|
+
has_username = form_content.match(/name=["']?username["']?/i)
|
88
|
+
has_password = form_content.match(/name=["']?password["']?/i)
|
89
|
+
|
90
|
+
return CGI.unescapeHTML(action_url) if has_username && has_password
|
35
91
|
end
|
92
|
+
|
93
|
+
# :nocov:
|
94
|
+
raise 'Login form not found'
|
95
|
+
# :nocov:
|
36
96
|
end
|
37
97
|
|
38
|
-
def
|
39
|
-
|
98
|
+
def submit_credentials(form_url)
|
99
|
+
credentials = { username:, password: }
|
100
|
+
response = http_request(:post, form_url, data: credentials)
|
101
|
+
raise 'Login failed' unless response.status == 302
|
102
|
+
|
103
|
+
response.headers['location'] || raise('No redirect location')
|
40
104
|
end
|
41
105
|
|
42
|
-
|
106
|
+
def extract_authorization_code(redirect_url)
|
107
|
+
raise 'Invalid redirect URL' unless redirect_url&.start_with?(REDIRECT_URI)
|
108
|
+
|
109
|
+
uri = URI(redirect_url)
|
110
|
+
params = URI.decode_www_form(uri.query).to_h
|
111
|
+
|
112
|
+
params['code'] || raise('No authorization code found')
|
113
|
+
end
|
114
|
+
|
115
|
+
def ensure_token_valid
|
116
|
+
authenticate! unless authenticated?
|
117
|
+
return true unless oauth_token.expired?
|
43
118
|
|
44
|
-
|
45
|
-
|
119
|
+
self.oauth_token = oauth_token.refresh!
|
120
|
+
true
|
121
|
+
rescue StandardError => e
|
122
|
+
# :nocov:
|
123
|
+
warn "Token refresh failed: #{e.message}"
|
124
|
+
false
|
125
|
+
# :nocov:
|
46
126
|
end
|
47
127
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
128
|
+
def fetch_payload(url, default = nil)
|
129
|
+
return default unless ensure_token_valid
|
130
|
+
|
131
|
+
response = oauth_token.get(url)
|
132
|
+
return default unless response.status == 200
|
133
|
+
|
134
|
+
JSON.parse(response.body)
|
135
|
+
rescue StandardError => e
|
136
|
+
# :nocov:
|
137
|
+
warn "API error: #{e.message}"
|
138
|
+
default
|
139
|
+
# :nocov:
|
140
|
+
end
|
53
141
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
142
|
+
def http_request(method, url, data: nil)
|
143
|
+
Faraday
|
144
|
+
.new
|
145
|
+
.send(method, url) do |req|
|
146
|
+
req.headers['user-agent'] = user_agent
|
147
|
+
req.headers['connection'] = 'keep-alive'
|
148
|
+
req.headers['cookie'] = cookie_string if cookies.any?
|
149
|
+
req.body = URI.encode_www_form(data) if data
|
59
150
|
end
|
60
151
|
end
|
61
152
|
|
62
|
-
def
|
63
|
-
|
153
|
+
def oauth_client
|
154
|
+
@oauth_client ||=
|
155
|
+
OAuth2::Client.new(
|
156
|
+
CLIENT_ID,
|
157
|
+
nil,
|
158
|
+
site: openid_config['issuer'],
|
159
|
+
authorize_url: openid_config['authorization_endpoint'],
|
160
|
+
token_url: openid_config['token_endpoint'],
|
161
|
+
)
|
162
|
+
end
|
64
163
|
|
65
|
-
|
66
|
-
|
67
|
-
|
164
|
+
def openid_config
|
165
|
+
@openid_config ||= JSON.parse(http_request(:get, CONFIG_URL).body)
|
166
|
+
rescue StandardError => e
|
167
|
+
# :nocov:
|
168
|
+
raise "Failed to load OpenID configuration: #{e.message}"
|
169
|
+
# :nocov:
|
170
|
+
end
|
171
|
+
|
172
|
+
def cookies
|
173
|
+
@cookies ||= {}
|
174
|
+
end
|
68
175
|
|
69
|
-
|
176
|
+
def cookie_string
|
177
|
+
cookies.map { |k, v| "#{k}=#{v}" }.join('; ')
|
178
|
+
end
|
179
|
+
|
180
|
+
def store_cookies(response)
|
181
|
+
set_cookie = response.headers['set-cookie']
|
182
|
+
return unless set_cookie
|
183
|
+
|
184
|
+
set_cookie
|
185
|
+
.split(', ')
|
186
|
+
.each do |cookie_header|
|
187
|
+
name, value = cookie_header.split(';').first.split('=', 2)
|
188
|
+
cookies[name] = value if name && value
|
189
|
+
end
|
70
190
|
end
|
71
191
|
end
|
72
192
|
end
|
data/lib/senec/cloud/error.rb
CHANGED
data/lib/senec/local/request.rb
CHANGED
data/lib/senec/local/state.rb
CHANGED
@@ -39,6 +39,7 @@ module Senec
|
|
39
39
|
# Regex pattern to match the system_state_name definition in the JavaScript file
|
40
40
|
# The file may be minimized, so we need to be flexible with whitespace and line breaks
|
41
41
|
FILE_REGEX = /system_state_name\s*=\s*{\s*([^}]*)\s*}/m
|
42
|
+
private_constant :FILE_REGEX
|
42
43
|
|
43
44
|
def response(language:)
|
44
45
|
res = connection.get url(language:)
|
data/lib/senec/version.rb
CHANGED
data/lib/senec.rb
CHANGED
data/pkg/senec-0.1.0.gem
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/pkg/senec-0.2.0.gem
ADDED
Binary file
|
data/pkg/senec-0.3.0.gem
ADDED
Binary file
|
data/pkg/senec-0.4.0.gem
ADDED
Binary file
|
data/pkg/senec-0.5.0.gem
ADDED
Binary file
|
data/pkg/senec-0.6.0.gem
ADDED
Binary file
|
data/pkg/senec-0.6.1.gem
ADDED
Binary file
|
data/pkg/senec-0.6.2.gem
ADDED
Binary file
|
data/pkg/senec-0.7.0.gem
ADDED
Binary file
|
data/pkg/senec-0.7.1.gem
ADDED
Binary file
|
data/pkg/senec-0.7.2.gem
ADDED
Binary file
|
data/pkg/senec-0.8.0.gem
ADDED
Binary file
|
data/pkg/senec-0.9.0.gem
ADDED
Binary file
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: senec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.20.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Georg Ledermann
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: faraday
|
@@ -51,6 +51,20 @@ dependencies:
|
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: oauth2
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
54
68
|
description: Access your local SENEC Solar Battery Storage System
|
55
69
|
email:
|
56
70
|
- georg@ledermann.dev
|
@@ -58,19 +72,62 @@ executables: []
|
|
58
72
|
extensions: []
|
59
73
|
extra_rdoc_files: []
|
60
74
|
files:
|
75
|
+
- ".DS_Store"
|
76
|
+
- ".env"
|
61
77
|
- ".env.test"
|
78
|
+
- ".gitignore"
|
62
79
|
- ".rspec"
|
80
|
+
- ".rspec_status"
|
63
81
|
- ".rubocop.yml"
|
82
|
+
- ".ruby-lsp/.gitignore"
|
83
|
+
- ".ruby-lsp/Gemfile"
|
84
|
+
- ".ruby-lsp/Gemfile.lock"
|
85
|
+
- ".ruby-lsp/last_updated"
|
86
|
+
- ".ruby-lsp/main_lockfile_hash"
|
87
|
+
- ".ruby-lsp/needs_update"
|
64
88
|
- ".vscode/settings.json"
|
65
89
|
- CODE_OF_CONDUCT.md
|
66
90
|
- LICENSE
|
67
91
|
- README.md
|
68
92
|
- Rakefile
|
93
|
+
- coverage/.last_run.json
|
94
|
+
- coverage/.resultset.json
|
95
|
+
- coverage/.resultset.json.lock
|
96
|
+
- coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc.png
|
97
|
+
- coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc_disabled.png
|
98
|
+
- coverage/assets/0.13.2/DataTables-1.10.20/images/sort_both.png
|
99
|
+
- coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc.png
|
100
|
+
- coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc_disabled.png
|
101
|
+
- coverage/assets/0.13.2/application.css
|
102
|
+
- coverage/assets/0.13.2/application.js
|
103
|
+
- coverage/assets/0.13.2/colorbox/border.png
|
104
|
+
- coverage/assets/0.13.2/colorbox/controls.png
|
105
|
+
- coverage/assets/0.13.2/colorbox/loading.gif
|
106
|
+
- coverage/assets/0.13.2/colorbox/loading_background.png
|
107
|
+
- coverage/assets/0.13.2/favicon_green.png
|
108
|
+
- coverage/assets/0.13.2/favicon_red.png
|
109
|
+
- coverage/assets/0.13.2/favicon_yellow.png
|
110
|
+
- coverage/assets/0.13.2/images/ui-bg_flat_0_aaaaaa_40x100.png
|
111
|
+
- coverage/assets/0.13.2/images/ui-bg_flat_75_ffffff_40x100.png
|
112
|
+
- coverage/assets/0.13.2/images/ui-bg_glass_55_fbf9ee_1x400.png
|
113
|
+
- coverage/assets/0.13.2/images/ui-bg_glass_65_ffffff_1x400.png
|
114
|
+
- coverage/assets/0.13.2/images/ui-bg_glass_75_dadada_1x400.png
|
115
|
+
- coverage/assets/0.13.2/images/ui-bg_glass_75_e6e6e6_1x400.png
|
116
|
+
- coverage/assets/0.13.2/images/ui-bg_glass_95_fef1ec_1x400.png
|
117
|
+
- coverage/assets/0.13.2/images/ui-bg_highlight-soft_75_cccccc_1x100.png
|
118
|
+
- coverage/assets/0.13.2/images/ui-icons_222222_256x240.png
|
119
|
+
- coverage/assets/0.13.2/images/ui-icons_2e83ff_256x240.png
|
120
|
+
- coverage/assets/0.13.2/images/ui-icons_454545_256x240.png
|
121
|
+
- coverage/assets/0.13.2/images/ui-icons_888888_256x240.png
|
122
|
+
- coverage/assets/0.13.2/images/ui-icons_cd0a0a_256x240.png
|
123
|
+
- coverage/assets/0.13.2/loading.gif
|
124
|
+
- coverage/assets/0.13.2/magnify.png
|
125
|
+
- coverage/coverage.json
|
126
|
+
- coverage/index.html
|
127
|
+
- lib/.DS_Store
|
69
128
|
- lib/senec.rb
|
70
129
|
- lib/senec/cloud/connection.rb
|
71
|
-
- lib/senec/cloud/dashboard.rb
|
72
130
|
- lib/senec/cloud/error.rb
|
73
|
-
- lib/senec/cloud/technical_data.rb
|
74
131
|
- lib/senec/local/connection.rb
|
75
132
|
- lib/senec/local/constants.rb
|
76
133
|
- lib/senec/local/error.rb
|
@@ -78,6 +135,30 @@ files:
|
|
78
135
|
- lib/senec/local/state.rb
|
79
136
|
- lib/senec/local/value.rb
|
80
137
|
- lib/senec/version.rb
|
138
|
+
- pkg/senec-0.1.0.gem
|
139
|
+
- pkg/senec-0.10.0.gem
|
140
|
+
- pkg/senec-0.11.0.gem
|
141
|
+
- pkg/senec-0.12.0.gem
|
142
|
+
- pkg/senec-0.13.0.gem
|
143
|
+
- pkg/senec-0.14.0.gem
|
144
|
+
- pkg/senec-0.15.0.gem
|
145
|
+
- pkg/senec-0.17.0.gem
|
146
|
+
- pkg/senec-0.17.1.gem
|
147
|
+
- pkg/senec-0.17.2.gem
|
148
|
+
- pkg/senec-0.18.0.gem
|
149
|
+
- pkg/senec-0.19.0.gem
|
150
|
+
- pkg/senec-0.2.0.gem
|
151
|
+
- pkg/senec-0.3.0.gem
|
152
|
+
- pkg/senec-0.4.0.gem
|
153
|
+
- pkg/senec-0.5.0.gem
|
154
|
+
- pkg/senec-0.6.0.gem
|
155
|
+
- pkg/senec-0.6.1.gem
|
156
|
+
- pkg/senec-0.6.2.gem
|
157
|
+
- pkg/senec-0.7.0.gem
|
158
|
+
- pkg/senec-0.7.1.gem
|
159
|
+
- pkg/senec-0.7.2.gem
|
160
|
+
- pkg/senec-0.8.0.gem
|
161
|
+
- pkg/senec-0.9.0.gem
|
81
162
|
homepage: https://github.com/solectrus/senec
|
82
163
|
licenses:
|
83
164
|
- MIT
|
@@ -100,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
181
|
- !ruby/object:Gem::Version
|
101
182
|
version: '0'
|
102
183
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
184
|
+
rubygems_version: 3.7.1
|
104
185
|
specification_version: 4
|
105
186
|
summary: Unofficial Ruby Client for SENEC Home
|
106
187
|
test_files: []
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require_relative 'connection'
|
2
|
-
|
3
|
-
# Model for the Senec dashboard data.
|
4
|
-
#
|
5
|
-
# Example use:
|
6
|
-
#
|
7
|
-
# connection = Senec::Cloud::Connection.new(username: '...', password: '...')
|
8
|
-
#
|
9
|
-
# # Get the data of a specific system:
|
10
|
-
# Dashboard[connection].find('123456').data
|
11
|
-
#
|
12
|
-
# # Get the data of the default system:
|
13
|
-
# Dashboard[connection].first.data
|
14
|
-
#
|
15
|
-
# By default, it returns v1 data. To get v2 data, use:
|
16
|
-
#
|
17
|
-
# Dashboard[connection].find('123456').data(version: 'v2')
|
18
|
-
# or
|
19
|
-
# Dashboard[connection].first.data(version: 'v2')
|
20
|
-
#
|
21
|
-
module Senec
|
22
|
-
module Cloud
|
23
|
-
class Dashboard
|
24
|
-
AVAILABLE_VERSIONS = %w[v1 v2].freeze
|
25
|
-
DEFAULT_VERSION = 'v1'.freeze
|
26
|
-
|
27
|
-
class Finder
|
28
|
-
def initialize(connection)
|
29
|
-
@connection = connection
|
30
|
-
end
|
31
|
-
attr_reader :connection
|
32
|
-
|
33
|
-
def find(system_id)
|
34
|
-
Dashboard.new(connection:, system_id:)
|
35
|
-
end
|
36
|
-
|
37
|
-
def first
|
38
|
-
find(connection.default_system_id)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.[](connection)
|
43
|
-
Finder.new(connection)
|
44
|
-
end
|
45
|
-
|
46
|
-
def initialize(connection: nil, system_id: nil, data: nil)
|
47
|
-
raise ArgumentError unless connection.nil? ^ data.nil?
|
48
|
-
|
49
|
-
@connection = connection
|
50
|
-
@system_id = system_id
|
51
|
-
|
52
|
-
# Useful for testing only
|
53
|
-
@data = {
|
54
|
-
'v1' => data,
|
55
|
-
'v2' => data
|
56
|
-
}
|
57
|
-
end
|
58
|
-
|
59
|
-
def data(version: DEFAULT_VERSION)
|
60
|
-
@data ||= {}
|
61
|
-
@data[version] ||= fetch_data(version:)
|
62
|
-
end
|
63
|
-
|
64
|
-
attr_reader :system_id
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def get(path, params: nil)
|
69
|
-
@connection.get(path, params:)
|
70
|
-
end
|
71
|
-
|
72
|
-
def fetch_data(version:)
|
73
|
-
raise ArgumentError unless AVAILABLE_VERSIONS.include?(version)
|
74
|
-
return unless system_id
|
75
|
-
|
76
|
-
get("/#{version}/senec/systems/#{system_id}/dashboard")
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
require_relative 'connection'
|
2
|
-
|
3
|
-
# Model for the Senec technical data.
|
4
|
-
#
|
5
|
-
# Example use:
|
6
|
-
#
|
7
|
-
# connection = Senec::Cloud::Connection.new(username: '...', password: '...')
|
8
|
-
#
|
9
|
-
# # Get the data of a specific system:
|
10
|
-
# TechnicalData[connection].find('123456')
|
11
|
-
#
|
12
|
-
# # Get the data of the default system:
|
13
|
-
# TechnicalData[connection].first
|
14
|
-
#
|
15
|
-
module Senec
|
16
|
-
module Cloud
|
17
|
-
class TechnicalData
|
18
|
-
class Finder
|
19
|
-
def initialize(connection)
|
20
|
-
@connection = connection
|
21
|
-
end
|
22
|
-
attr_reader :connection
|
23
|
-
|
24
|
-
def find(system_id)
|
25
|
-
TechnicalData.new(connection:, system_id:).tap(&:load_data)
|
26
|
-
end
|
27
|
-
|
28
|
-
def first
|
29
|
-
find(connection.default_system_id)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.[](connection)
|
34
|
-
Finder.new(connection)
|
35
|
-
end
|
36
|
-
|
37
|
-
def initialize(connection: nil, system_id: nil, data: nil)
|
38
|
-
raise ArgumentError unless connection.nil? ^ data.nil?
|
39
|
-
|
40
|
-
@connection = connection
|
41
|
-
@system_id = system_id
|
42
|
-
|
43
|
-
# Useful for testing only
|
44
|
-
@data = data
|
45
|
-
end
|
46
|
-
|
47
|
-
def load_data
|
48
|
-
raise 'Data already present!' if @data
|
49
|
-
|
50
|
-
@system_id ||= connection.default_system_id
|
51
|
-
@data = fetch_data
|
52
|
-
end
|
53
|
-
|
54
|
-
attr_reader :system_id, :data
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def get(path, params: nil)
|
59
|
-
@connection.get(path, params:)
|
60
|
-
end
|
61
|
-
|
62
|
-
def fetch_data
|
63
|
-
return unless system_id
|
64
|
-
|
65
|
-
get("/v1/senec/systems/#{system_id}/technical-data")
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|