Leaner UI spike

This commit is contained in:
SomeoneOnEarth
2023-11-16 19:51:10 +01:00
parent a4db5e5699
commit 8764969537
5 changed files with 305 additions and 21 deletions

View File

@ -23,4 +23,6 @@
<!-- Best be a public URL in order to work away from your home LAN and have a trusted HTTPS certificate -->
<property id="config_url" type="string"></property>
<property id="lean_ui" type="boolean"></property>
</properties>

View File

@ -42,4 +42,13 @@
type="alphaNumeric"
/>
</setting>
<setting
propertyKey="@Properties.lean_ui"
title="More leaner UI"
>
<settingConfig
type="boolean"
/>
</setting>
</settings>

View File

@ -0,0 +1,165 @@
//-----------------------------------------------------------------------------------
//
// Distributed under MIT Licence
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
//
//-----------------------------------------------------------------------------------
//
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
// tested on a Venu 2 device. The source code is provided at:
// https://github.com/house-of-abbey/GarminHomeAssistant.
//
// P A Abbey & J D Abbey, 31 October 2023
//
//
// Description:
//
// Menu button that triggers a service.
//
//-----------------------------------------------------------------------------------
using Toybox.Lang;
using Toybox.WatchUi;
using Toybox.Graphics;
using Toybox.Application.Properties;
class HomeAssistantIconMenuItem extends WatchUi.IconMenuItem {
hidden var mApiKey as Lang.String;
hidden var strNoInternet as Lang.String;
hidden var strApiFlood as Lang.String;
hidden var strApiUrlNotFound as Lang.String;
hidden var strUnhandledHttpErr as Lang.String;
hidden var mService as Lang.String;
function initialize(
label as Lang.String or Lang.Symbol,
subLabel as Lang.String or Lang.Symbol or Null,
identifier as Lang.Object or Null,
service as Lang.String or Null,
icon as Graphics.BitmapType or WatchUi.Drawable,
options as {
:alignment as WatchUi.MenuItem.Alignment
} or Null
) {
strNoInternet = WatchUi.loadResource($.Rez.Strings.NoInternet);
strApiFlood = WatchUi.loadResource($.Rez.Strings.ApiFlood);
strApiUrlNotFound = WatchUi.loadResource($.Rez.Strings.ApiUrlNotFound);
strUnhandledHttpErr = WatchUi.loadResource($.Rez.Strings.UnhandledHttpErr);
mApiKey = Properties.getValue("api_key");
mService = service;
WatchUi.IconMenuItem.initialize(
label,
subLabel,
identifier,
icon,
options
);
}
// Callback function after completing the POST request to call a script.
//
function onReturnExecScript(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String) as Void {
if (Globals.scDebug) {
System.println("HomeAssistantIconMenuItem onReturnExecScript() Response Code: " + responseCode);
System.println("HomeAssistantIconMenuItem onReturnExecScript() Response Data: " + data);
}
if (responseCode == Communications.BLE_QUEUE_FULL) {
if (Globals.scDebug) {
System.println("HomeAssistantIconMenuItem onReturnExecScript() Response Code: BLE_QUEUE_FULL, API calls too rapid.");
}
var cw = WatchUi.getCurrentView();
if (!(cw[0] instanceof ErrorView)) {
// Avoid pushing multiple ErrorViews
WatchUi.pushView(new ErrorView(strApiFlood), new ErrorDelegate(), WatchUi.SLIDE_UP);
}
} else if (responseCode == 404) {
if (Globals.scDebug) {
System.println("HomeAssistantIconMenuItem onReturnExecScript() Response Code: 404, page not found. Check API URL setting.");
}
WatchUi.pushView(new ErrorView(strApiUrlNotFound), new ErrorDelegate(), WatchUi.SLIDE_UP);
} else if (responseCode == 200) {
if (Globals.scDebug) {
System.println("HomeAssistantIconMenuItem onReturnExecScript(): Service executed.");
}
var d = data as Lang.Array;
var toast = "Executed";
for(var i = 0; i < d.size(); i++) {
if ((d[i].get("entity_id") as Lang.String).equals(mIdentifier)) {
toast = (d[i].get("attributes") as Lang.Dictionary).get("friendly_name") as Lang.String;
}
}
if (WatchUi has :showToast) {
WatchUi.showToast(toast, null);
} else {
new Alert({
:timeout => Globals.scAlertTimeout,
:font => Graphics.FONT_MEDIUM,
:text => toast,
:fgcolor => Graphics.COLOR_WHITE,
:bgcolor => Graphics.COLOR_BLACK
}).pushView(WatchUi.SLIDE_IMMEDIATE);
}
} else {
if (Globals.scDebug) {
System.println("HomeAssistantIconMenuItem onReturnExecScript(): Unhandled HTTP response code = " + responseCode);
}
WatchUi.pushView(new ErrorView(strUnhandledHttpErr + responseCode ), new ErrorDelegate(), WatchUi.SLIDE_UP);
}
}
function execScript() as Void {
var options = {
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON,
"Authorization" => "Bearer " + mApiKey
},
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
};
if (System.getDeviceSettings().phoneConnected && System.getDeviceSettings().connectionAvailable) {
// Updated SDK and got a new error
// ERROR: venu: Cannot find symbol ':substring' on type 'PolyType<Null or $.Toybox.Lang.Object>'.
var id = mIdentifier as Lang.String;
if (mService == null) {
var url = (Properties.getValue("api_url") as Lang.String) + "/services/" + id.substring(0, id.find(".")) + "/" + id.substring(id.find(".")+1, null);
if (Globals.scDebug) {
System.println("HomeAssistantIconMenuItem execScript() URL=" + url);
System.println("HomeAssistantIconMenuItem execScript() mIdentifier=" + mIdentifier);
}
Communications.makeWebRequest(
url,
null,
options,
method(:onReturnExecScript)
);
} else {
var url = (Properties.getValue("api_url") as Lang.String) + "/services/" + mService.substring(0, mService.find(".")) + "/" + mService.substring(mService.find(".")+1, null);
if (Globals.scDebug) {
System.println("HomeAssistantIconMenuItem execScript() URL=" + url);
System.println("HomeAssistantIconMenuItem execScript() mService=" + mService);
}
Communications.makeWebRequest(
url,
{
"entity_id" => id
},
options,
method(:onReturnExecScript)
);
}
if (Attention has :vibrate) {
Attention.vibrate([
new Attention.VibeProfile(50, 100), // On for 100ms
new Attention.VibeProfile( 0, 100), // Off for 100ms
new Attention.VibeProfile(50, 100) // On for 100ms
]);
}
} else {
if (Globals.scDebug) {
System.println("HomeAssistantIconMenuItem execScript(): No Internet connection, skipping API call.");
}
WatchUi.pushView(new ErrorView(strNoInternet + "."), new ErrorDelegate(), WatchUi.SLIDE_UP);
}
}
}

