Skip to content

Commit 826a814

Browse files
committed
add getCapabilities route
1 parent 1369617 commit 826a814

4 files changed

Lines changed: 164 additions & 4 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ Completed features so far:
1313
- [x] get DeviceInfo
1414
- [x] get currently set AudioDSPControls
1515
- [x] set new AudioDSPMode for the DSP
16-
- [ ] Audio Product Level Controls
16+
- [ ] Audio Product Level Controls
1717
- [ ] Audio Product Tone Controls
1818
- [ ] Audio Speaker Attributes
1919
- [ ] Balance
2020
- [x] Bluetooth - Info
2121
- [x] Bluetooth - Clear Pairing List
2222
- [x] Bluetooth - Enter Pairing Mode
23-
- [ ] Capabilities
23+
- [x] Capabilities
2424
- [ ] Clock Display
2525
- [ ] Clock Time
2626
- [ ] Configuration Status

src/client.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
22
import { XMLParser } from "fast-xml-parser";
3-
import { AudioDSPControls, AudioDSPMode, Bass, BassCapabilities, BluetoothInfo, DeviceInfo } from "./types.js";
3+
import { AudioDSPControls, AudioDSPMode, Bass, BassCapabilities, BluetoothInfo, Capabilities, DeviceInfo } from "./types.js";
44

55
export class SoundTouchApiClient {
66
private client: AxiosInstance;
@@ -126,7 +126,7 @@ export class SoundTouchApiClient {
126126
}
127127

128128

129-
/**
129+
/**
130130
* Clears Bluetooth pairing info
131131
* @returns void
132132
*/
@@ -138,4 +138,11 @@ export class SoundTouchApiClient {
138138
return this.parseResponse<BluetoothInfo>(res as any);
139139
}
140140

141+
/**
142+
* Gets the devices Capabilities
143+
* @returns Capabilities
144+
*/
145+
async getCapabilities(): Promise<Capabilities> {
146+
return this.get<Capabilities>("/capabilities");
147+
}
141148
}

