Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a28078f
Update Node.js versions in workflow configuration
ashenshugarRET Apr 2, 2026
f2a3a81
Update GitHub Actions to use latest versions
ashenshugarRET Apr 2, 2026
f9fdf3a
Update dependencies and devDependencies versions
ashenshugarRET Apr 2, 2026
0b19bfe
Update dependencies to latest versions
ashenshugarRET Apr 26, 2026
04a0ea2
Update package-lock.json with latest dependency versions
ashenshugarRET Apr 26, 2026
e69354b
Add engines field specifying Node.js and npm requirements
ashenshugarRET Apr 26, 2026
68acf35
Create setup_tests.js for jest fetch mock configuration
ashenshugarRET Apr 26, 2026
fa7db96
Updating Claude's optimistic versioning
ashenshugarRET Apr 26, 2026
ce4bb27
Update package dependencies to newer versions, correcting Claude
ashenshugarRET Apr 26, 2026
3901b1f
Updating package-lock.json to latest version
ashenshugarRET Apr 26, 2026
da620ef
Merge branch 'main' of https://github.com/ashenshugarRET/node-sonos-nfc
ashenshugarRET Apr 26, 2026
56f20c2
Update to README.md to reflect changes for Debian Trixie
ashenshugarRET Apr 28, 2026
fcb9e4b
Update README.md
ashenshugarRET Apr 28, 2026
111781b
Updating README.md for clarity
ashenshugarRET Apr 28, 2026
be7cd95
Update README.md
ashenshugarRET Apr 28, 2026
fa3cd27
Update README.md
ashenshugarRET Apr 28, 2026
978ada8
Update README.md
ashenshugarRET May 3, 2026
9374ed3
Bump @babel/plugin-transform-modules-systemjs from 7.29.0 to 7.29.4
dependabot[bot] May 9, 2026
9031554
Update README.md
ashenshugarRET May 9, 2026
7709dd2
Merge pull request #1 from ashenshugarRET/dependabot/npm_and_yarn/bab…
ashenshugarRET May 9, 2026
34f5984
Updates to README.md and renaming of usersettings.json.example
ashenshugarRET May 10, 2026
f9c33a4
Re-establishing usersetting.json
ashenshugarRET May 10, 2026
2ff47ee
Update usersettings.json
ashenshugarRET May 10, 2026
7158de0
Update README.md
ashenshugarRET May 17, 2026
d296063
Update README.md
ashenshugarRET May 17, 2026
b85e2b8
Restore usersettings.json.example and fall back to it when unconfigured
claude Oct 2, 2026
cc7c98a
README: fix Trixie polkit rule, settings instructions and pm2 steps
claude Oct 2, 2026
0c7d373
Require Node 20+, update husky setup and setup-node action
claude Oct 2, 2026
d7d0ae9
Log nfc-pcsc errors so card connect failures are visible
claude Oct 3, 2026
344e659
README: add card reader troubleshooting with pcsc_scan
claude Oct 3, 2026
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
14 changes: 3 additions & 11 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,21 @@ jobs:

strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [20.x, 22.x, 24.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false

- name: Reconfigure git to use HTTP authentication
run: >
git config --global url."https://github.com/".insteadOf
ssh://git@github.com/
uses: actions/checkout@v4

- name: Install system dependencies
run: sudo apt install libpcsclite1 libpcsclite-dev pcscd

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- run: npm ci
- run: npm run build --if-present
- run: npm test
3 changes: 0 additions & 3 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run format
87 changes: 79 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,48 @@ You also need to make sure your computer can speak PC/SC. In Ubuntu/Debian, inst
$ sudo apt install libpcsclite1 libpcsclite-dev pcscd
```

If you're running a version of Linux, your computer may try to use the nfc kernel module to talk to tyour ACR122U. You don't want it to do this, so make sure the nfc and enabling modules are not loaded. In Ubuntu/Debian/Raspberry Pi OS, blacklist pn533, pn533_usb, nfc modules so that they don't hijack the card reader.
If you're running a version of Linux, your computer may try to use the nfc kernel module to talk to your ACR122U. You don't want it to do this, so make sure the nfc and enabling modules are not loaded. In Ubuntu/Debian/Raspberry Pi OS, blacklist pn533, pn533_usb, nfc modules so that they don't hijack the card reader.

```
$ printf '%s\n' 'pn533' 'pn533_usb' 'nfc' | sudo tee /etc/modprobe.d/blacklist-nfc.conf
$ printf '%s\n' 'blacklist pn533' 'blacklist pn533_usb' 'blacklist nfc' | sudo tee /etc/modprobe.d/blacklist-nfc.conf
```

### Debian Trixie and later

Starting with Debian Trixie (and Raspberry Pi OS based on it), `pcscd` uses polkit to decide who may use the card reader. By default only a user logged in at the console is allowed, so a program running in the background (e.g. started by pm2 at boot) is refused. To allow members of the `plugdev` group to use the reader, create `/etc/polkit-1/rules.d/60-pcscd.rules` with this content:

```
polkit.addRule(function(action, subject) {
if ((action.id == "org.debian.pcsc-lite.access_pcsc" ||
action.id == "org.debian.pcsc-lite.access_card") &&
subject.isInGroup("plugdev")) {
return polkit.Result.YES;
}
});
```

Both actions are needed: `access_pcsc` allows connecting to `pcscd` and `access_card` allows reading the card. Make sure the user that will run this program is in the `plugdev` group (the default user on Raspberry Pi OS already is). `groups` lists your groups, and this adds you:

```
$ sudo usermod -aG plugdev $USER
```

If you're using the ACR122U and it still isn't detected, create `/etc/udev/rules.d/99-acr122.rules` with this content:

```
SUBSYSTEM=="usb", ATTRS{idVendor}=="072f", ATTRS{idProduct}=="2200", GROUP="plugdev", MODE="0660"
```

and reload the udev rules:

```
$ sudo udevadm control --reload-rules
```

To make sure everything is square, it's probably a good idea to reboot. In Ubuntu/Debian/Raspberry Pi OS:

```
$ sudo reboot
$ sudo reboot now
```

## Setup Node
Expand All @@ -54,7 +86,7 @@ Install node and npm, e.g. download or follow the [official instructions](https:
so that you can run this code. On Ubuntu/Debian/Raspberry Pi OS, I do this:

```
$ curl -sL https://deb.nodesource.com/setup_15.x | sudo -E bash -
$ curl -sL https://deb.nodesource.com/setup_22.x | sudo -E bash -
$ sudo apt-get install -y nodejs
```

Expand All @@ -76,7 +108,19 @@ $ npm install

For simplicity, [sonos-http-api](https://github.com/jishi/node-sonos-http-api), needed for this program to work, is included as a dependency, though you don't need to use it if you already have an http api running elsewhere.

If you _DO_ want to use the included Sonos HTTP API, you'll need to configure it. Rename the `usersettings.json.example` to `usersettings.json` and edit it to your liking. You'll need to set the `spotify` and/or `apple` sections to your credentials. You can also set the `http` section to your liking. The defaults should work fine for most people.
If you _DO_ want to use the included Sonos HTTP API, it works out of the box with its built-in defaults. To use services that need credentials (e.g. Spotify or Apple Music) or to change other options, create a `settings.json` file in the `node_modules/sonos-http-api` directory, which is where sonos-http-api looks for it. See the [sonos-http-api](https://github.com/jishi/node-sonos-http-api) README for the available settings. Note that `npm install` and `npm ci` can delete and recreate `node_modules`, taking your `settings.json` with it, so keep a copy somewhere else and copy it back after updating this program.

## Configure this program

Copy the example settings to `usersettings.json` and edit it:

```
$ cp usersettings.json.example usersettings.json
```

Set `sonos_room` to the name of the Sonos room to play music in, as it appears in the Sonos app. `sonos_http_api` is the address of the Sonos HTTP API; the default, `http://127.0.0.1:5005`, is correct if you're running the included one on the same computer. If you have an older `usersettings.json` that uses `http://localhost:5005` and the program can't reach the API, change it to `http://127.0.0.1:5005`.

If there is no `usersettings.json`, the program prints a message at startup and uses the example settings instead.

## Run all the time

Expand All @@ -90,7 +134,13 @@ $ sudo npm install -g pm2
and spin-up sonos_nfc and sonos-http-api:

```
$ pm2 start npm -- run start-all
$ pm2 start npm --name sonos-nfc -- run start-all
```

If you already have the http API running elsewhere, set `sonos_http_api` in `usersettings.json` to point at that server and instead run just this program via `npm start`, so replace the `pm2 start` command above with

```
$ pm2 start npm --name sonos-nfc -- start
```

Then, to configure your system to run the startup, follow the instructions given when you run
Expand All @@ -105,12 +155,16 @@ e.g.
$ sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u pi --hp /home/pi
```

If you already have the http API running elsewhere, you can direct this program to that server via the `usersettings.json` (rename it from .example and update to how you would like to use) and instead run just this program via `npm start`, so replace the `pm2 start` command above with
and save the running process so pm2 restores it at boot:

```
$ pm2 start npm -- start
$ pm2 save
```

### If the reader can't be accessed

On Debian Trixie, if the program still can't read cards after adding the polkit rule above, first work through [Troubleshooting the card reader](#troubleshooting-the-card-reader) below; a typo in the rule file is easy to miss. As a last resort, you can run everything as root by putting `sudo` in front of each `pm2` command above, e.g. `sudo pm2 start npm --name sonos-nfc -- run start-all`, `sudo pm2 startup` and `sudo pm2 save`. Avoid this if you can: it also runs the included Sonos HTTP API, which listens on your network, as root.

## Debug

You can monitor the process output to see what's going on. If you're using pm2, you can see the process output via
Expand All @@ -119,6 +173,23 @@ You can monitor the process output to see what's going on. If you're using pm2,
$ pm2 log
```

(or `sudo pm2 log` if you started it with `sudo`).

### Troubleshooting the card reader

If the reader is detected but cards aren't read (for example, the log shows a card `with UID undefined`), check whether the reader works outside this program. Install `pcsc-tools` and run `pcsc_scan` as the same user that runs this program, without `sudo`, then present a card:

```
$ sudo apt install pcsc-tools
$ pcsc_scan
```

If `pcsc_scan` can't read the card either, the problem is in the system setup rather than this program. On Debian Trixie and later, the usual cause is polkit:

- Check the rule file for typos: `cat /etc/polkit-1/rules.d/60-pcscd.rules`. Errors in it are logged by `sudo journalctl -u polkit -b`.
- Check that your user is in the `plugdev` group with `id`. After adding yourself, log out and back in (or reboot).
- Look at what `pcscd` logged right after you presented a card: `sudo journalctl -u pcscd -b`. A refusal shows up as a line saying the process `is NOT authorized for action`.

# Programming cards

## Card record format
Expand Down
12 changes: 11 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { NFC } from 'nfc-pcsc';
import sonos_nfc from './lib/sonos_nfc.js';

const nfc = new NFC();
// nfc-pcsc silently drops some errors (e.g. failing to connect to a card) and only reports them
// through its logger, so print its errors and warnings
const logger = {
log() {},
debug() {},
info() {},
warn: console.warn,
error: console.error,
};

const nfc = new NFC(logger);

sonos_nfc(nfc);
11 changes: 10 additions & 1 deletion lib/process_sonos_command.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import fetch from 'node-fetch';
import fs from 'fs';

// Fall back to the example settings so the app (and tests) run before the user has created their own
let settings_file = 'usersettings.json';
if (!fs.existsSync(settings_file)) {
console.log(
'usersettings.json not found, using usersettings.json.example. Copy it to usersettings.json to configure.'
);
settings_file = 'usersettings.json.example';
}

var { sonos_room, sonos_http_api, reset_repeat, reset_shuffle, reset_crossfade } = JSON.parse(
fs.readFileSync('usersettings.json', 'utf-8')
fs.readFileSync(settings_file, 'utf-8')
);

export const get_sonos_url = (sonos_instruction, service_type) => {
Expand Down
Loading
Loading