Skip to content

Commit

Permalink
Address linter errors
Browse files Browse the repository at this point in the history
  • Loading branch information
duggan committed Feb 5, 2024
1 parent f426a04 commit af63914
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 27 deletions.
92 changes: 73 additions & 19 deletions src/helki_client.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import axios, { AxiosInstance } from 'axios';
import axiosRetry from 'axios-retry';
import { Logger } from 'homebridge';

interface TokenResponse {
access_token: string;
refresh_token: string;
expires_in: number;
}

interface NodeResponse {
nodes: Node[];
}

interface StatusArgs {
[key: string]: any;
[key: string]: string | number;
}

interface SetupArgs {
[key: string]: any;
[key: string]: string | number;
}

interface PowerLimitResponse {
Expand Down Expand Up @@ -93,6 +90,56 @@ interface Status {
sync_status: string; // Synchronization status, likely read-only
}

interface SetupResponse {
sync_status: 'ok';
control_mode: number;
units: 'C' | 'F';
power: string;
offset: string;
away_mode: number;
away_offset: string;
modified_auto_span: number;
window_mode_enabled: boolean;
true_radiant_enabled: boolean;
user_duty_factor: number;
flash_version: string;
factory_options: {
temp_compensation_enabled: boolean;
window_mode_available: boolean;
true_radiant_available: boolean;
duty_limit: number;
boost_config: number;
button_double_press: boolean;
prog_resolution: number;
bbc_value: number;
bbc_available: boolean;
lst_value: number;
lst_available: boolean;
fil_pilote_available: boolean;
backlight_time: number;
button_down_code: number;
button_up_code: number;
button_mode_code: number;
button_prog_code: number;
button_off_code: number;
button_boost_code: number;
splash_screen_type: number;
};
extra_options: {
boost_temp: string;
boost_time: number;
bright_on_level: number;
bright_off_level: number;
backlight_time: number;
beep_active: boolean;
language: number;
style: number;
time_format: number;
date_format: number;
};
}



const MIN_TOKEN_LIFETIME = 60; // seconds

Expand All @@ -105,21 +152,23 @@ class HelkiClient {
private axiosInstance: AxiosInstance;
private accessToken: string;
private expiresAt: Date;
private log: Logger;

constructor(
apiName: string,
clientId: string,
clientSecret: string,
username: string,
password: string,
retryAttempts = 5,
log: Logger,
) {
this.apiHost = `https://${apiName}.helki.com`;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.username = username;
this.password = password;

this.log = log;
this.accessToken = '';
this.expiresAt = new Date();

Expand All @@ -134,7 +183,7 @@ class HelkiClient {
}, error => {
if (error.response) {
// Log any request error
console.log('Request error:', error);
this.log.error('Request error:', error);
}
return Promise.reject(error);
});
Expand All @@ -146,13 +195,13 @@ class HelkiClient {
}, error => {
if (error.response) {
// Log error details including the response from the server
console.log(`Error response from ${error.response.config.url}:`, error.response);
this.log.error(`Error response from ${error.response.config.url}:`, error.response);
}
return Promise.reject(error);
});

axiosRetry(this.axiosInstance, {
retries: retryAttempts,
retries: 5,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: (error) => axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response?.status === 429,
});
Expand Down Expand Up @@ -181,7 +230,7 @@ class HelkiClient {
this.expiresAt = new Date(Date.now() + expires_in * 1000);

if (expires_in < MIN_TOKEN_LIFETIME) {
console.warn(`Token expires in ${expires_in}s, which is below the minimum lifetime of ${MIN_TOKEN_LIFETIME}s.`);
this.log.warn(`Token expires in ${expires_in}s, which is below the minimum lifetime of ${MIN_TOKEN_LIFETIME}s.`);
}
}

Expand All @@ -202,7 +251,8 @@ class HelkiClient {
};
}

private async apiRequest<T = any>(path: string, method: 'GET' | 'POST' = 'GET', data?: any): Promise<T> {
// eslint-disable-next-line
private async apiRequest<T>(path: string, method: 'GET' | 'POST' = 'GET', data?: any): Promise<T> {
await this.checkRefresh();
const url = `${this.apiHost}/api/v2/${path}`;
const headers = this.getHeaders();
Expand All @@ -212,8 +262,11 @@ class HelkiClient {
? this.axiosInstance.get<T>(url, { headers })
: this.axiosInstance.post<T>(url, data, { headers }));
return response.data;
} catch (error: any) {
throw new Error(`API request to ${path} failed: ${error.message}`);
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(`API request to ${path} failed: ${error.message}`);
}
throw new Error(`API request to ${path} failed: ${error}`);
}
}

