ruby-tado 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3e1847ff3773eb48da88d21baa51c630b8f535ffb04e6be296c6c44589f1d564
4
+ data.tar.gz: 2d1b7f5a83730ceeb3439b9fa0ecff22391bff8425ba88369b48468f8d373eb4
5
+ SHA512:
6
+ metadata.gz: e68e08a5fe1f854670e25309099cc7d459f57eab2fbec5ac5d7eeb967c29f1cb612e86112a483074dc5ae39a9daa90d58d0eaddbb5ad51643d7ade27d3193cd7
7
+ data.tar.gz: 6332814e981ecf1c9c6ce0630a52eab195393bbfb8a9fe0a1c8c3bd56aaa99dc90eb12f5955dd30c40af7eb764e7909a2366c961f5defd12e7dbc344b209aa9f
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # -*- ruby -*-
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 sdalu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,122 @@
1
+ require 'json'
2
+ require 'date'
3
+ require 'faraday'
4
+ require 'oauth2'
5
+ require 'faraday_middleware/oauth2_refresh'
6
+
7
+
8
+
9
+ #
10
+ # Access to the Tado API
11
+ #
12
+ # Unoffical API documentation is provided by:
13
+ # * https://shkspr.mobi/blog/2019/02/tado-api-guide-updated-for-2019/
14
+ # * http://blog.scphillips.com/posts/2017/01/the-tado-api-v2/
15
+ #
16
+ # Web app client id/secret are retrieved from:
17
+ # * https://my.tado.com/webapp/env.js
18
+ #
19
+ # Example:
20
+ #
21
+ # require 'tado'
22
+ #
23
+ # $t = Tado.new('login', 'password')
24
+ # puts $t.getHomes.first.getZones.first.getHistory.inspect
25
+ #
26
+ class Tado
27
+ # Client ID used the the Tado web application
28
+ CLIENT_ID = 'tado-web-app'
29
+ # Client secret used by the Tado web application
30
+ CLIENT_SECRET = 'wZaRN7rpjn3FoNyF5IFuxg9uMzYJcvOo' \
31
+ 'Q8QWiIqS3hfk6gLhVlG57j5YNoZL2Rtc'
32
+ # Site used sefor authentication
33
+ AUTH_SITE = 'https://auth.tado.com'.freeze
34
+ # Site used for API requests
35
+ API_SITE = 'https://my.tado.com'.freeze
36
+
37
+
38
+ # Name of Tado owner
39
+ #
40
+ # @return [String]
41
+ attr_reader :name
42
+
43
+
44
+ # Email of Tado owner
45
+ #
46
+ # @return [String]
47
+ attr_reader :email
48
+
49
+
50
+ # Username of Tado owner
51
+ #
52
+ # @return [String]
53
+ attr_reader :username
54
+
55
+
56
+ # Create a new instance of Tado
57
+ #
58
+ # @param [String] username account username
59
+ # @param [String] password account password
60
+ # @param [String] client_id client id used for oauth2
61
+ # @param [String] client_secret client secret used for oauth2
62
+ def initialize(username, password,
63
+ client_id: CLIENT_ID, client_secret: CLIENT_SECRET)
64
+ tclient = OAuth2::Client.new(client_id, client_secret, site: AUTH_SITE)
65
+ @token = tclient.password.get_token(username, password)
66
+
67
+ @client = Faraday.new(url: API_SITE) do |f|
68
+ f.request :oauth2_refresh, @token
69
+ f.request :url_encoded
70
+ f.adapter :net_http
71
+ end
72
+
73
+ refresh!
74
+ end
75
+
76
+
77
+ # Retrieve list of homes associated with the tado account
78
+ #
79
+ # @return [Array<Home>] list of homes
80
+ def getHomes
81
+ v2_get('me').dig('homes').map {|h|
82
+ Home.new( h.dig('id'), tado: self )
83
+ }
84
+ end
85
+
86
+ # Shortcut to get zone history from home/zone identifiers.
87
+ # @see Zone#getHistory
88
+ #
89
+ # @param home [Integer] home identifier
90
+ # @param zone [Integer] zone identifier
91
+ # @return [Hash{Symbol => Object}] history of the given day.
92
+ def getHistory(date = Date.today, home:, zone:)
93
+ History.get(date, home: home, zone: zone, tado: self)
94
+ end
95
+
96
+ # Force a refresh of tado attributs
97
+ #
98
+ # @return [self]
99
+ def refresh!
100
+ data = v2_get('me')
101
+ @name = data.dig('name')
102
+ @email = data.dig('email')
103
+ @username = data.dig('username')
104
+ self
105
+ end
106
+
107
+
108
+ # Retrieve a resource as a JSON
109
+ #
110
+ # @param [String] resource relative to the v2 API
111
+ # @return a JSON structure
112
+ def v2_get(resource, **opts)
113
+ JSON.parse(@client.get("/api/v2/#{resource}", **opts).body)
114
+ end
115
+ end
116
+
117
+ require_relative 'tado/version'
118
+ require_relative 'tado/errors'
119
+ require_relative 'tado/home'
120
+ require_relative 'tado/zone'
121
+ require_relative 'tado/device'
122
+ require_relative 'tado/history'
@@ -0,0 +1,107 @@
1
+ class Tado
2
+
3
+ #
4
+ # Class wrapping interaction with the Tado Device
5
+ #
6
+ class Device
7
+ # Create a device instance associated to the zone from
8
+ # the JSON returned by the Tado API
9
+ #
10
+ # @param json [Object] json returned by the Tado API
11
+ # @param zone [Zone ] zone to which this device belongs
12
+ def self.from_json(json, zone:)
13
+ self.new(json.dig('serialNo'),
14
+ data: self.parse_from_json(json),
15
+ zone: zone)
16
+ end
17
+
18
+
19
+ # Serial number of device
20
+ #
21
+ # @return [String] serial number
22
+ attr_reader :serial
23
+
24
+
25
+ # Type of device (heating, airconditioning)
26
+ #
27
+ # @return [:HEATING] heating device
28
+ # @return [Symbol]
29
+ attr_reader :type
30
+
31
+
32
+ # Current firmware version
33
+ #
34
+ # @return [String] firmware version
35
+ attr_reader :firmware
36
+
37
+
38
+ # Duties performed by the device
39
+ #
40
+ # @return [Array<Symbol>] list of duties
41
+ # * [:ZONE_UI ] user interface
42
+ # * [:ZONE_DRIVER] drive the heating/aircon
43
+ # * [:ZONE_LEADER] used as reference for temperature
44
+ attr_reader :duties
45
+
46
+
47
+ # Battery status
48
+ #
49
+ # @return [:NORMAL]
50
+ attr_reader :battery
51
+
52
+
53
+ # Capabilities
54
+ #
55
+ # @return [Array<Symbol>] list of capabilities
56
+ # * [:INSIDE_TEMPERATURE_MEASUREMENT] temperature
57
+ # * [:IDENTIFY ] identify
58
+ attr_reader :capabilities
59
+
60
+
61
+ # Time of device calilbration
62
+ #
63
+ # @return [nil ] device was not calibrated
64
+ # @return [Time] timestamp of device calibration
65
+ attr_reader :calibrated
66
+
67
+
68
+ # Time of last connection
69
+ #
70
+ # @return [nil ] device was not connected
71
+ # @return [Time] timestamp of last device connection
72
+ attr_reader :connection
73
+
74
+
75
+ # Create a device instance associated to the zone
76
+ #
77
+ # @param serial [String] device serial number
78
+ # @param zone [Zone ] zone to which this device belongs
79
+ # @param data [Hash{Symbol => Object}] initialising data
80
+ def initialize(serial, data: nil, zone:)
81
+ @zone = zone
82
+ @serial = serial
83
+
84
+ data&.each {|k, v| instance_variable_set("@#{k}", v) }
85
+ end
86
+
87
+ private
88
+
89
+ def self.parse_from_json(json)
90
+ {
91
+ :type => json.dig('deviceType'),
92
+ :firmware => json.dig('currentFwVersion'),
93
+ :duties => json.dig('duties').map(&:to_sym),
94
+ :battery => json.dig('batteryState').to_sym,
95
+ :capabilities => json.dig('characteristics', 'capabilities')
96
+ .map(&:to_sym),
97
+ :calibrated => if json.dig('mountingState', 'value') == 'CALIBRATED'
98
+ Time.parse(json.dig('mountingState', 'timestamp'))
99
+ end,
100
+ :connection => if json.dig('connectionState', 'value')
101
+ Time.parse(json.dig('connectionState', 'timestamp'))
102
+ end,
103
+ }
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,12 @@
1
+ class Tado
2
+
3
+
4
+ # Standard Tado error
5
+ class Error < StandardError
6
+ end
7
+
8
+ # Error related to the parsing/processing of Tado data
9
+ class ParsingError < Error
10
+ end
11
+
12
+ end
@@ -0,0 +1,92 @@
1
+ class Tado
2
+ module History
3
+ def self.get(date = Date.today, home: nil, zone: nil, tado: nil)
4
+ date = case date
5
+ when Date then date.strftime('%Y-%m-%d')
6
+ when /^\d{4}-\d{2}-\d{2}$/ then date
7
+ else raise ArgumentError
8
+ end
9
+
10
+ report = if Zone === zone
11
+ zone.v2_get("dayReport", date: date)
12
+ elsif Tado === tado && Integer === home && Integer === zone
13
+ tado.v2_get("homes/#{home}/zones/#{zone}/dayReport", date: date)
14
+ else
15
+ raise ArgumentError
16
+ end
17
+ self.parse_from_json(report)
18
+ end
19
+
20
+
21
+ private
22
+
23
+ MAPPING = [ {
24
+ name: [ :weather, :condition ],
25
+ series: [ 'weather', 'condition' ],
26
+ fields: [ 'state',
27
+ [ 'temperature', 'celsius' ] ] },
28
+ { name: [ :weather, :sunny ],
29
+ series: [ 'weather', 'sunny' ] },
30
+ { name: [ :weather, :slots ],
31
+ series: [ 'weather', 'slots' ],
32
+ fields: [ 'state', [ 'temperature', 'celsius' ] ] },
33
+ { name: :settings,
34
+ series: [ 'settings' ],
35
+ fields: [ 'type', 'power',
36
+ [ 'temperature', 'celsius' ] ] },
37
+ { name: [ :measured, :temperature ],
38
+ series: [ 'measuredData', 'insideTemperature' ],
39
+ fields: [ 'celsius' ] },
40
+ { name: [ :measured, :humidity ],
41
+ series: [ 'measuredData', 'humidity' ] },
42
+ { name: [ :measured, :device_connected ],
43
+ series: [ 'measuredData', 'measuringDeviceConnected' ] },
44
+ { name: :stripes,
45
+ series: [ 'stripes' ],
46
+ fields: [ 'stripeType',
47
+ [ 'setting', 'type' ],
48
+ [ 'setting', 'power' ],
49
+ [ 'setting', 'temperature', 'celsius' ] ] },
50
+ { name: :heating,
51
+ series: [ 'callForHeat' ] }
52
+ ]
53
+
54
+
55
+ def self.parse_from_json(json)
56
+ h = {}
57
+ MAPPING.each {|name:, series:, fields: nil|
58
+ e = h
59
+ fields = [ nil ] if fields.nil? || fields.empty?
60
+ name = Array(name)
61
+ name[0..-2].each {|n| e = e[n] ||= {} }
62
+ e[name.last] = History.timeSeries(json.dig(*series), fields)
63
+ }
64
+ h
65
+ end
66
+
67
+
68
+ def self.timeSeries(json, template)
69
+ case type = json.dig('timeSeriesType')
70
+ when 'slots'
71
+ json.dig('slots').transform_values {|v|
72
+ template.map {|fields| v.dig(*fields) }
73
+ }
74
+
75
+ when 'dataIntervals'
76
+ json.dig('dataIntervals').map {|v|
77
+ [ Time.parse(v.dig('from')) .. Time.parse(v.dig('to')) ] +
78
+ template.map {|fields| v.dig('value', *fields) }
79
+ }
80
+
81
+ when 'dataPoints'
82
+ json.dig('dataPoints').map {|v|
83
+ [ Time.parse(v.dig('timestamp')) ] +
84
+ template.map {|fields| v.dig('value', *fields) }
85
+ }
86
+
87
+ else raise ParsingError
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,142 @@
1
+ require 'time'
2
+ require 'tzinfo'
3
+
4
+ class Tado
5
+
6
+ #
7
+ # Class wrapping interaction with the Tado Home
8
+ #
9
+ class Home
10
+ # Create a new Home associated to the Tado account
11
+ #
12
+ # @param id [Integer]
13
+ # @param tado [Tado]
14
+ def initialize(id, tado:)
15
+ @tado = tado
16
+ @id = id
17
+
18
+ refresh!
19
+ end
20
+
21
+
22
+ # Retrieve weather information associated with this home
23
+ #
24
+ # @return [Hash<Symbol,Object>] weather information
25
+ # * :solar_intensity [Float]
26
+ # * :temperature [Float]
27
+ # * :state [Symbol]
28
+ # * :timestamp [Time]
29
+ def getWeather
30
+ w = v2_get('weather')
31
+
32
+ ts = [ 'solarIntensity', 'outsideTemperature', 'weatherState' ]
33
+ .map {|k| w.dig(k, 'timestamp') }.uniq
34
+ raise ParsingError if ts.size != 1
35
+
36
+ { :solar_intensity => w.dig('solarIntensity', 'percentage'),
37
+ :temperature => w.dig('outsideTemperature', 'celsius'),
38
+ :state => w.dig('weatherState', 'value').to_sym,
39
+ :timestamp => Time.parse(ts.first)
40
+ }
41
+ end
42
+
43
+
44
+ # Retrieve zones associated with this home
45
+ #
46
+ # @return [Array<Zone>] list of zones
47
+ def getZones
48
+ v2_get('zones').map{|data|
49
+ Zone.from_json(data, home: self)
50
+ }
51
+ end
52
+
53
+
54
+ # Home identifier
55
+ #
56
+ # @return [Integer] identifier
57
+ attr_reader :id
58
+
59
+
60
+ # Timezone
61
+ #
62
+ # @return [TZInfo::Timezone] timezone
63
+ attr_reader :tz
64
+
65
+
66
+ # Creation time
67
+ #
68
+ # @return [Time] timestamp
69
+ attr_reader :created
70
+
71
+
72
+ # Name of home
73
+ #
74
+ # @return [String] name
75
+ attr_reader :name
76
+
77
+
78
+ # Geolocalisation (GPS coordinate)
79
+ #
80
+ # @return [Array<Numeric>] latitude/longitude
81
+ attr_reader :geoloc
82
+
83
+
84
+ # Distance to consider user away from home
85
+ #
86
+ # @return [Float] distance in meters
87
+ attr_reader :away_radius
88
+
89
+
90
+ # Home address
91
+ #
92
+ # @return [Hash{Symbol => String,Array<String>}] home address
93
+ # * :address [Array<String>] address
94
+ # * :zipcode [String] zip code
95
+ # * :city [String] city
96
+ # * :state [String] state
97
+ # * :country [String] country (iso3)
98
+ attr_reader :address
99
+
100
+
101
+ # Home contact
102
+ #
103
+ # @return [Hash{Symbol => String}] contact information
104
+ # * :name [String] contact name
105
+ # * :email [String] email address
106
+ # * :phone [String] phone number
107
+ attr_reader :contact
108
+
109
+
110
+ # Force a refresh of home attributs
111
+ #
112
+ # @return [self]
113
+ def refresh!
114
+ data = @tado.v2_get("homes/#{@id}")
115
+ @tz = TZInfo::Timezone.get(data.dig('dateTimeZone'))
116
+ @created = Time.parse(data.dig('dateCreated'))
117
+ @name = data.dig('name')
118
+ @geoloc = [ data.dig('geolocation', 'latitude'),
119
+ data.dig('geolocation', 'longitude') ]
120
+ @away_radius = data.dig('awayRadiusInMeters')
121
+ @address = {
122
+ :address => [ data.dig('address', 'addressLine1'),
123
+ data.dig('address', 'addressLine2') ].compact,
124
+ :zipcode => data.dig('address', 'zipCode'),
125
+ :city => data.dig('address', 'city'),
126
+ :state => data.dig('address', 'state'),
127
+ :country => data.dig('address', 'country')
128
+ }.compact
129
+ @contact = data.dig('contactDetails').transform_keys(&:to_sym)
130
+ self
131
+ end
132
+
133
+
134
+ # Retrieve a resource as a JSON
135
+ #
136
+ # @param [String] resource relative to the home
137
+ # @return a JSON structure
138
+ def v2_get(resource, **opts)
139
+ @tado.v2_get("homes/#{@id}/#{resource}", **opts)
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,4 @@
1
+ class Tado
2
+ # Version of Tado ruby implementation
3
+ VERSION = '0.1.0'
4
+ end
@@ -0,0 +1,126 @@
1
+ class Tado
2
+ #
3
+ # Class wrapping interaction with the Tado Zone
4
+ #
5
+ class Zone
6
+ # Create a zone instance associated to the home from
7
+ # the JSON returned by the Tado API
8
+ #
9
+ # @param json [Object] json returned by the Tado API
10
+ # @param home [Home ] home to which this zone belongs
11
+ def self.from_json(json, home:)
12
+ self.new(json.dig('id'),
13
+ data: self.parse_from_json(json),
14
+ home: home)
15
+ end
16
+
17
+
18
+ # Zone identifier
19
+ #
20
+ # @return [Integer] identifier
21
+ attr_reader :id
22
+
23
+
24
+ # Name of zone
25
+ #
26
+ # @return [String] name
27
+ attr_reader :name
28
+
29
+
30
+ # Type of zone (heating, airconditioning)
31
+ #
32
+ # @return [:HEATING] heating zone
33
+ # @return [Symbol]
34
+ attr_reader :type
35
+
36
+
37
+ # Devices associated with this zone
38
+ #
39
+ # @return [Array<Device>] list of devices
40
+ attr_reader :devices
41
+
42
+
43
+ # Dazze mode
44
+ #
45
+ # @return [nil] not supported
46
+ # @return [Boolean] is dazzle mode enabled
47
+ attr_reader :dazzle
48
+
49
+
50
+ # Open window detection
51
+ #
52
+ # @return [nil] not supported
53
+ # @return [Boolean] is window detection enabled
54
+ attr_reader :open_window
55
+
56
+
57
+ # Open window timeout
58
+ #
59
+ # @return [nil] not supported
60
+ # @return [Integer] is window detection enabled
61
+ attr_reader :open_window_timeout
62
+
63
+
64
+ # Create a zone instance associated to the home
65
+ #
66
+ # @param id [String] home identifier
67
+ # @param home [Home ] home to which this zone belongs
68
+ # @param data [Hash{Symbol => Object}] initialising data
69
+ def initialize(id, data:, home:)
70
+ @home = home
71
+ @id = id
72
+
73
+ data&.each {|k, v| instance_variable_set("@#{k}", v) }
74
+ end
75
+
76
+
77
+ # Retrieve history (temperature, humidity, ...) for this zone
78
+ #
79
+ # @param date [String] date in yyyy-mm-dd format
80
+ # @return [Hash{Symbol => Object}] history of the given day.
81
+ # For example:
82
+ # { :wheather => {
83
+ # :condition => [ [ timeFrom..timeTo, state, temperature ]... ],
84
+ # :sunny => [ [ timeFrom..timeTo, is_sunny ]... ],
85
+ # :slots => { 'hh:mm' => [ state, temperature ] } },
86
+ # :settings => [ [ timeFrom..timeTo, type, powered, temperature ]... ],
87
+ # :measure => {
88
+ # :temperature => [ [ time, temperature ]... ],
89
+ # :humidity => [ [ time, humidity ]... ],
90
+ # :device_connected => [ [ timeFrom..timeTo, is_connected ]... ] },
91
+ # :stripes => [ [ timeFrom..timeTo, mode, type, is_powerd, temperature ]... ]
92
+ # }
93
+ #
94
+ def getHistory(date = Date.today)
95
+ History.get(date, zone: self)
96
+ end
97
+
98
+
99
+ # Retrieve a resource as a JSON
100
+ #
101
+ # @param [String] resource relative to the zone
102
+ # @return a JSON structure
103
+ def v2_get(resource, **opts)
104
+ @home.v2_get("zones/#{@id}/#{resource}", **opts)
105
+ end
106
+
107
+
108
+ private
109
+
110
+ def self.parse_from_json(json)
111
+ supported_enabled = ->(d) {
112
+ d.dig('supported') ? d.dig('enabled') : nil }
113
+ {
114
+ :name => json.dig('name'),
115
+ :type => json.dig('type'),
116
+ :created => Time.parse(json.dig('dateCreated')),
117
+ :devices => json.dig('devices').map {|data|
118
+ Device.from_json(data, zone: self) },
119
+ :dazzle => supported_enabled.(json.dig('dazzleMode')),
120
+ :open_window => supported_enabled.(json.dig('openWindowDetection')),
121
+ :open_window_timeout => json.dig('openWindowDetection', 'timeoutInSeconds'),
122
+ }
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.unshift File.expand_path("../lib", __FILE__)
3
+ require "tado/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ruby-tado"
7
+ s.version = Tado::VERSION
8
+ s.authors = [ "Stephane D'Alu" ]
9
+ s.email = [ "sdalu@sdalu.com" ]
10
+ s.homepage = "http://gitlab.com/sdalu/ruby-tado"
11
+ s.summary = "Access to Tado API"
12
+
13
+ s.add_dependency 'faraday'
14
+ s.add_dependency 'oauth2'
15
+ s.add_dependency 'faraday_middleware-oauth2_refresh'
16
+ s.add_dependency 'tzinfo'
17
+ s.add_dependency 'tzinfo-data'
18
+
19
+
20
+ s.add_development_dependency "yard"
21
+ s.add_development_dependency "rake"
22
+ s.add_development_dependency "redcarpet"
23
+
24
+ s.license = 'MIT'
25
+
26
+ s.files = %w[ LICENSE Gemfile ruby-tado.gemspec ] +
27
+ Dir['lib/**/*.rb']
28
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-tado
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stephane D'Alu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-07-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
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: oauth2
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_middleware-oauth2_refresh
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: tzinfo
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
+ - !ruby/object:Gem::Dependency
70
+ name: tzinfo-data
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: redcarpet
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description:
126
+ email:
127
+ - sdalu@sdalu.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - Gemfile
133
+ - LICENSE
134
+ - lib/tado.rb
135
+ - lib/tado/device.rb
136
+ - lib/tado/errors.rb
137
+ - lib/tado/history.rb
138
+ - lib/tado/home.rb
139
+ - lib/tado/version.rb
140
+ - lib/tado/zone.rb
141
+ - ruby-tado.gemspec
142
+ homepage: http://gitlab.com/sdalu/ruby-tado
143
+ licenses:
144
+ - MIT
145
+ metadata: {}
146
+ post_install_message:
147
+ rdoc_options: []
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubygems_version: 3.0.4
162
+ signing_key:
163
+ specification_version: 4
164
+ summary: Access to Tado API
165
+ test_files: []