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 = logging.getLogger(__name__)
 | 
				
			||||||
        logger.setLevel(log_level)
 | 
					        logger.setLevel(log_level)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        import kmk_keyboard_user
 | 
				
			||||||
 | 
					        self.encoders = getattr(kmk_keyboard_user, 'encoders', [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.hydrated = False
 | 
					        self.hydrated = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.store = Store(kmk_reducer, log_level=log_level)
 | 
					        self.store = Store(kmk_reducer, log_level=log_level)
 | 
				
			||||||
@@ -58,3 +61,10 @@ class Firmware:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if update:
 | 
					            if update:
 | 
				
			||||||
                self.store.dispatch(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):
 | 
					    def __repr__(self):
 | 
				
			||||||
        return 'Anything<{}>'.format(self.name)
 | 
					        return 'Anything<{}>'.format(self.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def init(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def value(self):
 | 
					    def value(self):
 | 
				
			||||||
        return None
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Passthrough:
 | 
					class Passthrough:
 | 
				
			||||||
@@ -23,6 +26,10 @@ class Passthrough:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Pin:
 | 
					class Pin:
 | 
				
			||||||
    board = Passthrough()
 | 
					    board = Passthrough()
 | 
				
			||||||
 | 
					    IN = 'IN'
 | 
				
			||||||
 | 
					    OUT = 'OUT'
 | 
				
			||||||
 | 
					    PULL_DOWN = 'PULL_DOWN'
 | 
				
			||||||
 | 
					    PULL_UP = 'PULL_UP'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __call__(self, *args, **kwargs):
 | 
					    def __call__(self, *args, **kwargs):
 | 
				
			||||||
        return self.board
 | 
					        return self.board
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
from kmk.common.consts import DiodeOrientation, UnicodeModes
 | 
					from kmk.common.consts import DiodeOrientation, UnicodeModes
 | 
				
			||||||
from kmk.common.keycodes import KC
 | 
					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.simple import send_string, simple_key_sequence
 | 
				
			||||||
from kmk.common.macros.unicode import unicode_string_sequence
 | 
					from kmk.common.macros.unicode import unicode_string_sequence
 | 
				
			||||||
from kmk.common.pins import Pin as P
 | 
					from kmk.common.pins import Pin as P
 | 
				
			||||||
@@ -15,6 +16,10 @@ rows = (P.D12, P.D11, P.D10)
 | 
				
			|||||||
diode_orientation = DiodeOrientation.COLUMNS
 | 
					diode_orientation = DiodeOrientation.COLUMNS
 | 
				
			||||||
unicode_mode = UnicodeModes.LINUX
 | 
					unicode_mode = UnicodeModes.LINUX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encoders = [
 | 
				
			||||||
 | 
					    VolumeRotaryEncoder(P.A3, P.A2),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
emoticons = AttrDict({
 | 
					emoticons = AttrDict({
 | 
				
			||||||
    # Emojis
 | 
					    # Emojis
 | 
				
			||||||
    'BEER': r'🍺',
 | 
					    'BEER': r'🍺',
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user