Skip to content

Feature/audio clip and ha #192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 15, 2023
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
35 changes: 16 additions & 19 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,33 +33,30 @@ Every setting of this library can be set with environment variables prefixed wit

```bash
sonos2mqtt 0.0.0-development
A smarthome bridge between your sonos system and a mqtt server.
Control and monitor Sonos speakers through MQTT

Usage: index.js [options]

Options:
--prefix instance name. used as prefix for all topics [default: "sonos"]
--mqtt mqtt broker url. See
https://svrooij.io/sonos2mqtt/getting-started.html#configuration
--prefix instance name. used as prefix for all topics [default: "sonos"]
--mqtt mqtt broker url. See
https://sonos2mqtt.svrooij.io/getting-started.html#configuration
[default: "mqtt://127.0.0.1"]
--clientid Specify the client id to be used
--wait Number of seconds to search for a speaker, until exit
[number] [default: 30]
--log Set the loglevel
[choices: "warning", "information", "debug"] [default: "information"]
-d, --distinct Publish distinct track states [boolean] [default: false]
-h, --help Show help [boolean]
--ttslang Default TTS language [default: "en-US"]
--ttsendpoint Default endpoint for text-to-speech
--device Start with one known IP instead of device discovery.
--discovery Emit retained auto-discovery messages for each player.
[boolean] [default: false]
--friendlynames Use device name or uuid in topics (except the united topic,
always uuid) [choices: "name", "uuid"]
--clientid Specify the client id to be used
--wait Number of seconds to search for speakers [number] [default: 30]
--log Set the loglevel [choices: "warning", "information", "debug"]
-d, --distinct Publish distinct track states [boolean]
-h, --help Show help [boolean]
--ttslang Default TTS language [default: "en-US"]
--ttsendpoint Default endpoint for text-to-speech
--device Start with one known IP instead of device discovery.
--discovery Emit retained auto-discovery messages for each player. [boolean]
--friendlynames Use device name or uuid in topics [choices: "name", "uuid"]
--tvGroup The UUID of the coordinator to which the Soundbar should be joined
--tvUuid The UUID of the soundbar which should auto stop the tvGroup
--tvVolume Volume the soundbar should go to when TV playback starts
--version Show version number
--experimental Activate some cutting edge features [boolean]
--version Show version number [boolean]
```

