stekker_easee 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e78ab3f32f01b4210e1d26b6022e5a5600720c59bca4ccb21f41109cb7add51
4
- data.tar.gz: 914f32cb07c37b7d0260a9e6cd9baeb2c51ef0afb8fe1b55e3c25abb09219c79
3
+ metadata.gz: cc3c3c0ca289246df91530ff1103931b3f0a847b0ccc812c836a1acfa6148b0f
4
+ data.tar.gz: f2d2eb1332ca65973f256feb4d8546ece4c4af06ca509229b865db7071ebbbc1
5
5
  SHA512:
6
- metadata.gz: e71594d0bf1f64c3d010d4d0f4df140927266bf3f8537b62c5b04b4ea375bec003bc01bc2d7f2804f26b92c4332718bcdfa8d00630ec480d4599ee7b146a3b69
7
- data.tar.gz: 57261c2b415fc0e8bdb8a86de3684afcc554040d1539f7ab3b27cc6897ea4186a22634efb697e4d9bbe1b064bd389d6493e44bba44399afb27772ab04fb59c5f
6
+ metadata.gz: d2bbbace02bafae50a3adcbf1a75850d84c6f11b720071064aa83cb0eb33134dac2363c1f8f7378a9f90d9f095a8b8c8b28cfd250972df6e3da8c71fe12376bb
7
+ data.tar.gz: 953c5edf8b8d077a3e2d7a84ee27f88b1f74f214b2e34383378e9e123c72e1d31ba9dcca0a89ae59e8b8cf4c90f1b3ce39da3e58b4d6d1bbf82df66d2224b2ff
data/.rubocop.yml CHANGED
@@ -3,6 +3,7 @@ require:
3
3
 
4
4
  AllCops:
5
5
  NewCops: enable
6
+ TargetRubyVersion: "3.2.1"
6
7
 
7
8
  Layout/BeginEndAlignment:
8
9
  Enabled: true
data/Gemfile.lock CHANGED
@@ -1,117 +1,119 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stekker_easee (1.0.0)
4
+ stekker_easee (1.0.2)
5
5
  activemodel
6
6
  activesupport
7
7
  faraday
8
- faraday_middleware
9
8
 
10
9
  GEM
11
10
  remote: https://rubygems.org/
12
11
  specs:
13
- activemodel (7.0.4.3)
14
- activesupport (= 7.0.4.3)
15
- activesupport (7.0.4.3)
12
+ activemodel (7.1.3.2)
13
+ activesupport (= 7.1.3.2)
14
+ activesupport (7.1.3.2)
15
+ base64
16
+ bigdecimal
16
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
+ connection_pool (>= 2.2.5)
19
+ drb
17
20
  i18n (>= 1.6, < 2)
18
21
  minitest (>= 5.1)
22
+ mutex_m
19
23
  tzinfo (~> 2.0)
20
- addressable (2.8.1)
24
+ addressable (2.8.6)
21
25
  public_suffix (>= 2.0.2, < 6.0)
22
26
  ast (2.4.2)
23
- concurrent-ruby (1.2.2)
24
- crack (0.4.5)
27
+ base64 (0.2.0)
28
+ bigdecimal (3.1.7)
29
+ concurrent-ruby (1.2.3)
30
+ connection_pool (2.4.1)
31
+ crack (1.0.0)
32
+ bigdecimal
25
33
  rexml
26
- diff-lcs (1.5.0)
27
- faraday (1.10.3)
28
- faraday-em_http (~> 1.0)
29
- faraday-em_synchrony (~> 1.0)
30
- faraday-excon (~> 1.1)
31
- faraday-httpclient (~> 1.0)
32
- faraday-multipart (~> 1.0)
33
- faraday-net_http (~> 1.0)
34
- faraday-net_http_persistent (~> 1.0)
35
- faraday-patron (~> 1.0)
36
- faraday-rack (~> 1.0)
37
- faraday-retry (~> 1.0)
38
- ruby2_keywords (>= 0.0.4)
39
- faraday-em_http (1.0.0)
40
- faraday-em_synchrony (1.0.0)
41
- faraday-excon (1.1.0)
42
- faraday-httpclient (1.0.1)
43
- faraday-multipart (1.0.4)
44
- multipart-post (~> 2)
45
- faraday-net_http (1.0.1)
46
- faraday-net_http_persistent (1.2.0)
47
- faraday-patron (1.0.0)
48
- faraday-rack (1.0.0)
49
- faraday-retry (1.0.3)
50
- faraday_middleware (1.2.0)
51
- faraday (~> 1.0)
34
+ diff-lcs (1.5.1)
35
+ drb (2.2.1)
36
+ faraday (2.9.0)
37
+ faraday-net_http (>= 2.0, < 3.2)
38
+ faraday-net_http (3.1.0)
39
+ net-http
52
40
  gem-release (2.2.2)
