mirror of
				https://github.com/house-of-abbey/GarminHomeAssistant.git
				synced 2025-10-31 07:48:13 +00:00 
			
		
		
		
	Merge pull request #4 from house-of-abbey/Picker-formatter
Suggested code changes from philipabbey
This commit is contained in:
		| @@ -283,50 +283,14 @@ | ||||
|             "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": { | ||||
|               "$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": [ | ||||
|             "name", | ||||
|             "type", | ||||
|             "min", | ||||
|             "max", | ||||
|             "step", | ||||
|             "entity", | ||||
|             "service", | ||||
|             "data_attribute" | ||||
|             "entity" | ||||
|           ], | ||||
|           "additionalProperties": false | ||||
|         }, | ||||
| @@ -816,6 +780,9 @@ | ||||
|       "title": "Action", | ||||
|       "description": "'confirm' field is optional.", | ||||
|       "properties": { | ||||
|         "picker": { | ||||
|           "$ref": "#/$defs/picker" | ||||
|         }, | ||||
|         "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": { | ||||
|       "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.", | ||||
|   | ||||
| @@ -630,9 +630,9 @@ class HomeAssistantApp extends Application.AppBase { | ||||
|                                 (item as HomeAssistantToggleMenuItem).updateToggleState(data[i.toString() + "t"]); | ||||
|                             } | ||||
|                             if (item instanceof HomeAssistantNumericMenuItem) { | ||||
|                                 if (data[i.toString() + "n"] != null) | ||||
|                                 { | ||||
|                                     (item as HomeAssistantNumericMenuItem).updateNumericState(data[i.toString() + "n"].toString()); | ||||
|                                var s = data[i.toString() + "n"]; | ||||
|                                if ((s instanceof Lang.Number) or (s instanceof Lang.Float)) { | ||||
|                                    (item as HomeAssistantNumericMenuItem).setValue(s); | ||||
|                                } | ||||
|                             } | ||||
|                         } | ||||
| @@ -833,7 +833,7 @@ class HomeAssistantApp extends Application.AppBase { | ||||
|         var phoneConnected = System.getDeviceSettings().phoneConnected; | ||||
|         var connectionAvailable = System.getDeviceSettings().connectionAvailable; | ||||
|  | ||||
|         // System.println("API URL = " + Settings.getApiUrl()); | ||||
|         // System.println("HomeAssistantApp fetchApiStatus(): API URL = " + Settings.getApiUrl()); | ||||
|         if (Settings.getApiUrl().equals("")) { | ||||
|             mApiStatus = WatchUi.loadResource($.Rez.Strings.Unconfigured) as Lang.String; | ||||
|             WatchUi.requestUpdate(); | ||||
|   | ||||
| @@ -158,7 +158,7 @@ class HomeAssistantMenuItemFactory { | ||||
|         entity_id as Lang.String?, | ||||
|         template  as Lang.String?, | ||||
|         service   as Lang.String?, | ||||
|         data      as Lang.Dictionary?, | ||||
|         picker    as Lang.Dictionary, | ||||
|         options   as { | ||||
|             :exit    as Lang.Boolean, | ||||
|             :confirm as Lang.Boolean, | ||||
| @@ -166,25 +166,21 @@ class HomeAssistantMenuItemFactory { | ||||
|             :icon    as WatchUi.Bitmap | ||||
|         } | ||||
|     ) as WatchUi.MenuItem { | ||||
|         var data = null; | ||||
|         if (entity_id != null) { | ||||
|             if (data == null) { | ||||
|             data = { "entity_id" => entity_id }; | ||||
|                  | ||||
|             } else { | ||||
|                 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])); | ||||
|         } | ||||
|         options.put(:icon, mTapTypeIcon); | ||||
|          | ||||
|         return new HomeAssistantNumericMenuItem( | ||||
|             label, | ||||
|             template, | ||||
|             service, | ||||
|             data, | ||||
|             picker, | ||||
|             options, | ||||
|             mHomeAssistantService | ||||
|         ); | ||||
|   | ||||
| @@ -9,13 +9,13 @@ | ||||
| // 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 & Someone0nEarth, 31 October 2023 | ||||
| // P A Abbey & J D Abbey & @thmichel, 13 October 2025 | ||||
| // | ||||
| //------------------------------------------------------------ | ||||
| 
 | ||||
| import Toybox.Graphics; | ||||
| import Toybox.Lang; | ||||
| import Toybox.WatchUi; | ||||
| using Toybox.Graphics; | ||||
| using Toybox.Lang; | ||||
| using Toybox.WatchUi; | ||||
| 
 | ||||
| //! Factory that controls which numbers can be picked | ||||
| class HomeAssistantNumericFactory extends WatchUi.PickerFactory { | ||||
| @@ -23,70 +23,78 @@ class HomeAssistantNumericFactory extends WatchUi.PickerFactory { | ||||
|     private var mStart        as Lang.Float  = 0.0; | ||||
|     private var mStop         as Lang.Float  = 100.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 | ||||
|     //! | ||||
|     public function initialize(data as Lang.Dictionary) { | ||||
|     // | ||||
|     public function initialize(picker as Lang.Dictionary) { | ||||
|         PickerFactory.initialize(); | ||||
| 
 | ||||
|         // Get values from data | ||||
| 
 | ||||
|         var val = data.get("min"); | ||||
|         var val = picker["min"]; | ||||
|         if (val != null) { | ||||
|             mStart = val.toString().toFloat(); | ||||
|         } | ||||
|         val = data.get("max"); | ||||
|         val = picker["max"]; | ||||
|         if (val != null) { | ||||
|             mStop = val.toString().toFloat(); | ||||
|         }  | ||||
|         val = data.get("step"); | ||||
|         val = picker["step"]; | ||||
|         if (val != null) { | ||||
|             mStep = val.toString().toFloat(); | ||||
|         }  | ||||
|         if (mStep < 0.01) { | ||||
|             mFormatString="%.3f"; | ||||
|         } else if (mStep < 0.1) { | ||||
|             mFormatString="%.2f"; | ||||
|         } else if (mStep < 1) { | ||||
|             mFormatString="%.1f"; | ||||
|         if (mStep > 0.0) { | ||||
|             var s = mStep; | ||||
|             var dp = 0; | ||||
|             while (s < 1.0) { | ||||
|                 s *= 10; | ||||
|                 dp++; | ||||
|                 // Assigned inside the loop and in each iteration to avoid clobbering the default '%d'. | ||||
|                 mFormatString = "%." + dp.toString() + "f"; | ||||
|             } | ||||
|         } 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 | ||||
|     //! | ||||
|     //! @param index The item index | ||||
|     //! @param selected true if the current item is selected, false otherwise | ||||
|     //! @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 text = "No item"; | ||||
|         if (value instanceof Lang.Float) { | ||||
|             text = value.format(mFormatString); | ||||
|         } | ||||
|         return new WatchUi.Text({:text=>text, :color=>Graphics.COLOR_WHITE,  | ||||
|             :locX=>WatchUi.LAYOUT_HALIGN_CENTER, :locY=>WatchUi.LAYOUT_VALIGN_CENTER}); | ||||
|         return new WatchUi.Text({ | ||||
|             :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 | ||||
|     //! | ||||
|     //! @param index Index of the item to get the value of | ||||
|     //! @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); | ||||
|     } | ||||
| 
 | ||||
|     //! Get the number of picker items | ||||
|     //! | ||||
|     //! @return Number of items | ||||
|     public function getSize() as Number { | ||||
|     // | ||||
|     public function getSize() as Lang.Number { | ||||
|         return ((mStop - mStart) / mStep).toNumber() + 1; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -9,7 +9,7 @@ | ||||
| // 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 & 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.Graphics; | ||||
|  | ||||
|  | ||||
| //! Menu button with an icon that opens a sub-menu, i.e. group, and optionally renders | ||||
| //! a Home Assistant Template. | ||||
| // | ||||
| @@ -28,9 +27,9 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem { | ||||
|     private var mExit                 as Lang.Boolean; | ||||
|     private var mPin                  as Lang.Boolean; | ||||
|     private var mData                 as Lang.Dictionary?; | ||||
|     private var mValue                as Lang.String?;   | ||||
|     private var mFormatString         as Lang.String="%.1f"; | ||||
|  | ||||
|     private var mPicker               as Lang.Dictionary?; | ||||
|     private var mValue                as Lang.Number or Lang.Float = 0; | ||||
|     private var mFormatString         as Lang.String = "%d"; | ||||
|  | ||||
|     //! Class Constructor | ||||
|     //! | ||||
| @@ -51,6 +50,7 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem { | ||||
|         template  as Lang.String, | ||||
|         service   as Lang.String?, | ||||
|         data      as Lang.Dictionary?, | ||||
|         picker    as Lang.Dictionary, | ||||
|         options   as { | ||||
|             :alignment as WatchUi.MenuItem.Alignment, | ||||
|             :icon      as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol, | ||||
| @@ -62,6 +62,7 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem { | ||||
|     ) { | ||||
|         mService              = service; | ||||
|         mData                 = data; | ||||
|         mPicker               = picker; | ||||
|         mExit                 = options[:exit]; | ||||
|         mConfirm              = options[:confirm]; | ||||
|         mPin                  = options[:pin]; | ||||
| @@ -76,9 +77,21 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem { | ||||
|                 :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 { | ||||
|         var hasTouchScreen = System.getDeviceSettings().isTouchScreen; | ||||
| @@ -130,65 +143,60 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     | ||||
|  | ||||
|     //! 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. | ||||
|     // | ||||
|     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 = mData.get("data_attribute"); | ||||
|         var dataAttribute = mPicker["data_attribute"]; | ||||
|         if (dataAttribute == null) { | ||||
|             //return without call service if no data attribute is set to avoid crash | ||||
|             WatchUi.popView(WatchUi.SLIDE_RIGHT); | ||||
|             return; | ||||
|         } | ||||
|         var entity_id = mData.get("entity_id"); | ||||
|         var entity_id = mData["entity_id"]; | ||||
|         if (entity_id == null) { | ||||
|             //return without call service if no entity_id is set to avoid crash | ||||
|             WatchUi.popView(WatchUi.SLIDE_RIGHT); | ||||
|             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); | ||||
|     } | ||||
|  | ||||
|     //! 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). | ||||
|     // | ||||
|     function getNumericTemplate() as Lang.String? { | ||||
|         var entity_id = mData.get("entity_id"); | ||||
|         if (entity_id != null) { | ||||
|             if (mData.get("attribute")!=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; | ||||
|         var entity_id = mData["entity_id"]; | ||||
|         var attribute = mPicker["attribute"] as Lang.String; | ||||
|         if (entity_id == null) { | ||||
|             return null; | ||||
|         } else { | ||||
|             // Catch possible error | ||||
|             mValue="0"; | ||||
|             if (attribute == null) { | ||||
|                 // 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. | ||||
|     //! | ||||
|     //! @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. | ||||
|     // | ||||
|     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) { | ||||
|             setSubLabel($.Rez.Strings.Empty); | ||||
|         } else if(data instanceof Lang.Float) { | ||||
| @@ -197,31 +205,45 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem { | ||||
|         } else if(data instanceof Lang.Number) { | ||||
|             var f = data.toFloat() as Lang.Float; | ||||
|             setSubLabel(f.format(mFormatString)); | ||||
|         } else if (data instanceof Lang.String){ | ||||
|         } else if (data instanceof Lang.String) { | ||||
|             // This should not happen | ||||
|             setSubLabel(data); | ||||
|         }   | ||||
|         else { | ||||
|         } else { | ||||
|             // 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); | ||||
|         } | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     // Get the original 'picker' field supplied by the JSON menu. | ||||
|     //! | ||||
|     //! @return Dictionary containing the 'picker' field. | ||||
|     // | ||||
|     public function getPicker() as Lang.Dictionary { | ||||
|         return mPicker; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| // 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 & Someone0nEarth, 31 October 2023 | ||||
| // P A Abbey & J D Abbey & @thmichel, 13 October 2025 | ||||
| // | ||||
| //------------------------------------------------------------ | ||||
| 
 | ||||
| @@ -20,88 +20,79 @@ using Toybox.System; | ||||
| using Toybox.WatchUi; | ||||
| 
 | ||||
| //! Picker that allows the user to choose a float value | ||||
| // | ||||
| class HomeAssistantNumericPicker extends WatchUi.Picker { | ||||
| 
 | ||||
|     private var mFactory as HomeAssistantNumericFactory; | ||||
|     private var mItem as HomeAssistantNumericMenuItem; | ||||
| 
 | ||||
|     //! 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; | ||||
| 
 | ||||
| 
 | ||||
|         var pickerOptions = {:pattern=>[mFactory]}; | ||||
|         mItem=haItem; | ||||
| 
 | ||||
| 
 | ||||
|         var data = mItem.getData(); | ||||
| 
 | ||||
|         var min = 0.0; | ||||
|         var val = data.get("min"); | ||||
|         if (val != null) { | ||||
|             min = val.toString().toFloat(); | ||||
|         if (min == null) { | ||||
|             min = 0.0; | ||||
|         } | ||||
|         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); | ||||
| 
 | ||||
|         if (step == null) { | ||||
|             step = 1.0; | ||||
|         } | ||||
| 
 | ||||
|         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()] | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     //! Get whether the user is done picking | ||||
|     //! Called when the user has completed picking. | ||||
|     //! | ||||
|     //! @param value Value user selected | ||||
|     //! @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.callService(); | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
| 
 | ||||
| //! Responds to a numeric  picker selection or cancellation | ||||
| //! Responds to a numeric picker selection or cancellation. | ||||
| // | ||||
| class HomeAssistantNumericPickerDelegate extends WatchUi.PickerDelegate { | ||||
|     private var mPicker as HomeAssistantNumericPicker; | ||||
| 
 | ||||
|     //! Constructor | ||||
|     // | ||||
|     public function initialize(picker as HomeAssistantNumericPicker) { | ||||
|         PickerDelegate.initialize(); | ||||
|         mPicker = picker; | ||||
|     } | ||||
| 
 | ||||
|     //! Handle a cancel event from the picker | ||||
|     //! | ||||
|     //! @return true if handled, false otherwise | ||||
|     // | ||||
|     public function onCancel() as Lang.Boolean { | ||||
|         WatchUi.popView(WatchUi.SLIDE_RIGHT); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     //! Handle a confirm event from the picker | ||||
|     //! | ||||
|     //! @param values The values chosen in the picker | ||||
|     //! @return true if handled, false otherwise | ||||
|     // | ||||
|     public function onAccept(values as Lang.Array) as Lang.Boolean { | ||||
|         var chosenValue = values[0].toString(); | ||||
|         mPicker.onConfirm(chosenValue); | ||||
|         mPicker.onConfirm(values[0]); | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -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. | ||||
|     // | ||||
|     function onConfirm(b as Lang.Boolean) as Void { | ||||
|     public function onConfirm(b as Lang.Boolean) as Void { | ||||
|         if (mService != null) { | ||||
|             mHomeAssistantService.call(mService, mData, mExit); | ||||
|         } | ||||
|   | ||||
| @@ -47,7 +47,6 @@ class HomeAssistantView extends WatchUi.Menu2 { | ||||
|                 var content    = items[i].get("content")    as Lang.String?; | ||||
|                 var entity     = items[i].get("entity")     as Lang.String?; | ||||
|                 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 confirm    = false                      as Lang.Boolean?; | ||||
|                 var pin        = false                      as Lang.Boolean?; | ||||
| @@ -128,6 +127,9 @@ class HomeAssistantView extends WatchUi.Menu2 { | ||||
|                             )); | ||||
|                         } | ||||
|                     } else if (type.equals("numeric") && service != null) { | ||||
|                         if (tap_action != null) { | ||||
|                             var picker = tap_action.get("picker") as Lang.Dictionary?; | ||||
|                             if (picker != null) { | ||||
|                                 addItem(HomeAssistantMenuItemFactory.create().numeric( | ||||
|                                     name, | ||||
|                                     entity, | ||||
| @@ -140,6 +142,8 @@ class HomeAssistantView extends WatchUi.Menu2 { | ||||
|                                         :pin     => pin | ||||
|                                     } | ||||
|                                 )); | ||||
|                             } | ||||
|                         } | ||||
|                     } else if (type.equals("info") && content != null) { | ||||
|                         // Cannot exit from a non-actionable information only menu item. | ||||
|                         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. | ||||
|     // | ||||
|  | ||||
|     function getItemsToUpdate() as Lang.Array<HomeAssistantToggleMenuItem or HomeAssistantTapMenuItem or HomeAssistantGroupMenuItem  or HomeAssistantNumericMenuItem or Null> { | ||||
|         var fullList = []; | ||||
|         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 | ||||
|     //! the state of this View and prepare it to be shown. This includes | ||||
|     //! loading resources into memory. | ||||
|     // | ||||
|     function onShow() as Void {} | ||||
|  | ||||
| } | ||||
|  | ||||
| //! Delegate for the HomeAssistantView. | ||||
| @@ -273,18 +276,13 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate { | ||||
|             var haItem = item as HomeAssistantNumericMenuItem; | ||||
|             // System.println(haItem.getLabel() + " " + haItem.getId()); | ||||
|             // create new view to select new value | ||||
|             | ||||
|             var mPickerFactory = new HomeAssistantNumericFactory(haItem.getData()); | ||||
|             | ||||
|             var mPickerFactory  = new HomeAssistantNumericFactory((haItem as HomeAssistantNumericMenuItem).getPicker()); | ||||
|             var mPicker         = new HomeAssistantNumericPicker(mPickerFactory,haItem); | ||||
|             var mPickerDelegate = new HomeAssistantNumericPickerDelegate(mPicker); | ||||
|             WatchUi.pushView(mPicker,mPickerDelegate,WatchUi.SLIDE_LEFT); | ||||
|         } else if (item instanceof HomeAssistantGroupMenuItem) { | ||||
|             var haMenuItem = item as HomeAssistantGroupMenuItem; | ||||
|             // System.println("IconMenu: " + haMenuItem.getLabel() + " " + haMenuItem.getId()); | ||||
|             WatchUi.pushView(haMenuItem.getMenuView(), new HomeAssistantViewDelegate(false), WatchUi.SLIDE_LEFT); | ||||
|         // } else { | ||||
|         //     System.println(item.getLabel() + " " + item.getId()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user