stekker_easee 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b5592a4edc9d574ac3d942adea5527716bb97f15090f33e6829a2e002f5fee5c
4
+ data.tar.gz: 9e26e2712745636dac113c1abc3282ea705a60f9598998e3d829dfd10222110a
5
+ SHA512:
6
+ metadata.gz: 836a495d985e421acf2508c2618de4034fb3bae460d1184eb40a09f2127875f6654bfd8a49c7986b7962623ec360addf5fd872d8b6ce54b525fd0f2683bfbe8c
7
+ data.tar.gz: '06358a19035ef71ea2d7d6a7a071a832b888963feb26a81b8802b595c4ad630afa3947cbdabbbdeab5bfc4d39827d800a7d1db4b680adcefe3caa5e023422bfb'
@@ -0,0 +1,23 @@
1
+ name: "Ruby"
2
+ on:
3
+ push:
4
+ branches: [ "main" ]
5
+ pull_request:
6
+ branches: [ "main" ]
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+
11
+ env:
12
+ RAILS_ENV: test
13
+ steps:
14
+ - name: Checkout code
15
+ uses: actions/checkout@v3
16
+
17
+ - name: Install Ruby and gems
18
+ uses: ruby/setup-ruby@ee2113536afb7f793eed4ce60e8d3b26db912da4
19
+ with:
20
+ bundler-cache: true
21
+
22
+ - name: Run tests and Rubocop
23
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1 @@
1
+ inherit_from: ../../.rubocop.yml
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.1
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in easee.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 13.0"
7
+ gem "rspec", "~> 3.0"
8
+ gem "rubocop", "~> 1.7"
9
+ gem "rubocop-rails", "~> 2.17"
10
+ gem "rubocop-rspec", "~> 2.10"
11
+ gem "timecop", "~> 0.9.6"
12
+ gem "webmock", "~> 3.18"
data/Gemfile.lock ADDED
@@ -0,0 +1,126 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ stekker_easee (0.1.0)
5
+ activemodel
6
+ activesupport
7
+ faraday
8
+ faraday_middleware
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activemodel (7.0.4.3)
14
+ activesupport (= 7.0.4.3)
15
+ activesupport (7.0.4.3)
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ i18n (>= 1.6, < 2)
18
+ minitest (>= 5.1)
19
+ tzinfo (~> 2.0)
20
+ addressable (2.8.1)
21
+ public_suffix (>= 2.0.2, < 6.0)
22
+ ast (2.4.2)
23
+ concurrent-ruby (1.2.2)
24
+ crack (0.4.5)
25
+ 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)
52
+ hashdiff (1.0.1)
53
+ i18n (1.12.0)
54
+ concurrent-ruby (~> 1.0)
55
+ json (2.6.3)
56
+ minitest (5.18.0)
57
+ multipart-post (2.3.0)
58
+ parallel (1.22.1)
59
+ parser (3.2.1.1)
60
+ ast (~> 2.4.1)
61
+ public_suffix (5.0.1)
62
+ rack (3.0.7)
63
+ rainbow (3.1.1)
64
+ rake (13.0.6)
65
+ regexp_parser (2.7.0)
66
+ rexml (3.2.5)
67
+ rspec (3.12.0)
68
+ rspec-core (~> 3.12.0)
69
+ rspec-expectations (~> 3.12.0)
70
+ rspec-mocks (~> 3.12.0)
71
+ rspec-core (3.12.1)
72
+ rspec-support (~> 3.12.0)
73
+ rspec-expectations (3.12.2)
74
+ diff-lcs (>= 1.2.0, < 2.0)
75
+ rspec-support (~> 3.12.0)
76
+ rspec-mocks (3.12.4)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.12.0)
79
+ rspec-support (3.12.0)
80
+ rubocop (1.48.1)
81
+ json (~> 2.3)
82
+ parallel (~> 1.10)
83
+ parser (>= 3.2.0.0)
84
+ rainbow (>= 2.2.2, < 4.0)
85
+ regexp_parser (>= 1.8, < 3.0)
86
+ rexml (>= 3.2.5, < 4.0)
87
+ rubocop-ast (>= 1.26.0, < 2.0)
88
+ ruby-progressbar (~> 1.7)
89
+ unicode-display_width (>= 2.4.0, < 3.0)
90
+ rubocop-ast (1.28.0)
91
+ parser (>= 3.2.1.0)
92
+ rubocop-capybara (2.17.1)
93
+ rubocop (~> 1.41)
94
+ rubocop-rails (2.18.0)
95
+ activesupport (>= 4.2.0)
96
+ rack (>= 1.1)
97
+ rubocop (>= 1.33.0, < 2.0)
98
+ rubocop-rspec (2.19.0)
99
+ rubocop (~> 1.33)
100
+ rubocop-capybara (~> 2.17)
101
+ ruby-progressbar (1.13.0)
102
+ ruby2_keywords (0.0.5)
103
+ timecop (0.9.6)
104
+ tzinfo (2.0.6)
105
+ concurrent-ruby (~> 1.0)
106
+ unicode-display_width (2.4.2)
107
+ webmock (3.18.1)
108
+ addressable (>= 2.8.0)
109
+ crack (>= 0.3.2)
110
+ hashdiff (>= 0.4.0, < 2.0.0)
111
+
112
+ PLATFORMS
113
+ arm64-darwin-22
114
+
115
+ DEPENDENCIES
116
+ rake (~> 13.0)
117
+ rspec (~> 3.0)
118
+ rubocop (~> 1.7)
119
+ rubocop-rails (~> 2.17)
120
+ rubocop-rspec (~> 2.10)
121
+ stekker_easee!
122
+ timecop (~> 0.9.6)
123
+ webmock (~> 3.18)
124
+
125
+ BUNDLED WITH
126
+ 2.4.8
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ require "rubocop/rake_task"
7
+
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: [:spec, :rubocop]
data/bin/console ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require "bundler/setup"
3
+ require "easee"
4
+
5
+ # You can add fixtures and/or initialization code here to make experimenting
6
+ # with your gem easier. You can also use a different console, if you like.
7
+
8
+ # (If you use this, don't forget to add pry to your Gemfile!)
9
+ # require "pry"
10
+ # Pry.start
11
+
12
+ require "irb"
13
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/easee.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ require_relative "lib/easee/version"
2
+ ruby_version = File.read(".ruby-version").strip
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "stekker_easee"
6
+ spec.version = Easee::VERSION
7
+ spec.authors = ["Team Stekker"]
8
+ spec.email = ["support@stekker.com"]
9
+
10
+ spec.summary = "Connect to your Easee charger"
11
+ spec.description = "Easee connector"
12
+ spec.homepage = "https://stekker.com"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= #{ruby_version}")
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/stekker/easee"
17
+ spec.metadata["changelog_uri"] = "https://github.com/stekker/easee"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_runtime_dependency "activemodel"
29
+ spec.add_runtime_dependency "activesupport"
30
+ spec.add_runtime_dependency "faraday"
31
+ spec.add_runtime_dependency "faraday_middleware"
32
+
33
+ spec.metadata["rubygems_mfa_required"] = "true"
34
+ end
@@ -0,0 +1,158 @@
1
+ require "active_support/notifications"
2
+ require "active_support/cache"
3
+
4
+ require_relative "null_encryptor"
5
+
6
+ module Easee
7
+ class Client
8
+ BASE_URL = "https://api.easee.cloud".freeze
9
+ TOKENS_CACHE_KEY = "easee.auth.tokens".freeze
10
+
11
+ def initialize(
12
+ user_name:,
13
+ password:,
14
+ token_cache: ActiveSupport::Cache::MemoryStore.new,
15
+ encryptor: NullEncryptor.new
16
+ )
17
+ @user_name = user_name
18
+ @password = password
19
+ @token_cache = token_cache
20
+ @encryptor = encryptor
21
+ end
22
+
23
+ # https://developer.easee.cloud/reference/post_api-chargers-id-unpair
24
+ def unpair(charger_id:, pin_code:)
25
+ post("/api/chargers/#{charger_id}/unpair", query: { pinCode: pin_code })
26
+ end
27
+
28
+ # https://developer.easee.cloud/reference/post_api-chargers-id-pair
29
+ def pair(charger_id:, pin_code:)
30
+ post("/api/chargers/#{charger_id}/pair", query: { pinCode: pin_code })
31
+ end
32
+
33
+ # https://developer.easee.cloud/reference/get_api-chargers-id-state
34
+ def state(charger_id)
35
+ get("/api/chargers/#{charger_id}/state")
36
+ .then { |response| State.new(response.body) }
37
+ end
38
+
39
+ # https://developer.easee.cloud/reference/post_api-chargers-id-commands-pause-charging
40
+ def pause_charging(charger_id)
41
+ post("/api/chargers/#{charger_id}/commands/pause_charging")
42
+ end
43
+
44
+ # https://developer.easee.cloud/reference/post_api-chargers-id-commands-resume-charging
45
+ def resume_charging(charger_id)
46
+ post("/api/chargers/#{charger_id}/commands/resume_charging")
47
+ end
48
+
49
+ def configuration(charger_id)
50
+ get("/api/chargers/#{charger_id}/config")
51
+ .then { |response| Configuration.new(response.body) }
52
+ end
53
+
54
+ def site(charger_id)
55
+ get("/api/chargers/#{charger_id}/site")
56
+ .then { |response| Site.new(response.body) }
57
+ end
58
+
59
+ def inspect
60
+ <<~INSPECT
61
+ #<#{self.class.name} @user_name="[FILTERED]", @password="[FILTERED]", @token_cache=#{@token_cache.inspect}, @encryptor=#{@encryptor.inspect}>
62
+ INSPECT
63
+ end
64
+
65
+ private
66
+
67
+ attr_reader :user_name, :password
68
+
69
+ def connection
70
+ Faraday.new(url: BASE_URL) do |conn|
71
+ conn.request :json
72
+ conn.response :raise_error
73
+ end
74
+ end
75
+
76
+ def authenticated_connection
77
+ connection.tap do |conn|
78
+ conn.request :authorization, "Bearer", access_token
79
+ conn.response :json, content_type: /\bjson$/
80
+ end
81
+ end
82
+
83
+ def get(endpoint, query = {})
84
+ with_error_handling do
85
+ authenticated_connection.get("#{BASE_URL}#{endpoint}", query)
86
+ end
87
+ end
88
+
89
+ def post(endpoint, body: nil, query: nil)
90
+ with_error_handling do
91
+ authenticated_connection.post("#{BASE_URL}#{endpoint}", body) do |req|
92
+ req.params = query unless query.nil?
93
+ end
94
+ end
95
+ end
96
+
97
+ def with_error_handling
98
+ token_refreshed ||= false
99
+
100
+ yield
101
+ rescue Faraday::UnauthorizedError => e
102
+ if token_refreshed
103
+ raise Errors::RequestFailed.new("Request returned status #{e.response_status}", e.response)
104
+ else
105
+ refresh_access_token!
106
+ token_refreshed = true
107
+
108
+ retry
109
+ end
110
+ rescue Faraday::Error => e
111
+ raise Errors::RequestFailed.new("Request returned status #{e.response_status}", e.response)
112
+ end
113
+
114
+ def access_token
115
+ encrypted_tokens = @token_cache.fetch(TOKENS_CACHE_KEY) do
116
+ @encryptor.encrypt(request_access_token, cipher_options: { deterministic: true })
117
+ end
118
+
119
+ plain_text_tokens = @encryptor.decrypt(encrypted_tokens)
120
+
121
+ JSON.parse(plain_text_tokens).fetch("accessToken")
122
+ end
123
+
124
+ def refresh_access_token!
125
+ @token_cache.write(
126
+ TOKENS_CACHE_KEY,
127
+ @encryptor.encrypt(refresh_access_token, cipher_options: { deterministic: true }),
128
+ expires_in: 1.day,
129
+ )
130
+ rescue Faraday::Error => e
131
+ raise Errors::RequestFailed.new("Request returned status #{e.response_status}", e.response)
132
+ end
133
+
134
+ # https://developer.easee.cloud/reference/post_api-accounts-login
135
+ def request_access_token
136
+ connection
137
+ .post("/api/accounts/login", userName: user_name, password:)
138
+ .then(&:body)
139
+ end
140
+
141
+ # https://developer.easee.cloud/reference/post_api-accounts-refresh-token
142
+ def refresh_access_token
143
+ tokens = JSON.parse(
144
+ @encryptor.decrypt(
145
+ @token_cache.fetch(TOKENS_CACHE_KEY),
146
+ ),
147
+ )
148
+
149
+ connection
150
+ .post(
151
+ "/api/accounts/refresh_token",
152
+ accessToken: tokens.fetch("accessToken"),
153
+ refreshToken: tokens.fetch("refreshToken"),
154
+ )
155
+ .then(&:body)
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,10 @@
1
+ module Easee
2
+ class Configuration
3
+ def initialize(data)
4
+ @data = data.symbolize_keys
5
+ end
6
+
7
+ def phase_mode = @data.fetch(:phaseMode)
8
+ def max_charger_current = @data.fetch(:maxChargerCurrent)
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Easee
2
+ module Errors
3
+ class Base < ::StandardError; end
4
+
5
+ class RequestFailed < Base
6
+ attr_reader :response
7
+
8
+ def initialize(message, response = nil)
9
+ @response = response
10
+ super(message)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Easee
2
+ class MeterReading
3
+ attr_reader :reading_kwh, :timestamp
4
+
5
+ def initialize(reading_kwh:, timestamp:)
6
+ @reading_kwh = reading_kwh
7
+ @timestamp = timestamp
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ module Easee
2
+ # rubocop:disable Lint/UnusedMethodArgument
3
+ class NullEncryptor
4
+ def encrypt(clear_text, key_provider: nil, cipher_options: {})
5
+ clear_text
6
+ end
7
+
8
+ def decrypt(encrypted_text, key_provider: nil, cipher_options: {})
9
+ encrypted_text
10
+ end
11
+
12
+ def encrypted?(text)
13
+ false
14
+ end
15
+ end
16
+ # rubocop:enable Lint/UnusedMethodArgument
17
+ end
data/lib/easee/site.rb ADDED
@@ -0,0 +1,22 @@
1
+ module Easee
2
+ class Site
3
+ def initialize(data)
4
+ @data = data.deep_symbolize_keys
5
+ end
6
+
7
+ def name = @data.fetch(:name)
8
+ def street = address.fetch(:street)
9
+ def building_number = address.fetch(:buildingNumber)
10
+ def zip = address.fetch(:zip)
11
+ def area = address.fetch(:area)
12
+ def country_id = address.fetch(:country).fetch(:id)
13
+ def latitude = address.fetch(:latitude)
14
+ def longitude = address.fetch(:longitude)
15
+
16
+ private
17
+
18
+ def address
19
+ @address ||= @data.fetch(:address)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ module Easee
2
+ class State
3
+ CHARGER_OP_MODES = {
4
+ 0 => :offline,
5
+ 1 => :disconnected,
6
+ 2 => :awaiting_start,
7
+ 3 => :charging,
8
+ 4 => :completed,
9
+ 5 => :error,
10
+ 6 => :ready_to_charge,
11
+ }.freeze
12
+
13
+ def initialize(data)
14
+ @data = data.symbolize_keys
15
+ end
16
+
17
+ def charging? = charger_op_mode == :charging
18
+ def disconnected? = charger_op_mode == :disconnected
19
+ def online? = @data.fetch(:isOnline)
20
+
21
+ def meter_reading
22
+ MeterReading.new(
23
+ reading_kwh: @data.fetch(:lifetimeEnergy),
24
+ timestamp: Time.current,
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ def charger_op_mode = CHARGER_OP_MODES.fetch(@data.fetch(:chargerOpMode))
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Easee
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,15 @@
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
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stekker_easee
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Team Stekker
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-04-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
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
+ description: Easee connector
70
+ email:
71
+ - support@stekker.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".github/workflows/ruby.yml"
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".rubocop.yml"
80
+ - ".ruby-version"
81
+ - Gemfile
82
+ - Gemfile.lock
83
+ - Rakefile
84
+ - bin/console
85
+ - bin/setup
86
+ - easee.gemspec
87
+ - lib/easee/client.rb
88
+ - lib/easee/configuration.rb
89
+ - lib/easee/errors.rb
90
+ - lib/easee/meter_reading.rb
91
+ - lib/easee/null_encryptor.rb
92
+ - lib/easee/site.rb
93
+ - lib/easee/state.rb
94
+ - lib/easee/version.rb
95
+ - lib/stekker_easee.rb
96
+ homepage: https://stekker.com
97
+ licenses: []
98
+ metadata:
99
+ homepage_uri: https://stekker.com
100
+ source_code_uri: https://github.com/stekker/easee
101
+ changelog_uri: https://github.com/stekker/easee
102
+ rubygems_mfa_required: 'true'
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 3.2.1
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.4.6
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Connect to your Easee charger
122
+ test_files: []