53
- hashdiff (1.0.1)
54
- i18n (1.12.0)
41
+ hashdiff (1.1.0)
42
+ i18n (1.14.4)
55
43
  concurrent-ruby (~> 1.0)
56
- json (2.6.3)
57
- minitest (5.18.0)
58
- multipart-post (2.3.0)
59
- parallel (1.22.1)
60
- parser (3.2.1.1)
44
+ json (2.7.1)
45
+ language_server-protocol (3.17.0.3)
46
+ minitest (5.22.3)
47
+ mutex_m (0.2.0)
48
+ net-http (0.4.1)
49
+ uri
50
+ parallel (1.24.0)
51
+ parser (3.3.0.5)
61
52
  ast (~> 2.4.1)
62
- public_suffix (5.0.1)
63
- rack (3.0.7)
53
+ racc
54
+ public_suffix (5.0.5)
55
+ racc (1.7.3)
56
+ rack (3.0.10)
64
57
  rainbow (3.1.1)
65
- rake (13.0.6)
66
- regexp_parser (2.7.0)
67
- rexml (3.2.5)
68
- rspec (3.12.0)
69
- rspec-core (~> 3.12.0)
70
- rspec-expectations (~> 3.12.0)
71
- rspec-mocks (~> 3.12.0)
72
- rspec-core (3.12.1)
73
- rspec-support (~> 3.12.0)
74
- rspec-expectations (3.12.2)
58
+ rake (13.2.0)
59
+ regexp_parser (2.9.0)
60
+ rexml (3.3.9)
61
+ rspec (3.13.0)
62
+ rspec-core (~> 3.13.0)
63
+ rspec-expectations (~> 3.13.0)
64
+ rspec-mocks (~> 3.13.0)
65
+ rspec-core (3.13.0)
66
+ rspec-support (~> 3.13.0)
67
+ rspec-expectations (3.13.0)
75
68
  diff-lcs (>= 1.2.0, < 2.0)
76
- rspec-support (~> 3.12.0)
77
- rspec-mocks (3.12.4)
69
+ rspec-support (~> 3.13.0)
70
+ rspec-mocks (3.13.0)
78
71
  diff-lcs (>= 1.2.0, < 2.0)
79
- rspec-support (~> 3.12.0)
80
- rspec-support (3.12.0)
81
- rubocop (1.48.1)
72
+ rspec-support (~> 3.13.0)
73
+ rspec-support (3.13.1)
74
+ rubocop (1.62.1)
82
75
  json (~> 2.3)
76
+ language_server-protocol (>= 3.17.0)
83
77
  parallel (~> 1.10)
84
- parser (>= 3.2.0.0)
78
+ parser (>= 3.3.0.2)
85
79
  rainbow (>= 2.2.2, < 4.0)
86
80
  regexp_parser (>= 1.8, < 3.0)
87
81
  rexml (>= 3.2.5, < 4.0)
88
- rubocop-ast (>= 1.26.0, < 2.0)
82
+ rubocop-ast (>= 1.31.1, < 2.0)
89
83
  ruby-progressbar (~> 1.7)
90
84
  unicode-display_width (>= 2.4.0, < 3.0)
91
- rubocop-ast (1.28.0)
92
- parser (>= 3.2.1.0)
93
- rubocop-capybara (2.17.1)
85
+ rubocop-ast (1.31.2)
86
+ parser (>= 3.3.0.4)
87
+ rubocop-capybara (2.20.0)
94
88
  rubocop (~> 1.41)
95
- rubocop-rails (2.18.0)
89
+ rubocop-factory_bot (2.25.1)
90
+ rubocop (~> 1.41)
91
+ rubocop-rails (2.24.1)
96
92
  activesupport (>= 4.2.0)
97
93
  rack (>= 1.1)
98
94
  rubocop (>= 1.33.0, < 2.0)
99
- rubocop-rspec (2.19.0)
100
- rubocop (~> 1.33)
95
+ rubocop-ast (>= 1.31.1, < 2.0)
96
+ rubocop-rspec (2.28.0)
97
+ rubocop (~> 1.40)
101
98
  rubocop-capybara (~> 2.17)
