mirror of
				https://github.com/house-of-abbey/GarminHomeAssistant.git
				synced 2025-10-31 07:48:13 +00:00 
			
		
		
		
	Added numeric Menu Item
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -7,3 +7,5 @@ Thumbs.db | ||||
| source/ClientId.mc | ||||
| # Gemini API key for automated translations | ||||
| gemini_api_key.txt | ||||
| # URLs ans settings modified for testing, so don't sync | ||||
| sourc/Settings.mc | ||||
							
								
								
									
										8
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @@ -4,14 +4,6 @@ | ||||
|   // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|   "version": "0.2.0", | ||||
|   "configurations": [ | ||||
|     { | ||||
|       "name": "Python: Current File", | ||||
|       "type": "python", | ||||
|       "request": "launch", | ||||
|       "program": "${file}", | ||||
|       "console": "integratedTerminal", | ||||
|       "justMyCode": true | ||||
|     }, | ||||
|     { | ||||
|       "type": "monkeyc", | ||||
|       "request": "launch", | ||||
|   | ||||
							
								
								
									
										244
									
								
								manifest.xml
									
									
									
									
									
								
							
							
						
						
									
										244
									
								
								manifest.xml
									
									
									
									
									
								
							| @@ -20,211 +20,77 @@ | ||||
|  | ||||
| --> | ||||
| <iq:manifest version="3" xmlns:iq="http://www.garmin.com/xml/connectiq"> | ||||
|   <!-- | ||||
|     <!-- | ||||
|     Use "Monkey C: Edit Application" from the Visual Studio Code command palette | ||||
|     to update the application attributes. | ||||
|   --> | ||||
|   <iq:application id="98c36259-498a-4458-9cef-74a273ad2bc3" type="watch-app" name="@Strings.AppName" entry="HomeAssistantApp" launcherIcon="@Drawables.LauncherIcon" minApiLevel="3.1.0"> | ||||
|     <!-- | ||||
|     <iq:application id="a9077d22-5b9c-4a2e-b4ff-7846f77b13cb" type="watch-app" name="@Strings.AppName" entry="HomeAssistantApp" launcherIcon="@Drawables.LauncherIcon" minApiLevel="3.1.0"> | ||||
|         <!-- | ||||
|       Use the following from the Visual Studio Code command palette to edit | ||||
|       the build targets: | ||||
|       "Monkey C: Set Products by Product Category" - Lets you add all products | ||||
|                                   that belong to the same product category | ||||
|       "Monkey C: Edit Products" - Lets you add or remove any product | ||||
|     --> | ||||
|     <iq:products> | ||||
|       <iq:product id="approachs50"/> | ||||
|       <iq:product id="approachs7042mm"/> | ||||
|       <iq:product id="approachs7047mm"/> | ||||
|       <iq:product id="d2air"/> | ||||
|       <iq:product id="d2airx10"/> | ||||
|       <iq:product id="d2delta"/> | ||||
|       <iq:product id="d2deltapx"/> | ||||
|       <iq:product id="d2deltas"/> | ||||
|       <iq:product id="d2mach1"/> | ||||
|       <iq:product id="descentg1"/> | ||||
|       <iq:product id="descentg2"/> | ||||
|       <iq:product id="descentmk1"/> | ||||
|       <iq:product id="descentmk2"/> | ||||
|       <iq:product id="descentmk2s"/> | ||||
|       <iq:product id="descentmk343mm"/> | ||||
|       <iq:product id="descentmk351mm"/> | ||||
|       <iq:product id="edge1030"/> | ||||
|       <iq:product id="edge1030bontrager"/> | ||||
|       <iq:product id="edge1030plus"/> | ||||
|       <iq:product id="edge1040"/> | ||||
|       <iq:product id="edge1050"/> | ||||
|       <iq:product id="edge520plus"/> | ||||
|       <iq:product id="edge530"/> | ||||
|       <iq:product id="edge540"/> | ||||
|       <iq:product id="edge550"/> | ||||
|       <iq:product id="edge820"/> | ||||
|       <iq:product id="edge830"/> | ||||
|       <iq:product id="edge840"/> | ||||
|       <iq:product id="edge850"/> | ||||
|       <iq:product id="edgeexplore"/> | ||||
|       <iq:product id="edgeexplore2"/> | ||||
|       <iq:product id="edgemtb"/> | ||||
|       <iq:product id="enduro"/> | ||||
|       <iq:product id="enduro3"/> | ||||
|       <iq:product id="epix2"/> | ||||
|       <iq:product id="epix2pro42mm"/> | ||||
|       <iq:product id="epix2pro47mm"/> | ||||
|       <iq:product id="epix2pro51mm"/> | ||||
|       <iq:product id="fenix5"/> | ||||
|       <iq:product id="fenix5plus"/> | ||||
|       <iq:product id="fenix5s"/> | ||||
|       <iq:product id="fenix5splus"/> | ||||
|       <iq:product id="fenix5x"/> | ||||
|       <iq:product id="fenix5xplus"/> | ||||
|       <iq:product id="fenix6"/> | ||||
|       <iq:product id="fenix6pro"/> | ||||
|       <iq:product id="fenix6s"/> | ||||
|       <iq:product id="fenix6spro"/> | ||||
|       <iq:product id="fenix6xpro"/> | ||||
|       <iq:product id="fenix7"/> | ||||
|       <iq:product id="fenix7pro"/> | ||||
|       <iq:product id="fenix7pronowifi"/> | ||||
|       <iq:product id="fenix7s"/> | ||||
|       <iq:product id="fenix7spro"/> | ||||
|       <iq:product id="fenix7x"/> | ||||
|       <iq:product id="fenix7xpro"/> | ||||
|       <iq:product id="fenix7xpronowifi"/> | ||||
|       <iq:product id="fenix843mm"/> | ||||
|       <iq:product id="fenix847mm"/> | ||||
|       <iq:product id="fenix8pro47mm"/> | ||||
|       <iq:product id="fenix8solar47mm"/> | ||||
|       <iq:product id="fenix8solar51mm"/> | ||||
|       <iq:product id="fenixchronos"/> | ||||
|       <iq:product id="fenixe"/> | ||||
|       <iq:product id="fr165"/> | ||||
|       <iq:product id="fr165m"/> | ||||
|       <iq:product id="fr245"/> | ||||
|       <iq:product id="fr245m"/> | ||||
|       <iq:product id="fr255"/> | ||||
|       <iq:product id="fr255m"/> | ||||
|       <iq:product id="fr255s"/> | ||||
|       <iq:product id="fr255sm"/> | ||||
|       <iq:product id="fr265"/> | ||||
|       <iq:product id="fr265s"/> | ||||
|       <iq:product id="fr55"/> | ||||
|       <iq:product id="fr57042mm"/> | ||||
|       <iq:product id="fr57047mm"/> | ||||
|       <iq:product id="fr645"/> | ||||
|       <iq:product id="fr645m"/> | ||||
|       <iq:product id="fr745"/> | ||||
|       <iq:product id="fr935"/> | ||||
|       <iq:product id="fr945"/> | ||||
|       <iq:product id="fr945lte"/> | ||||
|       <iq:product id="fr955"/> | ||||
|       <iq:product id="fr965"/> | ||||
|       <iq:product id="fr970"/> | ||||
|       <iq:product id="gpsmap66"/> | ||||
|       <iq:product id="gpsmap67"/> | ||||
|       <iq:product id="gpsmaph1"/> | ||||
|       <iq:product id="instinct2"/> | ||||
|       <iq:product id="instinct2s"/> | ||||
|       <iq:product id="instinct2x"/> | ||||
|       <iq:product id="instinct3amoled45mm"/> | ||||
|       <iq:product id="instinct3amoled50mm"/> | ||||
|       <iq:product id="instinct3solar45mm"/> | ||||
|       <iq:product id="instinctcrossover"/> | ||||
|       <iq:product id="instinctcrossoveramoled"/> | ||||
|       <iq:product id="instincte40mm"/> | ||||
|       <iq:product id="instincte45mm"/> | ||||
|       <iq:product id="legacyherocaptainmarvel"/> | ||||
|       <iq:product id="legacyherofirstavenger"/> | ||||
|       <iq:product id="legacysagadarthvader"/> | ||||
|       <iq:product id="legacysagarey"/> | ||||
|       <iq:product id="marq2"/> | ||||
|       <iq:product id="marq2aviator"/> | ||||
|       <iq:product id="marqadventurer"/> | ||||
|       <iq:product id="marqathlete"/> | ||||
|       <iq:product id="marqaviator"/> | ||||
|       <iq:product id="marqcaptain"/> | ||||
|       <iq:product id="marqcommander"/> | ||||
|       <iq:product id="marqdriver"/> | ||||
|       <iq:product id="marqexpedition"/> | ||||
|       <iq:product id="marqgolfer"/> | ||||
|       <iq:product id="montana7xx"/> | ||||
|       <iq:product id="venu"/> | ||||
|       <iq:product id="venu2"/> | ||||
|       <iq:product id="venu2plus"/> | ||||
|       <iq:product id="venu2s"/> | ||||
|       <iq:product id="venu3"/> | ||||
|       <iq:product id="venu3s"/> | ||||
|       <iq:product id="venu441mm"/> | ||||
|       <iq:product id="venu445mm"/> | ||||
|       <iq:product id="venud"/> | ||||
|       <iq:product id="venusq"/> | ||||
|       <iq:product id="venusq2"/> | ||||
|       <iq:product id="venusq2m"/> | ||||
|       <iq:product id="venusqm"/> | ||||
|       <iq:product id="venux1"/> | ||||
|       <iq:product id="vivoactive3"/> | ||||
|       <iq:product id="vivoactive3m"/> | ||||
|       <iq:product id="vivoactive3mlte"/> | ||||
|       <iq:product id="vivoactive4"/> | ||||
|       <iq:product id="vivoactive4s"/> | ||||
|       <iq:product id="vivoactive5"/> | ||||
|       <iq:product id="vivoactive6"/> | ||||
|     </iq:products> | ||||
|     <!-- | ||||
|         <iq:products> | ||||
|             <iq:product id="epix2pro51mm"/> | ||||
|         </iq:products> | ||||
|         <!-- | ||||
|       Use "Monkey C: Edit Permissions" from the Visual Studio Code command | ||||
|       palette to update permissions. | ||||
|     --> | ||||
|     <iq:permissions> | ||||
|       <iq:uses-permission id="Background"/> | ||||
|       <iq:uses-permission id="BluetoothLowEnergy"/> | ||||
|       <iq:uses-permission id="Communications"/> | ||||
|       <iq:uses-permission id="Positioning"/> | ||||
|     </iq:permissions> | ||||
|     <!-- | ||||
|         <iq:permissions> | ||||
|             <iq:uses-permission id="Background"/> | ||||
|             <iq:uses-permission id="BluetoothLowEnergy"/> | ||||
|             <iq:uses-permission id="Communications"/> | ||||
|             <iq:uses-permission id="Positioning"/> | ||||
|         </iq:permissions> | ||||
|         <!-- | ||||
|       Use "Monkey C: Edit Languages" from the Visual Studio Code command | ||||
|       palette to edit your compatible language list. | ||||
|     --> | ||||
|     <iq:languages> | ||||
|       <iq:language>ara</iq:language> | ||||
|       <iq:language>bul</iq:language> | ||||
|       <iq:language>ces</iq:language> | ||||
|       <iq:language>dan</iq:language> | ||||
|       <iq:language>deu</iq:language> | ||||
|       <iq:language>dut</iq:language> | ||||
|       <iq:language>eng</iq:language> | ||||
|       <iq:language>est</iq:language> | ||||
|       <iq:language>fin</iq:language> | ||||
|       <iq:language>fre</iq:language> | ||||
|       <iq:language>gre</iq:language> | ||||
|       <iq:language>heb</iq:language> | ||||
|       <iq:language>hrv</iq:language> | ||||
|       <iq:language>hun</iq:language> | ||||
|       <iq:language>ind</iq:language> | ||||
|       <iq:language>ita</iq:language> | ||||
|       <iq:language>jpn</iq:language> | ||||
|       <iq:language>kor</iq:language> | ||||
|       <iq:language>lav</iq:language> | ||||
|       <iq:language>lit</iq:language> | ||||
|       <iq:language>nob</iq:language> | ||||
|       <iq:language>pol</iq:language> | ||||
|       <iq:language>por</iq:language> | ||||
|       <iq:language>ron</iq:language> | ||||
|       <!-- <iq:language>rus</iq:language> --> | ||||
|       <iq:language>slo</iq:language> | ||||
|       <iq:language>slv</iq:language> | ||||
|       <iq:language>spa</iq:language> | ||||
|       <iq:language>swe</iq:language> | ||||
|       <iq:language>tha</iq:language> | ||||
|       <iq:language>tur</iq:language> | ||||
|       <iq:language>ukr</iq:language> | ||||
|       <iq:language>vie</iq:language> | ||||
|       <iq:language>zhs</iq:language> | ||||
|       <iq:language>zht</iq:language> | ||||
|       <iq:language>zsm</iq:language> | ||||
|     </iq:languages> | ||||
|     <!-- | ||||
|         <iq:languages> | ||||
|             <iq:language>ara</iq:language> | ||||
|             <iq:language>bul</iq:language> | ||||
|             <iq:language>ces</iq:language> | ||||
|             <iq:language>dan</iq:language> | ||||
|             <iq:language>deu</iq:language> | ||||
|             <iq:language>dut</iq:language> | ||||
|             <iq:language>eng</iq:language> | ||||
|             <iq:language>est</iq:language> | ||||
|             <iq:language>fin</iq:language> | ||||
|             <iq:language>fre</iq:language> | ||||
|             <iq:language>gre</iq:language> | ||||
|             <iq:language>heb</iq:language> | ||||
|             <iq:language>hrv</iq:language> | ||||
|             <iq:language>hun</iq:language> | ||||
|             <iq:language>ind</iq:language> | ||||
|             <iq:language>ita</iq:language> | ||||
|             <iq:language>jpn</iq:language> | ||||
|             <iq:language>kor</iq:language> | ||||
|             <iq:language>lav</iq:language> | ||||
|             <iq:language>lit</iq:language> | ||||
|             <iq:language>nob</iq:language> | ||||
|             <iq:language>pol</iq:language> | ||||
|             <iq:language>por</iq:language> | ||||
|             <iq:language>ron</iq:language> | ||||
|             <!-- <iq:language>rus</iq:language> --> | ||||
|             <iq:language>slo</iq:language> | ||||
|             <iq:language>slv</iq:language> | ||||
|             <iq:language>spa</iq:language> | ||||
|             <iq:language>swe</iq:language> | ||||
|             <iq:language>tha</iq:language> | ||||
|             <iq:language>tur</iq:language> | ||||
|             <iq:language>ukr</iq:language> | ||||
|             <iq:language>vie</iq:language> | ||||
|             <iq:language>zhs</iq:language> | ||||
|             <iq:language>zht</iq:language> | ||||
|             <iq:language>zsm</iq:language> | ||||
|         </iq:languages> | ||||
|         <!-- | ||||
|       Use "Monkey C: Configure Monkey Barrel" from the Visual Studio Code | ||||
|       command palette to edit the included barrels. | ||||
|     --> | ||||
|     <iq:barrels/> | ||||
|   </iq:application> | ||||
|         <iq:barrels/> | ||||
|     </iq:application> | ||||
| </iq:manifest> | ||||
| @@ -37,7 +37,7 @@ class HomeAssistantApp extends Application.AppBase { | ||||
|     private var mGlanceTimer    as Timer.Timer?; | ||||
|     private var mUpdateTimer    as Timer.Timer?; | ||||
|     // Array initialised by onReturnFetchMenuConfig() | ||||
|     private var mItemsToUpdate  as Lang.Array<HomeAssistantToggleMenuItem or HomeAssistantTapMenuItem or HomeAssistantGroupMenuItem>?; | ||||
|     private var mItemsToUpdate  as Lang.Array<HomeAssistantToggleMenuItem or HomeAssistantTapMenuItem or HomeAssistantGroupMenuItem or HomeAssistantNumericMenuItem>?; | ||||
|     private var mIsApp          as Lang.Boolean     = false; // Or Widget | ||||
|     private var mUpdating       as Lang.Boolean     = false; // Don't start a second chain of updates | ||||
|     private var mTemplates      as Lang.Dictionary? = null;  // Cache of compiled templates | ||||
|   | ||||
| @@ -148,7 +148,48 @@ class HomeAssistantMenuItemFactory { | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|     //! Numeric menu item. | ||||
|     //! | ||||
|     //! @param definition Items array from the JSON that defines this sub menu. | ||||
|     //! @param template   Template for Home Assistant to render (optional) | ||||
|     // | ||||
|     function numeric( | ||||
|         label     as Lang.String or Lang.Symbol, | ||||
|         entity_id as Lang.String?, | ||||
|         template  as Lang.String?, | ||||
|         service   as Lang.String?, | ||||
|         data      as Lang.Dictionary?, | ||||
|         options   as { | ||||
|             :exit    as Lang.Boolean, | ||||
|             :confirm as Lang.Boolean, | ||||
|             :pin     as Lang.Boolean, | ||||
|             :icon    as WatchUi.Bitmap | ||||
|         } | ||||
|     ) as WatchUi.MenuItem { | ||||
|         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, | ||||
|             entity_id, | ||||
|             template, | ||||
|             service, | ||||
|             data, | ||||
|             options, | ||||
|             mHomeAssistantService | ||||
|         ); | ||||
|     } | ||||
|     //! Group menu item. | ||||
|     //! | ||||
|     //! @param definition Items array from the JSON that defines this sub menu. | ||||
|   | ||||
							
								
								
									
										231
									
								
								source/HomeAssistantNumericMenuItem.mc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								source/HomeAssistantNumericMenuItem.mc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | ||||
| //----------------------------------------------------------------------------------- | ||||
| // | ||||
| // 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 & Someone0nEarth, 31 October 2023 | ||||
| // | ||||
| //----------------------------------------------------------------------------------- | ||||
|  | ||||
| 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. | ||||
| // | ||||
| class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem { | ||||
|     private var mHomeAssistantService as HomeAssistantService?; | ||||
|     private var mService              as Lang.String?; | ||||
|     private var mConfirm              as Lang.Boolean; | ||||
|     private var mExit                 as Lang.Boolean; | ||||
|     private var mPin                  as Lang.Boolean; | ||||
|     private var mData                 as Lang.Dictionary?; | ||||
|     private var mStep                 as Lang.Float=1.0; | ||||
|     private var mValueChanged         as Lang.Boolean = false; | ||||
|     private var mValue                as Lang.Float?;   | ||||
|     private var mEntity               as Lang.String?; | ||||
|     private var mFormatString         as Lang.String="%.1f"; | ||||
|  | ||||
|  | ||||
|     //! Class Constructor | ||||
|     //! | ||||
|     //! @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 icon      Icon to use for the menu item. | ||||
|     //! @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. | ||||
|     // | ||||
|     function initialize( | ||||
|         label     as Lang.String or Lang.Symbol, | ||||
|         entity    as Lang.String, | ||||
|         template  as Lang.String, | ||||
|         service   as Lang.String?, | ||||
|         data      as Lang.Dictionary?, | ||||
|         options   as { | ||||
|             :alignment as WatchUi.MenuItem.Alignment, | ||||
|             :icon      as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol, | ||||
|             :exit      as Lang.Boolean, | ||||
|             :confirm   as Lang.Boolean, | ||||
|             :pin       as Lang.Boolean | ||||
|         }?, | ||||
|         haService as HomeAssistantService | ||||
|     ) { | ||||
|         mService              = service; | ||||
|         mData                 = data; | ||||
|         mExit                 = options[:exit]; | ||||
|         mConfirm              = options[:confirm]; | ||||
|         mPin                  = options[:pin]; | ||||
|         mLabel                = label; | ||||
|         mHomeAssistantService = haService; | ||||
|         mEntity               = entity; | ||||
|  | ||||
|         HomeAssistantMenuItem.initialize( | ||||
|             label, | ||||
|             template, | ||||
|             { | ||||
|                 :alignment => options[:alignment], | ||||
|                 :icon      => options[:icon] | ||||
|             }  | ||||
|         ); | ||||
|  | ||||
|  | ||||
|        | ||||
|         if  (mData.get("step") != null) { | ||||
|             mStep = mData.get("step").toString().toFloat(); | ||||
|         } | ||||
|  | ||||
|         if (mData.get("formatString") != null) { | ||||
|             mFormatString=mData.get("formatString").toString(); | ||||
|         } | ||||
|          | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function callService() as Void { | ||||
|         if (!mValueChanged) { return; } | ||||
|         var hasTouchScreen = System.getDeviceSettings().isTouchScreen; | ||||
|         if (mPin && hasTouchScreen) { | ||||
|             var pin = Settings.getPin(); | ||||
|             if (pin != null) { | ||||
|                 var pinConfirmationView = new HomeAssistantPinConfirmationView(); | ||||
|                 WatchUi.pushView( | ||||
|                     pinConfirmationView, | ||||
|                     new HomeAssistantPinConfirmationDelegate({ | ||||
|                         :callback    => method(:onConfirm), | ||||
|                         :pin         => pin,  | ||||
|                         :state       => false, | ||||
|                         :view        => pinConfirmationView, | ||||
|                     }), | ||||
|                     WatchUi.SLIDE_IMMEDIATE | ||||
|                 ); | ||||
|             } | ||||
|         } else if (mConfirm) { | ||||
|             if ((! System.getDeviceSettings().phoneConnected || | ||||
|                  ! System.getDeviceSettings().connectionAvailable) && | ||||
|                 Settings.getWifiLteExecutionEnabled()) { | ||||
|                 var dialogMsg = WatchUi.loadResource($.Rez.Strings.WifiLtePrompt) as Lang.String; | ||||
|                 var dialog = new WatchUi.Confirmation(dialogMsg); | ||||
|                 WatchUi.pushView( | ||||
|                     dialog, | ||||
|                     new WifiLteExecutionConfirmDelegate({ | ||||
|                         :type    => "service", | ||||
|                         :service => mService, | ||||
|                         :data    => mData, | ||||
|                         :exit    => mExit, | ||||
|                     }, dialog), | ||||
|                     WatchUi.SLIDE_LEFT | ||||
|                 ); | ||||
|             } else { | ||||
|                 var view = new HomeAssistantConfirmation(); | ||||
|                 WatchUi.pushView( | ||||
|                     view, | ||||
|                     new HomeAssistantConfirmationDelegate({ | ||||
|                         :callback         => method(:onConfirm), | ||||
|                         :confirmationView => view, | ||||
|                         :state            => false, | ||||
|                     }), | ||||
|                     WatchUi.SLIDE_IMMEDIATE | ||||
|                 ); | ||||
|             } | ||||
|         } else { | ||||
|             onConfirm(false); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     //! 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 { | ||||
|         if (mService != null) { | ||||
|             mHomeAssistantService.call(mService, {"entity_id"  => mEntity,mData.get("valueLabel").toString() => mValue}, mExit); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     ///! Increase value when Up button is pressed or touch screen swipe down  | ||||
|  | ||||
|     function increaseValue() as Void { | ||||
|         if (mValueChanged) | ||||
|         { | ||||
|             mValue += mStep; | ||||
|         } | ||||
|         else { | ||||
|             mValue= getSubLabel().toFloat() + mStep; | ||||
|             mValueChanged=true; | ||||
|         } | ||||
|         setSubLabel(mValue.format(mFormatString)); | ||||
|     } | ||||
|  | ||||
|     ///! Decrease value when Down button is pressed or touch screen swipe up | ||||
|     function decreaseValue() as Void { | ||||
|         if (mValueChanged) | ||||
|         { | ||||
|             mValue -= mStep; | ||||
|         } | ||||
|         else { | ||||
|             mValue= getSubLabel().toFloat() - mStep; | ||||
|             mValueChanged=true; | ||||
|         } | ||||
|         setSubLabel(mValue.format(mFormatString)); | ||||
|     } | ||||
|  | ||||
|     //! 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 { | ||||
|         // If vlue has changed, don't use value from HomeAssitant but display target value | ||||
|         if (mValueChanged) { | ||||
|             setSubLabel(mValue.format(mFormatString)); | ||||
|             WatchUi.requestUpdate(); | ||||
|             return; | ||||
|         } | ||||
|         if (data == null) { | ||||
|             setSubLabel($.Rez.Strings.Empty); | ||||
|         } else if(data instanceof Lang.String) { | ||||
|             setSubLabel(data); | ||||
|         } else if(data instanceof Lang.Number) { | ||||
|             var d = data as Lang.Number; | ||||
|             setSubLabel(d.format("%d")); | ||||
|         } else if(data instanceof Lang.Float) { | ||||
|             var f = data as Lang.Float; | ||||
|             setSubLabel(f.format(mFormatString)); | ||||
|         } else if(data instanceof Lang.Dictionary) { | ||||
|             // System.println("HomeAssistantMenuItem updateState() data = " + data); | ||||
|             if (data.get("error") != null) { | ||||
|                 setSubLabel($.Rez.Strings.TemplateError); | ||||
|             } else { | ||||
|                 setSubLabel($.Rez.Strings.PotentialError); | ||||
|             } | ||||
|         } else { | ||||
|             // The template must return a Lang.String, Number or Float, or the item cannot be formatted locally without error. | ||||
|             setSubLabel(WatchUi.loadResource($.Rez.Strings.TemplateError) as Lang.String); | ||||
|         } | ||||
|         WatchUi.requestUpdate(); | ||||
|     } | ||||
|  | ||||
|     //! Set the mValuChanged value. | ||||
|     //! | ||||
|     //! Can be used to reenable update of subLabel | ||||
|     // | ||||
|     function setValueChanged(b as Lang.Boolean) as Void { | ||||
|         mValueChanged = b; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										212
									
								
								source/HomeAssistantNumericView.mc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								source/HomeAssistantNumericView.mc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | ||||
| //----------------------------------------------------------------------------------- | ||||
| // | ||||
| // 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 & Someone0nEarth & moesterheld, 31 October 2023 | ||||
| // | ||||
| //----------------------------------------------------------------------------------- | ||||
|  | ||||
| using Toybox.Application; | ||||
| using Toybox.Lang; | ||||
| using Toybox.Graphics; | ||||
| using Toybox.System; | ||||
| using Toybox.WatchUi; | ||||
|  | ||||
|  | ||||
| using Toybox.Application.Properties; | ||||
| using Toybox.Timer; | ||||
|  | ||||
|  | ||||
| //! Home Assistant menu construction. | ||||
| // | ||||
| class HomeAssistantNumericView extends WatchUi.Menu2 { | ||||
|  | ||||
|     private var mMenuItem as HomeAssistantNumericMenuItem; | ||||
|  | ||||
|  | ||||
|     //! Class Constructor | ||||
|     // | ||||
|     function initialize( | ||||
|        menuItem as HomeAssistantNumericMenuItem | ||||
|  | ||||
|     ) { | ||||
|         mMenuItem = menuItem;         | ||||
|  | ||||
|         WatchUi.Menu2.initialize({:title => mMenuItem.getLabel()}); | ||||
|          | ||||
|         addItem(mMenuItem); | ||||
|  | ||||
|         //updateState(mData); | ||||
|          | ||||
|     } | ||||
|      | ||||
|     //! Return the menu item | ||||
|     //! | ||||
|     //! @return A HomeAssitantTapMenuItem (or null). | ||||
|     // | ||||
|     function getMenuItem() as HomeAssistantNumericMenuItem? { | ||||
|         return mMenuItem; | ||||
|     } | ||||
|  | ||||
|     //! 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 { | ||||
|         if (data == null) { | ||||
|             mMenuItem.setSubLabel($.Rez.Strings.Empty); | ||||
|         } else if(data instanceof Lang.String) { | ||||
|             mMenuItem.setSubLabel(data); | ||||
|         } else if(data instanceof Lang.Number) { | ||||
|             var d = data as Lang.Number; | ||||
|             mMenuItem.setSubLabel(d.format("%d")); | ||||
|         } else if(data instanceof Lang.Float) { | ||||
|             var f = data as Lang.Float; | ||||
|             mMenuItem.setSubLabel(f.format("%f")); | ||||
|         } else if(data instanceof Lang.Dictionary) { | ||||
|             // System.println("HomeAssistantMenuItem updateState() data = " + data); | ||||
|             if (data.get("error") != null) { | ||||
|                 mMenuItem.setSubLabel($.Rez.Strings.TemplateError); | ||||
|             } else { | ||||
|                 mMenuItem.setSubLabel($.Rez.Strings.PotentialError); | ||||
|             } | ||||
|         } else { | ||||
|             // The template must return a Lang.String, Number or Float, or the item cannot be formatted locally without error. | ||||
|             mMenuItem.setSubLabel(WatchUi.loadResource($.Rez.Strings.TemplateError) as Lang.String); | ||||
|         } | ||||
|         WatchUi.requestUpdate(); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     //! Return a list of items that need to be updated within this menu structure. | ||||
|     //! | ||||
|     //! MN. Lang.Array.addAll() fails structural type checking without including "Null" in the return type | ||||
|     //! | ||||
|     //! @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 Null> { | ||||
|         var fullList = []; | ||||
|         var lmi = mItems as Lang.Array<WatchUi.MenuItem>; | ||||
|  | ||||
|         for(var i = 0; i < mItems.size(); i++) { | ||||
|             var item = lmi[i]; | ||||
|             if (item instanceof HomeAssistantGroupMenuItem) { | ||||
|                 // Group menu items can now have an optional template to evaluate | ||||
|                 var gmi = item as HomeAssistantGroupMenuItem; | ||||
|                 if (gmi.hasTemplate()) { | ||||
|                     fullList.add(item); | ||||
|                 } | ||||
|                 fullList.addAll(item.getMenuView().getItemsToUpdate()); | ||||
|             } else if (item instanceof HomeAssistantToggleMenuItem) { | ||||
|                 fullList.add(item); | ||||
|             } else if (item instanceof HomeAssistantTapMenuItem) { | ||||
|                 var tmi = item as HomeAssistantTapMenuItem; | ||||
|                 if (tmi.hasTemplate()) { | ||||
|                     fullList.add(item); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return fullList; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     //! 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. | ||||
| //! | ||||
| //! Reference: https://developer.garmin.com/connect-iq/core-topics/input-handling/ | ||||
| // | ||||
| class HomeAssistantNumericViewDelegate extends WatchUi.Menu2InputDelegate { | ||||
|     private var mIsRootMenuView as Lang.Boolean = false; | ||||
|     private var mTimer          as QuitTimer; | ||||
|     private var mItem           as HomeAssistantNumericMenuItem; | ||||
|  | ||||
|     //! Class Constructor | ||||
|     //! | ||||
|     //! @param isRootMenuView As menus can be nested, this state marks the top level menu so that the | ||||
|     //!                       back event can exit the application completely rather than just popping | ||||
|     //!                       a menu view. | ||||
|     //tap | ||||
|     function initialize(isRootMenuView as Lang.Boolean, item as HomeAssistantNumericMenuItem) { | ||||
|         Menu2InputDelegate.initialize(); | ||||
|         mIsRootMenuView = isRootMenuView; | ||||
|         mTimer          = getApp().getQuitTimer(); | ||||
|         mItem           = item; | ||||
|     } | ||||
|  | ||||
|     //! Handle the back button (ESC) | ||||
|     // | ||||
|     function onBack() { | ||||
|         mTimer.reset(); | ||||
|  | ||||
|         mItem.setValueChanged(false); | ||||
|  | ||||
|         if (mIsRootMenuView) { | ||||
|             // If its started from glance or as an activity, directly exit the widget/app | ||||
|             // (on widgets without glance, this exit() won't do anything, | ||||
|             // so the base view will be shown instead, through the popView below this "if body") | ||||
|             System.exit(); | ||||
|         } | ||||
|  | ||||
|         WatchUi.popView(WatchUi.SLIDE_RIGHT); | ||||
|     } | ||||
|  | ||||
|     //! Only for CheckboxMenu | ||||
|     // | ||||
|     function onDone() { | ||||
|         mTimer.reset(); | ||||
|     } | ||||
|  | ||||
|     //! Only for CustomMenu | ||||
|     // | ||||
|     function onFooter() { | ||||
|         mTimer.reset(); | ||||
|     } | ||||
|  | ||||
|     // Decrease Value | ||||
|     function onNextPage() as Lang.Boolean { | ||||
|         mItem.decreaseValue(); | ||||
|         return true; | ||||
|     } | ||||
|     //Increase Value | ||||
|     function onPreviousPage() as Lang.Boolean { | ||||
|         mItem.increaseValue(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     | ||||
|     //! Select event | ||||
|     //! | ||||
|     //! @param item Selected menu item. | ||||
|     // | ||||
|     function onSelect(item as WatchUi.MenuItem) as Void { | ||||
|         mTimer.reset(); | ||||
|         mItem.callService(); | ||||
|         WatchUi.popView(WatchUi.SLIDE_RIGHT); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     //! Only for CustomMenu | ||||
|     // | ||||
|     function onTitle() { | ||||
|         mTimer.reset(); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -126,6 +126,19 @@ class HomeAssistantView extends WatchUi.Menu2 { | ||||
|                                 } | ||||
|                             )); | ||||
|                         } | ||||
|                     } else if (type.equals("numeric") && service != null) { | ||||
|                         addItem(HomeAssistantMenuItemFactory.create().numeric( | ||||
|                             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( | ||||
| @@ -154,7 +167,8 @@ 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 Null> { | ||||
|  | ||||
|     function getItemsToUpdate() as Lang.Array<HomeAssistantToggleMenuItem or HomeAssistantTapMenuItem or HomeAssistantGroupMenuItem  or HomeAssistantNumericMenuItem or Null> { | ||||
|         var fullList = []; | ||||
|         var lmi = mItems as Lang.Array<WatchUi.MenuItem>; | ||||
|  | ||||
| @@ -167,6 +181,12 @@ class HomeAssistantView extends WatchUi.Menu2 { | ||||
|                     fullList.add(item); | ||||
|                 } | ||||
|                 fullList.addAll(item.getMenuView().getItemsToUpdate()); | ||||
|             } else if (item instanceof HomeAssistantNumericMenuItem) { | ||||
|                 // Numeric items can have an optional template to evaluate | ||||
|                 var nmi = item as HomeAssistantNumericMenuItem; | ||||
|                 if (nmi.hasTemplate()) { | ||||
|                     fullList.add(item); | ||||
|                 } | ||||
|             } else if (item instanceof HomeAssistantToggleMenuItem) { | ||||
|                 fullList.add(item); | ||||
|             } else if (item instanceof HomeAssistantTapMenuItem) { | ||||
| @@ -248,6 +268,12 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate { | ||||
|             var haItem = item as HomeAssistantTapMenuItem; | ||||
|             // System.println(haItem.getLabel() + " " + haItem.getId()); | ||||
|             haItem.callService(); | ||||
|         } else if (item instanceof HomeAssistantNumericMenuItem) { | ||||
|             var haItem = item as HomeAssistantNumericMenuItem; | ||||
|             // System.println(haItem.getLabel() + " " + haItem.getId()); | ||||
|             // create new view to select new valu | ||||
|             var numView = new HomeAssistantNumericView(haItem); | ||||
|             WatchUi.pushView(numView, new HomeAssistantNumericViewDelegate(false,haItem), WatchUi.SLIDE_LEFT); | ||||
|         } else if (item instanceof HomeAssistantGroupMenuItem) { | ||||
|             var haMenuItem = item as HomeAssistantGroupMenuItem; | ||||
|             // System.println("IconMenu: " + haMenuItem.getLabel() + " " + haMenuItem.getId()); | ||||
|   | ||||
| @@ -29,10 +29,10 @@ using Toybox.Time; | ||||
| // | ||||
| (:glance, :background) | ||||
| class Settings { | ||||
|     private static var mApiKey                as Lang.String? = ""; | ||||
|     private static var mApiKey                as Lang.String? = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJlNThlZDk1MjIwMmU0MmYyOTVmODYyMGVjNDQwZDk1MCIsImlhdCI6MTc0MjkwNzc4MSwiZXhwIjoyMDU4MjY3NzgxfQ.kM8pXYPRADMrDGqmYYEloZH50avOWtCSzpoZbC0gze0"; | ||||
|     private static var mWebhookId             as Lang.String? = ""; | ||||
|     private static var mApiUrl                as Lang.String? = ""; | ||||
|     private static var mConfigUrl             as Lang.String? = ""; | ||||
|     private static var mApiUrl                as Lang.String? = "https://homeassistant.michel.ruhr/api"; | ||||
|     private static var mConfigUrl             as Lang.String? = "https://homeassistant.michel.ruhr/local/garmin-test.json"; | ||||
|     private static var mCacheConfig           as Lang.Boolean = false; | ||||
|     private static var mClearCache            as Lang.Boolean = false; | ||||
|     private static var mMenuCheck             as Lang.Boolean = false; | ||||
| @@ -63,10 +63,10 @@ class Settings { | ||||
|     // | ||||
|     static function update() { | ||||
|         mIsApp                 = getApp().getIsApp(); | ||||
|         mApiKey                = Properties.getValue("api_key"); | ||||
|         //mApiKey                = Properties.getValue("api_key"); | ||||
|         mWebhookId             = Properties.getValue("webhook_id"); | ||||
|         mApiUrl                = Properties.getValue("api_url"); | ||||
|         mConfigUrl             = Properties.getValue("config_url"); | ||||
|         //mApiUrl                = Properties.getValue("api_url"); | ||||
|         //mConfigUrl             = Properties.getValue("config_url"); | ||||
|         mCacheConfig           = Properties.getValue("cache_config"); | ||||
|         mClearCache            = Properties.getValue("clear_cache"); | ||||
|         mMenuCheck             = Properties.getValue("enable_menu_update_check"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user