View File

@ -18,15 +18,17 @@
//
//-----------------------------------------------------------------------------------
using Toybox.Application;
using Toybox.Lang;
using Toybox.Graphics;
using Toybox.WatchUi;
class HomeAssistantView extends WatchUi.Menu2 {
hidden var strMenuItemTap as Lang.String;
// List of items that need to have their status updated periodically
hidden var mListToggleItems = [];
hidden var mListMenuItems = [];
hidden var mListToggleItems = [];
hidden var mListMenuItems = [];
hidden var mListIconMenuItems = [];
function initialize(
definition as Lang.Dictionary,
@ -36,11 +38,17 @@ class HomeAssistantView extends WatchUi.Menu2 {
:theme as WatchUi.MenuTheme or Null
} or Null
) {
strMenuItemTap = WatchUi.loadResource($.Rez.Strings.MenuItemTap);
var toggle_obj = {
:enabled => WatchUi.loadResource($.Rez.Strings.MenuItemOn) as Lang.String,
:disabled => WatchUi.loadResource($.Rez.Strings.MenuItemOff) as Lang.String
};
var toggle_obj = null;
var strMenuItemTap = null;
if ((Application.Properties.getValue("lean_ui") as Lang.Boolean) == false){
toggle_obj = {
:enabled => WatchUi.loadResource($.Rez.Strings.MenuItemOn) as Lang.String,
:disabled => WatchUi.loadResource($.Rez.Strings.MenuItemOff) as Lang.String
};
strMenuItemTap = WatchUi.loadResource($.Rez.Strings.MenuItemTap);
}
if (options == null) {
options = {
@ -69,19 +77,45 @@ class HomeAssistantView extends WatchUi.Menu2 {
addItem(item);
mListToggleItems.add(item);
} else if (type.equals("tap") && service != null) {
addItem(
new HomeAssistantMenuItem(
name,
strMenuItemTap,
entity,
service,
null
)
);
if ((Application.Properties.getValue("lean_ui") as Lang.Boolean) == true){
var icon = new WatchUi.Bitmap({
:rezId=>Rez.Drawables.ErrorIcon
});
var alignement = {:alignment => WatchUi.MenuItem.MENU_ITEM_LABEL_ALIGN_RIGHT};
addItem(
new HomeAssistantIconMenuItem(
name,
strMenuItemTap,
entity,
service,
icon,
alignement
)
);
} else {
addItem(
new HomeAssistantMenuItem(
name,
strMenuItemTap,
entity,
service,
null
)
);
}
} else if (type.equals("group")) {
var item = new HomeAssistantViewMenuItem(items[i]);
addItem(item);
mListMenuItems.add(item);
if ((Application.Properties.getValue("lean_ui") as Lang.Boolean) == true){
var item = new HomeAssistantViewIconMenuItem(items[i]);
addItem(item);
mListIconMenuItems.add(item);
} else {
var item = new HomeAssistantViewMenuItem(items[i]);
addItem(item);
mListMenuItems.add(item);
}
}
}
}
@ -89,10 +123,17 @@ class HomeAssistantView extends WatchUi.Menu2 {
function getItemsToUpdate() as Lang.Array<HomeAssistantToggleMenuItem> {
var fullList = [];
var lmi = mListMenuItems as Lang.Array<HomeAssistantViewMenuItem>;
for(var i = 0; i < lmi.size(); i++) {
fullList.addAll(lmi[i].getMenuView().getItemsToUpdate());
}
var limi = mListMenuItems as Lang.Array<HomeAssistantViewIconMenuItem>;
for(var i = 0; i < limi.size(); i++) {
fullList.addAll(limi[i].getMenuView().getItemsToUpdate());
}
return fullList.addAll(mListToggleItems);
}
@ -125,6 +166,12 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
System.println(haItem.getLabel() + " " + haItem.getId());
}
haItem.execScript();
} else if (item instanceof HomeAssistantIconMenuItem) {
var haItem = item as HomeAssistantIconMenuItem;
if (Globals.scDebug) {
System.println(haItem.getLabel() + " " + haItem.getId());
}
haItem.execScript();
} else if (item instanceof HomeAssistantViewMenuItem) {
var haMenuItem = item as HomeAssistantViewMenuItem;
if (Globals.scDebug) {
@ -132,6 +179,13 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
}
// No delegate state to be amended, so re-use 'self'.
WatchUi.pushView(haMenuItem.getMenuView(), self, WatchUi.SLIDE_LEFT);
} else if (item instanceof HomeAssistantViewIconMenuItem) {
var haMenuItem = item as HomeAssistantViewIconMenuItem;
if (Globals.scDebug) {
System.println("IconMenu: " + haMenuItem.getLabel() + " " + haMenuItem.getId());
}
// No delegate state to be amended, so re-use 'self'.
WatchUi.pushView(haMenuItem.getMenuView(), self, WatchUi.SLIDE_LEFT);
} else {
if (Globals.scDebug) {
System.println(item.getLabel() + " " + item.getId());
@ -143,4 +197,4 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
WatchUi.popView(WatchUi.SLIDE_RIGHT);
}
}
}

