homebridge-homeconnect
Version:
A Homebridge plugin that connects Home Connect appliances to Apple HomeKit
101 lines • 4.15 kB
JavaScript
// Homebridge plugin for Home Connect home appliances
// Copyright © 2023-2025 Alexander Thoukydides
import { CloudAPI } from '../api.js';
import { MockAPI } from '../mock/index.js';
import { assertIsDefined } from '../utils.js';
import { logError } from '../log-error.js';
import { APIStatusCodeError } from '../api-errors.js';
// Home Connect API authorisation and basic usage
export class ServerClientID {
constructor(log, ipc, persist) {
this.log = log;
this.ipc = ipc;
this.persist = persist;
// Cache of all API clients that have been created
this.clients = new Map();
}
// Set the API client
setClientID(config) {
// Select the appropriate configuration
let api;
let description;
if (config.debug?.includes('Mock Appliances')) {
api = MockAPI;
config.clientid = '';
config.simulator = true;
config.china = false;
description = 'mock appliances';
}
else {
api = CloudAPI;
config.simulator ?? (config.simulator = false);
config.china ?? (config.china = false);
description = `${config.simulator ? 'simulated' : 'physical'} appliances`
+ `${config.china ? ' in China' : ''} with clientid ${config.clientid}`;
}
// Select or create the client
const key = `${config.clientid}-${config.simulator}-${config.china}`;
let client = this.clients.get(key);
if (client === undefined) {
// Create a new client
this.log.info(`Creating Home Connect API client for ${description}`);
client = {
api: new api(this.log, config, this.persist),
status: {
clientid: config.clientid,
simulator: config.simulator ?? false,
china: config.china ?? false
}
};
this.clients.set(key, client);
// Send authorisation status updates to the client
this.authorisationEvents(client);
}
else {
this.log.info(`Resurrecting Home Connect API client for ${description}`);
}
// Return the API client status
return client.status;
}
// Trigger a retry of Device Flow authorisation
retryAuthorisation() {
assertIsDefined(this.selected);
this.selected.api.retryAuthorisation();
return this.selected.status;
}
// Monitor authorisation status and send updates to the client if selected
async authorisationEvents(client) {
// Monitor status until authorised
while (client.status.authorisation?.state !== 'success') {
const status = await client.api.getAuthorisationStatus();
client.status.authorisation = status;
if (this.selected === client)
this.ipc.pushEvent('status', client.status);
}
// Attempt to read the list of appliances
try {
client.status.appliances = await client.api.getAppliances();
this.log.info(`Authorised (${client.status.appliances.length} appliances)`);
this.ipc.pushEvent('status', client.status);
}
catch (err) {
logError(this.log, 'Reading appliances', err);
}
}
// Wait for authorisation status to indicate whether the clientid is valid
async isClientValid(client) {
// Wait for the authorisation attempt to succeed, fail, or wait for user
let status = await client.api.getAuthorisationStatus(true);
while (status.state === 'busy')
status = await client.api.getAuthorisationStatus();
// Anything other than 'unauthorized_client' indicates a valid clientid
if (status.state === 'fail') {
const err = status.error;
if (err instanceof APIStatusCodeError && err.response?.statusCode === 400
&& err.key === 'unauthorized_client')
return false;
}
return true;
}
}
//# sourceMappingURL=server-clientid.js.map