Skip to content

Commit 511fa9e

Browse files
authored
Merge pull request #56 from AlCalzone/connection-errors
Differentiate between the various possible connection and auth errors
2 parents c1a3f0a + 009a839 commit 511fa9e

6 files changed

Lines changed: 151 additions & 37 deletions

File tree

README.md

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ import { TradfriClient, Accessory, AccessoryTypes } from "node-tradfri-client";
3434

3535
// connect
3636
const tradfri = new TradfriClient("gw-abcdef012345");
37-
await tradfri.connect(identity, psk);
37+
try {
38+
await tradfri.connect(identity, psk);
39+
} catch (e) {
40+
// handle error - see below for details
41+
}
3842
```
3943

4044
### Make a lightbulb blink
@@ -194,16 +198,20 @@ try {
194198
```
195199
The returned `identity` and `psk` **have to be stored** for future connections to the gateway. To comply with IKEA's requests, the security code **must not be stored** permanently in your application.
196200

197-
The call throws an error if it wasn't successful which you should handle. The error `e` should be of type `TradfriError` and gives further information why the authentication failed. To check that, add `TradfriError` and `TradfriErrorCodes` to the list of imports and check as follows:
201+
If the authentication was not successful, this method throws (or rather rejects with) an error which you should handle. The error `e` should be of type `TradfriError` and gives further information why the authentication failed. To check that, add `TradfriError` and `TradfriErrorCodes` to the list of imports and check as follows:
198202
```TS
199203
if (e instanceof TradfriError) {
200204
switch (e.code) {
201-
case TradfriErrorCodes.ConnectionFailed: {
202-
// Gateway unreachable or security code wrong
205+
case TradfriErrorCodes.ConnectionTimedOut: {
206+
// The gateway is unreachable or did not respond in time
203207
}
204208
case TradfriErrorCodes.AuthenticationFailed: {
205-
// Something went wrong with the authentication.
206-
// It might be that this library has to be updated to be compatible with a new firmware.
209+
// The security code is wrong or something else went wrong with the authentication.
210+
// Check the error message for details. It might be that this library has to be updated
211+
// to be compatible with a new firmware.
212+
}
213+
case TradfriErrorCodes.ConnectionFailed: {
214+
// An unknown error happened while trying to connect
207215
}
208216
}
209217
}
@@ -212,9 +220,24 @@ if (e instanceof TradfriError) {
212220
### Connecting to the gateway
213221
When you have a valid identity and psk, you can connect to the gateway using the `connect` method:
214222
```TS
215-
const success = await tradfri.connect(identity, psk);
223+
try {
224+
await tradfri.connect(identity, psk);
225+
} catch (e: TradfriError) {
226+
// handle error
227+
switch (e.code) {
228+
case TradfriErrorCodes.ConnectionTimedOut: {
229+
// The gateway is unreachable or did not respond in time
230+
}
231+
case TradfriErrorCodes.AuthenticationFailed: {
232+
// The provided credentials are not valid. You need to re-authenticate using `authenticate()`.
233+
}
234+
case TradfriErrorCodes.ConnectionFailed: {
235+
// An unknown error happened while trying to connect
236+
}
237+
}
238+
}
216239
```
217-
If the connection was unsuccessful, either the gateway was unreachable or the identity/psk pair isn't valid.
240+
**NOTE:** As of v0.6.0, this no longer resolves with `false` if the connection was unsuccessful. Instead, it throws (or rejects with) a `TradfriError` which contains details about why the connection failed.
218241

219242
### Pinging the gateway
220243
```TS
@@ -507,7 +530,9 @@ A DeviceInfo object contains general information about a device. It has the foll
507530

508531
## Changelog
509532

510-
#### __WORK IN PROGRESS__
533+
#### __WORK IN PROGRESS__ - WARNING: BREAKING CHANGES!
534+
* (AlCalzone) **BREAKING**: The `connect()` method now either resolves with `true` or rejects with an error detailing why the connection failed.
535+
* (AlCalzone) The error thrown by `authentication()` now correctly reflects why the authentication failed.
511536
* (AlCalzone) Swallow `"DTLS handshake timed out"` promise rejections and emit an `"error"` instead
512537

513538
#### 0.10.1 (2018-03-15)