Expand All @@ -239,22 +292,23 @@ class HelkiClient {
await this.apiRequest<void>(`devs/${deviceId}/${node.type}/${node.addr}/status`, 'POST', status);
}

public async getSetup(deviceId: string, node: Node): Promise<any> {
public async getSetup(deviceId: string, node: Node): Promise<SetupResponse> {
return this.apiRequest(`devs/${deviceId}/${node.type}/${node.addr}/setup`);
}

public async setSetup(deviceId: string, node: Node, setupArgs: SetupArgs): Promise<any> {
public async setSetup(deviceId: string, node: Node, setupArgs: SetupArgs): Promise<unknown> {
let setupData = await this.getSetup(deviceId, node); // Assuming this returns the current setup in a directly usable format
setupData = { ...setupData, ...setupArgs }; // Merge with new setup arguments
return this.apiRequest(`devs/${deviceId}/${node.type}/${node.addr}/setup`, 'POST', setupData);
}

public async getDeviceAwayStatus(deviceId: string): Promise<any> {
public async getDeviceAwayStatus(deviceId: string): Promise<unknown> {
return this.apiRequest(`devs/${deviceId}/mgr/away_status`);
}

public async setDeviceAwayStatus(deviceId: string, statusArgs: StatusArgs): Promise<any> {
const data = Object.fromEntries(Object.entries(statusArgs).filter(([_, v]) => v != null));
public async setDeviceAwayStatus(deviceId: string, statusArgs: StatusArgs): Promise<unknown> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const data = Object.fromEntries(Object.entries(statusArgs).filter(([_, v]) => v !== null));
return this.apiRequest(`devs/${deviceId}/mgr/away_status`, 'POST', data);
}

Expand Down
13 changes: 9 additions & 4 deletions src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export class Technotherm implements DynamicPlatformPlugin {
this.config.clientId,
this.config.clientSecret,
this.config.username,
this.config.password);
this.config.password,
this.log);

const homes = await helki.getGroupedDevices();
for (const home of homes) {
Expand All @@ -73,7 +74,7 @@ export class Technotherm implements DynamicPlatformPlugin {
new Radiator(this, existingAccessory, helki);
} else {
// Accessory doesn't exist, add new
const accessoryName = `${device.name} (${home.name})`
const accessoryName = `${device.name} (${home.name})`;
this.log.info('Adding new accessory:', accessoryName); // Use device name for display name
const accessory = new this.api.platformAccessory(accessoryName, uuid);
accessory.context.device = device;
Expand All @@ -85,8 +86,12 @@ export class Technotherm implements DynamicPlatformPlugin {
}
}

} catch (error: any) {
this.log.error('Failed to discover devices:', error.message);
} catch (error: unknown) {
if (error instanceof Error) {
this.log.error(`Failed to discover devices: ${error.message}`);
} else {
this.log.error(`Failed to discover devices: ${error}`);
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/radiator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
import { Technotherm } from './platform';
import { HelkiClient, Node, Status } from './helki_client';
import { HelkiClient, Node } from './helki_client';

export class Radiator {
private service: Service;
Expand All @@ -9,7 +9,7 @@ export class Radiator {
constructor(
private readonly platform: Technotherm,
private readonly accessory: PlatformAccessory,
private readonly helkiClient: HelkiClient
private readonly helkiClient: HelkiClient,
) {
// Initialize node from accessory context
this.node = this.accessory.context.node;
Expand Down Expand Up @@ -50,7 +50,7 @@ export class Radiator {
{
stemp: value.toString(),
mode: 'manual',
units: 'C'
units: 'C',
});
} catch (error) {
this.platform.log.error('Failed to set target temperature:', error);
Expand Down Expand Up @@ -84,7 +84,7 @@ export class Radiator {
} else if (value === this.platform.Characteristic.TargetHeatingCoolingState.AUTO) {
mode = 'auto'; // Assuming auto control
} else {
mode = 'off'
mode = 'off';
}
// Set the mode accordingly
try {
Expand Down

0 comments on commit af63914

Please sign in to comment.