Skip to content

feat: add stock-like switch/button input modes for the SW terminal (#1) - #94

Merged
RapierXbox merged 1 commit into
RapierXbox:mainfrom
wagnst:main
Aug 26, 2026
Merged

RapierXbox merged 1 commit into
RapierXbox:mainfrom
wagnst:main

Conversation

@wagnst

@wagnst wagnst commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Fixes #29, #93

  • add sw input mode constants and pure state machine

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • add sw input handler and wire it into the application

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • route sw input edges from keyevents and the native monitor through the handler

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • expose sw input mode, relay target and invert in settings

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • publish initial sw input level and button mode discovery over mqtt

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • add /device/input state endpoint

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • add jvm tests for the sw input state machine

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • document sw input modes

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • label cally as wall display x1i per the official kb

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • fix follow mode test expectation to include the js event

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • note the json content type requirement in the sw input curl examples

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • add pr apk build workflow

runs unit tests and builds a release apk on every pull request, uploading it as a workflow artifact so changes can be sideloaded and live-tested before merging. signing secrets are optional: fork prs fall back to the debug key.

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • decode sw input keycodes as rising and falling edge pulses of one input

hardware testing on a wall display x1i showed the sw terminal reports each contact transition as a ~5ms down+up key pulse whose keycode encodes the direction: 141/KEY_F11 = line went active, 142/KEY_F12 = line went inactive. the old code treated 141 down/up as press/release and 142 as a second input, so falling edges were swallowed and multi-way taster circuits only toggled on every second press. the state machine now consumes decoded levels, only the pulse's down event is fed in, and the opposite-level debounce is replaced by a same-level duplicate window so genuine repeated presses still register.

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • warn when pr apks are signed with an ephemeral debug key and add a keystore generator

without the SIGNING_* secrets every ci run signs with a keystore generated fresh on that runner, so no artifact can ever update a previously installed one. the build step now surfaces that as a run warning and tools/ gains a script that generates a persistent keystore and prints the secrets to set.

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

  • print a portable base64 command from the keystore generator

base64 -w0 is gnu-only; bsd/macos base64 has no -w flag. openssl base64 -A produces single-line output on both platforms and the script already uses openssl anyway.

Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW


* add sw input mode constants and pure state machine

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* add sw input handler and wire it into the application

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* route sw input edges from keyevents and the native monitor through the handler

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* expose sw input mode, relay target and invert in settings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* publish initial sw input level and button mode discovery over mqtt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* add /device/input state endpoint

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* add jvm tests for the sw input state machine

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* document sw input modes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* label cally as wall display x1i per the official kb

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* fix follow mode test expectation to include the js event

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* note the json content type requirement in the sw input curl examples

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* add pr apk build workflow

runs unit tests and builds a release apk on every pull request, uploading it
as a workflow artifact so changes can be sideloaded and live-tested before
merging. signing secrets are optional: fork prs fall back to the debug key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* decode sw input keycodes as rising and falling edge pulses of one input

hardware testing on a wall display x1i showed the sw terminal reports each
contact transition as a ~5ms down+up key pulse whose keycode encodes the
direction: 141/KEY_F11 = line went active, 142/KEY_F12 = line went inactive.
the old code treated 141 down/up as press/release and 142 as a second input,
so falling edges were swallowed and multi-way taster circuits only toggled on
every second press. the state machine now consumes decoded levels, only the
pulse's down event is fed in, and the opposite-level debounce is replaced by
a same-level duplicate window so genuine repeated presses still register.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* warn when pr apks are signed with an ephemeral debug key and add a keystore generator

without the SIGNING_* secrets every ci run signs with a keystore generated
fresh on that runner, so no artifact can ever update a previously installed
one. the build step now surfaces that as a run warning and tools/ gains a
script that generates a persistent keystore and prints the secrets to set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

* print a portable base64 command from the keystore generator

base64 -w0 is gnu-only; bsd/macos base64 has no -w flag. openssl base64 -A
produces single-line output on both platforms and the script already uses
openssl anyway.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdbXCtBx5iM1nSWTGaD2jW

---------

Co-authored-by: Claude <noreply@anthropic.com>
@wagnst

wagnst commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Tested successfully on a X1i display connected to several tasters (Kreuzschaltung) that need to operate with edge switch function.

So far no issues running this build.

My setting via POST to the display related to this change:

  "swInputMode0": 2,
  "swInputRelayMap0": 0,
  "swInputInvert0": false

@wagnst wagnst changed the title add stock-like switch/button input modes for the SW terminal (#1) feat: add stock-like switch/button input modes for the SW terminal (#1) Aug 14, 2026
@dbochicchio
dbochicchio requested a lite review from Copilot August 17, 2026 13:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds stock-firmware-like behavior for the Shelly Wall Display SW terminal by introducing a dedicated, unit-tested SW input state machine and wiring it through the app so external switches/push-buttons can locally control relays while still reporting to MQTT/JS/REST.

Changes:

  • Introduces a pure Java SwInputStateMachine plus an Android SwInputHandler that deduplicates edge pulses and applies Button/Switch/Detached modes.
  • Wires SW input handling through app lifecycle and UI focus holders (MainActivity / SettingsActivity / screensaver), adds settings UI + SharedPreferences keys, and publishes/republishes input state via MQTT and a new /device/input REST endpoint.
  • Adds documentation + unit tests, plus a PR APK build workflow and a helper script to generate persistent signing secrets.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tools/generate-signing-keystore.sh Adds a helper to generate a release keystore and print SIGNING_* secrets for CI signing.
README.md Mentions SW terminal external switch/button support and links to documentation.
docs/sw-input.md Documents SW input hardware behavior, modes, settings keys, MQTT/JS behavior, and curl examples.
app/src/test/java/me/rapierxbox/shellyelevatev2/helper/SwInputStateMachineTest.java Adds JVM unit tests covering key decoding, deduping, and mode behaviors.
app/src/main/res/values/strings.xml Adds settings UI strings for SW input mode/relay/invert.
app/src/main/res/values/arrays.xml Adds sw_input_modes array to back the SW mode spinner.
app/src/main/res/layout/settings_fragment.xml Adds settings UI controls for SW input mode, relay mapping, and invert.
app/src/main/java/me/rapierxbox/shellyelevatev2/ShellyElevateApplication.java Boots SwInputHandler early and disposes it on termination.
app/src/main/java/me/rapierxbox/shellyelevatev2/SettingsFragment.kt Binds SW input settings, adds relay mapping spinner behavior, and persists relay mapping.
app/src/main/java/me/rapierxbox/shellyelevatev2/SettingsActivity.kt Forwards SW key events while settings activity has focus.
app/src/main/java/me/rapierxbox/shellyelevatev2/screensavers/activities/DigitalClockAndDateScreenSaverActivity.kt Forwards SW key events while this screensaver activity has focus.
app/src/main/java/me/rapierxbox/shellyelevatev2/mqtt/MQTTServer.java Republishes known SW input levels after MQTT reconnect to resync HA.
app/src/main/java/me/rapierxbox/shellyelevatev2/mqtt/MqttDiscoveryConfigBuilder.java Adds HA discovery for SW input “button event” entity when configured in button mode.
app/src/main/java/me/rapierxbox/shellyelevatev2/MainActivity.kt Routes SW keycodes to the shared handler and removes the prior per-key handling.
app/src/main/java/me/rapierxbox/shellyelevatev2/HttpServer.java Adds /device/input?num=0 to expose input level + current mode.
app/src/main/java/me/rapierxbox/shellyelevatev2/helper/SwInputStateMachine.java Implements the mode logic + dedup window and keycode decoding helpers.
app/src/main/java/me/rapierxbox/shellyelevatev2/helper/SwInputHandler.java Centralizes SW input ingestion (Android KeyEvent + native monitor), applies settings, drives relay/MQTT/JS.
app/src/main/java/me/rapierxbox/shellyelevatev2/helper/DeviceSensorManager.java Routes native SW key pulses to the SW handler so input works regardless of focused activity.
app/src/main/java/me/rapierxbox/shellyelevatev2/DeviceModel.java Corrects the CALLY display name to “Shelly Wall Display X1i” per official KB.
app/src/main/java/me/rapierxbox/shellyelevatev2/Constants.java Adds SW input settings keys and mode constants.
.github/workflows/pr-apk.yml Adds a PR workflow that runs unit tests and builds/uploads a (release) APK artifact.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +851 to +852
// 141/142: physical sw inputs, handled centrally so every activity shares one path
141, 142 -> { mSwInputHandler?.onKeyEvent(event); return true }
echo " gh secret set SIGNING_KEY_ALIAS --body \"$ALIAS\""
echo " gh secret set SIGNING_STORE_PASSWORD --body \"$PASSWORD\""
echo " gh secret set SIGNING_KEY_PASSWORD --body \"$PASSWORD\""
echo " gh secret set SIGNING_KEYSTORE_BASE64 --body \"\$(openssl base64 -A -in $KEYSTORE)\""
exit 1
fi

command -v keytool >/dev/null || { echo "keytool not found (install a JDK)" >&2; exit 1; }
@RapierXbox
RapierXbox merged commit d7b803b into RapierXbox:main Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for an external switch connected to the SW input

3 participants