build/tradfri-client.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export declare class TradfriClient extends EventEmitter implements OperationProv
5858
* @param identity A previously negotiated identity.
5959
* @param psk The pre-shared key belonging to the identity.
6060
*/
61-
connect(identity: string, psk: string): Promise<boolean>;
61+
connect(identity: string, psk: string): Promise<true>;
6262
/**
6363
* Try to establish a connection to the configured gateway.
6464
* @param identity The DTLS identity to use

build/tradfri-client.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@ class TradfriClient extends events_1.EventEmitter {
5454
* @param psk The pre-shared key belonging to the identity.
5555
*/
5656
connect(identity, psk) {
57-
return this.tryToConnect(identity, psk);
57+
return __awaiter(this, void 0, void 0, function* () {
58+
switch (yield this.tryToConnect(identity, psk)) {
59+
case true: return true;
60+
case "auth failed": throw new tradfri_error_1.TradfriError("The provided credentials are not valid. Please re-authenticate!", tradfri_error_1.TradfriErrorCodes.AuthenticationFailed);
61+
case "timeout": throw new tradfri_error_1.TradfriError("The gateway did not respond in time.", tradfri_error_1.TradfriErrorCodes.ConnectionTimedOut);
62+
case "error": throw new tradfri_error_1.TradfriError("An unknown error occured while connecting to the gateway", tradfri_error_1.TradfriErrorCodes.ConnectionFailed);
63+
}
64+
});
5865
}
5966
/**
6067
* Try to establish a connection to the configured gateway.
@@ -71,7 +78,12 @@ class TradfriClient extends events_1.EventEmitter {
7178
});
7279
logger_1.log(`Attempting connection. Identity = ${identity}, psk = ${psk}`, "debug");
7380
const result = yield node_coap_client_1.CoapClient.tryToConnect(this.requestBase);
74-
logger_1.log(`Connection ${result ? "" : "un"}successful`, "debug");
81+
if (result === true) {
82+
logger_1.log("Connection successful", "debug");
83+
}
84+
else {
85+
logger_1.log("Connection failed. Reason: " + result, "debug");
86+
}
7587
return result;
7688
});
7789
}
@@ -85,9 +97,11 @@ class TradfriClient extends events_1.EventEmitter {
8597
return __awaiter(this, void 0, void 0, function* () {
8698
// first, check try to connect with the security code
8799
logger_1.log("authenticate() > trying to connect with the security code", "debug");
88-
if (!(yield this.tryToConnect("Client_identity", securityCode))) {
89-
// that didn't work, so the code is wrong
90-
throw new tradfri_error_1.TradfriError("The security code is wrong", tradfri_error_1.TradfriErrorCodes.ConnectionFailed);
100+
switch (yield this.tryToConnect("Client_identity", securityCode)) {
101+
case true: break; // all good
102+
case "auth failed": throw new tradfri_error_1.TradfriError("The security code is wrong", tradfri_error_1.TradfriErrorCodes.AuthenticationFailed);
103+
case "timeout": throw new tradfri_error_1.TradfriError("The gateway did not respond in time.", tradfri_error_1.TradfriErrorCodes.ConnectionTimedOut);
104+
case "error": throw new tradfri_error_1.TradfriError("An unknown error occured while connecting to the gateway", tradfri_error_1.TradfriErrorCodes.ConnectionFailed);
91105
}
92106
// generate a new identity
93107
const identity = `tradfri_${Date.now()}`;

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-tradfri-client",
3-
"version": "0.10.1",
3+
"version": "0.11.0",
44
"description": "Library to talk to IKEA Trådfri Gateways without external binaries",
55
"keywords": [
66
"coap",
@@ -63,7 +63,7 @@
6363
"dependencies": {
6464
"bonjour": "^3.5.0",
6565
"debug": "^3.1.0",
66-
"node-coap-client": "^0.5.5",
66+
"node-coap-client": "^0.6.0",
6767
"reflect-metadata": "^0.1.12"
6868
},
6969
"scripts": {
@@ -104,4 +104,4 @@
104104
"instrument": true
105105
},
106106
"readme": "README.md"
107-
}
107+
}

src/tradfri-client.test.ts

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,10 @@ describe("tradfri-client => infrastructure => ", () => {
5959
const identity = "IDENTITY";
6060
const psk = "PSK";
6161

62-
it("should reset the CoAP client, provide new security params and return the result from tryToConnect", async () => {
63-
// test if both possible responses are passed through
62+
it("should reset the CoAP client, provide new security params and resolve with true on success", async () => {
6463
fakeCoap.tryToConnect.returns(Promise.resolve(true));
6564
await tradfri.connect(identity, psk).should.become(true);
6665

67-
fakeCoap.tryToConnect.returns(Promise.resolve(false));
68-
await tradfri.connect(identity, psk).should.become(false);
69-
7066
fakeCoap.reset.should.have.been.called;
7167
fakeCoap.setSecurityParams.should.have.been.called;
7268
fakeCoap.setSecurityParams.getCall(0).args[1].should.deep.equal({
@@ -76,6 +72,33 @@ describe("tradfri-client => infrastructure => ", () => {
7672

7773
fakeCoap.tryToConnect.resetBehavior();
7874
});
75+
76+
it("should reject with a `TradfriError` with code ConnectionTimedOut when the connection times out", async () => {
77+
fakeCoap.tryToConnect.returns(Promise.resolve("timeout"));
78+
await tradfri.connect(identity, psk).should.be.rejected.then(err => {
79+
expect(err).to.be.an.instanceof(TradfriError);
80+
expect(err.code).to.equal(TradfriErrorCodes.ConnectionTimedOut);
81+
});
82+
fakeCoap.tryToConnect.resetBehavior();
83+
});
84+
85+
it("should reject with a `TradfriError` with code AuthenticationFailed when the credentials are wrong", async () => {
86+
fakeCoap.tryToConnect.returns(Promise.resolve("auth failed"));
87+
await tradfri.connect(identity, psk).should.be.rejected.then(err => {
88+
expect(err).to.be.an.instanceof(TradfriError);
89+
expect(err.code).to.equal(TradfriErrorCodes.AuthenticationFailed);
90+
});
91+
fakeCoap.tryToConnect.resetBehavior();
92+
});
93+
94+
it("should reject with a `TradfriError` with code ConnectionFailed when some other error happens", async () => {
95+
fakeCoap.tryToConnect.returns(Promise.resolve("error"));
96+
await tradfri.connect(identity, psk).should.be.rejected.then(err => {
97+
expect(err).to.be.an.instanceof(TradfriError);
98+
expect(err.code).to.equal(TradfriErrorCodes.ConnectionFailed);
99+
});
100+
fakeCoap.tryToConnect.resetBehavior();
101+
});
79102
});
80103

81104
describe("authenticate => ", () => {
@@ -90,11 +113,6 @@ describe("tradfri-client => infrastructure => ", () => {
90113
fakeCoap.request.resetBehavior();
91114
});
92115

93-
it(`detects failure to connect with "Client_identity" as a wrong security code`, async () => {
94-
fakeCoap.tryToConnect.returns(Promise.resolve(false));
95-
await tradfri.authenticate(null).should.be.rejectedWith("security code");
96-
});
97-
98116
it(`should call coap.request with the correct endpoint and payload and return the identity and psk`, async () => {
99117
fakeCoap.tryToConnect.returns(Promise.resolve(true));
100118
fakeCoap.request.returns(Promise.resolve(authResponse));
@@ -114,6 +132,33 @@ describe("tradfri-client => infrastructure => ", () => {
114132
);
115133
});
116134

135+
it("should reject with a `TradfriError` with code ConnectionTimedOut when the authentication times out", async () => {
136+
fakeCoap.tryToConnect.returns(Promise.resolve("timeout"));
137+
await tradfri.authenticate(dummyIdentity).should.be.rejected.then(err => {
138+
expect(err).to.be.an.instanceof(TradfriError);
139+
expect(err.code).to.equal(TradfriErrorCodes.ConnectionTimedOut);
140+
});
141+
fakeCoap.tryToConnect.resetBehavior();
142+
});
143+
144+
it("should reject with a `TradfriError` with code AuthenticationFailed when the security code was wrong", async () => {
145+
fakeCoap.tryToConnect.returns(Promise.resolve("auth failed"));
146+
await tradfri.authenticate(dummyIdentity).should.be.rejected.then(err => {
147+
expect(err).to.be.an.instanceof(TradfriError);
148+
expect(err.code).to.equal(TradfriErrorCodes.AuthenticationFailed);
149+
});
150+
fakeCoap.tryToConnect.resetBehavior();
151+
});
152+
153+
it("should reject with a `TradfriError` with code ConnectionFailed when some other error happens", async () => {
154+
fakeCoap.tryToConnect.returns(Promise.resolve("error"));
155+
await tradfri.authenticate(dummyIdentity).should.be.rejected.then(err => {
156+
expect(err).to.be.an.instanceof(TradfriError);
157+
expect(err.code).to.equal(TradfriErrorCodes.ConnectionFailed);
158+
});
159+
fakeCoap.tryToConnect.resetBehavior();
160+
});
161+
117162
it(`if coap.request returns an error, throw AuthenticationFailed`, async () => {
118163
fakeCoap.tryToConnect.returns(Promise.resolve(true));
119164
fakeCoap.request.returns(Promise.resolve(failedAuthResponse));
@@ -122,7 +167,6 @@ describe("tradfri-client => infrastructure => ", () => {
122167
expect((err as TradfriError).code).to.equal(TradfriErrorCodes.AuthenticationFailed);
123168
});
124169
});
125-
126170
});
127171

128172
describe("ping =>", () => {

src/tradfri-client.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// load external modules
22
import { EventEmitter } from "events";
3-
import { CoapClient as coap, CoapResponse, RequestMethod } from "node-coap-client";
3+
import { CoapClient as coap, CoapResponse, ConnectionResult, RequestMethod } from "node-coap-client";
44

55
// load internal modules
66
import { Accessory, AccessoryTypes } from "./lib/accessory";
@@ -105,8 +105,22 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
105105
* @param identity A previously negotiated identity.
106106
* @param psk The pre-shared key belonging to the identity.
107107
*/
108-
public connect(identity: string, psk: string): Promise<boolean> {
109-
return this.tryToConnect(identity, psk);
108+
public async connect(identity: string, psk: string): Promise<true> {
109+
switch (await this.tryToConnect(identity, psk)) {
110+
case true: return true;
111+
case "auth failed": throw new TradfriError(
112+
"The provided credentials are not valid. Please re-authenticate!",
113+
TradfriErrorCodes.AuthenticationFailed,
114+
);
115+
case "timeout": throw new TradfriError(
116+
"The gateway did not respond in time.",
117+
TradfriErrorCodes.ConnectionTimedOut,
118+
);
119+
case "error": throw new TradfriError(
120+
"An unknown error occured while connecting to the gateway",
121+
TradfriErrorCodes.ConnectionFailed,
122+
);
123+
}
110124
}
111125

112126
/**
@@ -115,7 +129,7 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
115129
* @param psk The pre-shared key to use
116130
* @returns true if the connection attempt was successful, otherwise false.
117131
*/
118-
private async tryToConnect(identity: string, psk: string): Promise<boolean> {
132+
private async tryToConnect(identity: string, psk: string): Promise<ConnectionResult> {
119133

120134
// initialize CoAP client
121135
coap.reset();
@@ -125,7 +139,11 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
125139

126140
log(`Attempting connection. Identity = ${identity}, psk = ${psk}`, "debug");
127141
const result = await coap.tryToConnect(this.requestBase);
128-
log(`Connection ${result ? "" : "un"}successful`, "debug");
142+
if (result === true) {
143+
log("Connection successful", "debug");
144+
} else {
145+
log("Connection failed. Reason: " + result, "debug");
146+
}
129147
return result;
130148
}
131149

@@ -138,10 +156,23 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
138156
public async authenticate(securityCode: string): Promise<{identity: string, psk: string}> {
139157
// first, check try to connect with the security code
140158
log("authenticate() > trying to connect with the security code", "debug");
141-
if (!await this.tryToConnect("Client_identity", securityCode)) {
142-
// that didn't work, so the code is wrong
143-
throw new TradfriError("The security code is wrong", TradfriErrorCodes.ConnectionFailed);
159+
switch (await this.tryToConnect("Client_identity", securityCode)) {
160+
case true: break; // all good
161+
162+
case "auth failed": throw new TradfriError(
163+
"The security code is wrong",
164+
TradfriErrorCodes.AuthenticationFailed,
165+
);
166+
case "timeout": throw new TradfriError(
167+
"The gateway did not respond in time.",
168+
TradfriErrorCodes.ConnectionTimedOut,
169+
);
170+
case "error": throw new TradfriError(
171+
"An unknown error occured while connecting to the gateway",
172+
TradfriErrorCodes.ConnectionFailed,
173+
);
144174
}
175+
145176
// generate a new identity
146177
const identity = `tradfri_${Date.now()}`;
147178
log(`authenticating with identity "${identity}"`, "debug");

0 commit comments

Comments
 (0)