Merge pull request #4 from house-of-abbey/Picker-formatter

Suggested code changes from philipabbey
This commit is contained in:
thmichel
2025-10-29 18:33:54 +01:00
committed by GitHub
8 changed files with 223 additions and 206 deletions

View File

@@ -283,50 +283,14 @@
"exit": { "exit": {
"$ref": "#/$defs/exit" "$ref": "#/$defs/exit"
}, },
"min": {
"type": "number",
"title": "Minimum Value"
},
"max": {
"type": "number",
"title": "Maximum Value"
},
"step": {
"type": "number",
"title": "Step Size"
},
"display_format": {
"type": "string",
"title": "Display Format",
"description": "A C-Style format string for displaying the value in the UI. https://developer.garmin.com/connect-iq/api-docs/Toybox/Lang/Number.html#format-instance_function",
"default": "%.1f"
},
"entity": { "entity": {
"$ref": "#/$defs/entity" "$ref": "#/$defs/entity"
},
"attribute": {
"type": "string",
"title": "Attribute on the entity",
"description": "Attribute on the entity with the current numeric value. To use the state of the entity, do not specify."
},
"service": {
"$ref": "#/$defs/service"
},
"data_attribute": {
"type": "string",
"title": "Attribute on the service data",
"description": "Attribute on the service data for the value to set."
} }
}, },
"required": [ "required": [
"name", "name",
"type", "type",
"min", "entity"
"max",
"step",
"entity",
"service",
"data_attribute"
], ],
"additionalProperties": false "additionalProperties": false
}, },
@@ -816,6 +780,9 @@
"title": "Action", "title": "Action",
"description": "'confirm' field is optional.", "description": "'confirm' field is optional.",
"properties": { "properties": {
"picker": {
"$ref": "#/$defs/picker"
},
"confirm": { "confirm": {
"$ref": "#/$defs/confirm" "$ref": "#/$defs/confirm"
}, },
@@ -824,6 +791,41 @@
} }
} }
}, },
"picker": {
"type": "object",
"title": "Number picker configuration",
"description": "'attribute' field is optional.",
"properties": {
"min": {
"type": "number",
"title": "Minimum Value"
},
"max": {
"type": "number",
"title": "Maximum Value"
},
"step": {
"type": "number",
"title": "Step Size"
},
"attribute": {
"type": "string",
"title": "Attribute on the entity",
"description": "Attribute on the entity with the current numeric value. To use the state of the entity, do not specify."
},
"data_attribute": {
"type": "string",
"title": "Attribute on the service data",
"description": "Attribute on the service data for the value to set."
}
},
"required": [
"min",
"max",
"step",
"data_attribute"
]
},
"content": { "content": {
"title": "Home Assistant Template", "title": "Home Assistant Template",
"description": "Jinja2 template defining the text to display. Must be included in an 'info'. Optional in a 'toggle', 'tap' and 'group'. Special characters may not render in the glance context.", "description": "Jinja2 template defining the text to display. Must be included in an 'info'. Optional in a 'toggle', 'tap' and 'group'. Special characters may not render in the glance context.",
@@ -888,4 +890,4 @@
"description": "Choose to exit the application after this item has been selected. Disabled (false) by default. N.B. Only actionable menu items can have this field added." "description": "Choose to exit the application after this item has been selected. Disabled (false) by default. N.B. Only actionable menu items can have this field added."
} }
} }
} }

View File