99
+ rubocop-factory_bot (~> 2.22)
100
+ rubocop-rspec_rails (~> 2.28)
101
+ rubocop-rspec_rails (2.28.2)
102
+ rubocop (~> 1.40)
102
103
  ruby-progressbar (1.13.0)
103
- ruby2_keywords (0.0.5)
104
- timecop (0.9.6)
104
+ timecop (0.9.8)
105
105
  tzinfo (2.0.6)
106
106
  concurrent-ruby (~> 1.0)
107
- unicode-display_width (2.4.2)
108
- webmock (3.18.1)
107
+ unicode-display_width (2.5.0)
108
+ uri (0.13.0)
109
+ webmock (3.23.0)
109
110
  addressable (>= 2.8.0)
110
111
  crack (>= 0.3.2)
111
112
  hashdiff (>= 0.4.0, < 2.0.0)
112
113
 
113
114
  PLATFORMS
114
115
  arm64-darwin-22
116
+ arm64-darwin-23
115
117
  x86_64-linux
116
118
 
117
119
  DEPENDENCIES
@@ -126,4 +128,4 @@ DEPENDENCIES
126
128
  webmock (~> 3.18)
127
129
 
128
130
  BUNDLED WITH
129
- 2.4.8
131
+ 2.4.6
data/README.md CHANGED
@@ -61,15 +61,11 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/stekke
61
61
  ## Publishing
62
62
 
63
63
  ```bash
64
- # Bump the gem version
65
- # See https://github.com/svenfuchs/gem-release#gem-bump
66
- gem bump --version [major|minor|patch]
67
-
68
- # Release the gem to rubygems.org
69
- # See https://github.com/svenfuchs/gem-release#gem-release
70
- gem release
71
-
72
- # Push the commit and tag to git
73
- git push
74
- git push --tags
64
+ # - bumps the gem version to the next major, minor or patch version.
65
+ # - creates commit for the version bump
66
+ # - tags the commit
67
+ # - pushes the commit and tag
68
+ # - publishes the gem to Rubygems
69
+ bin/release [major|minor|patch]
70
+ # See also https://github.com/svenfuchs/gem-release#gem-bump
75
71
  ```
data/bin/release ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ # Read major/minor/patch argument
7
+ version="$1"
8
+
9
+ # Do a pretend version bump to determine what the new version should be
10
+ bumped_version=$(gem bump --version "$version" --pretend --no-commit | awk '{ print $4 }' | tr -d '[:space:]')
11
+
12
+ # Use ruby to get the root level directory name
13
+ gem_name=$(ruby -e "puts File.basename(Dir.pwd)")
14
+
15
+ # Update version file
16
+ gem bump --version "$bumped_version" --no-commit
17
+
18
+ # Run bundle install to ensure gem version in Gemfile is updated
19
+ bundle install
20
+
21
+ # Stage Gemfile.lock and the version file
22
+ git add Gemfile.lock "lib/$gem_name/version.rb"
23
+
24
+ # Finally create the bump commit
25
+ git commit -m "Bump $gem_name to $bumped_version"
26
+
27
+ # Create a tag, push the commit and tag, and release to Rubygems
28
+ gem bump --version "$bumped_version" --no-commit --tag --push --release
data/easee.gemspec CHANGED
@@ -28,7 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.add_runtime_dependency "activemodel"
29
29
  spec.add_runtime_dependency "activesupport"
30
30
  spec.add_runtime_dependency "faraday"
31
- spec.add_runtime_dependency "faraday_middleware"
32
31
 
33
32
  spec.metadata["rubygems_mfa_required"] = "true"
34
33
  end
@@ -0,0 +1,10 @@
1
+ module Easee
2
+ class AmazonGwMiddleware < Faraday::Middleware
3
+ def on_complete(env)
4
+ return unless env.response_headers["X-Amzn-Errortype"] == "ForbiddenException"
5
+
6
+ response_values = Faraday::Response::RaiseError.new.response_values(env)
7
+ raise(Faraday::ForbiddenError, response_values)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Easee
2
+ class Charger
3
+ def initialize(data)
4
+ @data = data.symbolize_keys
5
+ end
6
+
7
+ def id = @data.fetch(:id)
8
+ def name = @data.fetch(:name)
9
+ def color = @data.fetch(:color)
10
+ def product_code = @data.fetch(:productCode)
11
+ end
12
+ end
data/lib/easee/client.rb CHANGED
@@ -20,6 +20,13 @@ module Easee
20
20
  @encryptor = encryptor
