homebridge-homeconnect
Version:
A Homebridge plugin that connects Home Connect appliances to Apple HomeKit
158 lines • 8.22 kB
JavaScript
// Homebridge plugin for Home Connect home appliances
// Copyright © 2023-2026 Alexander Thoukydides
import chalk from 'chalk';
import { APIStatusCodeError } from './api-errors.js';
import { assertIsDefined, columns, plural } from './utils.js';
// Colours for the help message
const COLOUR_LO = chalk.green.dim;
const COLOUR_HI = chalk.greenBright;
// Generate helpful information for authorisation errors
export class AuthHelp {
// Retrieve the structured help message
getStructured() {
return this.message;
}
// Retrieve the message as text
getText(useColour = false) {
// Help isn't always available
if (!this.message)
return [];
// Add text with optional formatting
const text = [];
const addLines = (lines, colour = COLOUR_LO) => {
text.push(...lines.map(line => useColour ? colour(line) : line));
};
const addLink = (description, uri, colour = COLOUR_LO) => {
let lines = [`${description}:`, ` ${uri}`];
if (useColour)
lines = [colour(lines[0]), colour.bold(lines[1])];
text.push(...lines);
};
// Prescript
addLines(this.message.prescript, COLOUR_HI);
// Write the client creation/modification guide
if (this.message.client) {
const { action, uri, settings } = this.message.client;
addLink(`${{ create: 'Create a new', modify: 'Edit the' }[action]} application`, uri, COLOUR_HI);
addLines(['Ensure that the application settings are configured as follows:']);
addLines(columns(Object.entries(settings)).map(line => ` ${line}`));
}
// Postscript with extra support from the Home Connect Developer Portal
addLines(this.message.postscript);
addLink('Descriptions of authorisation error messages can be found in the Home Connect API documentation', 'https://api-docs.home-connect.com/authorization?#authorization-errors');
addLink('For additional support contact the Home Connect Developer team', 'https://developer.home-connect.com/support/contact');
return text;
}
// Write the help message to the log
log(log) {
for (const line of this.getText(true))
log.warn(line);
}
}
// Generate helpful information for Device Flow authorisation errors
export class AuthHelpDeviceFlow extends AuthHelp {
// Create a new help object
constructor(err, clientid) {
super();
// Only provide help when the status code was 400
if (!(err instanceof APIStatusCodeError))
return;
if (err.response?.statusCode !== 400)
return;
// Decode the problem
this.message = { prescript: [], postscript: [] };
const action = this.decodeError(err, clientid);
// Provide guidance for modifying or recreating the client
if (action === 'create' || action === 'modify')
this.clientSettingsGuide(action, clientid);
// Provide guidance for setting the clientid
this.clientChangeGuide(action);
}
// Decode the error message to provide suitable advice
decodeError(err, clientid) {
assertIsDefined(this.message);
const { prescript } = this.message;
switch (err.key) {
case 'access_denied':
prescript.push('The specified Home Connect or SingleKey ID account cannot currently be used.'
+ ' Check that it works correctly in the iPhone Home Connect app.'
+ ' It may be necessary to convert a Home Connect account to a SingleKey ID,'
+ ' or accept new terms of use within the app.');
break;
case 'expired_token':
// "Device authorization session not found, expired or blocked"
prescript.push('Authorisation of the Home Connect or SingleKey ID took too long.'
+ ' Visit the provided web link and complete authorisation quickly.', 'Pay careful attention to any error messages displayed on the Home Connect / SingleKey ID web pages.');
break;
case 'invalid_client':
// "client secret validation failed"
prescript.push('Client Secret is not required with Device Flow authorisation.'
+ ' Remove "clientsecret" from the "config.json" file (or set it to the correct value).');
break;
case 'unauthorized_client':
return this.decodeUnauthorizedClient(err, clientid);
}
}
// Decode an 'unauthorized_client' error message
decodeUnauthorizedClient(err, clientid) {
assertIsDefined(this.message);
const { prescript } = this.message;
switch (err.description) {
case 'Invalid client id':
if (clientid.length !== 64) {
prescript.push('The Client ID should be 64 hexadecimal characters,'
+ ` but the value specified for "clientid" is ${plural(clientid.length, 'character')} long.`);
return 'set';
}
else if (!/^[0-9A-F]+$/i.test(clientid)) {
prescript.push('The Client ID should be 64 hexadecimal characters,'
+ ' but the value specified for "clientid" includes non-hexadecimal characters.');
return 'set';
}
else {
prescript.push('The configured Client ID does not appear to be a valid Home Connect application.');
return 'modify';
}
case 'request rejected by client authorization authority (developer portal)':
prescript.push('The configured Client ID does not appear to be a valid Home Connect application.'
+ ' If it was created recently then it may still be propagating to the authorisation servers.');
return 'modify';
case 'client not authorized for this oauth flow (grant_type)':
prescript.push('The configured Client ID has been incorrectly configured'
+ ' with the OAuth Flow set to "Authorization Code Grant Flow".'
+ ' Create a new application using "Device Flow" instead.'
+ ' This setting cannot be changed after the application has been created.');
return 'create';
}
}
// Provide guidance for creating or modifying an application
clientSettingsGuide(action, clientid) {
const settings = {
'Home Connect User Account': 'Same as the Home Connect or SingleKey ID email address',
'Success Redirect': 'Leave blank',
'One Time Token': 'Not ticked',
'Status': 'Enabled',
'Client Secret Always Required': 'No',
'Sync to China': 'Enabled if located in China, otherwise Disabled'
};
if (action === 'create')
settings['OAuth Flow'] = 'Set to "Device Flow"';
const uri = action === 'create' ? 'https://developer.home-connect.com/applications/add'
: `https://developer.home-connect.com/applications/${clientid}/edit`;
assertIsDefined(this.message);
this.message.client = { action, settings, uri };
}
clientChangeGuide(action) {
assertIsDefined(this.message);
const { postscript } = this.message;
if (action === 'create' || action === 'modify') {
postscript.push('Accurately copy the Client ID value (64 hexadecimal characters) from the Home Connect Developer Program'
+ ' site to the "clientid" field of the "config.json" configuration file.');
}
postscript.push('Note that applications created or edited on the Home Connect Developer Program site often take'
+ ' several minutes to propagate to the authorisation servers.'
+ ' If you think the configuration is correct, then wait 15 minutes and try again'
+ ' before seeking help elsewhere or reporting an issue.');
}
}
//# sourceMappingURL=api-ua-auth-help.js.map