From d33bb5d3ea88b1c4bbd46087162538ac1bedcd35 Mon Sep 17 00:00:00 2001
From: Oleksiy Protas <elfy.ua@gmail.com>
Date: Sun, 21 May 2023 01:06:10 +0300
Subject: [PATCH 1/3] Dial control type

Uses the hat controls to update a value in both directions
---
 .../modules/mavproxy_joystick/controls.py     | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/MAVProxy/modules/mavproxy_joystick/controls.py b/MAVProxy/modules/mavproxy_joystick/controls.py
index 146ff5984f..b87d95e234 100644
--- a/MAVProxy/modules/mavproxy_joystick/controls.py
+++ b/MAVProxy/modules/mavproxy_joystick/controls.py
@@ -106,6 +106,43 @@ def value(self):
 
         return self._value
 
+class Dial (Control):
+    '''Emulates a dial with a hat axis.
+    When the axis goes negative, the corresponding channel value is
+    decremented by `step` towards `outlow`. When the axis goes
+    positive, the value is incremented by `step` towards `outhigh`.
+    `invert` affects the direction of hat controls. The output is centered
+    by default, setting `centered` to false causes the output to start at
+    `outputlow`'''
+
+    def __init__(self, joystick, id, axis, step=100, invert=False, centered=True, **kwargs):
+        super(Dial, self).__init__(joystick, **kwargs)
+        self.id = id
+        self.axis = axis
+        self.step = step
+        self.invert = invert
+        # Center by default
+        self._value = (self.outlow + self.outhigh) // 2 if centered else self.outlow
+        # Flag to make sure we identify discrete presses
+        self._last_value = 0
+
+    @property
+    def value(self):
+        x, y = self.joystick.get_hat(self.id)
+
+        value = x if self.axis == 'x' else y
+
+        # Check if it's a new value we've not processed before
+        if value != self._last_value:
+            self._last_value = value
+            if value != 0:
+                # In my testing the hat is clamped to -1, 0, 1 values only
+                # NOTE: check if this universal
+                self._value += value * self.step
+                self._value = min(self._value, self.outhigh)
+                self._value = max(self._value, self.outlow)
+
+        return self._value
 
 class Joystick (object):
     '''A Joystick manages a collection of Controls.'''
@@ -146,6 +183,13 @@ def __init__(self, joystick, controls):
 
                 handler = Hat(self.joystick, control['id'], control['axis'])
 
+            elif control['type'] == 'dial':
+                kwargs = {k: control[k]
+                          for k in control.keys()
+                          if k in ['outlow', 'outhigh', 'invert', 'step']}
+
+                handler = Dial(self.joystick, control['id'], control['axis'], **kwargs)
+
             self.channels[control['channel']-1] = handler
 
     def read(self):

From 61eb3a9a3634a9911720379e7df3ec647a271edc Mon Sep 17 00:00:00 2001
From: Oleksiy Protas <elfy.ua@gmail.com>
Date: Sun, 21 May 2023 01:07:04 +0300
Subject: [PATCH 2/3] Fix the hat handler description and make use of
 outlow/high parameters

---
 MAVProxy/modules/mavproxy_joystick/controls.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/MAVProxy/modules/mavproxy_joystick/controls.py b/MAVProxy/modules/mavproxy_joystick/controls.py
index b87d95e234..10ed881b67 100644
--- a/MAVProxy/modules/mavproxy_joystick/controls.py
+++ b/MAVProxy/modules/mavproxy_joystick/controls.py
@@ -85,8 +85,8 @@ def value(self):
 class Hat (Control):
     '''A Hat maps one axis of a hat as if it were a toggle switch.
     When the axis goes negative, the corresponding channel value is
-    set to `outputlow`.  When the axis goes positive, the value is set
-    to `outputhigh`.  No change is made when the axis returns to 0.'''
+    set to `outlow`.  When the axis goes positive, the value is set
+    to `outhigh`.  No change is made when the axis returns to 0.'''
 
     def __init__(self, joystick, id, axis, **kwargs):
         super(Hat, self).__init__(joystick, **kwargs)
@@ -181,7 +181,7 @@ def __init__(self, joystick, controls):
                           for k in control.keys()
                           if k in ['outlow', 'outhigh']}
 
-                handler = Hat(self.joystick, control['id'], control['axis'])
+                handler = Hat(self.joystick, control['id'], control['axis'], **kwargs)
 
             elif control['type'] == 'dial':
                 kwargs = {k: control[k]

From 0e29f86e21b3b228f9559fdc77df6d4a74e16cdc Mon Sep 17 00:00:00 2001
From: Oleksiy Protas <elfy.ua@gmail.com>
Date: Sun, 21 May 2023 01:12:56 +0300
Subject: [PATCH 3/3] Implement invert and centered properly

---
 MAVProxy/modules/mavproxy_joystick/controls.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/MAVProxy/modules/mavproxy_joystick/controls.py b/MAVProxy/modules/mavproxy_joystick/controls.py
index 10ed881b67..c43ed24358 100644
--- a/MAVProxy/modules/mavproxy_joystick/controls.py
+++ b/MAVProxy/modules/mavproxy_joystick/controls.py
@@ -136,6 +136,8 @@ def value(self):
         if value != self._last_value:
             self._last_value = value
             if value != 0:
+                if self.invert:
+                    value *= -1
                 # In my testing the hat is clamped to -1, 0, 1 values only
                 # NOTE: check if this universal
                 self._value += value * self.step
@@ -186,7 +188,7 @@ def __init__(self, joystick, controls):
             elif control['type'] == 'dial':
                 kwargs = {k: control[k]
                           for k in control.keys()
-                          if k in ['outlow', 'outhigh', 'invert', 'step']}
+                          if k in ['outlow', 'outhigh', 'invert', 'step', 'centered']}
 
                 handler = Dial(self.joystick, control['id'], control['axis'], **kwargs)