21
21
  end
22
22
 
23
+ # https://developer.easee.cloud/reference/post_api-accounts-login
24
+ def login
25
+ with_error_handling do
26
+ request_access_token
27
+ end
28
+ end
29
+
23
30
  # https://developer.easee.cloud/reference/post_api-chargers-id-unpair
24
31
  def unpair(charger_id:, pin_code:)
25
32
  post("/api/chargers/#{charger_id}/unpair", query: { pinCode: pin_code })
@@ -30,6 +37,13 @@ module Easee
30
37
  post("/api/chargers/#{charger_id}/pair", query: { pinCode: pin_code })
31
38
  end
32
39
 
40
+ # https://developer.easee.cloud/reference/get_api-chargers
41
+ def chargers
42
+ get("/api/chargers").then do |response|
43
+ response.body.map { |data| Charger.new(data) }
44
+ end
45
+ end
46
+
33
47
  # https://developer.easee.cloud/reference/get_api-chargers-id-state
34
48
  def state(charger_id)
35
49
  get("/api/chargers/#{charger_id}/state")
@@ -46,11 +60,18 @@ module Easee
46
60
  post("/api/chargers/#{charger_id}/commands/resume_charging")
47
61
  end
48
62
 
63
+ # https://developer.easee.com/reference/post_api-chargers-chargerid-commands-poll-lifetimeenergy
64
+ def poll_lifetime_energy(charger_id)
65
+ post("/api/chargers/#{charger_id}/commands/poll_lifetimeenergy")
66
+ end
67
+
68
+ # https://developer.easee.cloud/reference/get_api-chargers-id-config
49
69
  def configuration(charger_id)
50
70
  get("/api/chargers/#{charger_id}/config")
51
71
  .then { |response| Configuration.new(response.body) }
52
72
  end
53
73
 
74
+ # https://developer.easee.cloud/reference/get_api-chargers-id-site
54
75
  def site(charger_id)
55
76
  get("/api/chargers/#{charger_id}/site")
56
77
  .then { |response| Site.new(response.body) }
@@ -70,13 +91,14 @@ module Easee
70
91
  Faraday.new(url: BASE_URL) do |conn|
71
92
  conn.request :json
72
93
  conn.response :raise_error
94
+ conn.use AmazonGwMiddleware
95
+ conn.response :json, content_type: /\bjson$/
73
96
  end
74
97
  end
75
98
 
76
99
  def authenticated_connection
77
100
  connection.tap do |conn|
78
101
  conn.request :authorization, "Bearer", access_token
79
- conn.response :json, content_type: /\bjson$/
80
102
  end
81
103
  end
82
104
 
@@ -107,13 +129,21 @@ module Easee
107
129
 
108
130
  retry
109
131
  end
132
+ rescue Faraday::TooManyRequestsError => e
133
+ raise Errors::RateLimitExceeded.new("Rate limit exceeded", e.response)
134
+ rescue Faraday::ForbiddenError => e
135
+ raise Errors::Forbidden, "Access denied to charger"
110
136
  rescue Faraday::Error => e
137
+ if e.response_status == 400 && [100, 727].include?(e.response.dig(:body, "errorCode"))
138
+ raise Errors::InvalidCredentials, "Invalid username or password"
139
+ end
140
+
111
141
  raise Errors::RequestFailed.new("Request returned status #{e.response_status}", e.response)
112
142
  end
113
143
 
114
144
  def access_token
115
145
  encrypted_tokens = @token_cache.fetch(TOKENS_CACHE_KEY) do
116
- @encryptor.encrypt(request_access_token, cipher_options: { deterministic: true })
146
+ @encryptor.encrypt(request_access_token.to_json, cipher_options: { deterministic: true })
117
147
  end
118
148
 
119
149
  plain_text_tokens = @encryptor.decrypt(encrypted_tokens)
@@ -124,7 +154,7 @@ module Easee
124
154
  def refresh_access_token!
125
155
  @token_cache.write(
126
156
  TOKENS_CACHE_KEY,
127
- @encryptor.encrypt(refresh_access_token, cipher_options: { deterministic: true }),
157
+ @encryptor.encrypt(refresh_access_token.to_json, cipher_options: { deterministic: true }),
128
158
  expires_in: 1.day,
129
159
  )
130
160
  rescue Faraday::Error => e
data/lib/easee/errors.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  module Easee
2
2
  module Errors
