2929 */
3030
3131public class SnapshotState extends APICall {
32- // Class Variables
32+
33+ /*------------------------------------------------------------------------------
34+ *
35+ * Constants and Enums
36+ *
37+ *----------------------------------------------------------------------------*/
38+ private enum Keys {
39+ timestamp , odometer , speed , soc , elevation , est_heading ,
40+ est_lat , est_lng , power , shift_state , range };
41+ private final Keys [] keyList = Keys .values ();
42+ private final String allKeys =
43+ StringUtils .join (keyList , ',' , 1 , keyList .length );
44+
45+ private static final String endpointFormat =
46+ "https://streaming.vn.teslamotors.com/stream/%s/?values=%s" ;
3347
34- //
35- // Field Accessor Methods
36- //
48+ private static final int WakeupRetries = 3 ;
49+ private static final int ReadTimeoutInMillis = 1 * 1000 ;
50+
51+ /*------------------------------------------------------------------------------
52+ *
53+ * Internal State
54+ *
55+ *----------------------------------------------------------------------------*/
56+
57+ private Vehicle vehicleWithToken = null ;
58+ private BufferedReader reader = null ;
59+
3760
61+ /*==============================================================================
62+ * ------- -------
63+ * ------- Public Interface To This Class -------
64+ * ------- -------
65+ *============================================================================*/
66+
67+ // Accessors
3868 public Date timestamp () {return (new Date (getLong (Keys .timestamp ))); }
3969 public double speed () { return (getDouble (Keys .speed )); }
4070 public double odometer () {return (getDouble (Keys .odometer )); }
@@ -46,131 +76,141 @@ public class SnapshotState extends APICall {
4676 public int power () { return (getInteger (Keys .power )); }
4777 public String shiftState () { return (getString (Keys .shift_state )); }
4878 public int range () { return (getInteger (Keys .range )); }
79+ public String getStateName () { return "Unstreamed State" ; }
4980
5081
51- //
5282 // Constructors
53- //
54-
5583 public SnapshotState (Vehicle v ) {
5684 super (v );
5785 }
5886
5987
60- // Accessors
61- public String getStateName () { return "Unstreamed State" ; }
62-
63- // Update Methods
88+ /*------------------------------------------------------------------------------
89+ *
90+ * Methods overridden from APICall
91+ *
92+ *----------------------------------------------------------------------------*/
6493
6594 public boolean refresh () {
66- BufferedReader reader = prepareToProduce ();
67- JSONObject val ;
68- if (reader != null && (val = produce (reader )) != null ) {
69- setState (val );
70- return true ;
95+ reader = null ;
96+ return refreshStream ();
97+ }
98+
99+ public boolean refreshStream () {
100+ // We need to be prepared to try twice just in case the reader went
101+ // stale on us since the last refresh()
102+ for (int i = 0 ; i < 2 ; i ++) {
103+ prepare ();
104+ JSONObject val ;
105+ if (reader != null && (val = produce (reader )) != null ) {
106+ setState (val );
107+ return true ;
108+ }
109+ reader = null ;
71110 }
72111 invalidate ();
73112 return false ;
74113 }
75114
76115
77- //
78- // Override Methods
79- //
116+ /*------------------------------------------------------------------------------
117+ *
118+ * Methods overridden from Object
119+ *
120+ *----------------------------------------------------------------------------*/
121+
80122
81123 public String toString () {
82124 return String .format (
83- "Time Stamp: %s\n " +
125+ "Time Stamp: %s.%s \n " +
84126 "Speed: %3.1f\n " +
85127 "Location: [(Lat: %f, Lng: %3f), Heading: %d, Elevation: %d]\n " +
86128 "Charge Info: [SoC: %d, Power: %d]\n " +
87129 "Odometer: %7.1f\n " +
88130 "Range: %d\n " ,
89- timestamp ().getTime (),
131+ timestamp ().getTime ()/ 1000 , timestamp (). getTime ()% 1000 ,
90132 speed (),
91133 estLat (), estLng (), estHeading (), elevation (),
92134 soc (), power (),
93135 odometer (), range ()
94136 // Don't know what shift_state is! Always seems to be null
95137 );
96138 }
97-
98139
99- public enum Keys {
100- timestamp , odometer , speed , soc , elevation , est_heading ,
101- est_lat , est_lng , power , shift_state , range };
102-
103- private static final String endpointFormat =
104- "https://streaming.vn.teslamotors.com/stream/%s/?values=%s" ;
105-
106- // Instance Variables
107- private Keys [] keyList = Keys .values ();
108- private final String allKeys =
109- StringUtils .join (keyList , ',' , 1 , keyList .length );
140+ /*------------------------------------------------------------------------------
141+ *
142+ * Methods for setting up the Streaming connection and reading the data
143+ *
144+ *----------------------------------------------------------------------------*/
145+
146+ private JSONObject produce (BufferedReader reader ) {
147+ try {
148+ String line = reader .readLine ();
149+ if (line == null ) return null ;
110150
111-
112- private JSONObject produce (BufferedReader reader ) {
113- try {
114- String line = reader .readLine ();
115- if (line == null ) return null ;
116-
117- JSONObject jo = new JSONObject ();
118- String vals [] = line .split ("," );
119- for (int i = 0 ; i < keyList .length ; i ++) {
120- try {
121- jo .put (keyList [i ], vals [i ]);
122- } catch (JSONException ex ) {
123- Tesla .logger .log (Level .SEVERE , "Malformed data" , ex );
124- return null ;
125- }
151+ JSONObject jo = new JSONObject ();
152+ String vals [] = line .split ("," );
153+ for (int i = 0 ; i < keyList .length ; i ++) {
154+ try {
155+ jo .put (keyList [i ], vals [i ]);
156+ } catch (JSONException ex ) {
157+ Tesla .logger .log (Level .SEVERE , "Malformed data" , ex );
158+ return null ;
126159 }
127- return jo ;
128- } catch (IOException ex ) {
129- Tesla .logger .log (Level .FINEST , "Timeouts are expected here..." , ex );
130- return null ;
131160 }
161+ return jo ;
162+ } catch (IOException ex ) {
163+ Tesla .logger .log (Level .FINEST , "Timeouts are expected here..." , ex );
164+ return null ;
132165 }
166+ }
133167
134- private BufferedReader prepareToProduce () {
135- BufferedReader r = prepareInternal ();
136- if (r != null ) return r ;
137- return prepareInternal (); // Try again. Auth tokens may have expired.
138- }
168+ private void prepare () {
169+ prepareInternal ();
170+ if (reader != null ) return ;
171+ prepareInternal (); // Try again. Auth tokens may have expired.
172+ }
139173
140- private Vehicle vehicleWithToken = null ;
141174
142- private BufferedReader prepareInternal () {
143- if (vehicleWithToken == null ) {
144- vehicleWithToken = getVehicleWithAuthToken (v );
145- if (vehicleWithToken == null )
146- return null ;
147- }
175+ private void prepareInternal () {
176+ if (reader != null ) return ;
177+
148178
149- try {
150- String endpoint = String .format (
151- endpointFormat , vehicleWithToken .getStreamingVID (), allKeys );
152-
153- honorRateLimit ();
154- requestCount ++; // Count it even if it fails...
155- TextResource r = getAuthAPI (vehicleWithToken ).text (endpoint );
156- return new BufferedReader (new InputStreamReader (r .stream ()));
157- } catch (IOException ex ) {
158- // Timed out or other problem
159- Tesla .logger .log (Level .INFO , "Failed getting streaming data. HANDLED." , ex );
160- }
161- vehicleWithToken = null ; // Tokens may have expired, force refetch
162- return null ;
179+ if (vehicleWithToken == null ) {
180+ vehicleWithToken = getVehicleWithAuthToken (v );
181+ if (vehicleWithToken == null ) return ;
163182 }
183+
184+ String endpoint = String .format (
185+ endpointFormat , vehicleWithToken .getStreamingVID (), allKeys );
186+
187+ honorRateLimit ();
188+ requestCount ++; // Count it even if it fails...
164189
190+ try {
191+ TextResource r = getAuthAPI (vehicleWithToken ).text (endpoint );
192+ reader = new BufferedReader (new InputStreamReader (r .stream ()));
193+ } catch (IOException ex ) {
194+ // Timed out or other problem
195+ Tesla .logger .log (Level .INFO , "Failed getting streaming data. HANDLED." , ex .getMessage ());
196+ vehicleWithToken = null ; // Tokens may have expired, force refetch
197+ reader = null ;
198+ }
199+ }
200+
201+ /*------------------------------------------------------------------------------
202+ *
203+ * This section contains the methods and classes necessary to get auth
204+ * tokens and create an authenticated connection to Tesla
205+ *
206+ *----------------------------------------------------------------------------*/
165207
166208 private void setAuthHeader (Resty api , String username , String authToken ) {
167209 byte [] authString = (username + ":" + authToken ).getBytes ();
168210 String encodedString = Base64 .encodeBase64String (authString );
169211 api .withHeader ("Authorization" , "Basic " + encodedString );
170212 }
171213
172- private static final int WakeupRetries = 3 ;
173- private static final int ReadTimeoutInMillis = 5 * 1000 ;
174214
175215 private Vehicle getVehicleWithAuthToken (Vehicle basedOn ) {
176216 String vid = basedOn .getVID ();
0 commit comments