diff --git a/source/HomeAssistantConfirmation.mc b/source/HomeAssistantConfirmation.mc index a59829f..dab35e2 100644 --- a/source/HomeAssistantConfirmation.mc +++ b/source/HomeAssistantConfirmation.mc @@ -35,31 +35,43 @@ class HomeAssistantConfirmation extends WatchUi.Confirmation { //! Delegate to respond to the confirmation request. // class HomeAssistantConfirmationDelegate extends WatchUi.ConfirmationDelegate { - private var mConfirmMethod as Method(state as Lang.Boolean) as Void; - private var mTimer as Timer.Timer or Null; - private var mState as Lang.Boolean; - private var mToggleMethod as Method(state as Lang.Boolean) as Void or Null; + private static var mTimer as Timer.Timer or Null; + + private var mConfirmMethod as Method(state as Lang.Boolean) as Void; + private var mState as Lang.Boolean; + private var mToggleMethod as Method(state as Lang.Boolean) as Void or Null; + private var mConfirmationView as WatchUi.Confirmation; //! Class Constructor //! //! @param options A dictionary describing the following options: - //! - callback Method to call on confirmation. - //! - state Wanted state of a toggle button. - //! - toggle Optional setEnabled method to untoggle ToggleItem. + //! - callback Method to call on confirmation. + //! - confirmationView Confirmation the delegate is active for + //! - state Wanted state of a toggle button. + //! - toggle Optional setEnabled method to untoggle ToggleItem. // function initialize(options as { :callback as Method(state as Lang.Boolean) as Void, + :confirmationView as WatchUi.Confirmation, :state as Lang.Boolean, :toggleMethod as Method(state as Lang.Boolean) or Null, }) { + if (mTimer != null) { + mTimer.stop(); + } + WatchUi.ConfirmationDelegate.initialize(); mConfirmMethod = options[:callback]; + mConfirmationView = options[:confirmationView]; mState = options[:state]; mToggleMethod = options[:toggleMethod]; var timeout = Settings.getConfirmTimeout(); // ms if (timeout > 0) { - mTimer = new Timer.Timer(); + if (mTimer == null) { + mTimer = new Timer.Timer(); + } + mTimer.start(method(:onTimeout), timeout, true); } } @@ -92,6 +104,10 @@ class HomeAssistantConfirmationDelegate extends WatchUi.ConfirmationDelegate { if (mToggleMethod != null) { mToggleMethod.invoke(!mState); } - WatchUi.popView(WatchUi.SLIDE_RIGHT); + + var getCurrentView = WatchUi.getCurrentView(); + if (getCurrentView[0] == mConfirmationView) { + WatchUi.popView(WatchUi.SLIDE_RIGHT); + } } } diff --git a/source/HomeAssistantPinConfirmation.mc b/source/HomeAssistantPinConfirmation.mc index 2cb7501..5ccf475 100644 --- a/source/HomeAssistantPinConfirmation.mc +++ b/source/HomeAssistantPinConfirmation.mc @@ -244,6 +244,11 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { mTimer.stop(); } WatchUi.popView(WatchUi.SLIDE_RIGHT); + + // Set the toggle, if we have one + if (mToggleMethod != null) { + mToggleMethod.invoke(!mState); + } mConfirmMethod.invoke(mState); } else { error(); @@ -285,10 +290,7 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { if (mTimer != null) { mTimer.stop(); } - // Undo the toggle, if we have one - if (mToggleMethod != null) { - mToggleMethod.invoke(!mState); - } + WatchUi.popView(WatchUi.SLIDE_RIGHT); } @@ -314,6 +316,13 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { goBack(); } + //! Handle the back button (ESC) + // + function onBack() as Lang.Boolean { + goBack(); + return true; + } + } diff --git a/source/HomeAssistantService.mc b/source/HomeAssistantService.mc index c69b6f9..56f3f9f 100644 --- a/source/HomeAssistantService.mc +++ b/source/HomeAssistantService.mc @@ -140,7 +140,7 @@ class HomeAssistantService { :service => service, :data => data, :exit => exit, - }), + }, dialog), WatchUi.SLIDE_LEFT ); } else if (! phoneConnected) { diff --git a/source/HomeAssistantTapMenuItem.mc b/source/HomeAssistantTapMenuItem.mc index 0b57c25..8d22d81 100644 --- a/source/HomeAssistantTapMenuItem.mc +++ b/source/HomeAssistantTapMenuItem.mc @@ -92,14 +92,33 @@ class HomeAssistantTapMenuItem extends HomeAssistantMenuItem { ); } } else if (mConfirm) { - WatchUi.pushView( - new HomeAssistantConfirmation(), - new HomeAssistantConfirmationDelegate({ - :callback => method(:onConfirm), - :state => false, - }), - WatchUi.SLIDE_IMMEDIATE - ); + var phoneConnected = System.getDeviceSettings().phoneConnected; + var internetAvailable = System.getDeviceSettings().connectionAvailable; + if ((! phoneConnected || ! internetAvailable) && 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); } diff --git a/source/HomeAssistantToggleMenuItem.mc b/source/HomeAssistantToggleMenuItem.mc index 7a41b4d..3c22ed0 100644 --- a/source/HomeAssistantToggleMenuItem.mc +++ b/source/HomeAssistantToggleMenuItem.mc @@ -240,10 +240,6 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem { //! @param s Boolean indicating the desired state of the toggle switch. // function setState(s as Lang.Boolean) as Void { - // Toggle the UI back, we'll wait for confirmation from the Home Assistant - // Note: with Zigbee2MQTT a.o. we may not always get the state in the response. - setEnabled(!isEnabled()); - var phoneConnected = System.getDeviceSettings().phoneConnected; var internetAvailable = System.getDeviceSettings().connectionAvailable; @@ -256,28 +252,10 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem { ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoInternet) as Lang.String); } else { var id = mData.get("entity_id") as Lang.String; - var url = Settings.getApiUrl() + "/services/"; - if (s) { - url = url + id.substring(0, id.find(".")) + "/turn_on"; - } else { - url = url + id.substring(0, id.find(".")) + "/turn_off"; - } + var url = getUrl(id, s); if ((! phoneConnected || ! internetAvailable) && Settings.getWifiLteExecutionEnabled()) { - var dialogMsg = WatchUi.loadResource($.Rez.Strings.WifiLtePrompt) as Lang.String; - var dialog = new WatchUi.Confirmation(dialogMsg); - WatchUi.pushView( - dialog, - new WifiLteExecutionConfirmDelegate({ - :type => "entity", - :url => url, - :id => id, - :data => mData, - :callback => method(:setToggleStateWithData), - :exit => mExit, - }), - WatchUi.SLIDE_LEFT - ); + wifiPrompt(s); return; } @@ -311,6 +289,9 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem { function callService(b as Lang.Boolean) as Void { var hasTouchScreen = System.getDeviceSettings().isTouchScreen; if (mPin && hasTouchScreen) { + // Undo the toggle + setEnabled(!isEnabled()); + var pin = Settings.getPin(); if (pin != null) { var pinConfirmationView = new HomeAssistantPinConfirmationView(); @@ -327,15 +308,26 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem { ); } } else if (mConfirm) { - WatchUi.pushView( - new HomeAssistantConfirmation(), - new HomeAssistantConfirmationDelegate({ - :callback => method(:onConfirm), - :state => b, - :toggleMethod => method(:setEnabled), - }), - WatchUi.SLIDE_IMMEDIATE - ); + // Undo the toggle + setEnabled(!isEnabled()); + + var phoneConnected = System.getDeviceSettings().phoneConnected; + var internetAvailable = System.getDeviceSettings().connectionAvailable; + if ((! phoneConnected || ! internetAvailable) && Settings.getWifiLteExecutionEnabled()) { + wifiPrompt(b); + } else { + var confirmationView = new HomeAssistantConfirmation(); + WatchUi.pushView( + confirmationView, + new HomeAssistantConfirmationDelegate({ + :callback => method(:onConfirm), + :confirmationView => confirmationView, + :state => b, + :toggleMethod => method(:setEnabled), + }), + WatchUi.SLIDE_IMMEDIATE + ); + } } else { onConfirm(b); } @@ -349,4 +341,45 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem { setState(b); } + //! Displays a confirmation dialog before executing a service call via Wi-Fi/LTE. + //! + //! @param s Desired state: `true` to turn on, `false` to turn off. + // + private function wifiPrompt(s as Lang.Boolean) as Void { + var id = mData.get("entity_id") as Lang.String; + var url = getUrl(id, s); + + var dialogMsg = WatchUi.loadResource($.Rez.Strings.WifiLtePrompt) as Lang.String; + var dialog = new WatchUi.Confirmation(dialogMsg); + WatchUi.pushView( + dialog, + new WifiLteExecutionConfirmDelegate({ + :type => "entity", + :url => url, + :id => id, + :data => mData, + :callback => method(:setToggleStateWithData), + :exit => mExit, + }, dialog), + WatchUi.SLIDE_LEFT + ); + } + + //! Constructs a Home Assistant API URL for the given entity and desired state. + //! + //! @param id The entity ID, e.g., `"switch.kitchen"`. + //! @param s Desired state: `true` for "turn_on", `false` for "turn_off". + //! + //! @return Full service URL string. + // + private function getUrl(id as Lang.String, s as Lang.Boolean) as Lang.String { + var url = Settings.getApiUrl() + "/services/"; + if (s) { + url = url + id.substring(0, id.find(".")) + "/turn_on"; + } else { + url = url + id.substring(0, id.find(".")) + "/turn_off"; + } + + return url; + } } diff --git a/source/WifiLteExecutionConfirmDelegate.mc b/source/WifiLteExecutionConfirmDelegate.mc index a2cdd8e..563baf6 100644 --- a/source/WifiLteExecutionConfirmDelegate.mc +++ b/source/WifiLteExecutionConfirmDelegate.mc @@ -16,8 +16,9 @@ class WifiLteExecutionConfirmDelegate extends WatchUi.ConfirmationDelegate { :exit as Lang.Boolean }; + private static var mTimer as Timer.Timer or Null; private var mHasToast as Lang.Boolean = false; - private var mTimer as Timer.Timer or Null; + private var mConfirmationView as WatchUi.Confirmation; //! Initializes a confirmation delegate to confirm a Wi-Fi or LTE command exection //! @@ -28,6 +29,7 @@ class WifiLteExecutionConfirmDelegate extends WatchUi.ConfirmationDelegate { //! - callback: (For type `"entity"`) A callback method (Method) to handle the response. //! - data: (Optional) A dictionary of data to send with the request. //! - exit: Boolean: if set to true: exit after running command. + //! @param view The Confirmation view the delegate is active for function initialize(cOptions as { :type as Lang.String, :service as Lang.String or Null, @@ -35,13 +37,18 @@ class WifiLteExecutionConfirmDelegate extends WatchUi.ConfirmationDelegate { :url as Lang.String or Null, :callback as Lang.Method or Null, :exit as Lang.Boolean, - }) { + }, view as WatchUi.Confirmation) { ConfirmationDelegate.initialize(); + if (mTimer != null) { + mTimer.stop(); + } + if (WatchUi has :showToast) { mHasToast = true; } + mConfirmationView = view; mCommandData = { :type => cOptions[:type], :service => cOptions[:service], @@ -53,7 +60,10 @@ class WifiLteExecutionConfirmDelegate extends WatchUi.ConfirmationDelegate { var timeout = Settings.getConfirmTimeout(); // ms if (timeout > 0) { - mTimer = new Timer.Timer(); + if (mTimer == null) { + mTimer = new Timer.Timer(); + } + mTimer.start(method(:onTimeout), timeout, true); } } @@ -116,6 +126,10 @@ class WifiLteExecutionConfirmDelegate extends WatchUi.ConfirmationDelegate { //! Function supplied to a timer in order to limit the time for which the confirmation can be provided. function onTimeout() as Void { mTimer.stop(); - WatchUi.popView(WatchUi.SLIDE_RIGHT); + var getCurrentView = WatchUi.getCurrentView(); + + if (getCurrentView[0] == mConfirmationView) { + WatchUi.popView(WatchUi.SLIDE_RIGHT); + } } } \ No newline at end of file