homebridge-homeconnect
Version:
A Homebridge plugin that connects Home Connect appliances to Apple HomeKit
118 lines • 4.78 kB
JavaScript
// Homebridge plugin for Home Connect home appliances
// Copyright © 2023-2025 Alexander Thoukydides
import { STATUS_CODES } from 'http';
import { assertIsDefined, columns } from './utils.js';
import { checkers as apiCheckers } from './ti/api-types.js';
import { checkers as authCheckers } from './ti/api-auth-types.js';
// Base for reporting all Home Connect API errors
export class APIError extends Error {
constructor(request, response, message, options) {
// Standard error object initialisation
super(message);
this.request = request;
this.response = response;
Error.captureStackTrace(this, APIError);
this.name = 'Home Connect API Error';
if (options?.cause)
this.errCause = options.cause;
}
}
// API could not be authorised
export class APIAuthorisationError extends APIError {
constructor(request, response, message, options) {
super(request, response, message, options);
Error.captureStackTrace(this, APIAuthorisationError);
this.name = 'Home Connect API Authorisation Error';
}
}
// API returned a non-success status code
export class APIStatusCodeError extends APIError {
constructor(request, response, text, options) {
super(request, response, APIStatusCodeError.getMessage(response, text), options);
this.text = text;
Error.captureStackTrace(this, APIStatusCodeError);
this.name = 'Home Connect Status Code Error';
}
// Construct an error message from a response
static getMessage(response, text) {
const statusCode = response.statusCode;
const statusCodeName = STATUS_CODES[statusCode];
const description = APIStatusCodeError.getBodyDescription(text)
?? 'No error message returned';
return `[${statusCode} ${statusCodeName}] ${description}`;
}
// Attempt to retrieve the error key
get key() {
return APIStatusCodeError.parseBody(this.text).key;
}
// Attempt to retrieve the error key
get description() {
return APIStatusCodeError.parseBody(this.text).description;
}
// Attempt to retrieve the error description
get simpleMessage() {
return APIStatusCodeError.getBodyDescription(this.text);
}
// Attempt to extract a useful description from the response body
static getBodyDescription(text) {
const { key, description } = APIStatusCodeError.parseBody(text);
if (key) {
return (description ? `${description} ` : '') + `[${key}]`;
}
else {
return text.length ? text : undefined;
}
}
// Attempt to parse the response body
static parseBody(text) {
try {
const json = JSON.parse(text);
if (apiCheckers.ErrorResponse.test(json)) {
return {
key: json.error.key,
description: json.error.developerMessage
?? json.error.description
?? json.error.value
};
}
else if (authCheckers.AuthorisationError.test(json)) {
return {
key: json.error,
description: json.error_description
};
}
}
catch { /* empty */ }
return {};
}
}
// API returned a response that failed checker validation
export class APIValidationError extends APIError {
constructor(request, response, validation, options) {
super(request, response, APIValidationError.getMessage(validation), options);
this.validation = validation;
Error.captureStackTrace(this, APIValidationError);
this.name = 'Home Connect API Validation Error';
}
// Construct an error message from a checker validation error
static getMessage(errors) {
assertIsDefined(errors[0]);
const description = `${errors[0].path} ${errors[0].message}`;
return `Structure validation failed (${description})`;
}
}
// API returned event stream that could not be parsed
export class APIEventStreamError extends APIError {
constructor(request, response, message, sse, options) {
super(request, response, APIEventStreamError.getMessage(message, sse), options);
this.sse = sse;
Error.captureStackTrace(this, APIEventStreamError);
this.name = 'Home Connect API Event Stream Error';
}
// Construct an error message from a checker validation error
static getMessage(description, sse) {
const fields = Object.entries(sse).map(([name, value]) => [`${name}:`, value]);
return [`Unable to parse ${description}:`, ...columns(fields)].join('\n');
}
}
//# sourceMappingURL=api-errors.js.map