3
- class Base < ::StandardError; end
3
+ class Base < ::StandardError
4
+ def retryable? = false
5
+ end
6
+
7
+ class InvalidCredentials < Base; end
4
8
 
5
9
  class RequestFailed < Base
6
10
  attr_reader :response
@@ -10,5 +14,11 @@ module Easee
10
14
  super(message)
11
15
  end
12
16
  end
17
+
18
+ class Forbidden < Base; end
19
+
20
+ class RateLimitExceeded < RequestFailed
21
+ def retryable? = true
22
+ end
13
23
  end
14
24
  end
data/lib/easee/site.rb CHANGED
@@ -9,12 +9,14 @@ module Easee
9
9
  def building_number = address.fetch(:buildingNumber)
10
10
  def zip = address.fetch(:zip)
11
11
  def area = address.fetch(:area)
12
- def country_id = address.fetch(:country).fetch(:id)
12
+ def country_id = country[:id]
13
13
  def latitude = address.fetch(:latitude)
14
14
  def longitude = address.fetch(:longitude)
15
15
 
16
16
  private
17
17
 
18
+ def country = address.fetch(:country) || {}
19
+
18
20
  def address
19
21
  @address ||= @data.fetch(:address)
20
22
  end
data/lib/easee/state.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  module Easee
2
2
  class State
3
+ OP_MODE_UNKNOWN = :unknown
4
+
3
5
  CHARGER_OP_MODES = {
4
6
  0 => :offline,
5
7
  1 => :disconnected,
@@ -21,12 +23,15 @@ module Easee
21
23
  def meter_reading
22
24
  MeterReading.new(
23
25
  reading_kwh: @data.fetch(:lifetimeEnergy),
24
- timestamp: Time.current,
26
+ timestamp: Time.zone.parse(@data.fetch(:latestPulse)),
25
27
  )
26
28
  end
27
29
 
28
30
  private
29
31
 
30
- def charger_op_mode = CHARGER_OP_MODES.fetch(@data.fetch(:chargerOpMode))
32
+ def charger_op_mode
33
+ numeric_op_mode = @data.fetch(:chargerOpMode)
34
+ CHARGER_OP_MODES.fetch(numeric_op_mode) { OP_MODE_UNKNOWN }
35
+ end
31
36
  end
32
37
  end
data/lib/easee/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Easee
2
- VERSION = "1.0.0".freeze
2
+ VERSION = "1.0.2".freeze
3
3
  end
data/lib/stekker_easee.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "faraday"
2
- require "active_support/core_ext/time/calculations"
2
+ require "active_support/deprecation"
3
+ require "active_support/deprecator"
4
+ require "active_support/core_ext/numeric/time"
3
5
  require "active_support/isolated_execution_state"
4
6
  require "active_support/core_ext/hash"
5
7
 
@@ -10,6 +12,8 @@ require_relative "easee/errors"
10
12
  require_relative "easee/meter_reading"
11
13
  require_relative "easee/site"
12
14
  require_relative "easee/state"
15
+ require_relative "easee/charger"
16
+ require_relative "easee/amazon_gw_middleware"
13
17
 
14
18
  module Easee
15
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stekker_easee
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Team Stekker
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-04-12 00:00:00.000000000 Z
11
+ date: 2024-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: faraday_middleware
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
55
  description: Easee connector
70
56
  email:
71
57
  - support@stekker.com
@@ -83,9 +69,11 @@ files:
83
69
  - README.md
84
70
  - Rakefile
85
71
  - bin/console
72
+ - bin/release
86
73
  - bin/setup
87
74
  - easee.gemspec
88
- - lib/easee.rb
75
+ - lib/easee/amazon_gw_middleware.rb
76
+ - lib/easee/charger.rb
89
77
  - lib/easee/client.rb
90
78
  - lib/easee/configuration.rb
91
79
  - lib/easee/errors.rb
data/lib/easee.rb DELETED
@@ -1,15 +0,0 @@
1
- require "faraday"
2
- require "active_support/core_ext/time/calculations"
3
- require "active_support/isolated_execution_state"
4
- require "active_support/core_ext/hash"
5
-
6
- require_relative "easee/version"
7
- require_relative "easee/client"
8
- require_relative "easee/configuration"
9
- require_relative "easee/errors"
10
- require_relative "easee/meter_reading"
11
- require_relative "easee/site"
12
- require_relative "easee/state"
13
-
14
- module Easee
15
- end