@@ -630,10 +630,10 @@ class HomeAssistantApp extends Application.AppBase {
(item as HomeAssistantToggleMenuItem).updateToggleState(data[i.toString() + "t"]); (item as HomeAssistantToggleMenuItem).updateToggleState(data[i.toString() + "t"]);
} }
if (item instanceof HomeAssistantNumericMenuItem) { if (item instanceof HomeAssistantNumericMenuItem) {
if (data[i.toString() + "n"] != null) var s = data[i.toString() + "n"];
{ if ((s instanceof Lang.Number) or (s instanceof Lang.Float)) {
(item as HomeAssistantNumericMenuItem).updateNumericState(data[i.toString() + "n"].toString()); (item as HomeAssistantNumericMenuItem).setValue(s);
} }
} }
} }
if (Settings.getMenuCheck() && Settings.getCacheConfig() && !mIsCacheChecked) { if (Settings.getMenuCheck() && Settings.getCacheConfig() && !mIsCacheChecked) {
@@ -833,7 +833,7 @@ class HomeAssistantApp extends Application.AppBase {
var phoneConnected = System.getDeviceSettings().phoneConnected; var phoneConnected = System.getDeviceSettings().phoneConnected;
var connectionAvailable = System.getDeviceSettings().connectionAvailable; var connectionAvailable = System.getDeviceSettings().connectionAvailable;
// System.println("API URL = " + Settings.getApiUrl()); // System.println("HomeAssistantApp fetchApiStatus(): API URL = " + Settings.getApiUrl());
if (Settings.getApiUrl().equals("")) { if (Settings.getApiUrl().equals("")) {
mApiStatus = WatchUi.loadResource($.Rez.Strings.Unconfigured) as Lang.String; mApiStatus = WatchUi.loadResource($.Rez.Strings.Unconfigured) as Lang.String;
WatchUi.requestUpdate(); WatchUi.requestUpdate();

View File

@@ -158,7 +158,7 @@ class HomeAssistantMenuItemFactory {
entity_id as Lang.String?, entity_id as Lang.String?,
template as Lang.String?, template as Lang.String?,
service as Lang.String?, service as Lang.String?,
data as Lang.Dictionary?, picker as Lang.Dictionary,
options as { options as {
:exit as Lang.Boolean, :exit as Lang.Boolean,
:confirm as Lang.Boolean, :confirm as Lang.Boolean,
@@ -166,25 +166,21 @@ class HomeAssistantMenuItemFactory {
:icon as WatchUi.Bitmap :icon as WatchUi.Bitmap
} }
) as WatchUi.MenuItem { ) as WatchUi.MenuItem {
var data = null;
if (entity_id != null) { if (entity_id != null) {
if (data == null) { data = { "entity_id" => entity_id };
data = { "entity_id" => entity_id }; }
} else {
data.put("entity_id", entity_id);
}
}
var keys = mMenuItemOptions.keys(); var keys = mMenuItemOptions.keys();
for (var i = 0; i < keys.size(); i++) { for (var i = 0; i < keys.size(); i++) {
options.put(keys[i], mMenuItemOptions.get(keys[i])); options.put(keys[i], mMenuItemOptions.get(keys[i]));
} }
options.put(:icon, mTapTypeIcon); options.put(:icon, mTapTypeIcon);
return new HomeAssistantNumericMenuItem( return new HomeAssistantNumericMenuItem(
label, label,
template, template,
service, service,
data, data,
picker,
options, options,
mHomeAssistantService mHomeAssistantService
); );

View File

@@ -9,84 +9,92 @@
// tested on a Venu 2 device. The source code is provided at: // tested on a Venu 2 device. The source code is provided at:
// https://github.com/house-of-abbey/GarminHomeAssistant. // https://github.com/house-of-abbey/GarminHomeAssistant.
// //
// P A Abbey & J D Abbey & Someone0nEarth, 31 October 2023 // P A Abbey & J D Abbey & @thmichel, 13 October 2025
// //
//------------------------------------------------------------ //------------------------------------------------------------
import Toybox.Graphics; using Toybox.Graphics;
import Toybox.Lang; using Toybox.Lang;
import Toybox.WatchUi; using Toybox.WatchUi;
//! Factory that controls which numbers can be picked //! Factory that controls which numbers can be picked
class HomeAssistantNumericFactory extends WatchUi.PickerFactory { class HomeAssistantNumericFactory extends WatchUi.PickerFactory {
// define default values in case not contained in data // define default values in case not contained in data
private var mStart as Lang.Float = 0.0; private var mStart as Lang.Float = 0.0;
private var mStop as Lang.Float = 100.0; private var mStop as Lang.Float = 100.0;
private var mStep as Lang.Float = 1.0; private var mStep as Lang.Float = 1.0;
private var mFormatString as Lang.String = "%.2f"; private var mFormatString as Lang.String = "%d";
//! Class Constructor //! Class Constructor
//! //
public function initialize(data as Lang.Dictionary) { public function initialize(picker as Lang.Dictionary) {
PickerFactory.initialize(); PickerFactory.initialize();
// Get values from data // Get values from data
var val = picker["min"];
var val = data.get("min");
if (val != null) { if (val != null) {
mStart = val.toString().toFloat(); mStart = val.toString().toFloat();
} }
val = data.get("max"); val = picker["max"];
if (val != null) { if (val != null) {
mStop = val.toString().toFloat(); mStop = val.toString().toFloat();
} }
val = data.get("step"); val = picker["step"];
if (val != null) { if (val != null) {
mStep = val.toString().toFloat(); mStep = val.toString().toFloat();
} }
if (mStep < 0.01) { if (mStep > 0.0) {
mFormatString="%.3f"; var s = mStep;
} else if (mStep < 0.1) { var dp = 0;
mFormatString="%.2f"; while (s < 1.0) {
} else if (mStep < 1) { s *= 10;
mFormatString="%.1f"; dp++;
// Assigned inside the loop and in each iteration to avoid clobbering the default '%d'.
mFormatString = "%." + dp.toString() + "f";
}
} else { } else {
mFormatString="%d"; // The JSON menu definition defined a step size of 0, revert to the default.
mStep = 1.0;
} }
} }
//! Get the index of a number item
//! @param value The number to get the index of
//! @return The index of the number
public function getIndex(value as Float) as Number {
return ((value / mStep) - mStart).toNumber();
}
//! Generate a Drawable instance for an item //! Generate a Drawable instance for an item
//!
//! @param index The item index //! @param index The item index
//! @param selected true if the current item is selected, false otherwise //! @param selected true if the current item is selected, false otherwise
//! @return Drawable for the item //! @return Drawable for the item
public function getDrawable(index as Number, selected as Boolean) as Drawable? { //
public function getDrawable(
index as Lang.Number,
selected as Lang.Boolean
) as WatchUi.Drawable? {
var value = getValue(index); var value = getValue(index);
var text = "No item"; var text = "No item";
if (value instanceof Lang.Float) { if (value instanceof Lang.Float) {
text = value.format(mFormatString); text = value.format(mFormatString);
} }
return new WatchUi.Text({:text=>text, :color=>Graphics.COLOR_WHITE, return new WatchUi.Text({
:locX=>WatchUi.LAYOUT_HALIGN_CENTER, :locY=>WatchUi.LAYOUT_VALIGN_CENTER}); :text => text,
:color => Graphics.COLOR_WHITE,
:locX => WatchUi.LAYOUT_HALIGN_CENTER,
:locY => WatchUi.LAYOUT_VALIGN_CENTER
});
} }
//! Get the value of the item at the given index //! Get the value of the item at the given index
//!
//! @param index Index of the item to get the value of //! @param index Index of the item to get the value of
//! @return Value of the item //! @return Value of the item
public function getValue(index as Number) as Object? { //
public function getValue(index as Lang.Number) as Lang.Object? {
return mStart + (index * mStep); return mStart + (index * mStep);
} }
//! Get the number of picker items //! Get the number of picker items
//!
//! @return Number of items //! @return Number of items
public function getSize() as Number { //
public function getSize() as Lang.Number {
return ((mStop - mStart) / mStep).toNumber() + 1; return ((mStop - mStart) / mStep).toNumber() + 1;
} }
} }

View File

@@ -9,7 +9,7 @@
// tested on a Venu 2 device. The source code is provided at: // tested on a Venu 2 device. The source code is provided at:
// https://github.com/house-of-abbey/GarminHomeAssistant. // https://github.com/house-of-abbey/GarminHomeAssistant.
// //
// P A Abbey & J D Abbey & Someone0nEarth, 31 October 2023 // P A Abbey & J D Abbey & @thmichel, 13 October 2025
// //
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
@@ -17,7 +17,6 @@ using Toybox.Lang;
using Toybox.WatchUi; using Toybox.WatchUi;
using Toybox.Graphics; using Toybox.Graphics;
//! Menu button with an icon that opens a sub-menu, i.e. group, and optionally renders //! Menu button with an icon that opens a sub-menu, i.e. group, and optionally renders
//! a Home Assistant Template. //! a Home Assistant Template.
// //
@@ -28,9 +27,9 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
private var mExit as Lang.Boolean; private var mExit as Lang.Boolean;
private var mPin as Lang.Boolean; private var mPin as Lang.Boolean;
private var mData as Lang.Dictionary?; private var mData as Lang.Dictionary?;
private var mValue as Lang.String?; private var mPicker as Lang.Dictionary?;
private var mFormatString as Lang.String="%.1f"; private var mValue as Lang.Number or Lang.Float = 0;
private var mFormatString as Lang.String = "%d";
//! Class Constructor //! Class Constructor
//! //!
@@ -51,6 +50,7 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
template as Lang.String, template as Lang.String,
service as Lang.String?, service as Lang.String?,
data as Lang.Dictionary?, data as Lang.Dictionary?,
picker as Lang.Dictionary,
options as { options as {
:alignment as WatchUi.MenuItem.Alignment, :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,
@@ -62,6 +62,7 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
) { ) {
mService = service; mService = service;
mData = data; mData = data;
mPicker = picker;
mExit = options[:exit]; mExit = options[:exit];
mConfirm = options[:confirm]; mConfirm = options[:confirm];
mPin = options[:pin]; mPin = options[:pin];
@@ -74,12 +75,24 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
{ {
:alignment => options[:alignment], :alignment => options[:alignment],
:icon => options[:icon] :icon => options[:icon]
} }
); );
if (picker != null) {
var s = picker["step"];
if (s != null) {
var step = s.toFloat() as Lang.Float;
var dp = 0;
while (step < 1.0) {
step *= 10;
dp++;
// Assigned inside the loop and in each iteration to avoid clobbering the default '%d'.
mFormatString = "%." + dp.toString() + "f";
}
}
}
} }
function callService() as Void { function callService() as Void {
var hasTouchScreen = System.getDeviceSettings().isTouchScreen; var hasTouchScreen = System.getDeviceSettings().isTouchScreen;
if (mPin && hasTouchScreen) { if (mPin && hasTouchScreen) {
@@ -89,10 +102,10 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
WatchUi.pushView( WatchUi.pushView(
pinConfirmationView, pinConfirmationView,
new HomeAssistantPinConfirmationDelegate({ new HomeAssistantPinConfirmationDelegate({
:callback => method(:onConfirm), :callback => method(:onConfirm),
:pin => pin, :pin => pin,
:state => false, :state => false,
:view => pinConfirmationView, :view => pinConfirmationView,
}), }),
WatchUi.SLIDE_IMMEDIATE WatchUi.SLIDE_IMMEDIATE
); );
@@ -130,65 +143,60 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
} }
} }
//! Callback function after the menu items selection has been (optionally) confirmed. //! Callback function after the menu items selection has been (optionally) confirmed.
//! //!
//! @param b Ignored. It is included in order to match the expected function prototype of the callback method. //! @param b Ignored. It is included in order to match the expected function prototype of the callback method.
// //
function onConfirm(b as Lang.Boolean) as Void { function onConfirm(b as Lang.Boolean) as Void {
//mHomeAssistantService.call(mService, {"entity_id" => mData.get("entity_id").toString(),mData.get("valueLabel").toString() => mValue}, mExit); var dataAttribute = mPicker["data_attribute"];
var dataAttribute = mData.get("data_attribute");
if (dataAttribute == null) { if (dataAttribute == null) {
//return without call service if no data attribute is set to avoid crash //return without call service if no data attribute is set to avoid crash
WatchUi.popView(WatchUi.SLIDE_RIGHT); WatchUi.popView(WatchUi.SLIDE_RIGHT);
return; return;
} }
var entity_id = mData.get("entity_id"); var entity_id = mData["entity_id"];
if (entity_id == null) { if (entity_id == null) {
//return without call service if no entity_id is set to avoid crash //return without call service if no entity_id is set to avoid crash
WatchUi.popView(WatchUi.SLIDE_RIGHT); WatchUi.popView(WatchUi.SLIDE_RIGHT);
return; return;
} }
mHomeAssistantService.call(mService, {"entity_id" => entity_id.toString(),dataAttribute.toString() => mValue}, mExit); mHomeAssistantService.call(
mService,
{
"entity_id" => entity_id.toString(),
dataAttribute.toString() => mValue
},
mExit
);
WatchUi.popView(WatchUi.SLIDE_RIGHT); WatchUi.popView(WatchUi.SLIDE_RIGHT);
} }
//! Return a toggle menu item's state template. //! Return a numeric menu item's fetch state template.
//! //!
//! @return A string with the menu item's template definition (or null). //! @return A string with the menu item's template definition (or null).
// //
function getNumericTemplate() as Lang.String? { function getNumericTemplate() as Lang.String? {
var entity_id = mData.get("entity_id"); var entity_id = mData["entity_id"];
if (entity_id != null) { var attribute = mPicker["attribute"] as Lang.String;
if (mData.get("attribute")!=null) if (entity_id == null) {
{ return null;
return "{{state_attr('" + entity_id.toString() + "','" + mData.get("attribute").toString() +"')}}";
}
return "";
}
return "";
}
function updateNumericState(data as Lang.String or Lang.Dictionary or Null) as Void {
if (data == null) {
mValue="0";
return;
} else if(data instanceof Lang.String) {
mValue=data;
} else { } else {
// Catch possible error if (attribute == null) {
mValue="0"; // Compiler says: "Statement is not reachable."
// This is wrong because a break point on the following line proves it is executed!
return "{{states('" + entity_id.toString() + "')}}";
} else {
return "{{state_attr('" + entity_id.toString() + "','" + attribute + "')}}";
}
} }
} }
//! Update the menu item's sub label to display the template rendered by Home Assistant. //! Update the menu item's sub label to display the template rendered by Home Assistant.
//! //!
//! @param data The rendered template (typically a string) to be placed in the sub label. This may //! @param data The rendered template (typically a string) to be placed in the sub label. This may
//! unusually be a number if the SDK interprets the JSON returned by Home Assistant as such. //! unusually be a number if the SDK interprets the JSON returned by Home Assistant as such.
// //
function updateState(data as Lang.String or Lang.Dictionary or Lang.Number or Lang.Float or Null) as Void { public function updateState(data as Lang.String or Lang.Dictionary or Lang.Number or Lang.Float or Null) as Void {
if (data == null) { if (data == null) {
setSubLabel($.Rez.Strings.Empty); setSubLabel($.Rez.Strings.Empty);
} else if(data instanceof Lang.Float) { } else if(data instanceof Lang.Float) {
@@ -197,31 +205,45 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
} else if(data instanceof Lang.Number) { } else if(data instanceof Lang.Number) {
var f = data.toFloat() as Lang.Float; var f = data.toFloat() as Lang.Float;
setSubLabel(f.format(mFormatString)); setSubLabel(f.format(mFormatString));
} else if (data instanceof Lang.String){ } else if (data instanceof Lang.String) {
// This should not happen // This should not happen
setSubLabel(data); setSubLabel(data);
} } else {
else {
// The template must return a Float on Numeric value, or the item cannot be formatted locally without error. // The template must return a Float on Numeric value, or the item cannot be formatted locally without error.
setSubLabel(WatchUi.loadResource($.Rez.Strings.TemplateError) as Lang.String); setSubLabel(WatchUi.loadResource($.Rez.Strings.TemplateError) as Lang.String);
} }
WatchUi.requestUpdate(); WatchUi.requestUpdate();
} }
//! Set the mValue value. //! Set the Picker's value. Needed to set new value via the Service call
//! //!
//! Needed to set new value via the Service call //! @param value New value to set.
// //
function setValue(value as Lang.String) as Void { public function setValue(value as Lang.Number or Lang.Float) as Void {
mValue = value; mValue = value;
} }
function getValue() as Lang.String { //! Get the Picker's value.
//!
//! Needed to set new value via the Service call
//
public function getValue() as Lang.Number or Lang.Float {
return mValue; return mValue;
} }
function getData() as Lang.Dictionary { //! Get the original 'data' field supplied by the JSON menu.
//!
//! @return Dictionary containing the 'data' field.
//
public function getData() as Lang.Dictionary {
return mData; return mData;
} }
// Get the original 'picker' field supplied by the JSON menu.
//!
//! @return Dictionary containing the 'picker' field.
//
public function getPicker() as Lang.Dictionary {
return mPicker;
}
} }

View File

@@ -9,7 +9,7 @@
// tested on a Venu 2 device. The source code is provided at: // tested on a Venu 2 device. The source code is provided at:
// https://github.com/house-of-abbey/GarminHomeAssistant. // https://github.com/house-of-abbey/GarminHomeAssistant.
// //
// P A Abbey & J D Abbey & Someone0nEarth, 31 October 2023 // P A Abbey & J D Abbey & @thmichel, 13 October 2025
// //
//------------------------------------------------------------ //------------------------------------------------------------
@@ -20,88 +20,79 @@ using Toybox.System;
using Toybox.WatchUi; using Toybox.WatchUi;
//! Picker that allows the user to choose a float value //! Picker that allows the user to choose a float value
//
class HomeAssistantNumericPicker extends WatchUi.Picker { class HomeAssistantNumericPicker extends WatchUi.Picker {
private var mFactory as HomeAssistantNumericFactory;
private var mItem as HomeAssistantNumericMenuItem; private var mItem as HomeAssistantNumericMenuItem;
//! Constructor //! Constructor
public function initialize(factory as HomeAssistantNumericFactory, haItem as HomeAssistantNumericMenuItem) { //
public function initialize(
factory as HomeAssistantNumericFactory,
haItem as HomeAssistantNumericMenuItem
) {
mItem = haItem;
var picker = mItem.getPicker();
var min = (picker.get("min") as Lang.String).toFloat();
var step = (picker.get("step") as Lang.String).toFloat();
var val = haItem.getValue();
mFactory = factory; if (min == null) {
min = 0.0;
}
var pickerOptions = {:pattern=>[mFactory]}; if (step == null) {
mItem=haItem; step = 1.0;
var data = mItem.getData();
var min = 0.0;
var val = data.get("min");
if (val != null) {
min = val.toString().toFloat();
}
var step = 1.0;
val = data.get("step");
if (val != null) {
step = val.toString().toFloat();
}
val = haItem.getValue();
if (val != null) {
val = val.toString().toFloat();
} else {
// catch missing state to avoid crash
val = min;
} }
var index = ((val -min) / step).toNumber();
pickerOptions[:defaults] =[index];
var title = new WatchUi.Text({:text=>haItem.getLabel(), :locX=>WatchUi.LAYOUT_HALIGN_CENTER,
:locY=>WatchUi.LAYOUT_VALIGN_BOTTOM});
pickerOptions[:title] = title;
Picker.initialize(pickerOptions);
WatchUi.Picker.initialize({
:title => new WatchUi.Text({
:text => haItem.getLabel(),
:locX => WatchUi.LAYOUT_HALIGN_CENTER,
:locY => WatchUi.LAYOUT_VALIGN_BOTTOM
}),
:pattern => [factory],
:defaults => [((val - min) / step).toNumber()]
});
} }
//! Called when the user has completed picking.
//! Get whether the user is done picking //!
//! @param value Value user selected //! @param value Value user selected
//! @return true if user is done, false otherwise //! @return true if user is done, false otherwise
public function onConfirm(value as Lang.String) as Void { //
public function onConfirm(value as Lang.Number or Lang.Float) as Void {
mItem.setValue(value); mItem.setValue(value);
mItem.callService(); mItem.callService();
} }
} }
//! Responds to a numeric picker selection or cancellation //! Responds to a numeric picker selection or cancellation.
//
class HomeAssistantNumericPickerDelegate extends WatchUi.PickerDelegate { class HomeAssistantNumericPickerDelegate extends WatchUi.PickerDelegate {
private var mPicker as HomeAssistantNumericPicker; private var mPicker as HomeAssistantNumericPicker;
//! Constructor //! Constructor
//
public function initialize(picker as HomeAssistantNumericPicker) { public function initialize(picker as HomeAssistantNumericPicker) {
PickerDelegate.initialize(); PickerDelegate.initialize();
mPicker = picker; mPicker = picker;
} }
//! Handle a cancel event from the picker //! Handle a cancel event from the picker
//!
//! @return true if handled, false otherwise //! @return true if handled, false otherwise
//
public function onCancel() as Lang.Boolean { public function onCancel() as Lang.Boolean {
WatchUi.popView(WatchUi.SLIDE_RIGHT); WatchUi.popView(WatchUi.SLIDE_RIGHT);
return true; return true;
} }
//! Handle a confirm event from the picker //! Handle a confirm event from the picker
//!
//! @param values The values chosen in the picker //! @param values The values chosen in the picker
//! @return true if handled, false otherwise //! @return true if handled, false otherwise
//
public function onAccept(values as Lang.Array) as Lang.Boolean { public function onAccept(values as Lang.Array) as Lang.Boolean {
var chosenValue = values[0].toString(); mPicker.onConfirm(values[0]);
mPicker.onConfirm(chosenValue);
return true; return true;
} }
} }

View File

@@ -128,7 +128,7 @@ class HomeAssistantTapMenuItem extends HomeAssistantMenuItem {
//! //!
//! @param b Ignored. It is included in order to match the expected function prototype of the callback method. //! @param b Ignored. It is included in order to match the expected function prototype of the callback method.
// //
function onConfirm(b as Lang.Boolean) as Void { public function onConfirm(b as Lang.Boolean) as Void {
if (mService != null) { if (mService != null) {
mHomeAssistantService.call(mService, mData, mExit); mHomeAssistantService.call(mService, mData, mExit);
} }

View File

@@ -47,7 +47,6 @@ class HomeAssistantView extends WatchUi.Menu2 {
var content = items[i].get("content") as Lang.String?; var content = items[i].get("content") as Lang.String?;
var entity = items[i].get("entity") as Lang.String?; var entity = items[i].get("entity") as Lang.String?;
var tap_action = items[i].get("tap_action") as Lang.Dictionary?; var tap_action = items[i].get("tap_action") as Lang.Dictionary?;
var picker = items[i].get("picker") as Lang.Dictionary?; // optional for numeric items
var service = items[i].get("service") as Lang.String?; // Deprecated schema var service = items[i].get("service") as Lang.String?; // Deprecated schema
var confirm = false as Lang.Boolean?; var confirm = false as Lang.Boolean?;
var pin = false as Lang.Boolean?; var pin = false as Lang.Boolean?;
@@ -128,18 +127,23 @@ class HomeAssistantView extends WatchUi.Menu2 {
)); ));
} }
} else if (type.equals("numeric") && service != null) { } else if (type.equals("numeric") && service != null) {
addItem(HomeAssistantMenuItemFactory.create().numeric( if (tap_action != null) {
name, var picker = tap_action.get("picker") as Lang.Dictionary?;
entity, if (picker != null) {
content, addItem(HomeAssistantMenuItemFactory.create().numeric(
service, name,
picker, entity,
{ content,
:exit => exit, service,
:confirm => confirm, picker,
:pin => pin {
:exit => exit,
:confirm => confirm,
:pin => pin
}
));
} }
)); }
} else if (type.equals("info") && content != null) { } else if (type.equals("info") && content != null) {
// Cannot exit from a non-actionable information only menu item. // Cannot exit from a non-actionable information only menu item.
addItem(HomeAssistantMenuItemFactory.create().tap( addItem(HomeAssistantMenuItemFactory.create().tap(
@@ -168,7 +172,6 @@ class HomeAssistantView extends WatchUi.Menu2 {
//! //!
//! @return An array of menu items that need to be updated periodically to reflect the latest Home Assistant state. //! @return An array of menu items that need to be updated periodically to reflect the latest Home Assistant state.
// //
function getItemsToUpdate() as Lang.Array<HomeAssistantToggleMenuItem or HomeAssistantTapMenuItem or HomeAssistantGroupMenuItem or HomeAssistantNumericMenuItem or Null> { function getItemsToUpdate() as Lang.Array<HomeAssistantToggleMenuItem or HomeAssistantTapMenuItem or HomeAssistantGroupMenuItem or HomeAssistantNumericMenuItem or Null> {
var fullList = []; var fullList = [];
var lmi = mItems as Lang.Array<WatchUi.MenuItem>; var lmi = mItems as Lang.Array<WatchUi.MenuItem>;
@@ -204,8 +207,8 @@ class HomeAssistantView extends WatchUi.Menu2 {
//! Called when this View is brought to the foreground. Restore //! Called when this View is brought to the foreground. Restore
//! the state of this View and prepare it to be shown. This includes //! the state of this View and prepare it to be shown. This includes
//! loading resources into memory. //! loading resources into memory.
//
function onShow() as Void {} function onShow() as Void {}
} }
//! Delegate for the HomeAssistantView. //! Delegate for the HomeAssistantView.
@@ -273,18 +276,13 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
var haItem = item as HomeAssistantNumericMenuItem; var haItem = item as HomeAssistantNumericMenuItem;
// System.println(haItem.getLabel() + " " + haItem.getId()); // System.println(haItem.getLabel() + " " + haItem.getId());
// create new view to select new value // create new view to select new value
var mPickerFactory = new HomeAssistantNumericFactory((haItem as HomeAssistantNumericMenuItem).getPicker());
var mPickerFactory = new HomeAssistantNumericFactory(haItem.getData()); var mPicker = new HomeAssistantNumericPicker(mPickerFactory,haItem);
var mPicker = new HomeAssistantNumericPicker(mPickerFactory,haItem);
var mPickerDelegate = new HomeAssistantNumericPickerDelegate(mPicker); var mPickerDelegate = new HomeAssistantNumericPickerDelegate(mPicker);
WatchUi.pushView(mPicker,mPickerDelegate,WatchUi.SLIDE_LEFT); WatchUi.pushView(mPicker,mPickerDelegate,WatchUi.SLIDE_LEFT);
} else if (item instanceof HomeAssistantGroupMenuItem) { } else if (item instanceof HomeAssistantGroupMenuItem) {
var haMenuItem = item as HomeAssistantGroupMenuItem; var haMenuItem = item as HomeAssistantGroupMenuItem;
// System.println("IconMenu: " + haMenuItem.getLabel() + " " + haMenuItem.getId());
WatchUi.pushView(haMenuItem.getMenuView(), new HomeAssistantViewDelegate(false), WatchUi.SLIDE_LEFT); WatchUi.pushView(haMenuItem.getMenuView(), new HomeAssistantViewDelegate(false), WatchUi.SLIDE_LEFT);
// } else {
// System.println(item.getLabel() + " " + item.getId());
} }
} }