diff --git a/resources/settings/properties.xml b/resources/settings/properties.xml
index f2e3a27..0829069 100644
--- a/resources/settings/properties.xml
+++ b/resources/settings/properties.xml
@@ -23,4 +23,6 @@
+
+
diff --git a/resources/settings/settings.xml b/resources/settings/settings.xml
index 1ea3ca6..1b0ef4a 100644
--- a/resources/settings/settings.xml
+++ b/resources/settings/settings.xml
@@ -42,4 +42,13 @@
type="alphaNumeric"
/>
+
+
+
+
diff --git a/source/HomeAssistantIconMenuItem.mc b/source/HomeAssistantIconMenuItem.mc
new file mode 100644
index 0000000..853cd1b
--- /dev/null
+++ b/source/HomeAssistantIconMenuItem.mc
@@ -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'.
+ 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);
+ }
+ }
+
+}
diff --git a/source/HomeAssistantView.mc b/source/HomeAssistantView.mc
index fe74847..4c26d4b 100644
--- a/source/HomeAssistantView.mc
+++ b/source/HomeAssistantView.mc
@@ -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 {
var fullList = [];
+
var lmi = mListMenuItems as Lang.Array;
for(var i = 0; i < lmi.size(); i++) {
fullList.addAll(lmi[i].getMenuView().getItemsToUpdate());
}
+
+ var limi = mListMenuItems as Lang.Array;
+ 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);
}
-}
\ No newline at end of file
+}
diff --git a/source/HomeAssistantViewIconMenuItem.mc b/source/HomeAssistantViewIconMenuItem.mc
new file mode 100644
index 0000000..f2156b3
--- /dev/null
+++ b/source/HomeAssistantViewIconMenuItem.mc
@@ -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;
+ }
+
+}