Compare commits

...

8 Commits

Author SHA1 Message Date
Philip Abbey
6db7b67536 Update HISTORY.md
Added v2.31 text
2025-07-11 09:43:30 +01:00
Philip Abbey
e5df010af8 253 compilation failed on fenix5s (#254)
This is the fix to an annoying problem I faced as I was about to compile
the final v2.31 release. Please approve, but **don't merge** just yet.
I'll do that once the full device list compilation succeeds, just to
check if there are any residual issues.

Thanks
2025-07-11 09:31:37 +01:00
Philip Abbey
ee964ce882 Update HomeAssistantMenuItemFactory.mc
Function documentation fix.
2025-07-11 01:11:00 +01:00
Philip Abbey
ee9da24592 Fix for too many arguments to a function call on some devices 2025-07-11 01:06:15 +01:00
Philip Abbey
906cdf7371 Code documentation comments were not updated 2025-07-10 23:54:05 +01:00
__JosephAbbey
7dd85937fa 243 option to close application after command (#252)
Also closes #250.
2025-07-10 22:02:12 +01:00
Philip Abbey
d141c03104 Review comment
Moved point of application exit for the menu item option.
2025-07-10 19:46:04 +01:00
Philip Abbey
842a31a1cc Review comment
enable => enabled
2025-07-10 18:24:30 +01:00
7 changed files with 151 additions and 88 deletions

View File

@@ -43,3 +43,4 @@
| 2.28 | Added support for Vivoactive 6 device which also required an SDK update to 8.1.0. |
| 2.29 | Added support for three new devices, Forerunners 570 42mm & 47mm and 970. |
| 2.30 | <img src="images/Venu2_glance_default.png" width="200" title="Default Glance"/><br/>Extensive re-work of the [Glance](examples/Glance.md) view, including the ability to customise it with a user supplied template. |
| 2.31 | Adding [two new options](./examples/Actions.md#exit-on-tap) to the menu items: 1) The ability to disable a menu item, e.g. temporarily for seasonal changes, 2) The option to exit after a menu item has been select. |

View File

@@ -48,8 +48,8 @@
},
"additionalProperties": false
},
"enable": {
"$ref": "#/$defs/enable"
"enabled": {
"$ref": "#/$defs/enabled"
},
"exit": {
"$ref": "#/$defs/exit"
@@ -82,8 +82,8 @@
"title": "Schema change:",
"description": "Use 'info' or 'tap' instead."
},
"enable": {
"$ref": "#/$defs/enable"
"enabled": {
"$ref": "#/$defs/enabled"
}
},
"required": ["name", "content", "type"],
@@ -111,8 +111,8 @@
"tap_action": {
"$ref": "#/$defs/tap_action"
},
"enable": {
"$ref": "#/$defs/enable"
"enabled": {
"$ref": "#/$defs/enabled"
},
"exit": {
"$ref": "#/$defs/exit"
@@ -136,8 +136,8 @@
"$ref": "#/$defs/type",
"const": "info"
},
"enable": {
"$ref": "#/$defs/enable"
"enabled": {
"$ref": "#/$defs/enabled"
}
},
"required": ["name", "content", "type"],
@@ -168,8 +168,8 @@
"tap_action": {
"$ref": "#/$defs/tap_action"
},
"enable": {
"$ref": "#/$defs/enable"
"enabled": {
"$ref": "#/$defs/enabled"
},
"exit": {
"$ref": "#/$defs/exit"
@@ -206,8 +206,8 @@
"items": {
"$ref": "#/$defs/items"
},
"enable": {
"$ref": "#/$defs/enable"
"enabled": {
"$ref": "#/$defs/enabled"
}
},
"required": ["name", "title", "type", "items"],
@@ -321,7 +321,7 @@
}
]
},
"enable": {
"enabled": {
"type": "boolean",
"default": true,
"title": "Enable the menu item",

View File

@@ -70,25 +70,27 @@ class HomeAssistantMenuItemFactory {
//! @param label Menu item label.
//! @param entity_id Home Assistant Entity ID (optional)
//! @param template Template for Home Assistant to render (optional)
//! @param confirm Should this menu item selection be confirmed?
//! @param pin Should this menu item selection request the security PIN?
//! @param options Menu item options to be passed on, including both SDK and menu options, e.g. exit, confirm & pin.
//
function toggle(
label as Lang.String or Lang.Symbol,
entity_id as Lang.String or Null,
template as Lang.String or Null,
exit as Lang.Boolean,
confirm as Lang.Boolean,
pin as Lang.Boolean
options as {
:exit as Lang.Boolean,
:confirm as Lang.Boolean,
:pin as Lang.Boolean
}
) as WatchUi.MenuItem {
var keys = mMenuItemOptions.keys();
for (var i = 0; i < keys.size(); i++) {
options.put(keys[i], mMenuItemOptions.get(keys[i]));
}
return new HomeAssistantToggleMenuItem(
label,
template,
{ "entity_id" => entity_id },
exit,
confirm,
pin,
mMenuItemOptions
options
);
}
@@ -98,9 +100,8 @@ class HomeAssistantMenuItemFactory {
//! @param entity_id Home Assistant Entity ID (optional)
//! @param template Template for Home Assistant to render (optional)
//! @param service Template for Home Assistant to render (optional)
//! @param confirm Should this menu item selection be confirmed?
//! @param pin Should this menu item selection request the security PIN?
//! @param data Sourced from the menu JSON, this is the `data` field from the `tap_action` field.
//! @param options Menu item options to be passed on, including both SDK and menu options, e.g. exit, confirm & pin.
//
function tap(
label as Lang.String or Lang.Symbol,
@@ -108,9 +109,11 @@ class HomeAssistantMenuItemFactory {
template as Lang.String or Null,
service as Lang.String or Null,
data as Lang.Dictionary or Null,
exit as Lang.Boolean,
confirm as Lang.Boolean,
pin as Lang.Boolean
options as {
:exit as Lang.Boolean,
:confirm as Lang.Boolean,
:pin as Lang.Boolean
}
) as WatchUi.MenuItem {
if (entity_id != null) {
if (data == null) {
@@ -119,30 +122,28 @@ class HomeAssistantMenuItemFactory {
data.put("entity_id", entity_id);
}
}
var keys = mMenuItemOptions.keys();
for (var i = 0; i < keys.size(); i++) {
options.put(keys[i], mMenuItemOptions.get(keys[i]));
}
if (service != null) {
options.put(:icon, mTapTypeIcon);
return new HomeAssistantTapMenuItem(
label,
template,
service,
data,
exit,
confirm,
pin,
mTapTypeIcon,
mMenuItemOptions,
options,
mHomeAssistantService
);
} else {
options.put(:icon, mInfoTypeIcon);
return new HomeAssistantTapMenuItem(
label,
template,
service,
null,
data,
exit,
confirm,
pin,
mInfoTypeIcon,
mMenuItemOptions,
options,
mHomeAssistantService
);
}

View File

@@ -46,7 +46,13 @@ class HomeAssistantService {
data as Null or Lang.Dictionary or Lang.String,
context as Lang.Object
) as Void {
var entity_id = context as Lang.String or Null;
var c = context as Lang.Dictionary;
var entity_id;
var exit = false;
if (c != null) {
entity_id = c.get(:entity_id) as Lang.String;
exit = c.get(:exit) as Lang.Boolean;
}
// System.println("HomeAssistantService onReturnCall() Response Code: " + responseCode);
// System.println("HomeAssistantService onReturnCall() Response Data: " + data);
@@ -102,6 +108,9 @@ class HomeAssistantService {
:bgcolor => Graphics.COLOR_BLACK
}).pushView(WatchUi.SLIDE_IMMEDIATE);
}
if (exit) {
System.exit();
}
break;
default:
@@ -117,7 +126,8 @@ class HomeAssistantService {
//
function call(
service as Lang.String,
data as Lang.Dictionary or Null
data as Lang.Dictionary or Null,
exit as Lang.Boolean
) as Void {
if (! System.getDeviceSettings().phoneConnected) {
// System.println("HomeAssistantService call(): No Phone connection, skipping API call.");
@@ -149,7 +159,10 @@ class HomeAssistantService {
"Authorization" => "Bearer " + Settings.getApiKey()
},
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
:context => entity_id
:context => {
:entity_id => entity_id,
:exit => exit
}
},
method(:onReturnCall)
);

View File

@@ -32,11 +32,12 @@ class HomeAssistantTapMenuItem extends HomeAssistantMenuItem {
//! @param label Menu item label.
//! @param template Menu item template.
//! @param service Menu item service.
//! @param data Data to supply to the service call.
//! @param exit Should the service call complete and then exit?
//! @param confirm Should the service call be confirmed to avoid accidental invocation?
//! @param pin Should the service call be protected with a PIN for some low level of security?
//! @param data Data to supply to the service call.
//! @param icon Icon to use for the menu item.
//! @param options Menu item options to be passed on.
//! @param options Menu item options to be passed on, including both SDK and menu options, e.g. exit, confirm & pin.
//! @param haService Shared Home Assistant service object that will perform the required call. Only
//! one of these objects is created for all menu items to re-use.
//
@@ -45,34 +46,30 @@ class HomeAssistantTapMenuItem extends HomeAssistantMenuItem {
template as Lang.String,
service as Lang.String or Null,
data as Lang.Dictionary or Null,
exit as Lang.Boolean,
confirm as Lang.Boolean,
pin as Lang.Boolean,
icon as Graphics.BitmapType or WatchUi.Drawable,
options as {
:alignment as WatchUi.MenuItem.Alignment,
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol,
:exit as Lang.Boolean,
:confirm as Lang.Boolean,
:pin as Lang.Boolean
} or Null,
haService as HomeAssistantService
) {
if (options != null) {
options.put(:icon, icon);
} else {
options = { :icon => icon };
}
HomeAssistantMenuItem.initialize(
label,
template,
options
{
:alignment => options.get(:alignment),
:icon => options.get(:icon)
}
);
mHomeAssistantService = haService;
mService = service;
mData = data;
mExit = exit;
mConfirm = confirm;
mPin = pin;
mExit = options.get(:exit);
mConfirm = options.get(:confirm);
mPin = options.get(:acospin);
}
//! Call a Home Assistant service only after checks have been done for confirmation or PIN entry.
@@ -106,10 +103,7 @@ class HomeAssistantTapMenuItem extends HomeAssistantMenuItem {
//
function onConfirm(b as Lang.Boolean) as Void {
if (mService != null) {
mHomeAssistantService.call(mService, mData);
}
if (mExit) {
System.exit();
mHomeAssistantService.call(mService, mData, mExit);
}
}

View File

@@ -33,21 +33,19 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
//!
//! @param label Menu item label.
//! @param template Menu item template.
//! @param confirm Should the service call be confirmed to avoid accidental invocation?
//! @param pin Should the service call be protected with a PIN for some low level of security?
//! @param data Data to supply to the service call.
//! @param options Menu item options to be passed on.
//! @param options Menu item options to be passed on, including both SDK and menu options, e.g. exit, confirm & pin.
//
function initialize(
label as Lang.String or Lang.Symbol,
template as Lang.String,
data as Lang.Dictionary or Null,
exit as Lang.Boolean,
confirm as Lang.Boolean,
pin as Lang.Boolean,
options as {
:alignment as WatchUi.MenuItem.Alignment,
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol,
:exit as Lang.Boolean,
:confirm as Lang.Boolean,
:pin as Lang.Boolean
} or Null
) {
WatchUi.ToggleMenuItem.initialize(
@@ -55,16 +53,19 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
null,
null,
false,
options
{
:alignment => options.get(:alignment),
:icon => options.get(:icon)
}
);
if (Attention has :vibrate) {
mHasVibrate = true;
}
mData = data;
mTemplate = template;
mExit = exit;
mConfirm = confirm;
mPin = pin;
mExit = options.get(:exit);
mConfirm = options.get(:confirm);
mPin = options.get(:pin);
}
//! Set the state of a toggle menu item.
@@ -215,6 +216,9 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
ErrorView.show(WatchUi.loadResource($.Rez.Strings.UnhandledHttpErr) as Lang.String + responseCode);
}
getApp().setApiStatus(status);
if (mExit) {
System.exit();
}
}
//! Set the state of the toggle menu item.
@@ -297,9 +301,6 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
//
function onConfirm(b as Lang.Boolean) as Void {
setState(b);
if (mExit) {
System.exit();
}
}
}

View File

@@ -51,10 +51,10 @@ class HomeAssistantView extends WatchUi.Menu2 {
var confirm = false as Lang.Boolean or Null;
var pin = false as Lang.Boolean or Null;
var data = null as Lang.Dictionary or Null;
var enable = true as Lang.Boolean or Null;
var enabled = true as Lang.Boolean or Null;
var exit = false as Lang.Boolean or Null;
if (items[i].get("enable") != null) {
enable = items[i].get("enable"); // Optional
if (items[i].get("enabled") != null) {
enabled = items[i].get("enabled"); // Optional
}
if (items[i].get("exit") != null) {
exit = items[i].get("exit"); // Optional
@@ -69,24 +69,77 @@ class HomeAssistantView extends WatchUi.Menu2 {
pin = tap_action.get("pin"); // Optional
}
}
if (type != null && name != null && enable) {
if (type != null && name != null && enabled) {
if (type.equals("toggle") && entity != null) {
addItem(HomeAssistantMenuItemFactory.create().toggle(name, entity, content, exit, confirm, pin));
addItem(HomeAssistantMenuItemFactory.create().toggle(
name,
entity,
content,
{
:exit => exit,
:confirm => confirm,
:pin => pin
}
));
} else if (type.equals("tap") && service != null) {
addItem(HomeAssistantMenuItemFactory.create().tap(name, entity, content, service, data, exit, confirm, pin));
addItem(HomeAssistantMenuItemFactory.create().tap(
name,
entity,
content,
service,
data,
{
:exit => exit,
:confirm => confirm,
:pin => pin
}
));
} else if (type.equals("template") && content != null) {
// NB. "template" is deprecated in the schema and remains only for backward compatibility. All menu items can now use templates, so the replacement is "info".
// The exit option is dependent on the type of template.
if (tap_action == null) {
// No exit from an information only item
addItem(HomeAssistantMenuItemFactory.create().tap(name, entity, content, service, data, false, confirm, pin));
addItem(HomeAssistantMenuItemFactory.create().tap(
name,
entity,
content,
service,
data,
{
:exit => false,
:confirm => confirm,
:pin => pin
}
));
} else {
// You may exit from template item with a 'tap_action'.
addItem(HomeAssistantMenuItemFactory.create().tap(name, entity, content, service, data, exit, confirm, pin));
addItem(HomeAssistantMenuItemFactory.create().tap(
name,
entity,
content,
service,
data,
{
:exit => exit,
:confirm => confirm,
:pin => pin
}
));
}
} else if (type.equals("info") && content != null) {
// Cannot exit from a non-actionable information only menu item.
addItem(HomeAssistantMenuItemFactory.create().tap(name, entity, content, service, data, false, confirm, pin));
addItem(HomeAssistantMenuItemFactory.create().tap(
name,
entity,
content,
service,
data,
{
:exit => false,
:confirm => confirm,
:pin => pin
}
));
} else if (type.equals("group")) {
addItem(HomeAssistantMenuItemFactory.create().group(items[i], content));
}