mirror of
https://github.com/house-of-abbey/GarminHomeAssistant.git
synced 2025-06-20 05:18:38 +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 -->
|
<!-- 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="config_url" type="string"></property>
|
||||||
|
|
||||||
|
<property id="lean_ui" type="boolean"></property>
|
||||||
</properties>
|
</properties>
|
||||||
|
@ -42,4 +42,13 @@
|
|||||||
type="alphaNumeric"
|
type="alphaNumeric"
|
||||||
/>
|
/>
|
||||||
</setting>
|
</setting>
|
||||||
|
|
||||||
|
<setting
|
||||||
|
propertyKey="@Properties.lean_ui"
|
||||||
|
title="More leaner UI"
|
||||||
|
>
|
||||||
|
<settingConfig
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
|
</setting>
|
||||||
</settings>
|
</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.Lang;
|
||||||
using Toybox.Graphics;
|
using Toybox.Graphics;
|
||||||
using Toybox.WatchUi;
|
using Toybox.WatchUi;
|
||||||
|
|
||||||
class HomeAssistantView extends WatchUi.Menu2 {
|
class HomeAssistantView extends WatchUi.Menu2 {
|
||||||
hidden var strMenuItemTap as Lang.String;
|
|
||||||
// List of items that need to have their status updated periodically
|
// List of items that need to have their status updated periodically
|
||||||
hidden var mListToggleItems = [];
|
hidden var mListToggleItems = [];
|
||||||
hidden var mListMenuItems = [];
|
hidden var mListMenuItems = [];
|
||||||
|
hidden var mListIconMenuItems = [];
|
||||||
|
|
||||||
function initialize(
|
function initialize(
|
||||||
definition as Lang.Dictionary,
|
definition as Lang.Dictionary,
|
||||||
@ -36,11 +38,17 @@ class HomeAssistantView extends WatchUi.Menu2 {
|
|||||||
:theme as WatchUi.MenuTheme or Null
|
:theme as WatchUi.MenuTheme or Null
|
||||||
} or Null
|
} or Null
|
||||||
) {
|
) {
|
||||||
strMenuItemTap = WatchUi.loadResource($.Rez.Strings.MenuItemTap);
|
|
||||||
var toggle_obj = {
|
var toggle_obj = null;
|
||||||
:enabled => WatchUi.loadResource($.Rez.Strings.MenuItemOn) as Lang.String,
|
var strMenuItemTap = null;
|
||||||
:disabled => WatchUi.loadResource($.Rez.Strings.MenuItemOff) as Lang.String
|
|
||||||
};
|
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) {
|
if (options == null) {
|
||||||
options = {
|
options = {
|
||||||
@ -69,19 +77,45 @@ class HomeAssistantView extends WatchUi.Menu2 {
|
|||||||
addItem(item);
|
addItem(item);
|
||||||
mListToggleItems.add(item);
|
mListToggleItems.add(item);
|
||||||
} else if (type.equals("tap") && service != null) {
|
} else if (type.equals("tap") && service != null) {
|
||||||
addItem(
|
if ((Application.Properties.getValue("lean_ui") as Lang.Boolean) == true){
|
||||||
new HomeAssistantMenuItem(
|
|
||||||
name,
|
var icon = new WatchUi.Bitmap({
|
||||||
strMenuItemTap,
|
:rezId=>Rez.Drawables.ErrorIcon
|
||||||
entity,
|
});
|
||||||
service,
|
|
||||||
null
|
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")) {
|
} else if (type.equals("group")) {
|
||||||
var item = new HomeAssistantViewMenuItem(items[i]);
|
if ((Application.Properties.getValue("lean_ui") as Lang.Boolean) == true){
|
||||||
addItem(item);
|
var item = new HomeAssistantViewIconMenuItem(items[i]);
|
||||||
mListMenuItems.add(item);
|
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> {
|
function getItemsToUpdate() as Lang.Array<HomeAssistantToggleMenuItem> {
|
||||||
var fullList = [];
|
var fullList = [];
|
||||||
|
|
||||||
var lmi = mListMenuItems as Lang.Array<HomeAssistantViewMenuItem>;
|
var lmi = mListMenuItems as Lang.Array<HomeAssistantViewMenuItem>;
|
||||||
for(var i = 0; i < lmi.size(); i++) {
|
for(var i = 0; i < lmi.size(); i++) {
|
||||||
fullList.addAll(lmi[i].getMenuView().getItemsToUpdate());
|
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);
|
return fullList.addAll(mListToggleItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +166,12 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
|
|||||||
System.println(haItem.getLabel() + " " + haItem.getId());
|
System.println(haItem.getLabel() + " " + haItem.getId());
|
||||||
}
|
}
|
||||||
haItem.execScript();
|
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) {
|
} else if (item instanceof HomeAssistantViewMenuItem) {
|
||||||
var haMenuItem = item as HomeAssistantViewMenuItem;
|
var haMenuItem = item as HomeAssistantViewMenuItem;
|
||||||
if (Globals.scDebug) {
|
if (Globals.scDebug) {
|
||||||
@ -132,6 +179,13 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
|
|||||||
}
|
}
|
||||||
// No delegate state to be amended, so re-use 'self'.
|
// No delegate state to be amended, so re-use 'self'.
|
||||||
WatchUi.pushView(haMenuItem.getMenuView(), self, WatchUi.SLIDE_LEFT);
|
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 {
|
} else {
|
||||||
if (Globals.scDebug) {
|
if (Globals.scDebug) {
|
||||||
System.println(item.getLabel() + " " + item.getId());
|
System.println(item.getLabel() + " " + item.getId());
|
||||||
@ -143,4 +197,4 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
|
|||||||
WatchUi.popView(WatchUi.SLIDE_RIGHT);
|
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