tesla_api 3.0.0 → 3.0.5
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 +4 -4
- data/docs/README.md +1 -1
- data/docs/SUMMARY.md +35 -34
- data/docs/api-basics/vehicles.md +5 -6
- data/docs/miscellaneous/endpoints.md +29 -4
- data/docs/vehicle/commands/README.md +11 -3
- data/docs/vehicle/commands/climate.md +21 -0
- data/docs/vehicle/commands/homelink.md +21 -0
- data/docs/vehicle/commands/sharing.md +38 -0
- data/docs/vehicle/commands/valet.md +7 -5
- data/docs/vehicle/commands/wake.md +4 -1
- data/docs/vehicle/commands/windows.md +26 -0
- data/docs/vehicle/optioncodes.md +22 -6
- data/docs/vehicle/state/chargestate.md +1 -0
- data/docs/vehicle/state/climatestate.md +1 -1
- data/docs/vehicle/state/data.md +27 -7
- data/docs/vehicle/state/guisettings.md +1 -0
- data/docs/vehicle/state/vehiclestate.md +46 -15
- data/lib/tesla_api.rb +4 -2
- data/lib/tesla_api/autopark.rb +23 -41
- data/lib/tesla_api/client.rb +14 -7
- data/lib/tesla_api/stream.rb +40 -34
- data/lib/tesla_api/vehicle.rb +49 -1
- data/lib/tesla_api/version.rb +1 -1
- data/spec/cassettes/client-login_timeout.yml +83 -0
- data/spec/cassettes/vehicle-media_next_fav.yml +109 -0
- data/spec/cassettes/vehicle-media_next_track.yml +109 -0
- data/spec/cassettes/vehicle-media_prev_fav.yml +109 -0
- data/spec/cassettes/vehicle-media_prev_track.yml +109 -0
- data/spec/cassettes/vehicle-media_toggle_playback.yml +109 -0
- data/spec/cassettes/vehicle-media_volume_down.yml +109 -0
- data/spec/cassettes/vehicle-media_volume_up.yml +109 -0
- data/spec/cassettes/vehicle-vehicle_config.yml +108 -0
- data/spec/lib/tesla_api/client_spec.rb +30 -0
- data/spec/lib/tesla_api/vehicle_spec.rb +57 -0
- data/tesla_api.gemspec +1 -1
- metadata +30 -10
- data/docs/vehicle/commands/navigation.md +0 -36
@@ -43,6 +43,7 @@ Information on the state of charge in the battery and its various settings.
|
|
43
43
|
"managed_charging_start_time": null,
|
44
44
|
"managed_charging_user_canceled": false,
|
45
45
|
"max_range_charge_counter": 0,
|
46
|
+
"minutes_to_full_charge": 0,
|
46
47
|
"not_enough_power_to_heat": false,
|
47
48
|
"scheduled_charging_pending": false,
|
48
49
|
"scheduled_charging_start_time": null,
|
@@ -12,6 +12,7 @@ Information on the current internal temperature and climate control system.
|
|
12
12
|
"battery_heater": false,
|
13
13
|
"battery_heater_no_power": false,
|
14
14
|
"climate_keeper_mode": "dog",
|
15
|
+
"defrost_mode": 0,
|
15
16
|
"driver_temp_setting": 21.6,
|
16
17
|
"fan_status": 0,
|
17
18
|
"inside_temp": null,
|
@@ -35,7 +36,6 @@ Information on the current internal temperature and climate control system.
|
|
35
36
|
"seat_heater_rear_right_back": 0,
|
36
37
|
"seat_heater_right": 2,
|
37
38
|
"side_mirror_heaters": false,
|
38
|
-
"smart_preconditioning": false,
|
39
39
|
"steering_wheel_heater": false,
|
40
40
|
"timestamp": 1543187641727,
|
41
41
|
"wiper_blade_heater": false
|
data/docs/vehicle/state/data.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
A rollup of all the `data_request` endpoints plus vehicle configuration.
|
6
6
|
|
7
|
-
|
7
|
+
_Note:_ all `*_range` values are in miles, irrespective of GUI configuration.
|
8
8
|
|
9
9
|
### Response
|
10
10
|
|
@@ -23,7 +23,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
|
|
23
23
|
"in_service": false,
|
24
24
|
"id_s": "12345678901234567",
|
25
25
|
"calendar_enabled": true,
|
26
|
-
"api_version":
|
26
|
+
"api_version": 7,
|
27
27
|
"backseat_token": null,
|
28
28
|
"backseat_token_updated_at": null,
|
29
29
|
"drive_state": {
|
@@ -44,6 +44,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
|
|
44
44
|
"battery_heater": false,
|
45
45
|
"battery_heater_no_power": false,
|
46
46
|
"climate_keeper_mode": "dog",
|
47
|
+
"defrost_mode": 0,
|
47
48
|
"driver_temp_setting": 21.6,
|
48
49
|
"fan_status": 0,
|
49
50
|
"inside_temp": null,
|
@@ -67,7 +68,6 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
|
|
67
68
|
"seat_heater_rear_right_back": 0,
|
68
69
|
"seat_heater_right": 2,
|
69
70
|
"side_mirror_heaters": false,
|
70
|
-
"smart_preconditioning": false,
|
71
71
|
"steering_wheel_heater": false,
|
72
72
|
"timestamp": 1543186971731,
|
73
73
|
"wiper_blade_heater": false
|
@@ -107,6 +107,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
|
|
107
107
|
"managed_charging_start_time": null,
|
108
108
|
"managed_charging_user_canceled": false,
|
109
109
|
"max_range_charge_counter": 0,
|
110
|
+
"minutes_to_full_charge": 0,
|
110
111
|
"not_enough_power_to_heat": false,
|
111
112
|
"scheduled_charging_pending": false,
|
112
113
|
"scheduled_charging_start_time": null,
|
@@ -122,34 +123,51 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
|
|
122
123
|
"gui_distance_units": "mi/hr",
|
123
124
|
"gui_range_display": "Rated",
|
124
125
|
"gui_temperature_units": "F",
|
126
|
+
"show_range_units": true,
|
125
127
|
"timestamp": 1543186971728
|
126
128
|
},
|
127
129
|
"vehicle_state": {
|
128
|
-
"api_version":
|
130
|
+
"api_version": 7,
|
129
131
|
"autopark_state_v2": "standby",
|
130
132
|
"autopark_style": "standard",
|
131
133
|
"calendar_supported": true,
|
132
|
-
"car_version": "
|
134
|
+
"car_version": "2019.40.2.1 38f55d9f9205",
|
133
135
|
"center_display_state": 0,
|
134
136
|
"df": 0,
|
135
137
|
"dr": 0,
|
138
|
+
"fd_window": 0,
|
139
|
+
"fp_window": 0,
|
136
140
|
"ft": 0,
|
141
|
+
"homelink_device_count": 0,
|
137
142
|
"homelink_nearby": true,
|
138
143
|
"is_user_present": false,
|
139
144
|
"last_autopark_error": "no_error",
|
140
145
|
"locked": true,
|
141
|
-
"media_state": {
|
146
|
+
"media_state": {
|
147
|
+
"remote_control_enabled": true
|
148
|
+
},
|
142
149
|
"notifications_supported": true,
|
143
150
|
"odometer": 33561.422505,
|
144
151
|
"parsed_calendar_supported": true,
|
145
152
|
"pf": 0,
|
146
153
|
"pr": 0,
|
154
|
+
"rd_window": 0,
|
147
155
|
"remote_start": false,
|
148
156
|
"remote_start_enabled": true,
|
149
157
|
"remote_start_supported": true,
|
158
|
+
"rp_window": 0,
|
150
159
|
"rt": 0,
|
151
160
|
"sentry_mode": true,
|
152
|
-
"
|
161
|
+
"sentry_mode_available": true,
|
162
|
+
"smart_summon_available": true,
|
163
|
+
"software_update": {
|
164
|
+
"download_perc": 100,
|
165
|
+
"expected_duration_sec": 2700,
|
166
|
+
"install_perc": 10,
|
167
|
+
"scheduled_time_ms": 1575689678432,
|
168
|
+
"status": "scheduled",
|
169
|
+
"version": "2019.40.2.1"
|
170
|
+
},
|
153
171
|
"speed_limit_mode": {
|
154
172
|
"active": false,
|
155
173
|
"current_limit_mph": 75.0,
|
@@ -157,6 +175,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
|
|
157
175
|
"min_limit_mph": 50,
|
158
176
|
"pin_code_set": false
|
159
177
|
},
|
178
|
+
"summon_standby_mode_enabled": true,
|
160
179
|
"sun_roof_percent_open": 0,
|
161
180
|
"sun_roof_state": "unknown",
|
162
181
|
"timestamp": 1538364666096,
|
@@ -188,6 +207,7 @@ A rollup of all the `data_request` endpoints plus vehicle configuration.
|
|
188
207
|
"third_row_seats": "None",
|
189
208
|
"timestamp": 1538364666096,
|
190
209
|
"trim_badging": "p90d",
|
210
|
+
"use_range_badging": false,
|
191
211
|
"wheel_type": "AeroTurbine19"
|
192
212
|
}
|
193
213
|
}
|
@@ -4,36 +4,75 @@
|
|
4
4
|
|
5
5
|
Returns the vehicle's physical state, such as which doors are open.
|
6
6
|
|
7
|
+
For the trunk (rt) and frunk (ft) fields, you should interpret a zero (0) value as closed and a non-zero value as open (partially or fully).
|
8
|
+
|
9
|
+
Here are the currently known values for the `center_display_state` field:
|
10
|
+
|
11
|
+
| State | Description |
|
12
|
+
| ----- | --------------- |
|
13
|
+
| 0 | Off |
|
14
|
+
| 2 | Normal On |
|
15
|
+
| 3 | Charging Screen |
|
16
|
+
| 7 | Sentry Mode |
|
17
|
+
| 8 | Dog Mode |
|
18
|
+
|
19
|
+
Here are the descriptions for the shorthand fields:
|
20
|
+
|
21
|
+
| Field | Description |
|
22
|
+
| ----- | --------------- |
|
23
|
+
| df | driver front |
|
24
|
+
| dr | driver rear |
|
25
|
+
| pf | passenger front |
|
26
|
+
| pr | passenger rear |
|
27
|
+
| ft | front trunk |
|
28
|
+
| rt | rear trunk |
|
29
|
+
|
7
30
|
### Response
|
8
31
|
|
9
32
|
```json
|
10
33
|
{
|
11
34
|
"response": {
|
12
|
-
"api_version":
|
13
|
-
"
|
14
|
-
"autopark_style": "
|
35
|
+
"api_version": 7,
|
36
|
+
"autopark_state_v3": "standby",
|
37
|
+
"autopark_style": "dead_man",
|
15
38
|
"calendar_supported": true,
|
16
|
-
"car_version": "
|
39
|
+
"car_version": "2019.40.2.1 38f55d9f9205",
|
17
40
|
"center_display_state": 0,
|
18
41
|
"df": 0,
|
19
42
|
"dr": 0,
|
43
|
+
"fd_window": 0,
|
44
|
+
"fp_window": 0,
|
20
45
|
"ft": 0,
|
46
|
+
"homelink_device_count": 0,
|
21
47
|
"homelink_nearby": true,
|
22
48
|
"is_user_present": false,
|
23
49
|
"last_autopark_error": "no_error",
|
24
50
|
"locked": true,
|
25
|
-
"media_state": {
|
51
|
+
"media_state": {
|
52
|
+
"remote_control_enabled": true
|
53
|
+
},
|
26
54
|
"notifications_supported": true,
|
27
55
|
"odometer": 36051.517239,
|
28
56
|
"parsed_calendar_supported": true,
|
29
57
|
"pf": 0,
|
30
58
|
"pr": 0,
|
59
|
+
"rd_window": 0,
|
31
60
|
"remote_start": false,
|
32
61
|
"remote_start_enabled": true,
|
33
62
|
"remote_start_supported": true,
|
63
|
+
"rp_window": 0,
|
34
64
|
"rt": 0,
|
35
65
|
"sentry_mode": true,
|
36
|
-
"
|
66
|
+
"sentry_mode_available": true,
|
67
|
+
"smart_summon_available": true,
|
68
|
+
"software_update": {
|
69
|
+
"download_perc": 100,
|
70
|
+
"expected_duration_sec": 2700,
|
71
|
+
"install_perc": 10,
|
72
|
+
"scheduled_time_ms": 1575689678432,
|
73
|
+
"status": "scheduled",
|
74
|
+
"version": "2019.40.2.1"
|
75
|
+
},
|
37
76
|
"speed_limit_mode": {
|
38
77
|
"active": false,
|
39
78
|
"current_limit_mph": 50.0,
|
@@ -41,6 +80,7 @@ Returns the vehicle's physical state, such as which doors are open.
|
|
41
80
|
"min_limit_mph": 50,
|
42
81
|
"pin_code_set": false
|
43
82
|
},
|
83
|
+
"summon_standby_mode_enabled": true,
|
44
84
|
"sun_roof_percent_open": 0,
|
45
85
|
"sun_roof_state": "unknown",
|
46
86
|
"timestamp": 1543187581934,
|
@@ -50,12 +90,3 @@ Returns the vehicle's physical state, such as which doors are open.
|
|
50
90
|
}
|
51
91
|
}
|
52
92
|
```
|
53
|
-
|
54
|
-
### Center Display States
|
55
|
-
| State | Description |
|
56
|
-
|-------|-----------------|
|
57
|
-
| 0 | Off |
|
58
|
-
| 2 | Normal On |
|
59
|
-
| 3 | Charging Screen |
|
60
|
-
| 7 | Sentry Mode |
|
61
|
-
| 8 | Dog Mode |
|
data/lib/tesla_api.rb
CHANGED
@@ -3,8 +3,10 @@ require 'base64'
|
|
3
3
|
|
4
4
|
require 'faraday'
|
5
5
|
require 'faraday_middleware'
|
6
|
-
|
7
|
-
require '
|
6
|
+
|
7
|
+
require 'async'
|
8
|
+
require 'async/http/endpoint'
|
9
|
+
require 'async/websocket/client'
|
8
10
|
|
9
11
|
require 'tesla_api/version'
|
10
12
|
require 'tesla_api/client'
|
data/lib/tesla_api/autopark.rb
CHANGED
@@ -1,60 +1,42 @@
|
|
1
1
|
module TeslaApi
|
2
2
|
module Autopark
|
3
3
|
def start_autopark(&handler)
|
4
|
-
|
5
|
-
|
6
|
-
message =
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
4
|
+
Async do |task|
|
5
|
+
Async::WebSocket::Client.connect(autopark_endpoint, headers: headers) do |connection|
|
6
|
+
while message = connection.read
|
7
|
+
case message[:msg_type]
|
8
|
+
when 'control:hello'
|
9
|
+
interval = message[:autopark][:heartbeat_frequency] / 1000.0
|
10
|
+
task.async do |subtask|
|
11
|
+
subtask.sleep interval
|
12
|
+
connection.write({ msg_type: 'autopark:heartbeat_app', timestamp: Time.now.to_i }.to_json)
|
13
|
+
end
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
@heartbeat && @heartbeat.cancel
|
19
|
-
EventMachine.stop
|
16
|
+
handler.call(message)
|
17
|
+
end
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
24
22
|
private
|
25
23
|
|
26
|
-
def
|
27
|
-
|
28
|
-
when 'control:hello'
|
29
|
-
interval = message['autopark']['heartbeat_frequency'] / 1000.0
|
30
|
-
@heartbeat = EventMachine::Timer.new(interval) do
|
31
|
-
beat = {
|
32
|
-
msg_type: 'autopark:heartbeat_app',
|
33
|
-
timestamp: Time.now.to_i
|
34
|
-
}
|
35
|
-
autopark_socket.send(beat.to_json)
|
36
|
-
end
|
37
|
-
end
|
24
|
+
def autopark_endpoint
|
25
|
+
Async::HTTP::Endpoint.parse(autopark_endpoint_url)
|
38
26
|
end
|
39
27
|
|
40
|
-
def
|
41
|
-
|
42
|
-
autopark_socket_endpoint,
|
43
|
-
nil,
|
44
|
-
{
|
45
|
-
headers: {
|
46
|
-
'Authorization' => "Basic #{socket_auth}"
|
47
|
-
}
|
48
|
-
}
|
49
|
-
)
|
28
|
+
def autopark_endpoint_url
|
29
|
+
"wss://streaming.vn.teslamotors.com/connect/#{self['vehicle_id']}"
|
50
30
|
end
|
51
31
|
|
52
|
-
def
|
53
|
-
|
32
|
+
def autopark_headers
|
33
|
+
{
|
34
|
+
'Authorization' => "Basic #{socket_auth}"
|
35
|
+
}
|
54
36
|
end
|
55
37
|
|
56
|
-
def
|
57
|
-
"
|
38
|
+
def autopark_socket_auth
|
39
|
+
Base64.strict_encode64("#{email}:#{self['tokens'].first}")
|
58
40
|
end
|
59
41
|
end
|
60
42
|
end
|
data/lib/tesla_api/client.rb
CHANGED
@@ -2,7 +2,7 @@ module TeslaApi
|
|
2
2
|
class Client
|
3
3
|
attr_reader :api, :email, :access_token, :access_token_expires_at, :refresh_token, :client_id, :client_secret
|
4
4
|
|
5
|
-
BASE_URI = 'https://owner-api.teslamotors.com
|
5
|
+
BASE_URI = 'https://owner-api.teslamotors.com'
|
6
6
|
|
7
7
|
def initialize(
|
8
8
|
email: nil,
|
@@ -10,9 +10,13 @@ module TeslaApi
|
|
10
10
|
access_token_expires_at: nil,
|
11
11
|
refresh_token: nil,
|
12
12
|
client_id: ENV['TESLA_CLIENT_ID'],
|
13
|
-
client_secret: ENV['TESLA_CLIENT_SECRET']
|
13
|
+
client_secret: ENV['TESLA_CLIENT_SECRET'],
|
14
|
+
retry_options: nil,
|
15
|
+
base_uri: nil,
|
16
|
+
client_options: {}
|
14
17
|
)
|
15
18
|
@email = email
|
19
|
+
@base_uri = base_uri || BASE_URI
|
16
20
|
|
17
21
|
@client_id = client_id
|
18
22
|
@client_secret = client_secret
|
@@ -22,19 +26,22 @@ module TeslaApi
|
|
22
26
|
@refresh_token = refresh_token
|
23
27
|
|
24
28
|
@api = Faraday.new(
|
25
|
-
|
26
|
-
|
29
|
+
@base_uri + '/api/1',
|
30
|
+
{
|
31
|
+
headers: { 'User-Agent' => "github.com/timdorr/tesla-api v:#{VERSION}" }
|
32
|
+
}.merge(client_options)
|
27
33
|
) do |conn|
|
28
34
|
conn.request :json
|
29
35
|
conn.response :json
|
30
36
|
conn.response :raise_error
|
37
|
+
conn.request :retry, retry_options if retry_options # Must be registered after :raise_error
|
31
38
|
conn.adapter Faraday.default_adapter
|
32
39
|
end
|
33
40
|
end
|
34
41
|
|
35
42
|
def refresh_access_token
|
36
43
|
response = api.post(
|
37
|
-
'
|
44
|
+
@base_uri + '/oauth/token',
|
38
45
|
{
|
39
46
|
grant_type: 'refresh_token',
|
40
47
|
client_id: client_id,
|
@@ -52,7 +59,7 @@ module TeslaApi
|
|
52
59
|
|
53
60
|
def login!(password)
|
54
61
|
response = api.post(
|
55
|
-
'
|
62
|
+
@base_uri + '/oauth/token',
|
56
63
|
{
|
57
64
|
grant_type: 'password',
|
58
65
|
client_id: client_id,
|
@@ -87,7 +94,7 @@ module TeslaApi
|
|
87
94
|
end
|
88
95
|
|
89
96
|
def vehicle(id)
|
90
|
-
Vehicle.new(self, email, id, self.
|
97
|
+
Vehicle.new(self, email, id, self.get("/vehicles/#{id}")['response'])
|
91
98
|
end
|
92
99
|
end
|
93
100
|
end
|
data/lib/tesla_api/stream.rb
CHANGED
@@ -1,54 +1,60 @@
|
|
1
1
|
module TeslaApi
|
2
2
|
module Stream
|
3
3
|
def stream(&receiver)
|
4
|
-
|
5
|
-
|
4
|
+
Async do |task|
|
5
|
+
Async::WebSocket::Client.connect(streaming_endpoint) do |connection|
|
6
|
+
on_timeout = ->(subtask) do
|
7
|
+
subtask.sleep TIMEOUT
|
8
|
+
task.stop
|
9
|
+
end
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
end
|
11
|
+
connection.write(streaming_connect_message)
|
12
|
+
timeout = task.async(&on_timeout)
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
time: DateTime.strptime((attributes[0].to_i/1000).to_s, '%s'),
|
19
|
-
speed: attributes[1].to_f,
|
20
|
-
odometer: attributes[2].to_f,
|
21
|
-
soc: attributes[3].to_f,
|
22
|
-
elevation: attributes[4].to_f,
|
23
|
-
est_heading: attributes[5].to_f,
|
24
|
-
est_lat: attributes[6].to_f,
|
25
|
-
est_lng: attributes[7].to_f,
|
26
|
-
power: attributes[8].to_f,
|
27
|
-
shift_state: attributes[9].to_s,
|
28
|
-
range: attributes[10].to_f,
|
29
|
-
est_range: attributes[11].to_f,
|
30
|
-
heading: attributes[12].to_f
|
31
|
-
})
|
32
|
-
end
|
33
|
-
end
|
14
|
+
while message = connection.read
|
15
|
+
timeout.stop
|
16
|
+
timeout = task.async(&on_timeout)
|
17
|
+
|
18
|
+
case message[:msg_type]
|
19
|
+
when 'data:update'
|
20
|
+
attributes = message[:value].split(',')
|
34
21
|
|
35
|
-
|
36
|
-
|
22
|
+
receiver.call({
|
23
|
+
time: DateTime.strptime((attributes[0].to_i/1000).to_s, '%s'),
|
24
|
+
speed: attributes[1].to_f,
|
25
|
+
odometer: attributes[2].to_f,
|
26
|
+
soc: attributes[3].to_f,
|
27
|
+
elevation: attributes[4].to_f,
|
28
|
+
est_heading: attributes[5].to_f,
|
29
|
+
est_lat: attributes[6].to_f,
|
30
|
+
est_lng: attributes[7].to_f,
|
31
|
+
power: attributes[8].to_f,
|
32
|
+
shift_state: attributes[9].to_s,
|
33
|
+
range: attributes[10].to_f,
|
34
|
+
est_range: attributes[11].to_f,
|
35
|
+
heading: attributes[12].to_f
|
36
|
+
})
|
37
|
+
when 'data:error'
|
38
|
+
task.stop
|
39
|
+
end
|
40
|
+
end
|
37
41
|
end
|
38
42
|
end
|
39
43
|
end
|
40
44
|
|
41
45
|
private
|
42
46
|
|
43
|
-
|
44
|
-
Faye::WebSocket::Client.new(streaming_endpoint)
|
45
|
-
end
|
47
|
+
TIMEOUT = 30
|
46
48
|
|
47
49
|
def streaming_endpoint
|
50
|
+
Async::HTTP::Endpoint.parse(streaming_endpoint_url)
|
51
|
+
end
|
52
|
+
|
53
|
+
def streaming_endpoint_url
|
48
54
|
'wss://streaming.vn.teslamotors.com/streaming/'
|
49
55
|
end
|
50
56
|
|
51
|
-
def
|
57
|
+
def streaming_connect_message
|
52
58
|
{
|
53
59
|
msg_type: 'data:subscribe',
|
54
60
|
token: Base64.strict_encode64("#{email}:#{self['tokens'].first}"),
|