View File

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------------
//
// Distributed under MIT Licence
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
//
//-----------------------------------------------------------------------------------
//
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
// tested on a Venu 2 device. The source code is provided at:
// https://github.com/house-of-abbey/GarminHomeAssistant.
//
// P A Abbey & J D Abbey, 31 October 2023
//
//
// Description:
//
// Menu button that opens a sub-menu.
//
//-----------------------------------------------------------------------------------
using Toybox.Lang;
using Toybox.WatchUi;
class HomeAssistantViewIconMenuItem extends WatchUi.IconMenuItem {
hidden var mMenu as HomeAssistantView;
function initialize(definition as Lang.Dictionary) {
var label = definition.get("name") as Lang.String;
var identifier = definition.get("entity") as Lang.String;
var icon = new WatchUi.Bitmap({
:rezId=>Rez.Drawables.LauncherIcon,
:locX=>WatchUi.LAYOUT_HALIGN_CENTER,
:locY=>WatchUi.LAYOUT_VALIGN_CENTER
});
var alignement = {:alignment => WatchUi.MenuItem.MENU_ITEM_LABEL_ALIGN_RIGHT};
WatchUi.IconMenuItem.initialize(
label,
null,
identifier,
icon,
alignement
);
mMenu = new HomeAssistantView(definition, null);
}
function getMenuView() as HomeAssistantView {
return mMenu;
}
}