From 8b29dd0985c8f4afb854fed071eb3e35ba4f1715 Mon Sep 17 00:00:00 2001 From: jens Date: Sun, 5 Dec 2021 14:51:00 +0100 Subject: [PATCH] Rework --- .../receivers/ActivityDetectionReceiver.java | 2 +- .../java/com/jens/automation2/Rule.java | 48 +++++----- .../java/com/jens/automation2/Rule.java | 95 ++++++++++--------- .../ActivityManageTriggerDevicePosition.java | 26 ++++- .../com/jens/automation2/PointOfInterest.java | 4 +- .../location/LocationProvider.java | 2 +- .../location/WifiBroadcastReceiver.java | 2 +- .../receivers/BatteryReceiver.java | 10 +- .../receivers/BluetoothReceiver.java | 2 +- .../receivers/ConnectivityReceiver.java | 4 +- .../receivers/DateTimeListener.java | 2 +- .../receivers/DevicePositionListener.java | 17 ++-- .../receivers/HeadphoneJackListener.java | 2 +- .../automation2/receivers/NfcReceiver.java | 2 +- .../automation2/receivers/NoiseListener.java | 2 +- .../receivers/NotificationListener.java | 19 ++-- .../receivers/PhoneStatusListener.java | 6 +- .../receivers/ProcessListener.java | 45 ++++----- app/src/main/res/values/strings.xml | 3 +- 19 files changed, 157 insertions(+), 136 deletions(-) diff --git a/app/src/apkFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java b/app/src/apkFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java index f20ec687..8dd7755f 100644 --- a/app/src/apkFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java +++ b/app/src/apkFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java @@ -294,7 +294,7 @@ public class ActivityDetectionReceiver extends IntentService implements Automati ArrayList allRulesWithActivityDetection = Rule.findRuleCandidatesByActivityDetection(); for(int i=0; i Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleIsDeactivatedCantApply), this.getName()), 3); return false; } - + + /** + * This is actually a function of the class Trigger, but Rule is already distinguished by flavors, Trigger is not. + * Hence it is here. + * @param oneTrigger + * @return + */ + boolean checkActivityDetection(Trigger oneTrigger) + { + /* + Feature not present in FOSS edition. + */ + return false; + } + private class ActivateRuleTask extends AsyncTask { boolean wasActivated = false; @@ -393,7 +396,8 @@ public class Rule implements Comparable if (Looper.myLooper() == null) Looper.prepare(); - + + setLastExecution(Calendar.getInstance()); wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]); return null; @@ -418,7 +422,7 @@ public class Rule implements Comparable */ if(wasActivated) { - setLastExecution(Calendar.getInstance()); +// setLastExecution(Calendar.getInstance()); AutomationService.updateNotification(); ActivityMainScreen.updateMainScreen(); super.onPostExecute(result); @@ -437,8 +441,8 @@ public class Rule implements Comparable boolean doToggle = ruleToggle && isActuallyToggable; //if(notLastActive || force || doToggle) - if(force || doToggle) - { +// if(force || doToggle) +// { String message; if(!doToggle) message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName()); @@ -482,12 +486,12 @@ public class Rule implements Comparable } Miscellaneous.logEvent("i", "Rule", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleActivationComplete), Rule.this.getName()), 2); - } - else - { - Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); - return false; - } +// } +// else +// { +// Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); +// return false; +// } return true; } diff --git a/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java b/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java index 4a4fa69b..63d07eb4 100644 --- a/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java +++ b/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java @@ -358,40 +358,8 @@ public class Rule implements Comparable { for(Trigger oneTrigger : this.getTriggerSet()) { - if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.activityDetection)) - { - if (ActivityDetectionReceiver.getActivityDetectionLastResult() != null) - { - boolean found = false; - for (DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) - { - if (oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType()) - found = true; - } - - if (!found) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyActivityNotPresent), ActivityDetectionReceiver.getDescription(oneTrigger.getActivityDetectionType())), 3); - return false; - } - else - { - for (DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) - { - if (oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType() && oneDetectedActivity.getConfidence() < Settings.activityDetectionRequiredProbability) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyActivityGivenButTooLowProbability), ActivityDetectionReceiver.getDescription(oneDetectedActivity.getType()), String.valueOf(oneDetectedActivity.getConfidence()), String.valueOf(Settings.activityDetectionRequiredProbability)), 3); - return false; - } - } - } - } - } - else - { - if (!oneTrigger.applies(null, context)) - return false; - } + if (!oneTrigger.applies(null, context)) + return false; } return true; @@ -400,6 +368,44 @@ public class Rule implements Comparable Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleIsDeactivatedCantApply), this.getName()), 3); return false; } + + /** + * This is actually a function of the class Trigger, but Rule is already distinguished by flavors, Trigger is not. + * Hence it is here. + * @param oneTrigger + * @return + */ + boolean checkActivityDetection(Trigger oneTrigger) + { + if (ActivityDetectionReceiver.getActivityDetectionLastResult() != null) + { + boolean found = false; + for (DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) + { + if (oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType()) + found = true; + } + + if (!found) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyActivityNotPresent), ActivityDetectionReceiver.getDescription(oneTrigger.getActivityDetectionType())), 3); + return false; + } + else + { + for (DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) + { + if (oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType() && oneDetectedActivity.getConfidence() < Settings.activityDetectionRequiredProbability) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyActivityGivenButTooLowProbability), ActivityDetectionReceiver.getDescription(oneDetectedActivity.getType()), String.valueOf(oneDetectedActivity.getConfidence()), String.valueOf(Settings.activityDetectionRequiredProbability)), 3); + return false; + } + } + } + } + + return true; + } private class ActivateRuleTask extends AsyncTask { @@ -418,7 +424,8 @@ public class Rule implements Comparable if (Looper.myLooper() == null) Looper.prepare(); - + + setLastExecution(Calendar.getInstance()); wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]); return null; @@ -443,7 +450,7 @@ public class Rule implements Comparable */ if(wasActivated) { - setLastExecution(Calendar.getInstance()); +// setLastExecution(Calendar.getInstance()); AutomationService.updateNotification(); ActivityMainScreen.updateMainScreen(); super.onPostExecute(result); @@ -462,8 +469,8 @@ public class Rule implements Comparable boolean doToggle = ruleToggle && isActuallyToggable; //if(notLastActive || force || doToggle) - if(force || doToggle) - { +// if(force || doToggle) +// { String message; if(!doToggle) message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName()); @@ -507,12 +514,12 @@ public class Rule implements Comparable } Miscellaneous.logEvent("i", "Rule", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleActivationComplete), Rule.this.getName()), 2); - } - else - { - Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); - return false; - } +// } +// else +// { +// Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); +// return false; +// } return true; } diff --git a/app/src/main/java/com/jens/automation2/ActivityManageTriggerDevicePosition.java b/app/src/main/java/com/jens/automation2/ActivityManageTriggerDevicePosition.java index 69fd71da..42634bfa 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageTriggerDevicePosition.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageTriggerDevicePosition.java @@ -28,6 +28,8 @@ public class ActivityManageTriggerDevicePosition extends Activity boolean editMode = false; + boolean messageDisplayed = false; + float desiredAzimuth, desiredPitch, desiredRoll, desiredAzimuthTolerance, desiredPitchTolerance, desiredRollTolerance; public void updateFields(float azimuth, float pitch, float roll) @@ -185,8 +187,28 @@ public class ActivityManageTriggerDevicePosition extends Activity float dp = Float.parseFloat(etDesiredPitch.getText().toString()); float dr = Float.parseFloat(etDesiredRoll.getText().toString()); - if(Math.abs(da) <= 180 || Math.abs(dp) <= 180 || Math.abs(dr) <= 180) - return true; + if(Math.abs(da) > 180 || Math.abs(dp) > 180 || Math.abs(dr) > 180) + { + return false; + } + + if(!messageDisplayed) + { + float dat = Float.parseFloat(etDesiredAzimuthTolerance.getText().toString()); + float dpt = Float.parseFloat(etDesiredPitchTolerance.getText().toString()); + float drt = Float.parseFloat(etDesiredRollTolerance.getText().toString()); + + /* + The user may enter a tolerance of 180° for two directions, but not all three. + Otherwise this trigger would always apply. + */ + if (Math.abs(dat) >= 180 && Math.abs(dpt) >= 180 && Math.abs(drt) >= 180) + { + messageDisplayed = true; + Miscellaneous.messageBox(getResources().getString(R.string.warning), getResources().getString(R.string.toleranceOf180OnlyAllowedIn2Fields), ActivityManageTriggerDevicePosition.this).show(); + return false; + } + } } return false; diff --git a/app/src/main/java/com/jens/automation2/PointOfInterest.java b/app/src/main/java/com/jens/automation2/PointOfInterest.java index edf39597..2788541c 100644 --- a/app/src/main/java/com/jens/automation2/PointOfInterest.java +++ b/app/src/main/java/com/jens/automation2/PointOfInterest.java @@ -264,7 +264,7 @@ public class PointOfInterest implements Comparable for(int i=0; i Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " found in " + ruleCandidates.size() + " rule(s).", 2); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesBySpeed(); for (Rule oneRule : ruleCandidates) { - if (oneRule.applies(this.getParentService()) && oneRule.hasNotAppliedSinceLastExecution()) + if ((oneRule.applies(this.getParentService()) && oneRule.hasNotAppliedSinceLastExecution()) || oneRule.isActuallyToggable()) oneRule.activate(getParentService(), false); } } diff --git a/app/src/main/java/com/jens/automation2/location/WifiBroadcastReceiver.java b/app/src/main/java/com/jens/automation2/location/WifiBroadcastReceiver.java index b8a9a8c7..b1673b80 100644 --- a/app/src/main/java/com/jens/automation2/location/WifiBroadcastReceiver.java +++ b/app/src/main/java/com/jens/automation2/location/WifiBroadcastReceiver.java @@ -147,7 +147,7 @@ public class WifiBroadcastReceiver extends BroadcastReceiver ArrayList ruleCandidates = Rule.findRuleCandidatesByWifiConnection(); for(Rule oneRule : ruleCandidates) { - if(oneRule.applies(automationServiceInstance) && oneRule.hasNotAppliedSinceLastExecution()) + if((oneRule.applies(automationServiceInstance) && oneRule.hasNotAppliedSinceLastExecution()) || oneRule.isActuallyToggable()) oneRule.activate(automationServiceInstance, false); } } diff --git a/app/src/main/java/com/jens/automation2/receivers/BatteryReceiver.java b/app/src/main/java/com/jens/automation2/receivers/BatteryReceiver.java index 9d2de54e..2fd8db3e 100644 --- a/app/src/main/java/com/jens/automation2/receivers/BatteryReceiver.java +++ b/app/src/main/java/com/jens/automation2/receivers/BatteryReceiver.java @@ -206,7 +206,7 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList ArrayList ruleCandidates = Rule.findRuleCandidatesByCharging(true); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByBatteryLevel(); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByCharging(false); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByUsbHost(true); for(Rule oneRule : ruleCandidates) { - if(oneRule.applies(context) && oneRule.hasNotAppliedSinceLastExecution()) + if((oneRule.applies(context) && oneRule.hasNotAppliedSinceLastExecution()) || oneRule.isActuallyToggable()) oneRule.activate(automationServiceRef, false); } @@ -278,7 +278,7 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList ArrayList ruleCandidates = Rule.findRuleCandidatesByUsbHost(false); for(Rule oneRule : ruleCandidates) { - if(oneRule.applies(context) && oneRule.hasNotAppliedSinceLastExecution()) + if((oneRule.applies(context) && oneRule.hasNotAppliedSinceLastExecution()) || oneRule.isActuallyToggable()) oneRule.activate(automationServiceRef, false); } } diff --git a/app/src/main/java/com/jens/automation2/receivers/BluetoothReceiver.java b/app/src/main/java/com/jens/automation2/receivers/BluetoothReceiver.java index 0abc46b9..2fc23ce1 100644 --- a/app/src/main/java/com/jens/automation2/receivers/BluetoothReceiver.java +++ b/app/src/main/java/com/jens/automation2/receivers/BluetoothReceiver.java @@ -127,7 +127,7 @@ public class BluetoothReceiver extends BroadcastReceiver implements AutomationLi ArrayList ruleCandidates = Rule.findRuleCandidatesByBluetoothConnection(); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByAirplaneMode(isAirplaneMode); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByRoaming(isRoaming); for(int i=0; i allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime); for(int i=0; i ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.devicePosition); for (int i = 0; i < ruleCandidates.size(); i++) { - boolean applies = ruleCandidates.get(i).applies(Miscellaneous.getAnyContext()); - boolean flipped = ruleCandidates.get(i).hasNotAppliedSinceLastExecution(); + applies = ruleCandidates.get(i).applies(Miscellaneous.getAnyContext()); + flipped = ruleCandidates.get(i).hasNotAppliedSinceLastExecution(); + toggable = ruleCandidates.get(i).isActuallyToggable(); -// if ( -// ruleCandidates.get(i).applies(Miscellaneous.getAnyContext()) -// && -// ruleCandidates.get(i).hasNotAppliedSinceLastExecution() -// ) - if(applies && flipped) + if((applies && flipped) || toggable) ruleCandidates.get(i).activate(AutomationService.getInstance(), false); } } diff --git a/app/src/main/java/com/jens/automation2/receivers/HeadphoneJackListener.java b/app/src/main/java/com/jens/automation2/receivers/HeadphoneJackListener.java index 807fcd41..748f083f 100644 --- a/app/src/main/java/com/jens/automation2/receivers/HeadphoneJackListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/HeadphoneJackListener.java @@ -77,7 +77,7 @@ public class HeadphoneJackListener extends BroadcastReceiver implements Automati ArrayList ruleCandidates = Rule.findRuleCandidatesByHeadphoneJack(isHeadsetConnected()); for(int i=0; i allRulesWithNfcTags = Rule.findRuleCandidatesByNfc(); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByNoiseLevel(); for(Rule oneRule : ruleCandidates) { - if(oneRule.applies(automationService) && oneRule.hasNotAppliedSinceLastExecution()) + if((oneRule.applies(automationService) && oneRule.hasNotAppliedSinceLastExecution()) || oneRule.isActuallyToggable()) oneRule.activate(automationService, false); } } diff --git a/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java b/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java index f3d97b06..efffeed6 100644 --- a/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java @@ -89,19 +89,12 @@ public class NotificationListener extends NotificationListenerService lastNotification.title = title; lastNotification.text = text; -// if(lastResponseToNotification == null || lastResponseToNotification.getTimeInMillis() < lastNotification.publishTime.getTimeInMillis()) -// { -// lastResponseToNotification = Calendar.getInstance(); - - ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.notification); - for (int i = 0; i < ruleCandidates.size(); i++) - { - if (ruleCandidates.get(i).applies(NotificationListener.this) && ruleCandidates.get(i).hasNotAppliedSinceLastExecution()) - ruleCandidates.get(i).activate(AutomationService.getInstance(), false); - } -// } -// else -// Miscellaneous.logEvent("e", "NotificationCheck", "Ignoring notification as it is old.", 5); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.notification); + for (int i = 0; i < ruleCandidates.size(); i++) + { + if ((ruleCandidates.get(i).applies(NotificationListener.this) && ruleCandidates.get(i).hasNotAppliedSinceLastExecution()) || ruleCandidates.get(i).isActuallyToggable()) + ruleCandidates.get(i).activate(AutomationService.getInstance(), false); + } } return false; diff --git a/app/src/main/java/com/jens/automation2/receivers/PhoneStatusListener.java b/app/src/main/java/com/jens/automation2/receivers/PhoneStatusListener.java index 9e9f4100..98e633c0 100644 --- a/app/src/main/java/com/jens/automation2/receivers/PhoneStatusListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/PhoneStatusListener.java @@ -114,7 +114,7 @@ public class PhoneStatusListener implements AutomationListenerInterface { AutomationService asInstance = AutomationService.getInstance(); if(asInstance != null) - if(ruleCandidates.get(i).applies(asInstance) && ruleCandidates.get(i).hasNotAppliedSinceLastExecution()) + if((ruleCandidates.get(i).applies(asInstance) && ruleCandidates.get(i).hasNotAppliedSinceLastExecution()) || ruleCandidates.get(i).isActuallyToggable()) ruleCandidates.get(i).activate(asInstance, false); } } @@ -146,7 +146,7 @@ public class PhoneStatusListener implements AutomationListenerInterface { AutomationService asInstance = AutomationService.getInstance(); if (asInstance != null) - if (ruleCandidates.get(i).applies(asInstance) && ruleCandidates.get(i).hasNotAppliedSinceLastExecution()) + if ((ruleCandidates.get(i).applies(asInstance) && ruleCandidates.get(i).hasNotAppliedSinceLastExecution()) || ruleCandidates.get(i).isActuallyToggable()) ruleCandidates.get(i).activate(asInstance, false); } } @@ -183,7 +183,7 @@ public class PhoneStatusListener implements AutomationListenerInterface { AutomationService asInstance = AutomationService.getInstance(); if(asInstance != null) - if(ruleCandidates.get(i).applies(asInstance) && ruleCandidates.get(i).hasNotAppliedSinceLastExecution()) + if((ruleCandidates.get(i).applies(asInstance) && ruleCandidates.get(i).hasNotAppliedSinceLastExecution()) || ruleCandidates.get(i).isActuallyToggable()) ruleCandidates.get(i).activate(asInstance, false); } } diff --git a/app/src/main/java/com/jens/automation2/receivers/ProcessListener.java b/app/src/main/java/com/jens/automation2/receivers/ProcessListener.java index 4390ea0b..a0eec0aa 100644 --- a/app/src/main/java/com/jens/automation2/receivers/ProcessListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/ProcessListener.java @@ -43,34 +43,27 @@ public class ProcessListener implements AutomationListenerInterface @Override public void handleMessage(Message msg) { -// try -// { - Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.processMonitoring), automationService.getResources().getString(R.string.messageReceivedStatingProcessMonitoringIsComplete), 5); - // This will take care of results delivered by the actual monitoring instance - - for(String entry : getRunningApps()) - Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.runningApp), entry, 5); - - // execute matching rules containing processes - if(getRecentlyStartedApps().size()>0 | getRecentlyStoppedApps().size()>0) + Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.processMonitoring), automationService.getResources().getString(R.string.messageReceivedStatingProcessMonitoringIsComplete), 5); + // This will take care of results delivered by the actual monitoring instance + + for(String entry : getRunningApps()) + Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.runningApp), entry, 5); + + // execute matching rules containing processes + if(getRecentlyStartedApps().size()>0 | getRecentlyStoppedApps().size()>0) + { + for(String entry : getRecentlyStartedApps()) + Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.appStarted), entry, 3); + for(String entry : getRecentlyStoppedApps()) + Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.appStopped), entry, 3); + + ArrayList ruleCandidates = Rule.findRuleCandidatesByProcess(); + for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByProcess(); - for(int i=0; ihandleMessage(): " + e.getMessage()); -// } + } } }; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c4f521d5..5c0a5d59 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -719,7 +719,8 @@ Pitch: Roll: Enter valid numbers in all fields. - When you move your device the below numbers should update. What you can see there, is the current \"position\" of your device. If it is in the desired position, click the apply button to copy the current values to the desired fields.\nBecause you will probably not be able to reach this exact position ever again, enter a tolerance. The is amount to which the position can deviate in one direction or the other. + When you move your device the below numbers will update. What you can see there, is the current \"position\" of your device measured in degrees. If it is in the desired position, click the apply button to copy the current values to the desired fields.\nBecause reaching this exact position ever again is highly unlikely you must also enter a tolerance. The is amount of degrees to which the position can deviate in either direction. If you only care about one specific axis, specify a tolerance of 180° for the two other ones. Would currently apply? the device is in a certain position + A tolerance of 180 is allowed for 2 tolerance fields only, not all 3. Otherwise the trigger would ALWAYS apply. \ No newline at end of file