src/types.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,32 @@ export interface AudioDSPControls {
6060
export interface BluetoothInfo {
6161
"@_BluetoothMACAddress"?: string
6262
status?: string;
63+
}
64+
65+
export interface Capabilities {
66+
capabilities: {
67+
"@_deviceID"?: string;
68+
networkConfig?: {
69+
dualMode?: boolean;
70+
wsapiproxy?: boolean;
71+
allInterfacesSupported?: Record<string, unknown> | null;
72+
wlanInterfaces?: Record<string, unknown> | null;
73+
security?: Record<string, unknown> | null;
74+
};
75+
dspCapabilities?: {
76+
dspMonoStereo?: {
77+
"@_available"?: boolean;
78+
};
79+
};
80+
lightswitch?: boolean;
81+
clockDisplay?: boolean;
82+
capability?: Array<{
83+
"@_name"?: string;
84+
"@_url"?: string;
85+
"@_info"?: string;
86+
}>;
87+
lrStereoCapable?: boolean;
88+
bcoresetCapable?: boolean;
89+
disablePowerSaving?: boolean;
90+
};
6391
}

tests/capabilities.test.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
2+
import nock from "nock";
3+
import { SoundTouchApiClient } from "../src/client.js";
4+
5+
const mockXML_ST10 = `<?xml version="1.0" encoding="UTF-8" ?>
6+
<capabilities deviceID="1004567890AA">
7+
<networkConfig>
8+
<dualMode>true</dualMode>
9+
<wsapiproxy>true</wsapiproxy>
10+
<allInterfacesSupported />
11+
<wlanInterfaces />
12+
<security />
13+
</networkConfig>
14+
<dspCapabilities>
15+
<dspMonoStereo available="false" />
16+
</dspCapabilities>
17+
<lightswitch>false</lightswitch>
18+
<clockDisplay>false</clockDisplay>
19+
<capability name="systemtimeout" url="/systemtimeout" info="" />
20+
<capability name="rebroadcastlatencymode" url="/rebroadcastlatencymode" info="" />
21+
<lrStereoCapable>true</lrStereoCapable>
22+
<bcoresetCapable>false</bcoresetCapable>
23+
<disablePowerSaving>true</disablePowerSaving>
24+
</capabilities>`;
25+
26+
27+
const mockXML_ST300 = `<?xml version="1.0" encoding="UTF-8" ?>
28+
<capabilities deviceID="3004567890BB">
29+
<networkConfig>
30+
<dualMode>true</dualMode>
31+
<wsapiproxy>true</wsapiproxy>
32+
<allInterfacesSupported />
33+
<wlanInterfaces />
34+
<security />
35+
</networkConfig>
36+
<dspCapabilities>
37+
<dspMonoStereo available="false" />
38+
</dspCapabilities>
39+
<lightswitch>false</lightswitch>
40+
<clockDisplay>false</clockDisplay>
41+
<capability name="audiodspcontrols" url="/audiodspcontrols" info="" />
42+
<capability name="audiospeakerattributeandsetting" url="/audiospeakerattributeandsetting" info="" />
43+
<capability name="productcechdmicontrol" url="/productcechdmicontrol" info="" />
44+
<capability name="producthdmiassignmentcontrols" url="/producthdmiassignmentcontrols" info="" />
45+
<capability name="audioproducttonecontrols" url="/audioproducttonecontrols" info="" />
46+
<capability name="audioproductlevelcontrols" url="/audioproductlevelcontrols" info="" />
47+
<capability name="systemtimeoutcontrol" url="/systemtimeoutcontrol" info="" />
48+
<capability name="rebroadcastlatencymode" url="/rebroadcastlatencymode" info="" />
49+
<lrStereoCapable>false</lrStereoCapable>
50+
<bcoresetCapable>false</bcoresetCapable>
51+
<disablePowerSaving>false</disablePowerSaving>
52+
</capabilities>`;
53+
54+
describe("SoundTouchAPI /capabilities endpoint", () => {
55+
const baseURL = "http://localhost:8090";
56+
let api: SoundTouchApiClient;
57+
58+
beforeEach(() => {
59+
api = new SoundTouchApiClient(baseURL);
60+
nock.cleanAll();
61+
});
62+
63+
afterEach(() => {
64+
nock.cleanAll();
65+
});
66+
67+
it("parses XML response for ST-10 correctly", async () => {
68+
nock(baseURL)
69+
.get("/capabilities")
70+
.reply(200, mockXML_ST10, { "Content-Type": "application/xml" });
71+
72+
const capabilities = await api.getCapabilities();
73+
74+
expect(capabilities).toMatchObject({
75+
capabilities: {
76+
deviceID: "1004567890AA",
77+
networkConfig: {
78+
dualMode: true,
79+
wsapiproxy: true,
80+
},
81+
dspCapabilities: {
82+
dspMonoStereo: {
83+
available: "false",
84+
},
85+
},
86+
lightswitch: false,
87+
clockDisplay: false,
88+
capability: expect.any(Array),
89+
lrStereoCapable: true,
90+
bcoresetCapable: false,
91+
disablePowerSaving: true,
92+
},
93+
});
94+
});
95+
96+
97+
it("parses XML response for ST-300 correctly", async () => {
98+
nock(baseURL)
99+
.get("/capabilities")
100+
.reply(200, mockXML_ST300, { "Content-Type": "application/xml" });
101+
102+
const capabilities = await api.getCapabilities();
103+
104+
expect(capabilities).toMatchObject({
105+
capabilities: {
106+
deviceID: "3004567890BB",
107+
networkConfig: {
108+
dualMode: true,
109+
wsapiproxy: true,
110+
},
111+
dspCapabilities: {
112+
dspMonoStereo: {
113+
available: "false",
114+
},
115+
},
116+
lightswitch: false,
117+
clockDisplay: false,
118+
capability: expect.any(Array),
119+
lrStereoCapable: false,
120+
bcoresetCapable: false,
121+
disablePowerSaving: false,
122+
},
123+
});
124+
});
125+
});

0 commit comments

Comments
 (0)