ruby-tado 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/lib/tado.rb +122 -0
- data/lib/tado/device.rb +107 -0
- data/lib/tado/errors.rb +12 -0
- data/lib/tado/history.rb +92 -0
- data/lib/tado/home.rb +142 -0
- data/lib/tado/version.rb +4 -0
- data/lib/tado/zone.rb +126 -0
- data/ruby-tado.gemspec +28 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -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
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.
|
data/lib/tado.rb
ADDED
@@ -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'
|
data/lib/tado/device.rb
ADDED
@@ -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
|
data/lib/tado/errors.rb
ADDED
data/lib/tado/history.rb
ADDED
@@ -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
|
data/lib/tado/home.rb
ADDED
@@ -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
|
data/lib/tado/version.rb
ADDED
data/lib/tado/zone.rb
ADDED
@@ -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
|
data/ruby-tado.gemspec
ADDED
@@ -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: []
|