diff --git a/config/sonos.json.example b/config/sonos.json.example
index 2dd62eae..1eab0cc5 100644
--- a/config/sonos.json.example
+++ b/config/sonos.json.example
@@ -3,7 +3,11 @@
"enable": true,
"name": "MySonos",
"data": {
- "host": "192.168.0.150"
+ "host": "192.168.0.150",
+ "devicesAllow": ["move","beam"],
+ "devicesBlock": ["roam"],
+ "groupsAllow": ["family"],
+ "groupsBlock": ["bedroom"]
}
}
]
diff --git a/docsite/docs/configuration/sources/sonos.mdx b/docsite/docs/configuration/sources/sonos.mdx
index 2f48a63b..81d96d5e 100644
--- a/docsite/docs/configuration/sources/sonos.mdx
+++ b/docsite/docs/configuration/sources/sonos.mdx
@@ -81,12 +81,20 @@ Please include these additional logs when creating a bug report. They are essent
+### Filtering Activity
+
+The **optional** `devices` and `groups` related properties found in the config below can be used to include or exclude Sonos devices/groups by name, case-insensitive.
+
## Configuration
You **must** define the IP address of at least one Sonos/accessory in order for Multi-scrobbler to connect to your Sonos system.
| Environmental Variable | Required? | Default | Description |
- | ---------------------- | --------- | ------- | ---------------------------------------------------------------------- |
+ | ---------------------- | --------- | ------- | :--------------------------------------------------------------------- |
| `SONOS_HOST` | Yes | | The IP address of any active Sonos device/accessory EX `192.168.0.150` |
+ | `SONOS_DEVICES_ALLOW` | No | | Comma-separated list of Sonos device names to scrobble from |
+ | `SONOS_DEVICES_BLOCK` | No | | Comma-separated list of Sonos device names to disallow scrobbles from |
+ | `SONOS_GROUPS_ALLOW` | No | | Comma-separated list of device group names to scrobble from |
+ | `SONOS_GROUPS_BLOCK` | No | | Comma-separated list of devices group names to disallow scrobbles from |
\ No newline at end of file
diff --git a/src/backend/common/infrastructure/config/source/sonos.ts b/src/backend/common/infrastructure/config/source/sonos.ts
index d76d6b52..c2a5575c 100644
--- a/src/backend/common/infrastructure/config/source/sonos.ts
+++ b/src/backend/common/infrastructure/config/source/sonos.ts
@@ -8,6 +8,24 @@ export interface SonosData extends CommonSourceData, PollingOptions {
* @examples ["192.168.0.170"]
* */
host: string
+
+ /**
+ * Only scrobble if device name contains strings from this list (case-insensitive)
+ * */
+ devicesAllow?: string | string[]
+ /**
+ * Do not scrobble if device name contains strings from this list (case-insensitive)
+ * */
+ devicesBlock?: string | string[]
+
+ /**
+ * Only scrobble if the name of a group the playing device belongs to contains strings from this list (case-insensitive)
+ * */
+ groupsAllow?: string | string[]
+ /**
+ * Do not scrobble if the name of a group the playing device belongs to contains strings from this list (case-insensitive)
+ * */
+ groupsBlock?: string | string[]
}
export interface SonosSourceConfig extends CommonSourceConfig {
data: SonosData
diff --git a/src/backend/sources/ScrobbleSources.ts b/src/backend/sources/ScrobbleSources.ts
index 6e4ccd59..abfca60a 100644
--- a/src/backend/sources/ScrobbleSources.ts
+++ b/src/backend/sources/ScrobbleSources.ts
@@ -798,6 +798,10 @@ export default class ScrobbleSources {
case 'sonos': {
const sonos = {
host: process.env.SONOS_HOST,
+ devicesAllow: process.env.SONOS_DEVICES_ALLOW,
+ devicesBlock: process.env.SONOS_DEVICES_BLOCK,
+ groupsAllow: process.env.SONOS_GROUPS_ALLOW,
+ groupsBlocks: process.env.SONOS_GROUPS_BLOCK
}
if (!Object.values(sonos).every(x => x === undefined)) {
configs.push({
diff --git a/src/backend/sources/SonosSource.ts b/src/backend/sources/SonosSource.ts
index 87354b74..9fec8ab2 100644
--- a/src/backend/sources/SonosSource.ts
+++ b/src/backend/sources/SonosSource.ts
@@ -19,7 +19,7 @@ import { GroupTransportState } from "@svrooij/sonos/lib/models/transport-state.j
import { Track } from "@svrooij/sonos/lib/models/track.js";
import { parseDurationFromTimestamp } from "../utils/TimeUtils.js";
import { FixedSizeList } from "fixed-size-list";
-import { buildStatePlayerPlayIdententifyingInfo } from "../utils/StringUtils.js";
+import { buildStatePlayerPlayIdententifyingInfo, parseArrayFromMaybeString } from "../utils/StringUtils.js";
import { isDebugMode } from "../utils.js";
export interface UniquePlay {
@@ -46,6 +46,11 @@ export class SonosSource extends MemoryPositionalSource {
uniqueDropReasons: FixedSizeList;
logFilterFailure: false | 'debug' | 'warn';
+ devicesAllow: string[] = [];
+ devicesBlock: string[] = [];
+ groupsAllow: string[] = [];
+ groupsBlock: string[] = [];
+
constructor(name: any, config: SonosSourceConfig, internal: InternalConfig, emitter: EventEmitter) {
const {
data,
@@ -67,6 +72,12 @@ export class SonosSource extends MemoryPositionalSource {
const {
options: {
logFilterFailure = (isDebugMode() ? 'debug' : 'warn'),
+ } = {},
+ data: {
+ devicesAllow = [],
+ devicesBlock = [],
+ groupsAllow = [],
+ groupsBlock = []
} = {}
} = this.config;
if (logFilterFailure !== false && !['debug', 'warn'].includes(logFilterFailure)) {
@@ -75,6 +86,11 @@ export class SonosSource extends MemoryPositionalSource {
this.logFilterFailure = logFilterFailure;
}
+ this.devicesAllow = parseArrayFromMaybeString(devicesAllow, {lower: true});
+ this.devicesBlock = parseArrayFromMaybeString(devicesBlock, {lower: true});
+ this.groupsAllow = parseArrayFromMaybeString(groupsAllow, {lower: true});
+ this.groupsBlock = parseArrayFromMaybeString(groupsBlock, {lower: true});
+
return true;
}
@@ -95,6 +111,19 @@ export class SonosSource extends MemoryPositionalSource {
if (!data.state.positionInfo.TrackMetaData.UpnpClass.toLocaleLowerCase().includes('musictrack')) {
return `UpnpClass does include 'musictrack', found '${data.state.positionInfo.TrackMetaData.UpnpClass}'`;
}
+
+ if(this.devicesAllow.length > 0 && !this.devicesAllow.some(x => data.device.Name.toLocaleLowerCase().includes(x))) {
+ return `'devicesAllow does not include a phrase found in ${data.device.Name}`;
+ }
+ if(this.devicesBlock.length > 0 && this.devicesBlock.some(x => data.device.Name.toLocaleLowerCase().includes(x))) {
+ return `'devicesBlock includes a phrase found in ${data.device.Name}`;
+ }
+ if(this.groupsAllow.length > 0 && !this.groupsAllow.some(x => data.device.GroupName.toLocaleLowerCase().includes(x))) {
+ return `'groupsAllow does not include a phrase found in ${data.device.GroupName}`;
+ }
+ if(this.groupsBlock.length > 0 && this.groupsBlock.some(x => data.device.GroupName.toLocaleLowerCase().includes(x))) {
+ return `'groupsBlock includes a phrase found in ${data.device.GroupName}`;
+ }
return;
}