@lox-audio-server/sonos 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/esm/index.js +27 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/sonos/api/namespace.js +38 -0
- package/dist/esm/sonos/api/namespaces/audioClip.js +25 -0
- package/dist/esm/sonos/api/namespaces/groupVolume.js +25 -0
- package/dist/esm/sonos/api/namespaces/groups.js +31 -0
- package/dist/esm/sonos/api/namespaces/homeTheater.js +13 -0
- package/dist/esm/sonos/api/namespaces/playback.js +56 -0
- package/dist/esm/sonos/api/namespaces/playbackMetadata.js +16 -0
- package/dist/esm/sonos/api/namespaces/playbackSession.js +67 -0
- package/dist/esm/sonos/api/namespaces/playerVolume.js +41 -0
- package/dist/esm/sonos/api/websocket.js +310 -0
- package/dist/esm/sonos/client.js +193 -0
- package/dist/esm/sonos/constants.js +18 -0
- package/dist/esm/sonos/errors.js +49 -0
- package/dist/esm/sonos/group.js +231 -0
- package/dist/esm/sonos/models.js +63 -0
- package/dist/esm/sonos/player.js +112 -0
- package/dist/esm/sonos/types.js +46 -0
- package/dist/esm/sonos/utils.js +18 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +27 -0
- package/dist/sonos/api/namespace.d.ts +16 -0
- package/dist/sonos/api/namespace.js +38 -0
- package/dist/sonos/api/namespaces/audioClip.d.ts +16 -0
- package/dist/sonos/api/namespaces/audioClip.js +25 -0
- package/dist/sonos/api/namespaces/groupVolume.d.ts +10 -0
- package/dist/sonos/api/namespaces/groupVolume.js +25 -0
- package/dist/sonos/api/namespaces/groups.d.ts +10 -0
- package/dist/sonos/api/namespaces/groups.js +31 -0
- package/dist/sonos/api/namespaces/homeTheater.d.ts +5 -0
- package/dist/sonos/api/namespaces/homeTheater.js +13 -0
- package/dist/sonos/api/namespaces/playback.d.ts +17 -0
- package/dist/sonos/api/namespaces/playback.js +56 -0
- package/dist/sonos/api/namespaces/playbackMetadata.d.ts +7 -0
- package/dist/sonos/api/namespaces/playbackMetadata.js +16 -0
- package/dist/sonos/api/namespaces/playbackSession.d.ts +30 -0
- package/dist/sonos/api/namespaces/playbackSession.js +67 -0
- package/dist/sonos/api/namespaces/playerVolume.d.ts +12 -0
- package/dist/sonos/api/namespaces/playerVolume.js +41 -0
- package/dist/sonos/api/websocket.d.ts +51 -0
- package/dist/sonos/api/websocket.js +310 -0
- package/dist/sonos/client.d.ts +44 -0
- package/dist/sonos/client.js +193 -0
- package/dist/sonos/constants.d.ts +14 -0
- package/dist/sonos/constants.js +18 -0
- package/dist/sonos/errors.d.ts +25 -0
- package/dist/sonos/errors.js +49 -0
- package/dist/sonos/group.d.ts +58 -0
- package/dist/sonos/group.js +231 -0
- package/dist/sonos/models.d.ts +20 -0
- package/dist/sonos/models.js +63 -0
- package/dist/sonos/player.d.ts +33 -0
- package/dist/sonos/player.js +112 -0
- package/dist/sonos/types.d.ts +279 -0
- package/dist/sonos/types.js +46 -0
- package/dist/sonos/utils.d.ts +6 -0
- package/dist/sonos/utils.js +18 -0
- package/package.json +49 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PlayModesWrapper = exports.PlaybackActionsWrapper = void 0;
|
|
4
|
+
exports.normalizeContainerType = normalizeContainerType;
|
|
5
|
+
exports.normalizeMusicService = normalizeMusicService;
|
|
6
|
+
const types_1 = require("./types");
|
|
7
|
+
class PlaybackActionsWrapper {
|
|
8
|
+
raw;
|
|
9
|
+
constructor(raw) {
|
|
10
|
+
this.raw = raw;
|
|
11
|
+
}
|
|
12
|
+
get canSkipForward() {
|
|
13
|
+
return this.raw.canSkipForward ?? this.raw.canSkip ?? false;
|
|
14
|
+
}
|
|
15
|
+
get canSkipBackward() {
|
|
16
|
+
return this.raw.canSkipBack ?? this.raw.canSkipToPrevious ?? false;
|
|
17
|
+
}
|
|
18
|
+
get canPlay() {
|
|
19
|
+
return this.raw.canPlay ?? false;
|
|
20
|
+
}
|
|
21
|
+
get canPause() {
|
|
22
|
+
return this.raw.canPause ?? false;
|
|
23
|
+
}
|
|
24
|
+
get canStop() {
|
|
25
|
+
return this.raw.canStop ?? false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.PlaybackActionsWrapper = PlaybackActionsWrapper;
|
|
29
|
+
class PlayModesWrapper {
|
|
30
|
+
raw;
|
|
31
|
+
constructor(raw) {
|
|
32
|
+
this.raw = raw;
|
|
33
|
+
}
|
|
34
|
+
get crossfade() {
|
|
35
|
+
return this.raw.crossfade;
|
|
36
|
+
}
|
|
37
|
+
get repeat() {
|
|
38
|
+
return this.raw.repeat;
|
|
39
|
+
}
|
|
40
|
+
get repeatOne() {
|
|
41
|
+
return this.raw.repeatOne;
|
|
42
|
+
}
|
|
43
|
+
get shuffle() {
|
|
44
|
+
return this.raw.shuffle;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.PlayModesWrapper = PlayModesWrapper;
|
|
48
|
+
function normalizeContainerType(containerType) {
|
|
49
|
+
if (!containerType)
|
|
50
|
+
return null;
|
|
51
|
+
if (Object.values(types_1.ContainerType).includes(containerType)) {
|
|
52
|
+
return containerType;
|
|
53
|
+
}
|
|
54
|
+
return containerType;
|
|
55
|
+
}
|
|
56
|
+
function normalizeMusicService(serviceId) {
|
|
57
|
+
if (!serviceId)
|
|
58
|
+
return null;
|
|
59
|
+
if (Object.values(types_1.MusicService).includes(serviceId)) {
|
|
60
|
+
return serviceId;
|
|
61
|
+
}
|
|
62
|
+
return serviceId;
|
|
63
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SonosClient } from './client';
|
|
2
|
+
import { SonosGroup } from './group';
|
|
3
|
+
import { Player } from './types';
|
|
4
|
+
export declare class SonosPlayer {
|
|
5
|
+
private client;
|
|
6
|
+
private data;
|
|
7
|
+
private volumeData;
|
|
8
|
+
private activeGroup;
|
|
9
|
+
constructor(client: SonosClient, data: Player);
|
|
10
|
+
init(): Promise<void>;
|
|
11
|
+
get id(): string;
|
|
12
|
+
get name(): string;
|
|
13
|
+
get icon(): string;
|
|
14
|
+
get volumeLevel(): number | undefined;
|
|
15
|
+
get volumeMuted(): boolean | undefined;
|
|
16
|
+
get hasFixedVolume(): boolean | undefined;
|
|
17
|
+
get group(): SonosGroup | null;
|
|
18
|
+
get groupMembers(): string[];
|
|
19
|
+
get isCoordinator(): boolean;
|
|
20
|
+
get isPassive(): boolean;
|
|
21
|
+
setVolume(volume?: number, muted?: boolean): Promise<void>;
|
|
22
|
+
duck(durationMillis?: number): Promise<void>;
|
|
23
|
+
leaveGroup(): Promise<void>;
|
|
24
|
+
joinGroup(groupId: string): Promise<void>;
|
|
25
|
+
playAudioClip(url: string, options?: {
|
|
26
|
+
volume?: number;
|
|
27
|
+
name?: string;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
loadHomeTheaterPlayback(): Promise<void>;
|
|
30
|
+
updateData(newData: Player): void;
|
|
31
|
+
checkActiveGroup(): void;
|
|
32
|
+
private handleVolumeUpdate;
|
|
33
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SonosPlayer = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
class SonosPlayer {
|
|
6
|
+
client;
|
|
7
|
+
data;
|
|
8
|
+
volumeData = null;
|
|
9
|
+
activeGroup = null;
|
|
10
|
+
constructor(client, data) {
|
|
11
|
+
this.client = client;
|
|
12
|
+
this.data = data;
|
|
13
|
+
this.activeGroup = this.client.groups.find((g) => g.coordinatorId === this.id || g.playerIds.includes(this.id)) ?? null;
|
|
14
|
+
}
|
|
15
|
+
async init() {
|
|
16
|
+
this.volumeData = await this.client.api.playerVolume.getVolume(this.id);
|
|
17
|
+
await this.client.api.playerVolume.subscribe(this.id, (data) => this.handleVolumeUpdate(data));
|
|
18
|
+
}
|
|
19
|
+
get id() {
|
|
20
|
+
return this.data.id;
|
|
21
|
+
}
|
|
22
|
+
get name() {
|
|
23
|
+
return this.data.name;
|
|
24
|
+
}
|
|
25
|
+
get icon() {
|
|
26
|
+
return this.data.icon ?? '';
|
|
27
|
+
}
|
|
28
|
+
get volumeLevel() {
|
|
29
|
+
return this.volumeData?.volume;
|
|
30
|
+
}
|
|
31
|
+
get volumeMuted() {
|
|
32
|
+
return this.volumeData?.muted;
|
|
33
|
+
}
|
|
34
|
+
get hasFixedVolume() {
|
|
35
|
+
return this.volumeData?.fixed;
|
|
36
|
+
}
|
|
37
|
+
get group() {
|
|
38
|
+
return this.activeGroup;
|
|
39
|
+
}
|
|
40
|
+
get groupMembers() {
|
|
41
|
+
return this.group?.playerIds ?? [];
|
|
42
|
+
}
|
|
43
|
+
get isCoordinator() {
|
|
44
|
+
return Boolean(this.group && this.group.coordinatorId === this.id);
|
|
45
|
+
}
|
|
46
|
+
get isPassive() {
|
|
47
|
+
return Boolean(this.group && this.group.coordinatorId !== this.id);
|
|
48
|
+
}
|
|
49
|
+
async setVolume(volume, muted) {
|
|
50
|
+
await this.client.api.playerVolume.setVolume(this.id, volume, muted);
|
|
51
|
+
}
|
|
52
|
+
async duck(durationMillis) {
|
|
53
|
+
await this.client.api.playerVolume.duck(this.id, durationMillis);
|
|
54
|
+
}
|
|
55
|
+
async leaveGroup() {
|
|
56
|
+
if (!this.group)
|
|
57
|
+
return;
|
|
58
|
+
await this.client.api.groups.modifyGroupMembers(this.group.id, [], [this.id]);
|
|
59
|
+
}
|
|
60
|
+
async joinGroup(groupId) {
|
|
61
|
+
await this.client.api.groups.modifyGroupMembers(groupId, [this.id], []);
|
|
62
|
+
}
|
|
63
|
+
async playAudioClip(url, options = {}) {
|
|
64
|
+
await this.client.api.audioClip.loadAudioClip(this.id, {
|
|
65
|
+
name: options.name ?? 'lox-sonos',
|
|
66
|
+
appId: 'lox-sonos',
|
|
67
|
+
streamUrl: url,
|
|
68
|
+
volume: options.volume,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async loadHomeTheaterPlayback() {
|
|
72
|
+
await this.client.api.homeTheater.loadHomeTheaterPlayback(this.id);
|
|
73
|
+
}
|
|
74
|
+
updateData(newData) {
|
|
75
|
+
this.checkActiveGroup();
|
|
76
|
+
let changed = false;
|
|
77
|
+
for (const [key, value] of Object.entries(newData)) {
|
|
78
|
+
if (this.data[key] !== value) {
|
|
79
|
+
this.data[key] = value;
|
|
80
|
+
changed = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (changed) {
|
|
84
|
+
this.client.signalEvent({
|
|
85
|
+
eventType: constants_1.EventType.PLAYER_UPDATED,
|
|
86
|
+
objectId: this.id,
|
|
87
|
+
data: this,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
checkActiveGroup() {
|
|
92
|
+
const prevGroupId = this.activeGroup?.id;
|
|
93
|
+
this.activeGroup =
|
|
94
|
+
this.client.groups.find((g) => g.coordinatorId === this.id || g.playerIds.includes(this.id)) ?? null;
|
|
95
|
+
if (prevGroupId !== this.activeGroup?.id) {
|
|
96
|
+
this.client.signalEvent({
|
|
97
|
+
eventType: constants_1.EventType.PLAYER_UPDATED,
|
|
98
|
+
objectId: this.id,
|
|
99
|
+
data: this,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
handleVolumeUpdate(data) {
|
|
104
|
+
this.volumeData = data;
|
|
105
|
+
this.client.signalEvent({
|
|
106
|
+
eventType: constants_1.EventType.PLAYER_UPDATED,
|
|
107
|
+
objectId: this.id,
|
|
108
|
+
data: this,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.SonosPlayer = SonosPlayer;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { EventType } from './constants';
|
|
2
|
+
export interface CommandMessage {
|
|
3
|
+
namespace: string;
|
|
4
|
+
command: string;
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
cmdId?: string;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface ResultMessage {
|
|
10
|
+
namespace: string;
|
|
11
|
+
response: string;
|
|
12
|
+
householdId: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
sessionId?: string;
|
|
15
|
+
cmdId?: string;
|
|
16
|
+
success?: boolean;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface ErrorResponse {
|
|
20
|
+
errorCode: string;
|
|
21
|
+
reason?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare enum PlayBackState {
|
|
24
|
+
IDLE = "PLAYBACK_STATE_IDLE",
|
|
25
|
+
BUFFERING = "PLAYBACK_STATE_BUFFERING",
|
|
26
|
+
PAUSED = "PLAYBACK_STATE_PAUSED",
|
|
27
|
+
PLAYING = "PLAYBACK_STATE_PLAYING"
|
|
28
|
+
}
|
|
29
|
+
export declare enum SonosCapability {
|
|
30
|
+
CLOUD = "CLOUD",
|
|
31
|
+
PLAYBACK = "PLAYBACK",
|
|
32
|
+
AIRPLAY = "AIRPLAY",
|
|
33
|
+
LINE_IN = "LINE_IN",
|
|
34
|
+
VOICE = "VOICE",
|
|
35
|
+
AUDIO_CLIP = "AUDIO_CLIP",
|
|
36
|
+
MICROPHONE_SWITCH = "MICROPHONE_SWITCH",
|
|
37
|
+
HT_PLAYBACK = "HT_PLAYBACK"
|
|
38
|
+
}
|
|
39
|
+
export interface Group {
|
|
40
|
+
_objectType: 'group';
|
|
41
|
+
coordinatorId: string;
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
playbackState?: string;
|
|
45
|
+
playerIds: string[];
|
|
46
|
+
areaIds?: string[];
|
|
47
|
+
}
|
|
48
|
+
export interface GroupInfo {
|
|
49
|
+
_objectType: 'groupInfo';
|
|
50
|
+
group: Group;
|
|
51
|
+
}
|
|
52
|
+
export interface ZoneMemberState {
|
|
53
|
+
_objectType: 'zoneMemberState';
|
|
54
|
+
disconnected: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface ActiveZoneMember {
|
|
57
|
+
_objectType: 'activeZoneMember';
|
|
58
|
+
channelMap: string[];
|
|
59
|
+
id: string;
|
|
60
|
+
state: ZoneMemberState;
|
|
61
|
+
}
|
|
62
|
+
export interface ActiveZone {
|
|
63
|
+
_objectType: 'activeZone';
|
|
64
|
+
members: ActiveZoneMember[];
|
|
65
|
+
name: string;
|
|
66
|
+
zoneId?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface DeviceInfo {
|
|
69
|
+
_objectType: 'deviceInfo';
|
|
70
|
+
id: string;
|
|
71
|
+
primaryDeviceId?: string;
|
|
72
|
+
serialNumber?: string;
|
|
73
|
+
modelDisplayName?: string;
|
|
74
|
+
color?: string;
|
|
75
|
+
capabilities: SonosCapability[];
|
|
76
|
+
apiVersion?: string;
|
|
77
|
+
minApiVersion?: string;
|
|
78
|
+
name?: string;
|
|
79
|
+
websocketUrl?: string;
|
|
80
|
+
softwareVersion?: string;
|
|
81
|
+
hwVersion?: string;
|
|
82
|
+
swGen?: number;
|
|
83
|
+
}
|
|
84
|
+
export interface Player {
|
|
85
|
+
_objectType: 'player';
|
|
86
|
+
id: string;
|
|
87
|
+
name: string;
|
|
88
|
+
websocketUrl: string;
|
|
89
|
+
softwareVersion: string;
|
|
90
|
+
apiVersion: string;
|
|
91
|
+
minApiVersion: string;
|
|
92
|
+
devices: DeviceInfo[];
|
|
93
|
+
zoneInfo: ActiveZone;
|
|
94
|
+
icon?: string;
|
|
95
|
+
}
|
|
96
|
+
export interface Groups {
|
|
97
|
+
_objectType: 'groups';
|
|
98
|
+
groups: Group[];
|
|
99
|
+
partial: boolean;
|
|
100
|
+
players: Player[];
|
|
101
|
+
}
|
|
102
|
+
export interface DiscoveryInfo {
|
|
103
|
+
_objectType: 'discoveryInfo';
|
|
104
|
+
device: DeviceInfo;
|
|
105
|
+
householdId: string;
|
|
106
|
+
locationId: string;
|
|
107
|
+
playerId: string;
|
|
108
|
+
groupId: string;
|
|
109
|
+
websocketUrl: string;
|
|
110
|
+
restUrl: string;
|
|
111
|
+
}
|
|
112
|
+
export interface PlaybackActions {
|
|
113
|
+
_objectType?: 'playbackAction';
|
|
114
|
+
canCrossfade?: boolean;
|
|
115
|
+
canPause?: boolean;
|
|
116
|
+
canPlay?: boolean;
|
|
117
|
+
canRepeat?: boolean;
|
|
118
|
+
canRepeatOne?: boolean;
|
|
119
|
+
canSeek?: boolean;
|
|
120
|
+
canShuffle?: boolean;
|
|
121
|
+
canSkip?: boolean;
|
|
122
|
+
canSkipBack?: boolean;
|
|
123
|
+
canSkipToPrevious?: boolean;
|
|
124
|
+
canStop?: boolean;
|
|
125
|
+
canSkipForward?: boolean;
|
|
126
|
+
}
|
|
127
|
+
export interface PlayModes {
|
|
128
|
+
_objectType?: 'playModes';
|
|
129
|
+
crossfade?: boolean;
|
|
130
|
+
repeat?: boolean;
|
|
131
|
+
repeatOne?: boolean;
|
|
132
|
+
shuffle?: boolean;
|
|
133
|
+
}
|
|
134
|
+
export interface PlaybackStatus {
|
|
135
|
+
_objectType: 'playbackStatus';
|
|
136
|
+
availablePlaybackActions: PlaybackActions;
|
|
137
|
+
isDucking: boolean;
|
|
138
|
+
playbackState: PlayBackState;
|
|
139
|
+
playModes: PlayModes;
|
|
140
|
+
positionMillis: number;
|
|
141
|
+
previousPositionMillis: number;
|
|
142
|
+
}
|
|
143
|
+
export interface GroupVolume {
|
|
144
|
+
_objectType: 'groupVolume';
|
|
145
|
+
fixed: boolean;
|
|
146
|
+
muted: boolean;
|
|
147
|
+
volume: number;
|
|
148
|
+
}
|
|
149
|
+
export interface PlayerVolume {
|
|
150
|
+
_objectType: 'playerVolume';
|
|
151
|
+
fixed: boolean;
|
|
152
|
+
muted: boolean;
|
|
153
|
+
volume: number;
|
|
154
|
+
}
|
|
155
|
+
export declare enum MusicService {
|
|
156
|
+
SPOTIFY = "9",
|
|
157
|
+
MUSIC_ASSISTANT = "mass",
|
|
158
|
+
TUNEIN = "303",
|
|
159
|
+
QOBUZ = "31",
|
|
160
|
+
YOUTUBE_MUSIC = "284",
|
|
161
|
+
LOCAL_LIBRARY = "local-library"
|
|
162
|
+
}
|
|
163
|
+
export interface MetadataId {
|
|
164
|
+
_objectType: 'id';
|
|
165
|
+
serviceId?: string;
|
|
166
|
+
objectId?: string;
|
|
167
|
+
accountId?: string;
|
|
168
|
+
}
|
|
169
|
+
export interface Service {
|
|
170
|
+
_objectType: 'service';
|
|
171
|
+
name: string;
|
|
172
|
+
images?: Image[];
|
|
173
|
+
}
|
|
174
|
+
export interface Image {
|
|
175
|
+
_objectType: 'image';
|
|
176
|
+
url: string;
|
|
177
|
+
name?: string;
|
|
178
|
+
type?: string;
|
|
179
|
+
}
|
|
180
|
+
export interface Artist {
|
|
181
|
+
_objectType: 'artist';
|
|
182
|
+
name: string;
|
|
183
|
+
}
|
|
184
|
+
export interface Album {
|
|
185
|
+
_objectType: 'album';
|
|
186
|
+
name: string;
|
|
187
|
+
}
|
|
188
|
+
export interface Track {
|
|
189
|
+
_objectType: 'track';
|
|
190
|
+
type: string;
|
|
191
|
+
name: string;
|
|
192
|
+
mediaUrl?: string;
|
|
193
|
+
images?: Image[];
|
|
194
|
+
contentType?: string;
|
|
195
|
+
album?: Album;
|
|
196
|
+
artist?: Artist;
|
|
197
|
+
releaseDate?: string;
|
|
198
|
+
id?: MetadataId;
|
|
199
|
+
service?: Service;
|
|
200
|
+
durationMillis?: number;
|
|
201
|
+
trackNumber?: number;
|
|
202
|
+
explicit?: boolean;
|
|
203
|
+
}
|
|
204
|
+
export declare enum ContainerType {
|
|
205
|
+
LINEIN = "linein",
|
|
206
|
+
STATION = "station",
|
|
207
|
+
PLAYLIST = "playlist",
|
|
208
|
+
AIRPLAY = "linein.airplay",
|
|
209
|
+
PODCAST = "podcast",
|
|
210
|
+
BOOK = "book",
|
|
211
|
+
ARTIST = "artist",
|
|
212
|
+
ALBUM = "album",
|
|
213
|
+
ARTIST_LOCAL = "artist.local",
|
|
214
|
+
ALBUM_LOCAL = "album.local",
|
|
215
|
+
HOME_THEATER_SPDIF = "linein.homeTheater.spdif",
|
|
216
|
+
HOME_THEATER_HDMI = "linein.homeTheater.hdmi"
|
|
217
|
+
}
|
|
218
|
+
export interface Container {
|
|
219
|
+
_objectType: 'container';
|
|
220
|
+
name: string;
|
|
221
|
+
type: string;
|
|
222
|
+
id?: MetadataId;
|
|
223
|
+
service?: Service;
|
|
224
|
+
images?: Image[];
|
|
225
|
+
explicit?: boolean;
|
|
226
|
+
}
|
|
227
|
+
export interface QueueItem {
|
|
228
|
+
_objectType: 'queueItem';
|
|
229
|
+
id: string;
|
|
230
|
+
deleted?: boolean;
|
|
231
|
+
track?: Track;
|
|
232
|
+
}
|
|
233
|
+
export interface PlaybackSession {
|
|
234
|
+
_objectType: 'sessionStatus';
|
|
235
|
+
sessionId: string;
|
|
236
|
+
sessionState: string;
|
|
237
|
+
sessionCreated: boolean;
|
|
238
|
+
customData: string;
|
|
239
|
+
}
|
|
240
|
+
export interface MetadataStatus {
|
|
241
|
+
_objectType: 'metadataStatus';
|
|
242
|
+
container?: Container;
|
|
243
|
+
currentItem?: QueueItem;
|
|
244
|
+
nextItem?: QueueItem;
|
|
245
|
+
streamInfo?: string;
|
|
246
|
+
playbackSession?: PlaybackSession;
|
|
247
|
+
}
|
|
248
|
+
export interface AudioClip {
|
|
249
|
+
_objectType: 'audioClip';
|
|
250
|
+
id: string;
|
|
251
|
+
name: string;
|
|
252
|
+
appId: string;
|
|
253
|
+
priority: string;
|
|
254
|
+
clipType: string;
|
|
255
|
+
status: string;
|
|
256
|
+
clipLEDBehavior: string;
|
|
257
|
+
}
|
|
258
|
+
export interface AudioClipStatusEvent {
|
|
259
|
+
_objectType: 'audioClipStatus';
|
|
260
|
+
audioClips: AudioClip[];
|
|
261
|
+
}
|
|
262
|
+
export interface LoadContentRequest {
|
|
263
|
+
type: string;
|
|
264
|
+
id: MetadataId;
|
|
265
|
+
playbackAction: string;
|
|
266
|
+
playModes?: PlayModes;
|
|
267
|
+
}
|
|
268
|
+
export interface SessionStatus {
|
|
269
|
+
_objectType: 'sessionStatus';
|
|
270
|
+
sessionId: string;
|
|
271
|
+
sessionState: string;
|
|
272
|
+
sessionCreated: boolean;
|
|
273
|
+
customData: string;
|
|
274
|
+
}
|
|
275
|
+
export interface SonosEvent {
|
|
276
|
+
eventType: EventType;
|
|
277
|
+
objectId?: string | null;
|
|
278
|
+
data?: any;
|
|
279
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContainerType = exports.MusicService = exports.SonosCapability = exports.PlayBackState = void 0;
|
|
4
|
+
var PlayBackState;
|
|
5
|
+
(function (PlayBackState) {
|
|
6
|
+
PlayBackState["IDLE"] = "PLAYBACK_STATE_IDLE";
|
|
7
|
+
PlayBackState["BUFFERING"] = "PLAYBACK_STATE_BUFFERING";
|
|
8
|
+
PlayBackState["PAUSED"] = "PLAYBACK_STATE_PAUSED";
|
|
9
|
+
PlayBackState["PLAYING"] = "PLAYBACK_STATE_PLAYING";
|
|
10
|
+
})(PlayBackState || (exports.PlayBackState = PlayBackState = {}));
|
|
11
|
+
var SonosCapability;
|
|
12
|
+
(function (SonosCapability) {
|
|
13
|
+
SonosCapability["CLOUD"] = "CLOUD";
|
|
14
|
+
SonosCapability["PLAYBACK"] = "PLAYBACK";
|
|
15
|
+
SonosCapability["AIRPLAY"] = "AIRPLAY";
|
|
16
|
+
SonosCapability["LINE_IN"] = "LINE_IN";
|
|
17
|
+
SonosCapability["VOICE"] = "VOICE";
|
|
18
|
+
SonosCapability["AUDIO_CLIP"] = "AUDIO_CLIP";
|
|
19
|
+
SonosCapability["MICROPHONE_SWITCH"] = "MICROPHONE_SWITCH";
|
|
20
|
+
SonosCapability["HT_PLAYBACK"] = "HT_PLAYBACK";
|
|
21
|
+
})(SonosCapability || (exports.SonosCapability = SonosCapability = {}));
|
|
22
|
+
var MusicService;
|
|
23
|
+
(function (MusicService) {
|
|
24
|
+
MusicService["SPOTIFY"] = "9";
|
|
25
|
+
MusicService["MUSIC_ASSISTANT"] = "mass";
|
|
26
|
+
MusicService["TUNEIN"] = "303";
|
|
27
|
+
MusicService["QOBUZ"] = "31";
|
|
28
|
+
MusicService["YOUTUBE_MUSIC"] = "284";
|
|
29
|
+
MusicService["LOCAL_LIBRARY"] = "local-library";
|
|
30
|
+
})(MusicService || (exports.MusicService = MusicService = {}));
|
|
31
|
+
var ContainerType;
|
|
32
|
+
(function (ContainerType) {
|
|
33
|
+
ContainerType["LINEIN"] = "linein";
|
|
34
|
+
ContainerType["STATION"] = "station";
|
|
35
|
+
ContainerType["PLAYLIST"] = "playlist";
|
|
36
|
+
ContainerType["AIRPLAY"] = "linein.airplay";
|
|
37
|
+
ContainerType["PODCAST"] = "podcast";
|
|
38
|
+
ContainerType["BOOK"] = "book";
|
|
39
|
+
ContainerType["ARTIST"] = "artist";
|
|
40
|
+
ContainerType["ALBUM"] = "album";
|
|
41
|
+
ContainerType["ARTIST_LOCAL"] = "artist.local";
|
|
42
|
+
ContainerType["ALBUM_LOCAL"] = "album.local";
|
|
43
|
+
ContainerType["HOME_THEATER_SPDIF"] = "linein.homeTheater.spdif";
|
|
44
|
+
ContainerType["HOME_THEATER_HDMI"] = "linein.homeTheater.hdmi";
|
|
45
|
+
// fallbacks for unknown types will be treated as strings
|
|
46
|
+
})(ContainerType || (exports.ContainerType = ContainerType = {}));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDiscoveryInfo = getDiscoveryInfo;
|
|
4
|
+
const undici_1 = require("undici");
|
|
5
|
+
const constants_1 = require("./constants");
|
|
6
|
+
async function getDiscoveryInfo(playerIp, options) {
|
|
7
|
+
const dispatcher = options?.dispatcher ?? new undici_1.Agent({ connect: { rejectUnauthorized: false } });
|
|
8
|
+
const resp = await (0, undici_1.fetch)(`https://${playerIp}:1443/api/v1/players/local/info`, {
|
|
9
|
+
method: 'GET',
|
|
10
|
+
headers: { 'X-Sonos-Api-Key': constants_1.LOCAL_API_TOKEN },
|
|
11
|
+
dispatcher,
|
|
12
|
+
});
|
|
13
|
+
if (!resp.ok) {
|
|
14
|
+
throw new Error(`Sonos discovery failed (${resp.status} ${resp.statusText})`);
|
|
15
|
+
}
|
|
16
|
+
const data = (await resp.json());
|
|
17
|
+
return data;
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lox-audio-server/sonos",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Sonos Controller",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"import": "./dist/esm/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist/**/*", "README.md", "LICENSE"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "npm run build:cjs && npm run build:esm",
|
|
18
|
+
"build:cjs": "tsc -p tsconfig.json",
|
|
19
|
+
"build:esm": "tsc -p tsconfig.esm.json && node -e \"const fs=require('fs');fs.mkdirSync('dist/esm',{recursive:true});fs.writeFileSync('dist/esm/package.json','{\\\"type\\\":\\\"module\\\"}\\n');\"",
|
|
20
|
+
"clean": "rimraf dist",
|
|
21
|
+
"prepare": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/rudyberends/lox-audio-server-sonos.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/rudyberends/lox-audio-server-sonos/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/rudyberends/lox-audio-server-sonos#readme",
|
|
31
|
+
"keywords": ["sonos", "controller", "audio", "streaming"],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"undici": "^6.19.8",
|
|
40
|
+
"ws": "^8.18.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/ws": "^8.5.13",
|
|
44
|
+
"@types/node": "^22.19.3",
|
|
45
|
+
"rimraf": "^6.0.1",
|
|
46
|
+
"typescript": "^5.9.3"
|
|
47
|
+
},
|
|
48
|
+
"license": "AGPL-3.0"
|
|
49
|
+
}
|