Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# PySwitch v2.4.9
- Features:
- Added **EFFECT_STATE_PER_RIG** action: works like EFFECT_STATE but allows assigning a different Kemper effect slot per rig. When the active rig changes, the button automatically controls the slot defined in the rig_overrides mapping, falling back to the default slot_id. Supports None as a slot value to disable the button for a specific rig.

### Emulator 2.4.9.16
- Features:
- Added **EFFECT_STATE_PER_RIG** to the web editor: new rig_map parameter type renders a Bank/Rig/Slot table with add/remove rows and full round-trip serialization with inputs.py.

# PySwitch v2.4.8
- Bug fixes:
- Reset memory of actions after page change (this guarantees correct LED states). Came up with the @GlanzGuitar example, which contained a workaround for this issue.
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,38 @@ Inputs = [
]
```

##### Per-Rig Effect Slot Override (EFFECT_STATE_PER_RIG)

By default, `EFFECT_STATE` always controls the same slot regardless of which rig is active. This means all rigs must place the same effect type in the same slot — for example, the compressor must always be in Slot A. `EFFECT_STATE_PER_RIG` solves this by routing each button to a different slot depending on the active rig:

```python
from pyswitch.clients.kemper.actions.effect_state_per_rig import EFFECT_STATE_PER_RIG
from pyswitch.clients.kemper import KemperEffectSlot

# Absolute rig ID = (bank - 1) * 5 + (rig - 1)
# Bank 1 Rig 1 = 0, Bank 1 Rig 3 = 2, Bank 2 Rig 1 = 5

Inputs = [
{
"assignment": Hardware.PA_MIDICAPTAIN_10_SWITCH_1,
"actions": [
EFFECT_STATE_PER_RIG(
slot_id = KemperEffectSlot.EFFECT_SLOT_ID_A, # default slot
rig_overrides = {
2: KemperEffectSlot.EFFECT_SLOT_ID_C, # Bank 1 Rig 3 → Slot C
5: KemperEffectSlot.EFFECT_SLOT_ID_DLY, # Bank 2 Rig 1 → Slot DLY
},
display = DISPLAY_HEADER_1
)
]
}
]
```

When the rig changes, the button automatically controls the slot defined for that rig (or falls back to `slot_id` if no override is defined). The LED color and display label update in real time to reflect the new slot's effect type. Setting a slot to `None` in `rig_overrides` disables the button for that rig (LED off, label cleared).

`EFFECT_STATE_PER_RIG` accepts all parameters of `EFFECT_STATE` plus the mandatory `rig_overrides` dict. The web editor exposes a Bank/Rig/Slot table for visual configuration. For full details see [docs/effect_state_per_rig.html](docs/effect_state_per_rig.html).

##### Actions on Long Press (Hold)

You can assign different actions to long pressing of switches. This is done by providing the "holdActions" parameter of the switch definitions:
Expand Down
103 changes: 35 additions & 68 deletions content/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,82 +11,52 @@
"font": "/fonts/H20.pcf",
"backColor": DEFAULT_LABEL_COLOR,
"stroke": 1,

}

_DISPLAY_WIDTH = const(
240
)
_DISPLAY_HEIGHT = const(
240
)
_SLOT_WIDTH = const(
120
)
_SLOT_HEIGHT = const(
40
)
_FOOTER_Y = const(
200
)
_RIG_NAME_HEIGHT = const(
160
)
_DISPLAY_WIDTH = const(240)
_DISPLAY_HEIGHT = const(240)
_SLOT_WIDTH = const(120)
_SLOT_HEIGHT = const(40)
_FOOTER_Y = const(200)
_RIG_NAME_Y = const(40)
_RIG_NAME_H = const(160)


DISPLAY_HEADER_1 = DisplayLabel(
layout = _ACTION_LABEL_LAYOUT,
# Switch 3 (bottom-left of the screen)
DISPLAY_SWITCH_3 = DisplayLabel(
layout = _ACTION_LABEL_LAYOUT,
bounds = DisplayBounds(
x = 0,
y = 0,
w = _SLOT_WIDTH,
h = _SLOT_HEIGHT
)
)
DISPLAY_HEADER_2 = DisplayLabel(
layout = _ACTION_LABEL_LAYOUT,
bounds = DisplayBounds(
x = _SLOT_WIDTH,
y = 0,
w = _SLOT_WIDTH,
x = 0,
y = _FOOTER_Y,
w = _SLOT_WIDTH,
h = _SLOT_HEIGHT
)
)


DISPLAY_FOOTER_1 = DisplayLabel(
layout = _ACTION_LABEL_LAYOUT,
bounds = DisplayBounds(
x = 0,
y = _FOOTER_Y,
w = _SLOT_WIDTH,
h = _SLOT_HEIGHT
)
)
DISPLAY_FOOTER_2 = DisplayLabel(
layout = _ACTION_LABEL_LAYOUT,
# Switch 4 (bottom-right of the screen)
DISPLAY_SWITCH_4 = DisplayLabel(
layout = _ACTION_LABEL_LAYOUT,
bounds = DisplayBounds(
x = _SLOT_WIDTH,
y = _FOOTER_Y,
w = _SLOT_WIDTH,
x = _SLOT_WIDTH,
y = _FOOTER_Y,
w = _SLOT_WIDTH,
h = _SLOT_HEIGHT
)
)

DISPLAY_RIG_NAME = DisplayLabel(
bounds = DisplayBounds(
x = 0,
y = _SLOT_HEIGHT,
w = _DISPLAY_WIDTH,
h = _RIG_NAME_HEIGHT
),
x = 0,
y = _RIG_NAME_Y,
w = _DISPLAY_WIDTH,
h = _RIG_NAME_H
),
layout = {
"font": "/fonts/PTSans-NarrowBold-40.pcf",
"lineSpacing": 0.8,
"maxTextWidth": 220,
"text": KemperRigNameCallback.DEFAULT_TEXT,

},
},
callback = KemperRigNameCallback(
show_rig_id = True
)
Expand All @@ -96,26 +66,23 @@
Splashes = TunerDisplayCallback(
splash_default = DisplayElement(
bounds = DisplayBounds(
x = 0,
y = 0,
w = _DISPLAY_WIDTH,
x = 0,
y = 0,
w = _DISPLAY_WIDTH,
h = _DISPLAY_HEIGHT
),
),
children = [
DISPLAY_HEADER_1,
DISPLAY_HEADER_2,
DISPLAY_FOOTER_1,
DISPLAY_FOOTER_2,
DISPLAY_SWITCH_3,
DISPLAY_SWITCH_4,
DISPLAY_RIG_NAME,
BidirectionalProtocolState(
DisplayBounds(
x = 0,
y = _SLOT_HEIGHT,
w = _DISPLAY_WIDTH,
h = _RIG_NAME_HEIGHT
x = 0,
y = _RIG_NAME_Y,
w = _DISPLAY_WIDTH,
h = _RIG_NAME_H
)
),

]
)
)
Loading