Skip to content
Merged
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
159 changes: 158 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ clap = { version = "4.6.0", features = ["derive"] }
hidapi = { version = "2.6.5", default-features = false, features = [
"linux-static-hidraw",
] }
serde_json = { version = "1.0.154", features = ["preserve_order"] }
toml = "1.1.8"
115 changes: 115 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ It currently supports:
- setting main/backlight RGB effects
- setting side-light effects
- setting decorative/strip-light effects
- reading, remapping, backing up and restoring the keymap (declaratively from a TOML file)
- sending raw 64-byte HID reports for reverse engineering

## Build
Expand Down Expand Up @@ -106,6 +107,119 @@ Side/decorative effects:

`time`, `neon`, `static`, `breathe`, `rhythm`

## Keymap

The `keymap` commands read and write the keymap stored on the keyboard (the same data
NuPhy IO edits). Every command that changes the keyboard first saves a binary backup of
the whole keymap, writes only keys whose value differs, then reads the keymap back and
fails unless it matches exactly. Changes persist across replugs; there is no separate
save step.

Named positions follow the Air75 V3 layout (Windows legends). Any board also accepts
`row,col` and `#index` positions.

Show firmware version, current mode and keymap geometry:

```bash
nuphyctl keymap info
nuphyctl keymap info --json
```

Layers: `0-3` are Mac mode, `4-7` Windows mode. Within a mode the layers are `base`,
`fn`, `fn2` and `fn3`, so `windows.fn` is layer `5`. Commands accept either form.

Print the keymap, a single layer, or one mode (`--defaults` reads the factory keymap,
`--changed` shows only keys that differ from it):

```bash
nuphyctl keymap dump --layer windows.base
nuphyctl keymap dump --mode windows --json
nuphyctl keymap dump --changed
```

Save a full binary backup (all layers) and write one back later. Only differing keys are
written, and the result is verified:

```bash
nuphyctl keymap dump --raw-out air75.bin
nuphyctl keymap restore-backup air75.bin --dry-run
nuphyctl keymap restore-backup air75.bin
```

Set one key. Positions are names (`F1`, `ESC`, `YELLOW`, `KNOB`, `KNOB_CCW`, ...), `row,col`
or `#index`; keycodes are names (`BRID`, `F20`, `MISSION_CONTROL`, `DND`, `MO(5)`, `TRNS`, ...)
or hex (`0x00be`):

```bash
nuphyctl keymap set --layer windows.base F1 BRID
nuphyctl keymap set --layer 4 3,12 MUTE
nuphyctl keymap set --layer 4 KNOB_CW 0x00a9 --json
```

List the names:

```bash
nuphyctl keymap keycodes
nuphyctl keymap positions --json
```

### Declarative keymap files

Keep the keymap in a TOML file (for example in your dotfiles) and apply it. Only keys
listed in the file are touched. `apply` prints the diff (current -> desired), writes only
the changes, verifies them, and is idempotent: a second run reports `no changes`.

```toml
[windows.base] # layer 4
F1 = "BRID"
F2 = "BRIU"
F3 = "MISSION_CONTROL"
F6 = "DND"
YELLOW = "DEL"
"3,12" = "MUTE" # row,col and "#index" work too

[windows.base.knob] # same as KNOB_CCW / KNOB_CW / KNOB
ccw = "VOLD"
cw = "VOLU"

[windows.fn] # layer 5
F1 = "F1"
F2 = 0x3b # integers are raw keycodes

[layers.6] # absolute layer number
ESC = "TRNS"
```

```bash
nuphyctl keymap apply keymap.toml --dry-run
nuphyctl keymap apply keymap.toml
nuphyctl keymap apply keymap.toml --json
```

Sections are `[mac.<layer>]`, `[windows.<layer>]` (`base`, `fn`, `fn2`, `fn3`) and
`[layers.<n>]`. Unknown sections, positions or keycodes, and two entries for the same key,
are errors; nothing is written when the file does not parse.

To start a file from the keyboard's current state, export the keys that differ from the
factory keymap:

```bash
nuphyctl keymap export > keymap.toml
```

### Backups and reset

Before any write, `set`, `apply`, `restore-backup` and `reset` save the current keymap to
`$XDG_STATE_HOME/nuphyctl/keymap-<UTC timestamp>.bin` (default
`~/.local/state/nuphyctl/`) and print the path. Restore one with
`nuphyctl keymap restore-backup <file>`.

Reset a mode's layers (or all layers) to the factory keymap. `--yes` is required:

```bash
nuphyctl keymap reset --mode windows --yes
```

## Device selection

If more than one matching HID interface is present, narrow the target with one or more selector flags.
Expand Down Expand Up @@ -158,6 +272,7 @@ cargo run -- raw send --report-id 0 --hex "...128 hex chars..."
- `rgb set`, `rgb side`, and `rgb decorative` automatically retry a few common transient HID failures
- `rgb side` and `rgb decorative` require `--hex` only for effects that use custom RGB color
- `raw send` expects exactly 64 payload bytes
- `keymap` commands negotiate a new session key, so a NuPhy IO tab that is open at the same time may need a reload afterwards

## Reverse engineering

Expand Down
Loading