Skip to content

Commit

Permalink
Merge pull request #192 from svrooij/feature/audio-clip-and-ha
Browse files Browse the repository at this point in the history
Feature/audio clip and ha
  • Loading branch information
svrooij committed Feb 15, 2023
2 parents 91d43cb + 0dbd867 commit 8373525
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 48 deletions.
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:https://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:https://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:https://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

0 comments on commit 8373525

Please sign in to comment.