stekker_easee 1.0.0 → 1.0.2

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 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