mirror of
https://github.com/house-of-abbey/GarminHomeAssistant.git
synced 2025-05-03 06:02:27 +00:00
Leaner UI spike
This commit is contained in:
@ -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>
|
||||
|
@ -42,4 +42,13 @@
|
||||
type="alphaNumeric"
|
||||
/>
|
||||
</setting>
|
||||
|
||||
<setting
|
||||
propertyKey="@Properties.lean_ui"
|
||||
title="More leaner UI"
|
||||
>
|
||||
<settingConfig
|
||||
type="boolean"
|
||||
/>
|
||||
</setting>
|
||||
</settings>
|
||||
|
165
source/HomeAssistantIconMenuItem.mc
Normal file
165
source/HomeAssistantIconMenuItem.mc
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
54
source/HomeAssistantViewIconMenuItem.mc
Normal file
54
source/HomeAssistantViewIconMenuItem.mc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user