Support a special form of macro based on rotary encoder directions
This commit is contained in:
		
							
								
								
									
										76
									
								
								kmk/common/macros/rotary_encoder.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								kmk/common/macros/rotary_encoder.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
import math
 | 
			
		||||
 | 
			
		||||
from kmk.common.event_defs import (hid_report_event, keycode_down_event,
 | 
			
		||||
                                   keycode_up_event)
 | 
			
		||||
from kmk.common.keycodes import Media
 | 
			
		||||
from kmk.common.rotary_encoder import RotaryEncoder
 | 
			
		||||
 | 
			
		||||
VAL_FALSE = False + 1
 | 
			
		||||
VAL_NONE = True + 2
 | 
			
		||||
VAL_TRUE = True + 1
 | 
			
		||||
VOL_UP_PRESS = keycode_down_event(Media.KC_AUDIO_VOL_UP)
 | 
			
		||||
VOL_UP_RELEASE = keycode_up_event(Media.KC_AUDIO_VOL_UP)
 | 
			
		||||
VOL_DOWN_PRESS = keycode_down_event(Media.KC_AUDIO_VOL_DOWN)
 | 
			
		||||
VOL_DOWN_RELEASE = keycode_up_event(Media.KC_AUDIO_VOL_DOWN)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RotaryEncoderMacro:
 | 
			
		||||
    def __init__(self, pos_pin, neg_pin, slop_history=24, slop_threshold=0.7):
 | 
			
		||||
        self.encoder = RotaryEncoder(pos_pin, neg_pin)
 | 
			
		||||
        self.max_history = slop_history
 | 
			
		||||
        self.history = bytearray(slop_history)
 | 
			
		||||
        self.history_idx = 0
 | 
			
		||||
        self.history_threshold = math.floor(slop_threshold * slop_history)
 | 
			
		||||
 | 
			
		||||
    def scan(self):
 | 
			
		||||
        # Anti-slop logic
 | 
			
		||||
        self.history[self.history_idx] = 0
 | 
			
		||||
 | 
			
		||||
        reading = self.encoder.direction()
 | 
			
		||||
        self.history[self.history_idx] = VAL_NONE if reading is None else reading + 1
 | 
			
		||||
 | 
			
		||||
        self.history_idx += 1
 | 
			
		||||
 | 
			
		||||
        if self.history_idx >= self.max_history:
 | 
			
		||||
            self.history_idx = 0
 | 
			
		||||
 | 
			
		||||
        nones = 0
 | 
			
		||||
        trues = 0
 | 
			
		||||
        falses = 0
 | 
			
		||||
 | 
			
		||||
        for val in self.history:
 | 
			
		||||
            if val == VAL_NONE:
 | 
			
		||||
                nones += 1
 | 
			
		||||
            elif val == VAL_TRUE:
 | 
			
		||||
                trues += 1
 | 
			
		||||
            elif val == VAL_FALSE:
 | 
			
		||||
                falses += 1
 | 
			
		||||
 | 
			
		||||
        if nones >= self.history_threshold:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        if trues >= self.history_threshold:
 | 
			
		||||
            return self.on_increase()
 | 
			
		||||
 | 
			
		||||
        if falses >= self.history_threshold:
 | 
			
		||||
            return self.on_decrease()
 | 
			
		||||
 | 
			
		||||
    def on_decrease(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def on_increase(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VolumeRotaryEncoder(RotaryEncoderMacro):
 | 
			
		||||
    def on_decrease(self):
 | 
			
		||||
        yield VOL_DOWN_PRESS
 | 
			
		||||
        yield hid_report_event
 | 
			
		||||
        yield VOL_DOWN_RELEASE
 | 
			
		||||
        yield hid_report_event
 | 
			
		||||
 | 
			
		||||
    def on_increase(self):
 | 
			
		||||
        yield VOL_UP_PRESS
 | 
			
		||||
        yield hid_report_event
 | 
			
		||||
        yield VOL_UP_RELEASE
 | 
			
		||||
        yield hid_report_event
 | 
			
		||||
@@ -19,6 +19,9 @@ class Firmware:
 | 
			
		||||
        logger = logging.getLogger(__name__)
 | 
			
		||||
        logger.setLevel(log_level)
 | 
			
		||||
 | 
			
		||||
        import kmk_keyboard_user
 | 
			
		||||
        self.encoders = getattr(kmk_keyboard_user, 'encoders', [])
 | 
			
		||||
 | 
			
		||||
        self.hydrated = False
 | 
			
		||||
 | 
			
		||||
        self.store = Store(kmk_reducer, log_level=log_level)
 | 
			
		||||
@@ -58,3 +61,10 @@ class Firmware:
 | 
			
		||||
 | 
			
		||||
            if update:
 | 
			
		||||
                self.store.dispatch(update)
 | 
			
		||||
 | 
			
		||||
            for encoder in self.encoders:
 | 
			
		||||
                eupdate = encoder.scan()
 | 
			
		||||
 | 
			
		||||
                if eupdate:
 | 
			
		||||
                    for event in eupdate:
 | 
			
		||||
                        self.store.dispatch(event)
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,12 @@ class Anything:
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return 'Anything<{}>'.format(self.name)
 | 
			
		||||
 | 
			
		||||
    def init(self, *args, **kwargs):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def value(self):
 | 
			
		||||
        return None
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Passthrough:
 | 
			
		||||
@@ -23,6 +26,10 @@ class Passthrough:
 | 
			
		||||
 | 
			
		||||
class Pin:
 | 
			
		||||
    board = Passthrough()
 | 
			
		||||
    IN = 'IN'
 | 
			
		||||
    OUT = 'OUT'
 | 
			
		||||
    PULL_DOWN = 'PULL_DOWN'
 | 
			
		||||
    PULL_UP = 'PULL_UP'
 | 
			
		||||
 | 
			
		||||
    def __call__(self, *args, **kwargs):
 | 
			
		||||
        return self.board
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
from kmk.common.consts import DiodeOrientation, UnicodeModes
 | 
			
		||||
from kmk.common.keycodes import KC
 | 
			
		||||
from kmk.common.macros.rotary_encoder import VolumeRotaryEncoder
 | 
			
		||||
from kmk.common.macros.simple import send_string, simple_key_sequence
 | 
			
		||||
from kmk.common.macros.unicode import unicode_string_sequence
 | 
			
		||||
from kmk.common.pins import Pin as P
 | 
			
		||||
@@ -15,6 +16,10 @@ rows = (P.D12, P.D11, P.D10)
 | 
			
		||||
diode_orientation = DiodeOrientation.COLUMNS
 | 
			
		||||
unicode_mode = UnicodeModes.LINUX
 | 
			
		||||
 | 
			
		||||
encoders = [
 | 
			
		||||
    VolumeRotaryEncoder(P.A3, P.A2),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
emoticons = AttrDict({
 | 
			
		||||
    # Emojis
 | 
			
		||||
    'BEER': r'🍺',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user