mirror of
https://github.com/house-of-abbey/GarminHomeAssistant.git
synced 2025-12-16 11:08:15 +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
|
source/ClientId.mc
|
||||||
# Gemini API key for automated translations
|
# Gemini API key for automated translations
|
||||||
gemini_api_key.txt
|
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
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
|
||||||
"name": "Python: Current File",
|
|
||||||
"type": "python",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${file}",
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"justMyCode": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "monkeyc",
|
"type": "monkeyc",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
|
|||||||
136
manifest.xml
136
manifest.xml
@@ -24,7 +24,7 @@
|
|||||||
Use "Monkey C: Edit Application" from the Visual Studio Code command palette
|
Use "Monkey C: Edit Application" from the Visual Studio Code command palette
|
||||||
to update the application attributes.
|
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
|
Use the following from the Visual Studio Code command palette to edit
|
||||||
the build targets:
|
the build targets:
|
||||||
@@ -33,141 +33,7 @@
|
|||||||
"Monkey C: Edit Products" - Lets you add or remove any product
|
"Monkey C: Edit Products" - Lets you add or remove any product
|
||||||
-->
|
-->
|
||||||
<iq:products>
|
<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="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>
|
||||||
<!--
|
<!--
|
||||||
Use "Monkey C: Edit Permissions" from the Visual Studio Code command
|
Use "Monkey C: Edit Permissions" from the Visual Studio Code command
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class HomeAssistantApp extends Application.AppBase {
|
|||||||
private var mGlanceTimer as Timer.Timer?;
|
private var mGlanceTimer as Timer.Timer?;
|
||||||
private var mUpdateTimer as Timer.Timer?;
|
private var mUpdateTimer as Timer.Timer?;
|
||||||
// Array initialised by onReturnFetchMenuConfig()
|
// 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 mIsApp as Lang.Boolean = false; // Or Widget
|
||||||
private var mUpdating as Lang.Boolean = false; // Don't start a second chain of updates
|
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
|
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.
|
//! Group menu item.
|
||||||
//!
|
//!
|
||||||
//! @param definition Items array from the JSON that defines this sub menu.
|
//! @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) {
|
} 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(
|
||||||
@@ -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.
|
//! @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 fullList = [];
|
||||||
var lmi = mItems as Lang.Array<WatchUi.MenuItem>;
|
var lmi = mItems as Lang.Array<WatchUi.MenuItem>;
|
||||||
|
|
||||||
@@ -167,6 +181,12 @@ class HomeAssistantView extends WatchUi.Menu2 {
|
|||||||
fullList.add(item);
|
fullList.add(item);
|
||||||
}
|
}
|
||||||
fullList.addAll(item.getMenuView().getItemsToUpdate());
|
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) {
|
} else if (item instanceof HomeAssistantToggleMenuItem) {
|
||||||
fullList.add(item);
|
fullList.add(item);
|
||||||
} else if (item instanceof HomeAssistantTapMenuItem) {
|
} else if (item instanceof HomeAssistantTapMenuItem) {
|
||||||
@@ -248,6 +268,12 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
|
|||||||
var haItem = item as HomeAssistantTapMenuItem;
|
var haItem = item as HomeAssistantTapMenuItem;
|
||||||
// System.println(haItem.getLabel() + " " + haItem.getId());
|
// System.println(haItem.getLabel() + " " + haItem.getId());
|
||||||
haItem.callService();
|
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) {
|
} else if (item instanceof HomeAssistantGroupMenuItem) {
|
||||||
var haMenuItem = item as HomeAssistantGroupMenuItem;
|
var haMenuItem = item as HomeAssistantGroupMenuItem;
|
||||||
// System.println("IconMenu: " + haMenuItem.getLabel() + " " + haMenuItem.getId());
|
// System.println("IconMenu: " + haMenuItem.getLabel() + " " + haMenuItem.getId());
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ using Toybox.Time;
|
|||||||
//
|
//
|
||||||
(:glance, :background)
|
(:glance, :background)
|
||||||
class Settings {
|
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 mWebhookId as Lang.String? = "";
|
||||||
private static var mApiUrl as Lang.String? = "";
|
private static var mApiUrl as Lang.String? = "https://homeassistant.michel.ruhr/api";
|
||||||
private static var mConfigUrl as Lang.String? = "";
|
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 mCacheConfig as Lang.Boolean = false;
|
||||||
private static var mClearCache as Lang.Boolean = false;
|
private static var mClearCache as Lang.Boolean = false;
|
||||||
private static var mMenuCheck as Lang.Boolean = false;
|
private static var mMenuCheck as Lang.Boolean = false;
|
||||||
@@ -63,10 +63,10 @@ class Settings {
|
|||||||
//
|
//
|
||||||
static function update() {
|
static function update() {
|
||||||
mIsApp = getApp().getIsApp();
|
mIsApp = getApp().getIsApp();
|
||||||
mApiKey = Properties.getValue("api_key");
|
//mApiKey = Properties.getValue("api_key");
|
||||||
mWebhookId = Properties.getValue("webhook_id");
|
mWebhookId = Properties.getValue("webhook_id");
|
||||||
mApiUrl = Properties.getValue("api_url");
|
//mApiUrl = Properties.getValue("api_url");
|
||||||
mConfigUrl = Properties.getValue("config_url");
|
//mConfigUrl = Properties.getValue("config_url");
|
||||||
mCacheConfig = Properties.getValue("cache_config");
|
mCacheConfig = Properties.getValue("cache_config");
|
||||||
mClearCache = Properties.getValue("clear_cache");
|
mClearCache = Properties.getValue("clear_cache");
|
||||||
mMenuCheck = Properties.getValue("enable_menu_update_check");
|
mMenuCheck = Properties.getValue("enable_menu_update_check");
|
||||||
|
|||||||
Reference in New Issue
Block a user