@@ -35,10 +35,8 @@ class HomeAssistantApp extends Application.AppBase {
35
35
private var mUpdateTimer as Timer.Timer or Null;
36
36
// Array initialised by onReturnFetchMenuConfig()
37
37
private var mItemsToUpdate as Lang.Array<HomeAssistantToggleMenuItem or HomeAssistantTemplateMenuItem> or Null;
38
- private var mNextItemToUpdate as Lang.Number = 0 ; // Index into the above array
39
38
private var mIsGlance as Lang.Boolean = false ;
40
39
private var mIsApp as Lang.Boolean = false ; // Or Widget
41
- private var mIsInitUpdateCompl as Lang.Boolean = false ;
42
40
private var mUpdating as Lang.Boolean = false ; // Don't start a second chain of updates
43
41
44
42
function initialize () {
@@ -262,15 +260,129 @@ class HomeAssistantApp extends Application.AppBase {
262
260
mQuitTimer .begin ();
263
261
}
264
262
263
+ var mTemplates as Lang.Dictionary = {};
265
264
function startUpdates () {
266
265
if (mHaMenu != null and ! mUpdating ) {
267
266
mItemsToUpdate = mHaMenu .getItemsToUpdate ();
268
267
// Start the continuous update process that continues for as long as the application is running.
269
- // The chain of functions from 'updateNextMenuItem()' calls 'updateNextMenuItem()' on completion.
270
- if (mItemsToUpdate .size () > 0 ) {
271
- mUpdating = true ;
272
- updateNextMenuItemInternal ();
268
+ mTemplates = {};
269
+ for (var i = 0 ; i < mItemsToUpdate .size (); i ++ ) {
270
+ var item = mItemsToUpdate [i ];
271
+ var template = item .buildTemplate ();
272
+ if (template != null ) {
273
+ mTemplates .put (i .toString (), {
274
+ "template" => template
275
+ });
276
+ }
277
+ if (item instanceof HomeAssistantToggleMenuItem ) {
278
+ mTemplates .put (i .toString () + " t" , {
279
+ "template" => (item as HomeAssistantToggleMenuItem).buildToggleTemplate()
280
+ });
281
+ }
273
282
}
283
+ updateMenuItems ();
284
+ }
285
+ }
286
+
287
+ function onReturnUpdateMenuItems (responseCode as Lang .Number , data as Null or Lang .Dictionary ) as Void {
288
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: " + responseCode);
289
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Data: " + data);
290
+
291
+ var status = WatchUi .loadResource ($ .Rez .Strings .Unavailable ) as Lang .String ;
292
+ switch (responseCode ) {
293
+ case Communications .BLE_HOST_TIMEOUT :
294
+ case Communications .BLE_CONNECTION_UNAVAILABLE :
295
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: BLE_HOST_TIMEOUT or BLE_CONNECTION_UNAVAILABLE, Bluetooth connection severed.");
296
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .NoPhone ) as Lang .String + " ." );
297
+ break ;
298
+
299
+ case Communications .BLE_QUEUE_FULL :
300
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: BLE_QUEUE_FULL, API calls too rapid.");
301
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .ApiFlood ) as Lang .String );
302
+ break ;
303
+
304
+ case Communications .NETWORK_REQUEST_TIMED_OUT :
305
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: NETWORK_REQUEST_TIMED_OUT, check Internet connection.");
306
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .NoResponse ) as Lang .String );
307
+ break ;
308
+
309
+ case Communications .INVALID_HTTP_BODY_IN_NETWORK_RESPONSE :
310
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: INVALID_HTTP_BODY_IN_NETWORK_RESPONSE, check JSON is returned.");
311
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .NoJson ) as Lang .String );
312
+ break ;
313
+
314
+ case Communications .NETWORK_RESPONSE_OUT_OF_MEMORY :
315
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: NETWORK_RESPONSE_OUT_OF_MEMORY, are we going too fast?");
316
+ var myTimer = new Timer .Timer ();
317
+ // Now this feels very "closely coupled" to the application, but it is the most reliable method instead of using a timer.
318
+ myTimer .start (method (:updateMenuItems ), Globals .scApiBackoff , false );
319
+ // Revert status
320
+ status = getApiStatus ();
321
+ break ;
322
+
323
+ case 404 :
324
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: 404, page not found. Check API URL setting.");
325
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .ApiUrlNotFound ) as Lang .String );
326
+ break ;
327
+
328
+ case 400 :
329
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: 400, bad request. Template error.");
330
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .TemplateError ) as Lang .String );
331
+ break ;
332
+
333
+ case 200 :
334
+ status = WatchUi .loadResource ($ .Rez .Strings .Available ) as Lang .String ;
335
+ for (var i = 0 ; i < mItemsToUpdate .size (); i ++ ) {
336
+ var item = mItemsToUpdate [i ];
337
+ var state = data .get (i .toString ());
338
+ item .updateState (state );
339
+ if (item instanceof HomeAssistantToggleMenuItem ) {
340
+ (item as HomeAssistantToggleMenuItem ).updateToggleState (data .get (i .toString () + " t" ));
341
+ }
342
+ }
343
+ var delay = Settings .getPollDelay ();
344
+ if (delay > 0 ) {
345
+ mUpdateTimer .start (method (:updateMenuItems ), delay , false );
346
+ } else {
347
+ updateMenuItems ();
348
+ }
349
+ break ;
350
+
351
+ default :
352
+ // System.println("HomeAssistantApp onReturnUpdateMenuItems(): Unhandled HTTP response code = " + responseCode);
353
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .UnhandledHttpErr ) as Lang .String + responseCode );
354
+ }
355
+ setApiStatus (status );
356
+ }
357
+
358
+ function updateMenuItems () as Void {
359
+ if (! System .getDeviceSettings ().phoneConnected ) {
360
+ // System.println("HomeAssistantApp updateMenuItems(): No Phone connection, skipping API call.");
361
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .NoPhone ) as Lang .String + " ." );
362
+ setApiStatus (WatchUi .loadResource ($ .Rez .Strings .Unavailable ) as Lang .String );
363
+ } else if (! System .getDeviceSettings ().connectionAvailable ) {
364
+ // System.println("HomeAssistantApp updateMenuItems(): No Internet connection, skipping API call.");
365
+ ErrorView .show (WatchUi .loadResource ($ .Rez .Strings .NoInternet ) as Lang .String + " ." );
366
+ setApiStatus (WatchUi .loadResource ($ .Rez .Strings .Unavailable ) as Lang .String );
367
+ } else {
368
+ // https://developers.home-assistant.io/docs/api/native-app-integration/sending-data/#render-templates
369
+ var url = Settings .getApiUrl () + " /webhook/" + Settings .getWebhookId ();
370
+ // System.println("HomeAssistantApp updateMenuItems() URL=" + url + ", Template='" + mTemplate + "'");
371
+ Communications .makeWebRequest (
372
+ url ,
373
+ {
374
+ "type" => "render_template",
375
+ "data" => mTemplates
376
+ },
377
+ {
378
+ :method => Communications .HTTP_REQUEST_METHOD_POST ,
379
+ :headers => {
380
+ "Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
381
+ },
382
+ :responseType => Communications .HTTP_RESPONSE_CONTENT_TYPE_JSON
383
+ },
384
+ method (:onReturnUpdateMenuItems )
385
+ );
274
386
}
275
387
}
276
388
@@ -403,45 +515,14 @@ class HomeAssistantApp extends Application.AppBase {
403
515
WatchUi .pushView (mHaMenu , new HomeAssistantViewDelegate (true ), WatchUi .SLIDE_IMMEDIATE );
404
516
}
405
517
406
- function updateNextMenuItem () as Void {
407
- var delay = Settings .getPollDelay ();
408
- if (mIsInitUpdateCompl and (delay > 0 ) and (mNextItemToUpdate == 0 )) {
409
- mUpdateTimer .start (method (:updateNextMenuItemInternal ), delay , false );
410
- } else {
411
- updateNextMenuItemInternal ();
412
- }
413
- }
414
-
415
518
// Only call this function if Settings.getPollDelay() > 0. This must be tested locally as it is then efficient to take
416
519
// alternative action if the test fails.
417
520
function forceStatusUpdates () as Void {
418
521
// Don't mess with updates unless we are using a timer.
419
522
if (Settings .getPollDelay () > 0 ) {
420
523
mUpdateTimer .stop ();
421
- mIsInitUpdateCompl = false ;
422
- // Start from the beginning, or we will only get a partial round of updates before mIsInitUpdateCompl is flipped.
423
- mNextItemToUpdate = 0 ;
424
524
// For immediate updates
425
- updateNextMenuItem ();
426
- }
427
- }
428
-
429
- // We need to spread out the API calls so as not to overload the results queue and cause Communications.BLE_QUEUE_FULL
430
- // (-101) error. This function is called by a timer every Globals.menuItemUpdateInterval ms.
431
- function updateNextMenuItemInternal () as Void {
432
- if (mItemsToUpdate != null ) {
433
- // System.println("HomeAssistantApp updateNextMenuItemInternal(): Doing update for item " + mNextItemToUpdate + ", mIsInitUpdateCompl=" + mIsInitUpdateCompl);
434
- mItemsToUpdate [mNextItemToUpdate ].getState ();
435
- // mNextItemToUpdate = (mNextItemToUpdate + 1) % mItemsToUpdate.size() - But with roll-over detection
436
- if (mNextItemToUpdate == mItemsToUpdate .size ()- 1 ) {
437
- // Last item completed return to the start of the list
438
- mNextItemToUpdate = 0 ;
439
- mIsInitUpdateCompl = true ;
440
- } else {
441
- mNextItemToUpdate ++ ;
442
- }
443
- // } else {
444
- // System.println("HomeAssistantApp updateNextMenuItemInternal(): No menu items to update");
525
+ updateMenuItems ();
445
526
}
446
527
}
447
528
0 commit comments