@@ -53,6 +53,56 @@ class Token {
5353 }
5454}
5555
56+ /**
57+ * Invalid client configuration
58+ */
59+ class ConfigError extends Error {
60+ constructor ( message : string ) {
61+ super ( message )
62+ this . name = 'ConfigError'
63+ }
64+ }
65+
66+ /**
67+ * Invalid input data
68+ */
69+ class InputError extends Error {
70+ constructor ( message : string ) {
71+ super ( message )
72+ this . name = 'InputError'
73+ }
74+ }
75+
76+ /**
77+ * Authentication error
78+ */
79+ class AuthError extends Error {
80+ constructor ( message : string ) {
81+ super ( message )
82+ this . name = 'AuthError'
83+ }
84+ }
85+
86+ /**
87+ * HTTP error (syntax issue or incorrect parameter)
88+ */
89+ class RequestError extends Error {
90+ constructor ( message : string ) {
91+ super ( message )
92+ this . name = 'RequestError'
93+ }
94+ }
95+
96+ /**
97+ * Network error
98+ */
99+ class NetworkError extends Error {
100+ constructor ( message : string ) {
101+ super ( message )
102+ this . name = 'NetworkError'
103+ }
104+ }
105+
56106class NetatmoClient {
57107 clientId : string
58108 clientSecret : string
@@ -76,7 +126,7 @@ class NetatmoClient {
76126 */
77127 constructor ( clientId : string , clientSecret : string , scope : string , requestConfig : AxiosRequestConfig = { } ) {
78128 if ( ! clientId || ! clientSecret ) {
79- throw new Error ( 'Client id and client secret must be provided, see https://dev.netatmo.com/apidocumentation' )
129+ throw new ConfigError ( 'Client id and client secret must be provided, see https://dev.netatmo.com/apidocumentation' )
80130 }
81131 this . clientId = clientId
82132 this . clientSecret = clientSecret
@@ -110,7 +160,7 @@ class NetatmoClient {
110160 if ( refreshToken ) {
111161 return this . authenticateByRefreshToken ( refreshToken )
112162 }
113- throw new Error ( 'Refresh token is missing' )
163+ throw new InputError ( 'Refresh token is missing' )
114164 }
115165
116166 /**
@@ -125,7 +175,7 @@ class NetatmoClient {
125175 this . redirectUrl = redirectUrl
126176 }
127177 if ( ! this . redirectUrl ) {
128- throw new Error ( 'Redirect url must be provided' )
178+ throw new InputError ( 'Redirect url must be provided' )
129179 }
130180 this . state = statePrefix + Math . random ( ) * 10000000000000000
131181 const query = querystring . stringify ( {
@@ -147,10 +197,10 @@ class NetatmoClient {
147197 */
148198 async authenticateByAuthorizationCode ( authorizationCode : string , redirectUrl : string , state : string ) : Promise < Token > {
149199 if ( ! authorizationCode || ! redirectUrl ) {
150- throw new Error ( 'Authorization code and redirect url must be provided' )
200+ throw new InputError ( 'Authorization code and redirect url must be provided' )
151201 }
152202 if ( this . state !== state ) {
153- throw new Error ( 'State is not identical as the one provided during authorize url request' )
203+ throw new InputError ( 'State is not identical as the one provided during authorize url request' )
154204 }
155205 this . authorizationCode = authorizationCode
156206 this . redirectUrl = redirectUrl
@@ -173,7 +223,7 @@ class NetatmoClient {
173223 */
174224 async authenticateByRefreshToken ( refreshToken : string ) : Promise < Token > {
175225 if ( ! refreshToken ) {
176- throw new Error ( 'Refresh token must be provided' )
226+ throw new InputError ( 'Refresh token must be provided' )
177227 }
178228 this . refreshToken = refreshToken
179229 const authentication : NetatmoToken = await this . request ( HTTP_POST , PATH_AUTH , null , {
@@ -193,7 +243,7 @@ class NetatmoClient {
193243 */
194244 setToken ( netatmoAuthentication : NetatmoToken ) : Token {
195245 if ( ! netatmoAuthentication . access_token || ! netatmoAuthentication . refresh_token || ! netatmoAuthentication . expires_in ) {
196- throw new Error ( 'Invalid Netatmo token' )
246+ throw new InputError ( 'Invalid Netatmo token' )
197247 }
198248 this . accessToken = netatmoAuthentication . access_token
199249 this . refreshToken = netatmoAuthentication . refresh_token
@@ -247,7 +297,7 @@ class NetatmoClient {
247297
248298 if ( path !== PATH_AUTH ) {
249299 if ( ! this . accessToken ) {
250- throw new Error ( 'Access token must be provided' )
300+ throw new AuthError ( 'Access token must be provided' )
251301 }
252302 config . headers ! . Authorization = `Bearer ${ this . accessToken } `
253303 }
@@ -265,23 +315,26 @@ class NetatmoClient {
265315 return await this . request ( method , path , params , data , true )
266316 }
267317 if ( error . response . data . error_description ) {
268- // bad request error
269- throw new Error ( `HTTP request ${ path } failed: ${ error . response . data . error_description } (${ error . response . status } )` )
318+ // bad request error (Oauth2 RFC syntax)
319+ throw new AuthError ( `HTTP request ${ path } failed: ${ error . response . data . error_description } (${ error . response . status } )` )
270320 }
271321 if ( error . response . data . error && error . response . data . error . message ) {
272322 // standard error
273- throw new Error ( `HTTP request ${ path } failed: ${ error . response . data . error . message } (${ error . response . status } )` )
323+ throw new RequestError ( `HTTP request ${ path } failed: ${ error . response . data . error . message } (${ error . response . status } )` )
274324 }
275325 if ( error . response . data . error ) {
276326 // other error
277- throw new Error ( `HTTP request ${ path } failed: ${ JSON . stringify ( error . response . data . error ) } (${ error . response . status } )` )
327+ throw new RequestError ( `HTTP request ${ path } failed: ${ JSON . stringify ( error . response . data . error ) } (${ error . response . status } )` )
278328 }
279329 }
330+ if ( error . code && [ 'ENOTFOUND' , 'ECONNRESET' , 'ECONNREFUSED' , 'ECONNABORTED' ] . includes ( error . code ) ) {
331+ throw new NetworkError ( `HTTP request ${ path } failed: ${ error . message } ` )
332+ }
280333 }
281334 if ( error instanceof Error ) {
282- throw new Error ( `HTTP request ${ path } failed: ${ error . message } ` )
335+ throw new RequestError ( `HTTP request ${ path } failed: ${ error . message } ` )
283336 }
284- throw new Error ( `HTTP request ${ path } failed: ${ error } ` )
337+ throw new RequestError ( `HTTP request ${ path } failed: ${ error } ` )
285338 }
286339 }
287340
@@ -309,7 +362,7 @@ class NetatmoClient {
309362 */
310363 async getEventsUntil ( homeId : string , eventId : string ) : Promise < EventsList > {
311364 if ( ! homeId || ! eventId ) {
312- throw new Error ( 'Home id and event id must be provided' )
365+ throw new InputError ( 'Home id and event id must be provided' )
313366 }
314367 const params = {
315368 home_id : homeId ,
@@ -328,7 +381,7 @@ class NetatmoClient {
328381 */
329382 async getLastEventOf ( homeId : string , personId : string , offset : number ) : Promise < EventsList > {
330383 if ( ! homeId || ! personId ) {
331- throw new Error ( 'Home id and person id must be provided' )
384+ throw new InputError ( 'Home id and person id must be provided' )
332385 }
333386 const params = {
334387 home_id : homeId ,
@@ -348,7 +401,7 @@ class NetatmoClient {
348401 */
349402 async getNextEvents ( homeId : string , eventId : string , size : number ) : Promise < EventsList > {
350403 if ( ! homeId || ! eventId ) {
351- throw new Error ( 'Home id and event id must be provided' )
404+ throw new InputError ( 'Home id and event id must be provided' )
352405 }
353406 const params = {
354407 home_id : homeId ,
@@ -367,7 +420,7 @@ class NetatmoClient {
367420 */
368421 async getCameraPicture ( imageId : string , key : string ) : Promise < CameraImage > {
369422 if ( ! imageId || ! key ) {
370- throw new Error ( 'Image id and key must be provided' )
423+ throw new InputError ( 'Image id and key must be provided' )
371424 }
372425 const params = {
373426 image_id : imageId ,
@@ -389,7 +442,7 @@ class NetatmoClient {
389442 */
390443 async getPublicData ( latNE : number , lonNE : number , latSW : number , lonSW : number , requiredData : string , filter = false ) : Promise < PublicData [ ] > {
391444 if ( ! latNE || ! lonNE || ! latSW || ! lonSW ) {
392- throw new Error ( 'Latitude and Longitude must be provided' )
445+ throw new InputError ( 'Latitude and Longitude must be provided' )
393446 }
394447 const params = {
395448 lat_ne : latNE ,
@@ -433,7 +486,7 @@ class NetatmoClient {
433486 */
434487 async getMeasure ( deviceId : string , moduleId : string , scale : string , type : string , dateBegin : number , dateEnd : number , limit : number , optimize : boolean , realTime : boolean ) : Promise < Measure [ ] > {
435488 if ( ! deviceId || ! scale || ! type ) {
436- throw new Error ( 'Device id, scale and type must be provided' )
489+ throw new InputError ( 'Device id, scale and type must be provided' )
437490 }
438491 const params = {
439492 device_id : deviceId ,
@@ -450,4 +503,15 @@ class NetatmoClient {
450503 }
451504}
452505
453- export { NetatmoClient , SCOPE_BASIC_CAMERA , SCOPE_FULL_CAMERA , SCOPE_FULL , SCOPE_BASIC_WEATHER }
506+ export {
507+ NetatmoClient ,
508+ ConfigError ,
509+ InputError ,
510+ AuthError ,
511+ RequestError ,
512+ NetworkError ,
513+ SCOPE_BASIC_CAMERA ,
514+ SCOPE_FULL_CAMERA ,
515+ SCOPE_FULL ,
516+ SCOPE_BASIC_WEATHER ,
517+ }
0 commit comments