You can configure the **mqtt** url by setting a supported [URL](https://nodejs.org/api/url.html#url_constructor_new_url_input_base).
Expand Down
46 changes: 37 additions & 9 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"test": "npm run lint"
},
"dependencies": {
"@svrooij/sonos": "^2.6.0-beta.4",
"@svrooij/sonos": "^2.6.0-beta.5",
"mqtt": "4.3.7",
"serilogger": "^0.4.1",
"yalm": "4.1.0",
Expand Down
16 changes: 9 additions & 7 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ export interface Config {
tvGroup?: string;
tvUuid?: string;
tvVolume?: number;
experimental?: boolean;
}

const defaultConfig: Config = {
mqtt: 'mqtt://127.0.0.1',
prefix: 'sonos',
wait: 30,
distinct: false,
discovery: true,
discovery: false,
log: 'information',
friendlynames: 'name'
friendlynames: 'name',
experimental: false,
}

export class ConfigLoader {
Expand Down Expand Up @@ -63,9 +65,9 @@ export class ConfigLoader {
return yargs
.usage(pkg.name + ' ' + pkg.version + '\n' + pkg.description + '\n\nUsage: $0 [options]')
.describe('prefix', 'instance name. used as prefix for all topics')
.describe('mqtt', 'mqtt broker url. See https://svrooij.io/sonos2mqtt/getting-started.html#configuration')
.describe('mqtt', 'mqtt broker url. See https://sonos2mqtt.svrooij.io/getting-started.html#configuration')
.describe('clientid', 'Specify the client id to be used')
.describe('wait', 'Number of seconds to search for a speaker, until exit')
.describe('wait', 'Number of seconds to search for speakers')
.describe('log', 'Set the loglevel')
.describe('d', 'Publish distinct track states')
.describe('h', 'show help')
Expand All @@ -79,6 +81,8 @@ export class ConfigLoader {
.describe('tvUuid', 'The UUID of the soundbar which should auto stop the tvGroup')
.describe('tvVolume', 'Volume the soundbar should go to when TV playback starts')
.number('tv_volume')
.describe('experimental', 'Activate some cutting edge features')
.boolean('experimental')
.alias({
h: 'help',
d: 'distinct'
Expand All @@ -89,12 +93,10 @@ export class ConfigLoader {
.default({
mqtt: 'mqtt://127.0.0.1',
prefix: 'sonos',
d: false,
wait: 30,
'ttslang': 'en-US',
'ttsendpoint': undefined,
discovery: false,
log: 'information'
log: 'information',
})
.choices('log', ['warning', 'information', 'debug'])
.wrap(90)
Expand Down
14 changes: 10 additions & 4 deletions src/ha-discovery.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SonosDevice } from "@svrooij/sonos/lib";
import DeviceDescription from "@svrooij/sonos/lib/models/device-description";
import { GetZoneInfoResponse } from "@svrooij/sonos/lib/services";

interface AutoDiscoveryDevice {
identifiers?: string[];
Expand Down Expand Up @@ -36,14 +37,18 @@ export interface AutoDiscoveryMessage {
}

export class HaAutoDiscovery {
private static deviceDescriptionForSonos(sonos: SonosDevice, description: DeviceDescription, prefix: string): AutoDiscoveryDevice {
private static deviceDescriptionForSonos(sonos: SonosDevice, description: DeviceDescription, zoneInfo: GetZoneInfoResponse, prefix: string): AutoDiscoveryDevice {
return {
identifiers: [sonos.Uuid],
manufacturer: description.manufacturer,
model: description.modelName,
name: sonos.Name,
sw_version: description.softwareVersion,
connections: [["host", `${sonos.Host}:${sonos.Port}`], ["mqtt", `${prefix}/${sonos.Uuid}`]]
connections: [
["host", `${sonos.Host}:${sonos.Port}`],
["mqtt", `${prefix}/${sonos.Uuid}`],
["mac", `${zoneInfo.MACAddress}`],
]
};
}

Expand All @@ -53,7 +58,7 @@ export class HaAutoDiscovery {
payload: {
device: device,
device_class: 'speaker',
icon: 'mdi:speaker',
icon: device.model?.includes('Playbar') ? 'mdi:soundbar' : 'mdi:speaker',
name: name,
state_topic: `${prefix}/${uuid}`,
command_topic: `${prefix}/${uuid}/control`,
Expand Down Expand Up @@ -83,7 +88,8 @@ export class HaAutoDiscovery {

public static async GenerateAutoDiscoveryMessages(sonos: SonosDevice, prefix = 'sonos'): Promise<AutoDiscoveryMessage[]> {
const description = await sonos.GetDeviceDescription();
const deviceDescription = HaAutoDiscovery.deviceDescriptionForSonos(sonos, description, prefix);
const zoneInfo = await sonos.DevicePropertiesService.GetZoneInfo();
const deviceDescription = HaAutoDiscovery.deviceDescriptionForSonos(sonos, description, zoneInfo, prefix);
return [
HaAutoDiscovery.autoDiscoverMediaPlayer(sonos.Name, sonos.Uuid, deviceDescription, prefix),
//HaAutoDiscovery.autoDiscoverCrossfadeSwitch(sonos.Name, sonos.Uuid, deviceDescription, prefix, discoveryPrefix)
Expand Down
14 changes: 7 additions & 7 deletions src/sonos-command-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { DeviceControl } from "./device-control";
import { SonosCommands } from "./sonos-commands";

export class SonosCommandMapping {
static async ExecuteControl(device: SonosDevice, control: DeviceControl): Promise<any> {
static async ExecuteControl(device: SonosDevice, control: DeviceControl, experimental = false): Promise<any> {
if(control.command !== undefined) {
return await SonosCommandMapping.ExecuteCommand(device, control.command, control.input)
return await SonosCommandMapping.ExecuteCommand(device, control.command, control.input, experimental)
} else if(control.sonosCommand !== undefined) {
return await device.ExecuteCommand(control.sonosCommand, control.input)
}
}
static async ExecuteCommand(device: SonosDevice, command: SonosCommands, input: unknown): Promise<any> {
static async ExecuteCommand(device: SonosDevice, command: SonosCommands, input: unknown, experimental = false): Promise<any> {
const payload = input as any;
switch(command) {
case SonosCommands.AdvancedCommand:
Expand All @@ -24,7 +24,7 @@ export class SonosCommandMapping {

case SonosCommands.Command:
if (payload.cmd && Object.values(SonosCommands).some(v => v === payload.cmd))
return SonosCommandMapping.ExecuteCommand(device, payload.cmd as SonosCommands, payload.val)
return SonosCommandMapping.ExecuteCommand(device, payload.cmd as SonosCommands, payload.val, experimental)
break;

case SonosCommands.Crossfade:
Expand All @@ -46,8 +46,8 @@ export class SonosCommandMapping {

case SonosCommands.Notify:
if (typeof payload === 'string')
return await device.PlayNotification({ trackUri: payload });
return await device.PlayNotification(payload);
return experimental ? await device.PlayNotificationAudioClip({ trackUri: payload }) : await device.PlayNotification({ trackUri: payload });
return experimental ? await device.PlayNotificationAudioClip(payload) : await device.PlayNotification(payload);

case SonosCommands.NotifyTwo:
return await device.PlayNotificationTwo(payload);
Expand Down Expand Up @@ -153,7 +153,7 @@ export class SonosCommandMapping {
break;
case SonosCommands.Speak:
if(typeof payload === "object") {
return await device.PlayTTS(payload)
return experimental ? await device.PlayTTSAudioClip(payload) : await device.PlayTTS(payload)
}
break;

Expand Down
5 changes: 4 additions & 1 deletion src/sonos-to-mqtt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export class SonosToMqtt {

if (success) {
this.log.info('Found {numDevices} sonos speakers', this.sonosManager.Devices.length)
if (this.config.experimental === true) {
this.log.info('Experimental features activated, please provide feedback on notifications. https://github.com/svrooij/sonos2mqtt/discussions/191')
}
this.setupMqttEvents()
this.setupSonosEvents()
this.mqtt.connect()
Expand Down Expand Up @@ -111,7 +114,7 @@ export class SonosToMqtt {
return;
}
try {
const response = await SonosCommandMapping.ExecuteControl(correctDevice, payload);
const response = await SonosCommandMapping.ExecuteControl(correctDevice, payload, this.config.experimental === true);
if (payload?.command === SonosCommands.Seek) {
await this.periodicallyUpdatePosition(correctDevice);
}
Expand Down