Skip to content

Commit 2d0fbb2

Browse files
committed
Introduced singleton HS connection and improved logging w.r.t fault isolation tips.
1 parent 428a35d commit 2d0fbb2

5 files changed

Lines changed: 71 additions & 28 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "homebridge-hsd",
33
"displayName": "Homebridge Gira Homeserver URL-Endpoint",
4-
"version": "1.1.4",
4+
"version": "1.1.5",
55
"description": "Plugin to access KNX bus via Gira Homeserver",
66
"license": "MIT",
77
"keywords": [

src/hs.ts

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { API } from 'homebridge';
44
import * as WebSocket from 'ws';
55
import { Logging } from 'homebridge';
66
import { HsdAccessory } from './hsdAccessory';
7+
import { randomUUID } from 'crypto';
78

89
export enum CONNECTION_STATE {
910
INIT = 0,
@@ -16,11 +17,14 @@ export enum CONNECTION_STATE {
1617

1718
export class HomeServerConnector {
1819

20+
private static _instance: HomeServerConnector;
21+
1922
private _ws: WebSocket.WebSocket | null = null;
2023
private _connState = CONNECTION_STATE.INIT;
2124
private _transactionIdCnt = 0;
2225
private _listeners: Map<string, (reading: string) => void> = new Map();
2326
private _msgQueu = {};
27+
private _uuid = '';
2428

2529
private requestPromiseResolver: Map<string, (value: string | PromiseLike<string>) => void> = new Map();
2630

@@ -33,11 +37,33 @@ export class HomeServerConnector {
3337
private _user = '';
3438
private _pw = '';
3539

40+
private _errorCodes = {
41+
0: 'Everything is fine.',
42+
400: 'Invalid request (Bad Request).',
43+
403: 'Access denied (Forbidden). Check if the read/write flag is set for the user groups on the Endpoint-tab.',
44+
404: 'The requested HS-Object does not exist in the called context.',
45+
500: 'An error occurred in the server when generating the response.',
46+
901: 'The specified key is invalid.',
47+
902: 'reserved',
48+
903: 'The object parameters are invalid.',
49+
904: 'The object is not subscribed.',
50+
};
51+
3652
/**
3753
*
3854
*/
39-
constructor(private api: API, private logger: Logging, private accessory: Map<string, HsdAccessory>) {
40-
//
55+
private constructor(private api: API, private logger: Logging, private accessory: Map<string, HsdAccessory>) {
56+
this._uuid = randomUUID();
57+
}
58+
59+
// This static method controls the access to the singleton instance.
60+
// On the first run, it creates the instance and stores it in a static field.
61+
// On subsequent runs, it returns the stored instance.
62+
public static getInstance(api: API, logger: Logging, accessory: Map<string, HsdAccessory>): HomeServerConnector {
63+
if (!HomeServerConnector._instance) {
64+
HomeServerConnector._instance = new HomeServerConnector(api, logger, accessory);
65+
}
66+
return HomeServerConnector._instance;
4167
}
4268

4369
/**
@@ -56,6 +82,11 @@ export class HomeServerConnector {
5682
* @param pw
5783
*/
5884
connect(hsIp: string, hsPort: number, user: string, pw: string) {
85+
if ((this._connState === CONNECTION_STATE.OPEN) || (this._connState === CONNECTION_STATE.CONNECTING)) {
86+
return; // already connected
87+
}
88+
this.logger.info('hs.ts | HomeServerConnector | connect > Current connection state is %d', this._connState);
89+
5990
this._hsIp = hsIp;
6091
this._hsPort = hsPort;
6192
this._user = user;
@@ -67,7 +98,7 @@ export class HomeServerConnector {
6798

6899
this._ws.on('open', () => {
69100
this._connState = CONNECTION_STATE.OPEN;
70-
this.logger.info('Connected to HS');
101+
this.logger.info('Connected to HS by %s', this._uuid);
71102
});
72103

73104
this._ws.on('message', (message: string) => {
@@ -102,10 +133,17 @@ export class HomeServerConnector {
102133

103134
if (code !== 0) {
104135
if ('request' in jsonMsg) {
105-
this.logger.error('hs.ts | HomeserverConnector | receivedMessage > Error and aborting due to code %d for %s requesting %s',
106-
code,
107-
jsonMsg.request.key,
108-
jsonMsg.request.method);
136+
if (code in this._errorCodes) {
137+
this.logger.error('hs.ts | HomeserverConnector | receivedMessage > Error and aborting due to "%s" for %s requesting %s',
138+
this._errorCodes[code],
139+
jsonMsg.request.key,
140+
jsonMsg.request.method);
141+
} else {
142+
this.logger.error('hs.ts | HomeserverConnector | receivedMessage > Error and aborting due to code %d for %s requesting %s',
143+
code,
144+
jsonMsg.request.key,
145+
jsonMsg.request.method);
146+
}
109147
} else {
110148
this.logger.error('hs.ts | HomeserverConnector | receivedMessage > Error and aborting due to %s', message);
111149
}
@@ -120,21 +158,31 @@ export class HomeServerConnector {
120158
this.logger.debug('hs.ts | HomeserverConnector | Received select/subscribe message');
121159
for (const item of data.items) {
122160
if (item.code !== 0) {
123-
this.logger.error('hs.ts | HomeserverConnector | receivedMessage > Error and aborting due to code %s for %s requesting %s',
124-
item.code,
125-
item.key,
126-
type);
127-
return false;
128-
}
129-
endpoint = item.key;
130-
value = String(item.data.value);
161+
if ('request' in jsonMsg) {
162+
if (code in this._errorCodes) {
163+
this.logger.error('hs.ts | HomeserverConnector | receivedMessage > Error and aborting due to "%s" for %s requesting %s',
164+
this._errorCodes[ item.code],
165+
item.key,
166+
type);
167+
} else {
168+
this.logger.error('hs.ts | HomeserverConnector | receivedMessage > Error and aborting due to code %d for %s requesting %s',
169+
item.code,
170+
item.key,
171+
type);
172+
}
173+
return false;
174+
}
131175

132-
/// return value via callback
133-
const callback = this._listeners.get(endpoint);
134-
if (callback) {
135-
callback(value);
136-
} else {
137-
this.logger.warn('hs.ts | HomeserverConnector | No callback for %s registered', endpoint);
176+
endpoint = item.key;
177+
value = String(item.data.value);
178+
179+
/// return value via callback
180+
const callback = this._listeners.get(endpoint);
181+
if (callback) {
182+
callback(value);
183+
} else {
184+
this.logger.warn('hs.ts | HomeserverConnector | No callback for %s registered', endpoint);
185+
}
138186
}
139187
}
140188

src/hsdPlatform.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class HsdPlatform implements DynamicPlatformPlugin {
1313
private config: HsdPlatformConfig;
1414

1515
private async connect (): Promise<HomeServerConnector> {
16-
const link = new HomeServerConnector(this.api, this.logger, this.hsdAccessories);
16+
const link = HomeServerConnector.getInstance(this.api, this.logger, this.hsdAccessories);
1717
link.connect(this.config.hsIp, this.config.hsPort, this.config.hsUserName, this.config.hsUserPw);
1818
this.logger.info(`hsdPlatform.ts | HsdPlatform | HSD IP gateway ${this.config.hsIp} connection established.`);
1919

src/service/Lightbulb.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,13 @@ import { addOnCharacteristic } from './characteristic/On';
66
import { addBrightnessCharacteristic } from './characteristic/Brightness';
77
import { HsdServiceConfig } from '../config';
88
import { AbstractHsdService } from './AbstractHsdService';
9-
// import { addListener } from 'process';
109

1110
export class Lightbulb extends AbstractHsdService {
1211

1312
public constructor (api: API, hsd: HomeServerConnector, accessory: HsdPlatformAccessory, config: HsdServiceConfig) {
1413
super(api, hsd, accessory, config);
1514

1615
const service = this.getService(this.api.hap.Service.Lightbulb);
17-
service.UUID;
18-
//service.setCharacteristic(this.api.hap.Service.name, config.serviceName);
19-
//service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.exampleDisplayName);
2016

2117
for (const characteristic of config.characteristics) {
2218
if (characteristic.characteristicName === 'On') {

src/service/Switch.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export class Switch extends AbstractHsdService {
1212
super(api, hsd, accessory, config);
1313

1414
const service = this.getService(this.api.hap.Service.Switch);
15-
//service.setCharacteristic(this.api.hap.Service.name, config.serviceName);
1615

1716
for (const characteristic of config.characteristics) {
1817
if (characteristic.characteristicName === 'On') {

0 commit comments

Comments
 (0)