Skip to content

Commit bca25f3

Browse files
committed
fix: URLs for tadoX and improve tadoX detection logic
1 parent b3fbdf8 commit bca25f3

File tree

2 files changed

+70
-24
lines changed

2 files changed

+70
-24
lines changed

src/index.ts

+18-21
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,16 @@ export class Tado {
113113
#username?: string;
114114
#password?: string;
115115
#firstLogin: boolean;
116-
#isX: boolean;
116+
#forceX: boolean;
117+
#xLookup: { [key: number]: boolean };
117118

118-
constructor(username?: string, password?: string) {
119+
constructor(username?: string, password?: string, forceX?: boolean) {
119120
this.#username = username;
120121
this.#password = password;
121122
this.#httpsAgent = new Agent({ keepAlive: true });
123+
this.#forceX = forceX ?? false;
122124
this.#firstLogin = true;
123-
this.#isX = false;
125+
this.#xLookup = {};
124126
}
125127

126128
async #login(): Promise<void> {
@@ -139,10 +141,9 @@ export class Tado {
139141
if (this.#firstLogin) {
140142
try {
141143
const me = await this.getMe();
142-
if (me.homes.length > 0) {
143-
const home_id = me.homes[0].id;
144+
for (const { id: home_id } of me.homes) {
144145
const home = await this.getHome(home_id);
145-
this.#isX = home.generation == "LINE_X";
146+
this.#xLookup[home_id] = home.generation == "LINE_X";
146147
}
147148
} catch (err) {
148149
console.error(`Could not determine TadoX status: ${err}`);
@@ -187,8 +188,8 @@ export class Tado {
187188
return this.#accessToken;
188189
}
189190

190-
get isX(): boolean {
191-
return this.#isX;
191+
isHomeX(home_id: number): boolean {
192+
return this.#forceX || this.#xLookup[home_id] || false;
192193
}
193194

194195
/**
@@ -353,7 +354,7 @@ export class Tado {
353354
* @returns A promise that resolves to an array of Device objects.
354355
*/
355356
getDevices(home_id: number): Promise<Device[]> {
356-
if (this.#isX) {
357+
if (this.isHomeX(home_id)) {
357358
return this.apiCallX(`/homes/${home_id}/roomsAndDevices`);
358359
} else {
359360
return this.apiCall(`/api/v2/homes/${home_id}/devices`);
@@ -584,7 +585,7 @@ export class Tado {
584585
* @returns A promise that resolves to an array of Zone objects.
585586
*/
586587
getZones(home_id: number): Promise<Zone[]> {
587-
if (this.#isX) {
588+
if (this.isHomeX(home_id)) {
588589
return this.apiCallX(`/homes/${home_id}/rooms`);
589590
} else {
590591
return this.apiCall(`/api/v2/homes/${home_id}/zones`);
@@ -599,7 +600,7 @@ export class Tado {
599600
* @returns A promise that resolves to the state of the specified zone.
600601
*/
601602
getZoneState(home_id: number, zone_id: number): Promise<ZoneState> {
602-
if (this.#isX) {
603+
if (this.isHomeX(home_id)) {
603604
return this.apiCallX(`/homes/${home_id}/rooms/${zone_id}`);
604605
} else {
605606
return this.apiCall(`/api/v2/homes/${home_id}/zones/${zone_id}/state`);
@@ -835,7 +836,7 @@ export class Tado {
835836
* @deprecated Use {@link clearZoneOverlays} instead.
836837
*/
837838
clearZoneOverlay(home_id: number, zone_id: number): Promise<void> {
838-
if (this.#isX) {
839+
if (this.isHomeX(home_id)) {
839840
return this.apiCallX(`/homes/${home_id}/rooms/${zone_id}/resumeSchedule`, "post", {});
840841
} else {
841842
return this.apiCall(`/api/v2/homes/${home_id}/zones/${zone_id}/overlay`, "delete");
@@ -957,12 +958,8 @@ export class Tado {
957958
};
958959
}
959960

960-
if (this.#isX) {
961-
return this.apiCallX(
962-
`/api/v2/homes/${home_id}/rooms/${zone_id}/manualControl`,
963-
"post",
964-
config,
965-
);
961+
if (this.isHomeX(home_id)) {
962+
return this.apiCallX(`/homes/${home_id}/rooms/${zone_id}/manualControl`, "post", config);
966963
} else {
967964
return this.apiCall(`/api/v2/homes/${home_id}/zones/${zone_id}/overlay`, "put", config);
968965
}
@@ -976,7 +973,7 @@ export class Tado {
976973
* @returns A promise that resolves when the overlays are cleared.
977974
*/
978975
async clearZoneOverlays(home_id: number, zone_ids: number[]): Promise<void> {
979-
if (this.#isX) {
976+
if (this.isHomeX(home_id)) {
980977
for (const zone_id of zone_ids) {
981978
return this.apiCallX(`/homes/${home_id}/rooms/${zone_id}/resumeSchedule`, "post", {});
982979
}
@@ -1122,10 +1119,10 @@ export class Tado {
11221119
config.push(overlay_config);
11231120
}
11241121

1125-
if (this.#isX) {
1122+
if (this.isHomeX(home_id)) {
11261123
for (const c of config) {
11271124
return this.apiCallX(
1128-
`/api/v2/homes/${home_id}/rooms/${c.room}/manualControl`,
1125+
`/homes/${home_id}/rooms/${c.room}/manualControl`,
11291126
"post",
11301127
c.overlay,
11311128
);

test/index.ts

+52-3
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ describe("High-level API tests (v2)", async () => {
119119

120120
tado = new Tado();
121121
await tado.login("username", "password");
122-
expect(tado.isX).to.equal(false);
122+
expect(tado.isHomeX(1907)).to.equal(false);
123123
});
124124

125125
afterEach(async () => {
@@ -849,7 +849,7 @@ describe("High-level API tests (TadoX)", async () => {
849849

850850
tado = new Tado();
851851
await tado.login("username", "password");
852-
expect(tado.isX).to.equal(true);
852+
expect(tado.isHomeX(1907)).to.equal(true);
853853
});
854854

855855
afterEach(async () => {
@@ -894,7 +894,7 @@ describe("High-level API tests (TadoX)", async () => {
894894
.reply(200, zone_capabilities_response);
895895

896896
nock("https://hops.tado.com")
897-
.post("/api/v2/homes/1907/rooms/1/manualControl")
897+
.post("/homes/1907/rooms/1/manualControl")
898898
.reply(200, (_uri, req) => {
899899
return req;
900900
});
@@ -903,4 +903,53 @@ describe("High-level API tests (TadoX)", async () => {
903903

904904
expect(typeof response).to.equal("object");
905905
});
906+
907+
it("Should clear multiple zone overlays", async () => {
908+
nock("https://hops.tado.com")
909+
.post("/homes/1907/rooms/1/resumeSchedule")
910+
.reply(200, {})
911+
.post("/homes/1907/rooms/2/resumeSchedule")
912+
.reply(200, {});
913+
914+
const response = await tado.clearZoneOverlays(1907, [1, 2]);
915+
916+
expect(typeof response).to.equal("object");
917+
});
918+
919+
it("Should allow multiple zone overlays", async () => {
920+
nock("https://my.tado.com")
921+
.get("/api/v2/homes/1907/zones/1/capabilities")
922+
.reply(200, zone_capabilities_response)
923+
.get("/api/v2/homes/1907/zones/2/capabilities")
924+
.reply(200, zone_capabilities_response);
925+
926+
nock("https://hops.tado.com")
927+
.post("/homes/1907/rooms/1/manualControl")
928+
.reply(200, {})
929+
.post("/homes/1907/rooms/2/manualControl")
930+
.reply(200, {});
931+
932+
const response = await tado.setZoneOverlays(
933+
1907,
934+
[
935+
{
936+
power: "ON",
937+
temperature: {
938+
celsius: 25,
939+
},
940+
zone_id: 1,
941+
},
942+
{
943+
power: "ON",
944+
temperature: {
945+
celsius: 25,
946+
},
947+
zone_id: 2,
948+
},
949+
],
950+
"AUTO",
951+
);
952+
953+
expect(typeof response).to.equal("object");
954+
});
906955
});

0 commit comments

Comments
 (0)