Skip to content

Commit 04eca27

Browse files
committed
Thermostats are discovered
1 parent f8f7009 commit 04eca27

5 files changed

Lines changed: 589 additions & 190 deletions

File tree

src/auth.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,15 +222,15 @@ export class AuthClient {
222222
/**
223223
* Accesseurs pour les métadonnées stockées
224224
*/
225-
get homeId(): string | undefined {
226-
return this.tokens?.homeId;
225+
get homeId(): string | null {
226+
return this.tokens?.homeId ?? null;
227227
}
228228

229-
get userId(): string | undefined {
230-
return this.tokens?.userId;
229+
get userId(): string | null {
230+
return this.tokens?.userId ?? null;
231231
}
232232

233-
get parkId(): string | undefined {
234-
return this.tokens?.parkId;
233+
get parkId(): string | null {
234+
return this.tokens?.parkId ?? null;
235235
}
236236
}

src/platform.ts

Lines changed: 103 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
1-
import type { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig } from 'homebridge';
1+
import type {
2+
API,
3+
DynamicPlatformPlugin,
4+
Logger,
5+
PlatformAccessory,
6+
PlatformConfig,
7+
} from 'homebridge';
28
import { PLATFORM_NAME, PLUGIN_NAME } from './settings';
39
import { TokenStore } from './tokenStore';
410
import { AuthClient } from './auth';
511
import { OtodoClient } from './otodoClient';
12+
import { ThermostatClient } from './thermostatClient';
13+
import type { ThermostatService, Room, Hub } from './types';
14+
import { ThermostatAccessory } from './thermostatAccessory';
615

716
export class OtodoVavPlatform implements DynamicPlatformPlugin {
817
public readonly accessories: PlatformAccessory[] = [];
18+
919
private auth!: AuthClient;
1020
private client!: OtodoClient;
21+
private tClient!: ThermostatClient;
22+
23+
private roomsById: Map<string, Room> = new Map();
24+
private hubsById: Map<string, Hub> = new Map();
1125

1226
constructor(
1327
public readonly log: Logger,
1428
public readonly config: PlatformConfig,
1529
public readonly api: API,
1630
) {
17-
// Vérification de la configuration
1831
if (!config || !config.email || !config.password) {
1932
this.log.error('❌ Configuration invalide : email et password requis');
2033
return;
2134
}
2235

23-
// ⭐ Activation du mode debug si demandé
2436
if (config.debug === true) {
2537
this.log.debug('🔍 Mode debug activé');
2638
}
@@ -31,19 +43,16 @@ export class OtodoVavPlatform implements DynamicPlatformPlugin {
3143
try {
3244
await this.initializePlugin();
3345
} catch (error) {
34-
this.log.error('❌ Échec de l\'initialisation du plugin:', error);
46+
this.log.error("❌ Échec de l'initialisation du plugin:", error);
3547
}
3648
});
3749
}
3850

39-
/**
40-
* Méthode d'initialisation séparée pour plus de clarté
41-
*/
4251
private async initializePlugin(): Promise<void> {
4352
this.log.debug('📦 Création du TokenStore...');
4453
const store = new TokenStore(this.api.user.storagePath());
4554

46-
this.log.debug('🔐 Initialisation de l\'authentification...');
55+
this.log.debug("🔐 Initialisation de l'authentification...");
4756
this.auth = new AuthClient(this.log, store, {
4857
email: String(this.config.email),
4958
password: String(this.config.password),
@@ -52,60 +61,112 @@ export class OtodoVavPlatform implements DynamicPlatformPlugin {
5261

5362
await this.auth.init();
5463

55-
this.log.debug('🌐 Création du client API...');
64+
this.log.debug('🌐 Création des clients API...');
5665
this.client = new OtodoClient(this.log, this.auth);
66+
this.tClient = new ThermostatClient(this.log, this.auth);
5767

58-
this.log.info('✅ Plugin initialisé avec succès');
59-
this.log.info(` HomeId: ${this.auth.homeId}`);
60-
this.log.info(` UserId: ${this.auth.userId}`);
61-
this.log.info(` ParkId: ${this.auth.parkId}`);
68+
this.log.info('✅ Plugin initialisé');
69+
if (this.auth.homeId) {
70+
this.log.info(` HomeId: ${this.auth.homeId}`);
71+
}
72+
if (this.auth.userId) {
73+
this.log.info(` UserId: ${this.auth.userId}`);
74+
}
75+
if (this.auth.parkId) {
76+
this.log.info(` ParkId: ${this.auth.parkId}`);
77+
}
6278

63-
// TODO: Découverte des thermostats
64-
// await this.discoverDevices();
79+
await this.loadRooms();
80+
await this.loadHubs();
81+
this.hubsById.forEach(async hub => {
82+
await this.discoverDevices(hub._id);
83+
});
6584
}
6685

6786
/**
68-
* Appelé par Homebridge pour restaurer les accessoires en cache
87+
* Chargement optionnel des pièces pour des noms plus jolis
88+
*/
89+
private async loadRooms(): Promise<void> {
90+
try {
91+
this.log.debug('📂 Récupération des rooms...');
92+
const rooms = await this.tClient.getRooms();
93+
this.roomsById = new Map(rooms.map(r => [r._id, r]));
94+
this.log.debug(`📂 ${rooms.length} rooms chargées`);
95+
} catch (e) {
96+
this.log.warn(
97+
'⚠️ Impossible de récupérer les rooms (optionnel) : ' + String(e),
98+
);
99+
}
100+
}
101+
102+
/**
103+
* Chargement des hubs
104+
*/
105+
private async loadHubs(): Promise<void> {
106+
try {
107+
this.log.debug('📂 Récupération des rooms...');
108+
const hubs = await this.tClient.getHubs();
109+
this.hubsById = new Map(hubs.map(r => [r._id, r]));
110+
this.log.debug(`📂 ${hubs.length} hubs chargées`);
111+
} catch (e) {
112+
this.log.warn('⚠️ Impossible de récupérer les hubs : ' + String(e));
113+
}
114+
}
115+
116+
/**
117+
* Restaurer depuis le cache
69118
*/
70119
configureAccessory(accessory: PlatformAccessory): void {
71-
this.log.debug('🔄 Restauration de l\'accessoire depuis le cache:', accessory.displayName);
120+
this.log.debug(
121+
"🔄 Restauration de l'accessoire depuis le cache:",
122+
accessory.displayName,
123+
);
72124
this.accessories.push(accessory);
73125
}
74126

75127
/**
76-
* Méthode pour créer ou mettre à jour un thermostat
128+
* Upsert d'un thermostat : création ou restauration
77129
*/
78-
private upsertThermostat(device: { name: string; id: string }): void {
79-
const uuid = this.api.hap.uuid.generate(device.id);
130+
private upsertThermostat(t: ThermostatService): void {
131+
const uuid = this.api.hap.uuid.generate(`hub:${t.hubId}:svc:${t._id}`);
80132
const existing = this.accessories.find(acc => acc.UUID === uuid);
81133

134+
const roomName = this.roomsById.get(t.roomId)?.name;
135+
const displayName = roomName
136+
? `Thermostat ${roomName}`
137+
: `Thermostat ${t._id}`;
138+
82139
if (existing) {
83-
this.log.info('♻️ Restauration du thermostat:', device.name);
84-
// TODO: new ThermostatAccessory(this, existing, device, this.client);
140+
this.log.info('♻️ Restauration du thermostat:', displayName);
141+
new ThermostatAccessory(this, existing, t, this.tClient);
85142
} else {
86-
this.log.info('➕ Enregistrement du nouveau thermostat:', device.name);
87-
const accessory = new this.api.platformAccessory(device.name, uuid);
88-
// TODO: new ThermostatAccessory(this, accessory, device, this.client);
89-
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
143+
this.log.info('➕ Enregistrement du nouveau thermostat:', displayName);
144+
const accessory = new this.api.platformAccessory(displayName, uuid);
145+
new ThermostatAccessory(this, accessory, t, this.tClient);
146+
147+
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [
148+
accessory,
149+
]);
150+
this.accessories.push(accessory);
90151
}
91152
}
92153

93154
/**
94-
* Future méthode de découverte des appareils
155+
* Découverte des thermostats via /local-services
95156
*/
96-
private async discoverDevices(): Promise<void> {
97-
this.log.debug('🔍 Recherche des thermostats...');
98-
99-
// TODO: Implémenter quand tu auras l'endpoint de découverte
100-
// Exemple :
101-
// const devices = await this.client.getJson<DeviceSummary[]>(
102-
// `https://api.gateway.otodo.io/homes/${this.auth.homeId}/devices`
103-
// );
104-
//
105-
// for (const device of devices) {
106-
// if (device.type === 'thermostat') {
107-
// this.upsertThermostat(device);
108-
// }
109-
// }
157+
private async discoverDevices(hubId: string): Promise<void> {
158+
this.log.info('🔍 Recherche des thermostats Otodo VAV...');
159+
try {
160+
const thermostats = await this.tClient.getAllThermostats(hubId);
161+
this.log.info(` ${thermostats.length} thermostat(s) trouvé(s)`);
162+
163+
for (const t of thermostats) {
164+
this.upsertThermostat(t);
165+
}
166+
} catch (e) {
167+
this.log.error(
168+
'❌ Erreur lors de la découverte des thermostats : ' + String(e),
169+
);
170+
}
110171
}
111-
}
172+
}

0 commit comments

Comments
 (0)