node-switchbot
Advanced tools
Comparing version 1.3.0 to 1.4.0
@@ -5,6 +5,15 @@ # Changelog | ||
## [Version 1.3.0](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.2.0) (2022-06-25) | ||
## [Version 1.4.0](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.4.0) (2022-08-19) | ||
### Changes | ||
## What's Changed | ||
- Added support for Plug Mini (j) & (g) | ||
- Housekeeping and update dependencies | ||
**Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.3.0...v1.4.0 | ||
## [Version 1.3.0](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.3.0) (2022-06-25) | ||
## What's Changed | ||
- Added more Device Types, not all supported though. | ||
@@ -17,3 +26,3 @@ - Housekeeping and update dependencies | ||
### Changes | ||
## What's Changed | ||
@@ -28,3 +37,3 @@ - Added support for SwitchBot "Contact" and "Motion" | ||
### Changes | ||
## What's Changed | ||
@@ -37,3 +46,3 @@ - Housekeeping and update dependencies | ||
### Changes | ||
## What's Changed | ||
@@ -46,3 +55,3 @@ - Change back from @node/noble to @abandonware/noble | ||
### Changes | ||
## What's Changed | ||
@@ -57,3 +66,3 @@ - Add Contact/Motion Sensor advertisement | ||
### Changes | ||
## What's Changed | ||
@@ -66,3 +75,3 @@ - fix extra trace of old noble from @abandonware/noble | ||
### Changes | ||
## What's Changed | ||
@@ -75,3 +84,3 @@ - Change from @abandonware/noble to @homebridge/noble | ||
### Changes | ||
## What's Changed | ||
@@ -84,3 +93,3 @@ - Fixes FATAL ERROR: ad_id is not defined | ||
### Changes | ||
## What's Changed | ||
@@ -94,3 +103,3 @@ - Adding code for Contact and Motion Sensors | ||
### Changes | ||
## What's Changed | ||
@@ -103,3 +112,3 @@ - Support for the discover method with id on macOS | ||
### Changes | ||
## What's Changed | ||
@@ -112,3 +121,3 @@ - Fixed misspelling. | ||
### Changes | ||
## What's Changed | ||
@@ -121,3 +130,3 @@ - Housekeeping and update dependencies | ||
### Changes | ||
## What's Changed | ||
@@ -130,5 +139,5 @@ - Fixed issue where after switching Bluetooth off and on, would not work properly. | ||
### Changes | ||
## What's Changed | ||
- * fix "No device was found" in MacOS | ||
- - fix "No device was found" in MacOS | ||
@@ -139,3 +148,3 @@ **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v0.2.0...v1.0.0 | ||
### Changes | ||
## What's Changed | ||
@@ -148,3 +157,3 @@ - Modify Curtain's action command to support group and running mode. (Thanks to [@SwitchBot-Wonderlabs](https://github.com/OpenWonderLabs/node-switchbot/pull/7/)) | ||
### Changes | ||
## What's Changed | ||
@@ -158,3 +167,3 @@ - Added support for SwitchBot Curtain. (Thanks to [@SwitchBot-Wonderlabs](https://github.com/OpenWonderLabs/node-switchbot/pull/6/)) | ||
### Changes | ||
## What's Changed | ||
@@ -167,3 +176,3 @@ - Improved the stability of discovering the BLE characteristics. (Thanks to [@dnicolson](https://github.com/OpenWonderLabs/node-switchbot/issues/3)) | ||
### Changes | ||
## What's Changed | ||
@@ -176,3 +185,3 @@ - Fixed the bug that temperature value lower than 0 degC could not be handled. (Thanks to [@musimasami](https://github.com/OpenWonderLabs/node-switchbot/issues/2)) | ||
### Changes | ||
## What's Changed | ||
@@ -186,4 +195,4 @@ - Now the characteristic UUID `0x2a00` (Device Name) is not mandatory. Some models of Bot don't seem to support the characteristic. (Thanks to [@dnicolson](https://github.com/OpenWonderLabs/node-switchbot/issues/1)) | ||
### Changes | ||
## What's Changed | ||
- First public release | ||
- First public release |
@@ -1,2 +0,2 @@ | ||
'use strict'; | ||
"use strict"; | ||
@@ -15,3 +15,3 @@ class ParameterChecker { | ||
// name: 'age', | ||
// } | ||
// } | ||
// --------------------------------- | ||
@@ -22,38 +22,38 @@ return this._error; | ||
isSpecified(value) { | ||
return (value === void 0) ? false : true; | ||
return value === void 0 ? false : true; | ||
} | ||
/* ------------------------------------------------------------------ | ||
* check(obj, rule, required) | ||
* - Check if the specified object contains valid values | ||
* | ||
* [Arguments] | ||
* - obj | Object | Required | Object including parameters you want to check | ||
* - rules | Object | Required | Object including rules for the parameters | ||
* - required | Boolean | Optional | Flag whther the `obj` is required or not. | ||
* | | | The default is `false` | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* | ||
* [Usage] | ||
* let valid = parameterChecker.check(params, { | ||
* level: { | ||
* required: false, | ||
* type: 'integer', | ||
* max: 100 | ||
* }, | ||
* greeting: { | ||
* required: true, // But an empty string is allowed. | ||
* type: 'string', | ||
* max: 20 // the number of characters must be up to 20. | ||
* } | ||
* }); | ||
* if(!valid) { | ||
* let e = parameterChecker.error.message; | ||
* throw new Error(message); | ||
* } | ||
* ---------------------------------------------------------------- */ | ||
* check(obj, rule, required) | ||
* - Check if the specified object contains valid values | ||
* | ||
* [Arguments] | ||
* - obj | Object | Required | Object including parameters you want to check | ||
* - rules | Object | Required | Object including rules for the parameters | ||
* - required | Boolean | Optional | Flag whther the `obj` is required or not. | ||
* | | | The default is `false` | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* | ||
* [Usage] | ||
* let valid = parameterChecker.check(params, { | ||
* level: { | ||
* required: false, | ||
* type: 'integer', | ||
* max: 100 | ||
* }, | ||
* greeting: { | ||
* required: true, // But an empty string is allowed. | ||
* type: 'string', | ||
* max: 20 // the number of characters must be up to 20. | ||
* } | ||
* }); | ||
* if(!valid) { | ||
* let e = parameterChecker.error.message; | ||
* throw new Error(message); | ||
* } | ||
* ---------------------------------------------------------------- */ | ||
check(obj, rules, required = false) { | ||
@@ -64,4 +64,4 @@ this._error = null; | ||
this._error = { | ||
code: 'MISSING_REQUIRED', | ||
message: 'The first argument is missing.' | ||
code: "MISSING_REQUIRED", | ||
message: "The first argument is missing.", | ||
}; | ||
@@ -78,4 +78,4 @@ return false; | ||
this._error = { | ||
code: 'MISSING_REQUIRED', | ||
message: 'The first argument is missing.' | ||
code: "MISSING_REQUIRED", | ||
message: "The first argument is missing.", | ||
}; | ||
@@ -100,4 +100,4 @@ return false; | ||
this._error = { | ||
code: 'MISSING_REQUIRED', | ||
message: 'The `' + name + '` is required.' | ||
code: "MISSING_REQUIRED", | ||
message: "The `" + name + "` is required.", | ||
}; | ||
@@ -110,13 +110,13 @@ break; | ||
if (rule.type === 'float') { | ||
if (rule.type === "float") { | ||
result = this.isFloat(v, rule, name); | ||
} else if (rule.type === 'integer') { | ||
} else if (rule.type === "integer") { | ||
result = this.isInteger(v, rule, name); | ||
} else if (rule.type === 'boolean') { | ||
} else if (rule.type === "boolean") { | ||
result = this.isBoolean(v, rule, name); | ||
} else if (rule.type === 'array') { | ||
} else if (rule.type === "array") { | ||
result = this.isArray(v, rule, name); | ||
} else if (rule.type === 'object') { | ||
} else if (rule.type === "object") { | ||
result = this.isObject(v, rule, name); | ||
} else if (rule.type === 'string') { | ||
} else if (rule.type === "string") { | ||
result = this.isString(v, rule, name); | ||
@@ -126,4 +126,8 @@ } else { | ||
this._error = { | ||
code: 'TYPE_UNKNOWN', | ||
message: 'The rule specified for the `' + name + '` includes an unknown type: ' + rule.type, | ||
code: "TYPE_UNKNOWN", | ||
message: | ||
"The rule specified for the `" + | ||
name + | ||
"` includes an unknown type: " + | ||
rule.type, | ||
}; | ||
@@ -142,23 +146,23 @@ } | ||
/* ------------------------------------------------------------------ | ||
* isFloat(value, rule, name) | ||
* - Check if the value is a float | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - min | Float | Optional | Minimum number | ||
* - max | Float | Optional | Maximum number | ||
* - enum | Array | Optional | list of possible values | ||
* - name | String | Optional | Parameter name | ||
* | ||
* If non-number value is specified to the `min` or `max`, | ||
* they will be ignored. | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isFloat(value, rule = {}, name = 'value') { | ||
* isFloat(value, rule, name) | ||
* - Check if the value is a float | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - min | Float | Optional | Minimum number | ||
* - max | Float | Optional | Maximum number | ||
* - enum | Array | Optional | list of possible values | ||
* - name | String | Optional | Parameter name | ||
* | ||
* If non-number value is specified to the `min` or `max`, | ||
* they will be ignored. | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isFloat(value, rule = {}, name = "value") { | ||
this._error = null; | ||
@@ -170,6 +174,6 @@ | ||
if (typeof (value) !== 'number') { | ||
if (typeof value !== "number") { | ||
this._error = { | ||
code: 'TYPE_INVALID', | ||
message: 'The `' + name + '` must be a number (integer or float).' | ||
code: "TYPE_INVALID", | ||
message: "The `" + name + "` must be a number (integer or float).", | ||
}; | ||
@@ -179,7 +183,12 @@ return false; | ||
if (typeof (rule.min) === 'number') { | ||
if (typeof rule.min === "number") { | ||
if (value < rule.min) { | ||
this._error = { | ||
code: 'VALUE_UNDERFLOW', | ||
message: 'The `' + name + '` must be grater than or equal to ' + rule.min + '.' | ||
code: "VALUE_UNDERFLOW", | ||
message: | ||
"The `" + | ||
name + | ||
"` must be grater than or equal to " + | ||
rule.min + | ||
".", | ||
}; | ||
@@ -189,7 +198,12 @@ return false; | ||
} | ||
if (typeof (rule.max) === 'number') { | ||
if (typeof rule.max === "number") { | ||
if (value > rule.max) { | ||
this._error = { | ||
code: 'VALUE_OVERFLOW', | ||
message: 'The `' + name + '` must be less than or equal to ' + rule.max + '.' | ||
code: "VALUE_OVERFLOW", | ||
message: | ||
"The `" + | ||
name + | ||
"` must be less than or equal to " + | ||
rule.max + | ||
".", | ||
}; | ||
@@ -202,4 +216,9 @@ return false; | ||
this._error = { | ||
code: 'ENUM_UNMATCH', | ||
message: 'The `' + name + '` must be any one of ' + JSON.stringify(rule.enum) + '.' | ||
code: "ENUM_UNMATCH", | ||
message: | ||
"The `" + | ||
name + | ||
"` must be any one of " + | ||
JSON.stringify(rule.enum) + | ||
".", | ||
}; | ||
@@ -214,23 +233,23 @@ return false; | ||
/* ------------------------------------------------------------------ | ||
* isInteger(value, rule) | ||
* - Check if the value is an integer | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`.| | ||
* - min | Float | Optional | Minimum number | ||
* - max | Float | Optional | Maximum number | ||
* - enum | Array | Optional | list of possible values | ||
* - name | String | Optional | Parameter name | ||
* | ||
* If non-number value is specified to the `min` or `max`, | ||
* they will be ignored. | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isInteger(value, rule = {}, name = 'value') { | ||
* isInteger(value, rule) | ||
* - Check if the value is an integer | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`.| | ||
* - min | Float | Optional | Minimum number | ||
* - max | Float | Optional | Maximum number | ||
* - enum | Array | Optional | list of possible values | ||
* - name | String | Optional | Parameter name | ||
* | ||
* If non-number value is specified to the `min` or `max`, | ||
* they will be ignored. | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isInteger(value, rule = {}, name = "value") { | ||
this._error = null; | ||
@@ -247,4 +266,4 @@ | ||
this._error = { | ||
code: 'TYPE_INVALID', | ||
message: 'The `' + name + '` must be an integer.' | ||
code: "TYPE_INVALID", | ||
message: "The `" + name + "` must be an integer.", | ||
}; | ||
@@ -259,17 +278,17 @@ return false; | ||
/* ------------------------------------------------------------------ | ||
* isBoolean(value, rule, name) | ||
* - Check if the value is a boolean. | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - name | String | Optional | Parameter name | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isBoolean(value, rule = {}, name = 'value') { | ||
* isBoolean(value, rule, name) | ||
* - Check if the value is a boolean. | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - name | String | Optional | Parameter name | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isBoolean(value, rule = {}, name = "value") { | ||
this._error = null; | ||
@@ -281,6 +300,6 @@ | ||
if (typeof (value) !== 'boolean') { | ||
if (typeof value !== "boolean") { | ||
this._error = { | ||
code: 'TYPE_INVALID', | ||
message: 'The `' + name + '` must be boolean.' | ||
code: "TYPE_INVALID", | ||
message: "The `" + name + "` must be boolean.", | ||
}; | ||
@@ -293,17 +312,17 @@ return false; | ||
/* ------------------------------------------------------------------ | ||
* isObject(value) | ||
* - Check if the value is an object | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - name | String | Optional | Parameter name | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isObject(value, rule = {}, name = 'value') { | ||
* isObject(value) | ||
* - Check if the value is an object | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - name | String | Optional | Parameter name | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isObject(value, rule = {}, name = "value") { | ||
this._error = null; | ||
@@ -314,6 +333,6 @@ if (!rule.required && !this.isSpecified(value)) { | ||
if (typeof (value) !== 'object' || value === null || Array.isArray(value)) { | ||
if (typeof value !== "object" || value === null || Array.isArray(value)) { | ||
this._error = { | ||
code: 'TYPE_INVALID', | ||
message: 'The `' + name + '` must be an object.' | ||
code: "TYPE_INVALID", | ||
message: "The `" + name + "` must be an object.", | ||
}; | ||
@@ -326,22 +345,22 @@ return false; | ||
/* ------------------------------------------------------------------ | ||
* isArray(value, rule, name) | ||
* - Check if the value is an `Array` object | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - min | Integer | Optional | Minimum number of elements in the array | ||
* - max | Integer | Optional | Maximum number of elements in the array | ||
* - name | String | Optional | Parameter name | ||
* | ||
* If non-number value is specified to the `min` or `max`, | ||
* they will be ignored. | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isArray(value, rule = {}, name = 'value') { | ||
* isArray(value, rule, name) | ||
* - Check if the value is an `Array` object | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - min | Integer | Optional | Minimum number of elements in the array | ||
* - max | Integer | Optional | Maximum number of elements in the array | ||
* - name | String | Optional | Parameter name | ||
* | ||
* If non-number value is specified to the `min` or `max`, | ||
* they will be ignored. | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isArray(value, rule = {}, name = "value") { | ||
this._error = null; | ||
@@ -355,4 +374,4 @@ | ||
this._error = { | ||
code: 'TYPE_INVALID', | ||
message: 'The value must be an array.' | ||
code: "TYPE_INVALID", | ||
message: "The value must be an array.", | ||
}; | ||
@@ -362,7 +381,12 @@ return false; | ||
if (typeof (rule.min) === 'number') { | ||
if (typeof rule.min === "number") { | ||
if (value.length < rule.min) { | ||
this._error = { | ||
code: 'LENGTH_UNDERFLOW', | ||
message: 'The number of characters in the `' + name + '` must be grater than or equal to ' + rule.min + '.' | ||
code: "LENGTH_UNDERFLOW", | ||
message: | ||
"The number of characters in the `" + | ||
name + | ||
"` must be grater than or equal to " + | ||
rule.min + | ||
".", | ||
}; | ||
@@ -372,7 +396,12 @@ return false; | ||
} | ||
if (typeof (rule.max) === 'number') { | ||
if (typeof rule.max === "number") { | ||
if (value.length > rule.max) { | ||
this._error = { | ||
code: 'LENGTH_OVERFLOW', | ||
message: 'The number of characters in the `' + name + '` must be less than or equal to ' + rule.max + '.' | ||
code: "LENGTH_OVERFLOW", | ||
message: | ||
"The number of characters in the `" + | ||
name + | ||
"` must be less than or equal to " + | ||
rule.max + | ||
".", | ||
}; | ||
@@ -387,26 +416,26 @@ return false; | ||
/* ------------------------------------------------------------------ | ||
* isString(value, rule, name) | ||
* - Check if the value is an `Array` object | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - min | Integer | Optional | Minimum number of characters in the string | ||
* - max | Integer | Optional | Maximum number of characters in the string | ||
* - minBytes | Integer | Optional | Minimum bytes of the string (UTF-8) | ||
* - maxBytes | Integer | Optional | Maximum bytes of the string (UTF-8) | ||
* - pattern | RegExp | Optional | Pattern of the string | ||
* - enum | Array | Optional | list of possible values | ||
* - name | String | Optional | Parameter name | ||
* | ||
* If non-number value is specified to the `min` or `max`, | ||
* they will be ignored. | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isString(value, rule = {}, name = 'value') { | ||
* isString(value, rule, name) | ||
* - Check if the value is an `Array` object | ||
* | ||
* [Arguments] | ||
* - value | Any | Required | The value you want to check | ||
* - rule | Object | Optional | | ||
* - required | Boolean | Optional | Required or not. Default is `false`. | ||
* - min | Integer | Optional | Minimum number of characters in the string | ||
* - max | Integer | Optional | Maximum number of characters in the string | ||
* - minBytes | Integer | Optional | Minimum bytes of the string (UTF-8) | ||
* - maxBytes | Integer | Optional | Maximum bytes of the string (UTF-8) | ||
* - pattern | RegExp | Optional | Pattern of the string | ||
* - enum | Array | Optional | list of possible values | ||
* - name | String | Optional | Parameter name | ||
* | ||
* If non-number value is specified to the `min` or `max`, | ||
* they will be ignored. | ||
* | ||
* [Return value] | ||
* - If the value is valid, this method will return `true`. | ||
* - If the value is invalid, this method will return `false` and | ||
* an `Error` object will be set to `this._error`. | ||
* ---------------------------------------------------------------- */ | ||
isString(value, rule = {}, name = "value") { | ||
this._error = null; | ||
@@ -418,6 +447,6 @@ | ||
if (typeof (value) !== 'string') { | ||
if (typeof value !== "string") { | ||
this._error = { | ||
code: 'TYPE_INVALID', | ||
message: 'The value must be a string.' | ||
code: "TYPE_INVALID", | ||
message: "The value must be a string.", | ||
}; | ||
@@ -427,7 +456,12 @@ return false; | ||
if (typeof (rule.min) === 'number') { | ||
if (typeof rule.min === "number") { | ||
if (value.length < rule.min) { | ||
this._error = { | ||
code: 'LENGTH_UNDERFLOW', | ||
message: 'The number of characters in the `' + name + '` must be grater than or equal to ' + rule.min + '.' | ||
code: "LENGTH_UNDERFLOW", | ||
message: | ||
"The number of characters in the `" + | ||
name + | ||
"` must be grater than or equal to " + | ||
rule.min + | ||
".", | ||
}; | ||
@@ -437,7 +471,12 @@ return false; | ||
} | ||
if (typeof (rule.max) === 'number') { | ||
if (typeof rule.max === "number") { | ||
if (value.length > rule.max) { | ||
this._error = { | ||
code: 'LENGTH_OVERFLOW', | ||
message: 'The number of characters in the `' + name + '` must be less than or equal to ' + rule.max + '.' | ||
code: "LENGTH_OVERFLOW", | ||
message: | ||
"The number of characters in the `" + | ||
name + | ||
"` must be less than or equal to " + | ||
rule.max + | ||
".", | ||
}; | ||
@@ -447,8 +486,15 @@ return false; | ||
} | ||
if (typeof (rule.minBytes) === 'number') { | ||
let blen = Buffer.from(value, 'utf8').length; | ||
if (typeof rule.minBytes === "number") { | ||
let blen = Buffer.from(value, "utf8").length; | ||
if (blen < rule.minBytes) { | ||
this._error = { | ||
code: 'LENGTH_UNDERFLOW', | ||
message: 'The byte length of the `' + name + '` (' + blen + ' bytes) must be grater than or equal to ' + rule.minBytes + ' bytes.' | ||
code: "LENGTH_UNDERFLOW", | ||
message: | ||
"The byte length of the `" + | ||
name + | ||
"` (" + | ||
blen + | ||
" bytes) must be grater than or equal to " + | ||
rule.minBytes + | ||
" bytes.", | ||
}; | ||
@@ -458,8 +504,15 @@ return false; | ||
} | ||
if (typeof (rule.maxBytes) === 'number') { | ||
let blen = Buffer.from(value, 'utf8').length; | ||
if (typeof rule.maxBytes === "number") { | ||
let blen = Buffer.from(value, "utf8").length; | ||
if (blen > rule.maxBytes) { | ||
this._error = { | ||
code: 'LENGTH_OVERFLOW', | ||
message: 'The byte length of the `' + name + '` (' + blen + ' bytes) must be less than or equal to ' + rule.maxBytes + ' bytes.' | ||
code: "LENGTH_OVERFLOW", | ||
message: | ||
"The byte length of the `" + | ||
name + | ||
"` (" + | ||
blen + | ||
" bytes) must be less than or equal to " + | ||
rule.maxBytes + | ||
" bytes.", | ||
}; | ||
@@ -472,4 +525,4 @@ return false; | ||
this._error = { | ||
code: 'PATTERN_UNMATCH', | ||
message: 'The `' + name + '` does not conform with the pattern.' | ||
code: "PATTERN_UNMATCH", | ||
message: "The `" + name + "` does not conform with the pattern.", | ||
}; | ||
@@ -482,4 +535,9 @@ return false; | ||
this._error = { | ||
code: 'ENUM_UNMATCH', | ||
message: 'The `' + name + '` must be any one of ' + JSON.stringify(rule.enum) + '.' | ||
code: "ENUM_UNMATCH", | ||
message: | ||
"The `" + | ||
name + | ||
"` must be any one of " + | ||
JSON.stringify(rule.enum) + | ||
".", | ||
}; | ||
@@ -486,0 +544,0 @@ return false; |
@@ -1,63 +0,63 @@ | ||
'use strict'; | ||
"use strict"; | ||
class SwitchbotAdvertising { | ||
constructor() { } | ||
constructor() {} | ||
/* ------------------------------------------------------------------ | ||
* parse(peripheral) | ||
* - Parse advertising packets coming from switchbot devices | ||
* | ||
* [Arguments] | ||
* - peripheral | Object | Required | A `Peripheral` object of noble | ||
* | ||
* [Return value] | ||
* - An object as follows: | ||
* | ||
* WoHand | ||
* { | ||
* id: 'c12e453e2008', | ||
* address: 'c1:2e:45:3e:20:08', | ||
* rssi: -43, | ||
* serviceData: { | ||
* model: 'H', | ||
* modelName: 'WoHand', | ||
* mode: false, | ||
* state: false, | ||
* battery: 95 | ||
* } | ||
* } | ||
* | ||
* WoSensorTH | ||
* { | ||
* id: 'cb4eb903c96d', | ||
* address: 'cb:4e:b9:03:c9:6d', | ||
* rssi: -54, | ||
* serviceData: { | ||
* model: 'T', | ||
* modelName: 'WoSensorTH', | ||
* temperature: { c: 26.2, f: 79.2 }, | ||
* fahrenheit: false, | ||
* humidity: 45, | ||
* battery: 100 | ||
* } | ||
* } | ||
* | ||
* WoCurtain | ||
* { | ||
* id: 'ec58c5d00111', | ||
* address: 'ec:58:c5:d0:01:11', | ||
* rssi: -39, | ||
* serviceData: { | ||
* model: 'c', | ||
* modelName: 'WoCurtain', | ||
* calibration: true, | ||
* battery: 91, | ||
* position: 1, | ||
* lightLevel: 1 | ||
* } | ||
* } | ||
* | ||
* If the specified `Peripheral` does not represent any switchbot | ||
* device, this method will return `null`. | ||
* ---------------------------------------------------------------- */ | ||
* parse(peripheral) | ||
* - Parse advertising packets coming from switchbot devices | ||
* | ||
* [Arguments] | ||
* - peripheral | Object | Required | A `Peripheral` object of noble | ||
* | ||
* [Return value] | ||
* - An object as follows: | ||
* | ||
* WoHand | ||
* { | ||
* id: 'c12e453e2008', | ||
* address: 'c1:2e:45:3e:20:08', | ||
* rssi: -43, | ||
* serviceData: { | ||
* model: 'H', | ||
* modelName: 'WoHand', | ||
* mode: false, | ||
* state: false, | ||
* battery: 95 | ||
* } | ||
* } | ||
* | ||
* WoSensorTH | ||
* { | ||
* id: 'cb4eb903c96d', | ||
* address: 'cb:4e:b9:03:c9:6d', | ||
* rssi: -54, | ||
* serviceData: { | ||
* model: 'T', | ||
* modelName: 'WoSensorTH', | ||
* temperature: { c: 26.2, f: 79.2 }, | ||
* fahrenheit: false, | ||
* humidity: 45, | ||
* battery: 100 | ||
* } | ||
* } | ||
* | ||
* WoCurtain | ||
* { | ||
* id: 'ec58c5d00111', | ||
* address: 'ec:58:c5:d0:01:11', | ||
* rssi: -39, | ||
* serviceData: { | ||
* model: 'c', | ||
* modelName: 'WoCurtain', | ||
* calibration: true, | ||
* battery: 91, | ||
* position: 1, | ||
* lightLevel: 1 | ||
* } | ||
* } | ||
* | ||
* If the specified `Peripheral` does not represent any switchbot | ||
* device, this method will return `null`. | ||
* ---------------------------------------------------------------- */ | ||
parse(peripheral, onlog) { | ||
@@ -69,35 +69,59 @@ let ad = peripheral.advertisement; | ||
let serviceData = ad.serviceData[0] || ad.serviceData; | ||
let manufacturerData = ad.manufacturerData; | ||
let buf = serviceData.data; | ||
if (!buf || !Buffer.isBuffer(buf) || buf.length < 3) { | ||
const bufIsInvalid = !buf || !Buffer.isBuffer(buf) || buf.length < 3; | ||
const manufacturerDataIsInvalid = | ||
!manufacturerData || | ||
!Buffer.isBuffer(manufacturerData) || | ||
manufacturerData.length < 3; | ||
if (bufIsInvalid && manufacturerDataIsInvalid) { | ||
return null; | ||
} | ||
let model = buf.slice(0, 1).toString('utf8'); | ||
let model = buf.slice(0, 1).toString("utf8"); | ||
let sd = null; | ||
if (model === 'H') { // WoHand | ||
if (model === "H") { | ||
// WoHand | ||
sd = this._parseServiceDataForWoHand(buf, onlog); | ||
} else if (model === 'T') { // WoSensorTH | ||
} else if (model === "T") { | ||
// WoSensorTH | ||
sd = this._parseServiceDataForWoSensorTH(buf, onlog); | ||
} else if (model === 'e') { // WoHumi | ||
} else if (model === "e") { | ||
// WoHumi | ||
sd = this._parseServiceDataForWoHumi(buf, onlog); | ||
} else if (model === 's') { // WoMotion | ||
} else if (model === "s") { | ||
// WoMotion | ||
sd = this._parseServiceDataForWoPresence(buf, onlog); | ||
} else if (model === 'd') { // WoContact | ||
} else if (model === "d") { | ||
// WoContact | ||
sd = this._parseServiceDataForWoContact(buf, onlog); | ||
} else if (model === 'c') { // WoCurtain | ||
} else if (model === "c") { | ||
// WoCurtain | ||
sd = this._parseServiceDataForWoCurtain(buf, onlog); | ||
} else if (model === 'u') { // WoColorBulb | ||
} else if (model === "u") { | ||
// WoColorBulb | ||
sd = this._parseServiceDataForWoColorBulb(buf, onlog); | ||
} else if (model === 'g') { // WoPlugMini | ||
} else if (model === "g") { | ||
// WoPlugMini | ||
sd = this._parseServiceDataForWoPlugMini(buf, onlog); | ||
} else if (model === 'o') { // WoSmartLock | ||
} else if (model === "j") { | ||
// WoPlugMini (JP) ?? | ||
sd = this._parseServiceDataForWoPlugMini(manufacturerData, onlog); | ||
} else if (model === "o") { | ||
// WoSmartLock | ||
sd = this._parseServiceDataForWoSmartLock(buf, onlog); | ||
} else if (model === 'i') { // WoMeterPlus | ||
} else if (model === "i") { | ||
// WoMeterPlus | ||
sd = this._parseServiceDataForWoSensorTHPlus(buf, onlog); | ||
} else if (model === 'r') { // WoLEDStripLight | ||
} else if (model === "r") { | ||
// WoLEDStripLight | ||
sd = this._parseServiceDataForWoLEDStripLight(buf, onlog); | ||
} else { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[parseAdvertising.${peripheral.id}] return null, model "${model}" not available!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[parseAdvertising.${peripheral.id}] return null, model "${model}" not available!` | ||
); | ||
} | ||
@@ -108,12 +132,16 @@ return null; | ||
if (!sd) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[parseAdvertising.${peripheral.id}.${model}] return null, parsed serviceData empty!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[parseAdvertising.${peripheral.id}.${model}] return null, parsed serviceData empty!` | ||
); | ||
} | ||
return null; | ||
} | ||
let address = peripheral.address || ''; | ||
if (address === '') { | ||
address = peripheral.advertisement.manufacturerData || ''; | ||
if (address !== '') { | ||
const str = peripheral.advertisement.manufacturerData.toString('hex').slice(4); | ||
let address = peripheral.address || ""; | ||
if (address === "") { | ||
address = peripheral.advertisement.manufacturerData || ""; | ||
if (address !== "") { | ||
const str = peripheral.advertisement.manufacturerData | ||
.toString("hex") | ||
.slice(4, 16); | ||
address = str.substr(0, 2); | ||
@@ -125,6 +153,5 @@ for (var i = 2; i < str.length; i += 2) { | ||
} | ||
} else { | ||
address = address.replace(/-/g, ":"); | ||
} | ||
else { | ||
address = address.replace(/-/g, ':'); | ||
} | ||
let data = { | ||
@@ -134,7 +161,11 @@ id: peripheral.id, | ||
rssi: peripheral.rssi, | ||
serviceData: sd | ||
serviceData: sd, | ||
}; | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[parseAdvertising.${peripheral.id}.${model}] return ${JSON.stringify(data)}`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[parseAdvertising.${peripheral.id}.${model}] return ${JSON.stringify( | ||
data | ||
)}` | ||
); | ||
} | ||
@@ -146,4 +177,6 @@ return data; | ||
if (buf.length !== 3) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoHand] Buffer length ${buf.length} !== 3!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoHand] Buffer length ${buf.length} !== 3!` | ||
); | ||
} | ||
@@ -155,12 +188,12 @@ return null; | ||
let mode = (byte1 & 0b10000000) ? true : false; // Whether the light switch Add-on is used or not | ||
let state = (byte1 & 0b01000000) ? true : false; // Whether the switch status is ON or OFF | ||
let mode = byte1 & 0b10000000 ? true : false; // Whether the light switch Add-on is used or not | ||
let state = byte1 & 0b01000000 ? true : false; // Whether the switch status is ON or OFF | ||
let battery = byte2 & 0b01111111; // % | ||
let data = { | ||
model: 'H', | ||
modelName: 'WoHand', | ||
model: "H", | ||
modelName: "WoHand", | ||
mode: mode, | ||
state: state, | ||
battery: battery | ||
battery: battery, | ||
}; | ||
@@ -173,4 +206,6 @@ | ||
if (buf.length !== 6) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoSensorTH] Buffer length ${buf.length} !== 6!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoSensorTH] Buffer length ${buf.length} !== 6!` | ||
); | ||
} | ||
@@ -184,17 +219,17 @@ return null; | ||
let temp_sign = (byte4 & 0b10000000) ? 1 : -1; | ||
let temp_c = temp_sign * ((byte4 & 0b01111111) + (byte3 / 10)); | ||
let temp_f = (temp_c * 9 / 5) + 32; | ||
let temp_sign = byte4 & 0b10000000 ? 1 : -1; | ||
let temp_c = temp_sign * ((byte4 & 0b01111111) + byte3 / 10); | ||
let temp_f = (temp_c * 9) / 5 + 32; | ||
temp_f = Math.round(temp_f * 10) / 10; | ||
let data = { | ||
model: 'T', | ||
modelName: 'WoSensorTH', | ||
model: "T", | ||
modelName: "WoSensorTH", | ||
temperature: { | ||
c: temp_c, | ||
f: temp_f | ||
f: temp_f, | ||
}, | ||
fahrenheit: (byte5 & 0b10000000) ? true : false, | ||
fahrenheit: byte5 & 0b10000000 ? true : false, | ||
humidity: byte5 & 0b01111111, | ||
battery: (byte2 & 0b01111111) | ||
battery: byte2 & 0b01111111, | ||
}; | ||
@@ -207,4 +242,6 @@ | ||
if (buf.length !== 8) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoHumi] Buffer length ${buf.length} !== 8!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoHumi] Buffer length ${buf.length} !== 8!` | ||
); | ||
} | ||
@@ -219,9 +256,9 @@ return null; | ||
let onState = (byte1 & 0b10000000) ? true : false; // 1 - on | ||
let autoMode = (byte4 & 0b10000000) ? true : false; // 1 - auto | ||
let onState = byte1 & 0b10000000 ? true : false; // 1 - on | ||
let autoMode = byte4 & 0b10000000 ? true : false; // 1 - auto | ||
let percentage = byte4 & 0b01111111; // 0-100%, 101/102/103 - Quick gear 1/2/3 | ||
let data = { | ||
model: 'e', | ||
modelName: 'WoHumi', | ||
model: "e", | ||
modelName: "WoHumi", | ||
onState: onState, | ||
@@ -237,4 +274,6 @@ autoMode: autoMode, | ||
if (buf.length !== 6) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoPresence] Buffer length ${buf.length} !== 6!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoPresence] Buffer length ${buf.length} !== 6!` | ||
); | ||
} | ||
@@ -249,3 +288,3 @@ return null; | ||
let pirState = (byte1 & 0b01000000) ? true : false; // 1 - Movement detected | ||
let pirState = byte1 & 0b01000000 ? true : false; // 1 - Movement detected | ||
let battery = byte2 & 0b01111111; // % | ||
@@ -255,7 +294,8 @@ let lightLevel = byte5 & 0b00000011; | ||
let data = { | ||
model: 's', | ||
modelName: 'WoMotion', | ||
model: "s", | ||
modelName: "WoMotion", | ||
movement: pirState, | ||
battery: battery, | ||
lightLevel: (lightLevel == 1) ? 'dark' : ((lightLevel == 2) ? 'bright' : 'unknown'), | ||
lightLevel: | ||
lightLevel == 1 ? "dark" : lightLevel == 2 ? "bright" : "unknown", | ||
}; | ||
@@ -268,4 +308,6 @@ | ||
if (buf.length !== 9) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoContact] Buffer length ${buf.length} !== 9!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoContact] Buffer length ${buf.length} !== 9!` | ||
); | ||
} | ||
@@ -284,3 +326,3 @@ return null; | ||
let pirState = (byte1 & 0b01000000) ? true : false; // 1 - Movement detected | ||
let pirState = byte1 & 0b01000000 ? true : false; // 1 - Movement detected | ||
let battery = byte2 & 0b01111111; // % | ||
@@ -291,8 +333,13 @@ let hallState = (byte3 >> 1) & 0b00000011; | ||
let data = { | ||
model: 'd', | ||
modelName: 'WoContact', | ||
model: "d", | ||
modelName: "WoContact", | ||
movement: pirState, | ||
battery: battery, | ||
doorState: (hallState == 0) ? 'close' : ((hallState == 1) ? 'open' : 'timeout no closed'), | ||
lightLevel: (lightLevel == 0) ? 'dark' : 'bright', | ||
doorState: | ||
hallState == 0 | ||
? "close" | ||
: hallState == 1 | ||
? "open" | ||
: "timeout no closed", | ||
lightLevel: lightLevel == 0 ? "dark" : "bright", | ||
}; | ||
@@ -305,4 +352,6 @@ | ||
if (buf.length !== 5 && buf.length !== 6) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoCurtain] Buffer length ${buf.length} !== 5 or 6!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoCurtain] Buffer length ${buf.length} !== 5 or 6!` | ||
); | ||
} | ||
@@ -317,3 +366,3 @@ return null; | ||
let calibration = (byte1 & 0b01000000) ? true : false; // Whether the calibration is completed | ||
let calibration = byte1 & 0b01000000 ? true : false; // Whether the calibration is completed | ||
let battery = byte2 & 0b01111111; // % | ||
@@ -324,8 +373,8 @@ let currPosition = byte3 & 0b01111111; // current positon % | ||
let data = { | ||
model: 'c', | ||
modelName: 'WoCurtain', | ||
model: "c", | ||
modelName: "WoCurtain", | ||
calibration: calibration, | ||
battery: battery, | ||
position: currPosition, | ||
lightLevel: lightLevel | ||
lightLevel: lightLevel, | ||
}; | ||
@@ -338,4 +387,6 @@ | ||
if (buf.length !== 6) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoColorBulb] Buffer length ${buf.length} !== 6!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoColorBulb] Buffer length ${buf.length} !== 6!` | ||
); | ||
} | ||
@@ -351,4 +402,4 @@ return null; | ||
let data = { | ||
model: 'u', | ||
modelName: 'WoColorBulb', | ||
model: "u", | ||
modelName: "WoColorBulb", | ||
}; | ||
@@ -360,17 +411,35 @@ | ||
_parseServiceDataForWoPlugMini(buf, onlog) { | ||
if (buf.length !== 6) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoPlugMini] Buffer length ${buf.length} !== 6!`); | ||
if (buf.length !== 14) { | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoPlugMini] Buffer length ${buf.length} should be 14` | ||
); | ||
} | ||
return null; | ||
} | ||
let byte1 = buf.readUInt8(1); | ||
let byte2 = buf.readUInt8(2); | ||
// let byte3 = buf.readUInt8(3); | ||
// let byte4 = buf.readUInt8(4); | ||
let byte5 = buf.readUInt8(5); | ||
const byte9 = buf.readUInt8(9); // byte9: plug mini state; 0x00=off, 0x80=on | ||
const byte10 = buf.readUInt8(10); // byte10: bit0: 0=no delay,1=delay, bit1:0=no timer, 1=timer; bit2:0=no sync time, 1=sync'ed time | ||
const byte11 = buf.readUInt8(11); // byte11: wifi rssi | ||
const byte12 = buf.readUInt8(12); // byte12: bit7: overload? | ||
const byte13 = buf.readUInt8(13); // byte12[bit0~6] + byte13: current power value | ||
let data = { | ||
model: 'g', | ||
modelName: 'WoPlugMini', | ||
const state = byte9 === 0x00 ? "off" : byte9 === 0x80 ? "on" : null; | ||
const delay = !!(byte10 & 0b00000001); | ||
const timer = !!(byte10 & 0b00000010); | ||
const syncUtcTime = !!(byte10 & 0b00000100); | ||
const wifiRssi = byte11; | ||
const overload = !!(byte12 & 0b10000000); | ||
const currentPower = (((byte12 & 0b01111111) << 8) + byte13) / 10; // in watt | ||
// TODO: voltage ??? | ||
const data = { | ||
model: "j", | ||
modelName: "WoPlugMini", | ||
state: state, | ||
delay: delay, | ||
timer: timer, | ||
syncUtcTime: syncUtcTime, | ||
wifiRssi: wifiRssi, | ||
overload: overload, | ||
currentPower: currentPower, | ||
}; | ||
@@ -383,4 +452,6 @@ | ||
if (buf.length !== 6) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoSmartLock] Buffer length ${buf.length} !== 6!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoSmartLock] Buffer length ${buf.length} !== 6!` | ||
); | ||
} | ||
@@ -400,4 +471,4 @@ return null; | ||
let data = { | ||
model: 'o', | ||
modelName: 'WoSmartLock', | ||
model: "o", | ||
modelName: "WoSmartLock", | ||
//movement: pirState, | ||
@@ -413,4 +484,6 @@ battery: battery, | ||
if (buf.length !== 6) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoSensorTHPlus] Buffer length ${buf.length} !== 6!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoSensorTHPlus] Buffer length ${buf.length} !== 6!` | ||
); | ||
} | ||
@@ -424,17 +497,17 @@ return null; | ||
let temp_sign = (byte4 & 0b10000000) ? 1 : -1; | ||
let temp_c = temp_sign * ((byte4 & 0b01111111) + (byte3 / 10)); | ||
let temp_f = (temp_c * 9 / 5) + 32; | ||
let temp_sign = byte4 & 0b10000000 ? 1 : -1; | ||
let temp_c = temp_sign * ((byte4 & 0b01111111) + byte3 / 10); | ||
let temp_f = (temp_c * 9) / 5 + 32; | ||
temp_f = Math.round(temp_f * 10) / 10; | ||
let data = { | ||
model: 'i', | ||
modelName: 'WoSensorTHPlus', | ||
model: "i", | ||
modelName: "WoSensorTHPlus", | ||
temperature: { | ||
c: temp_c, | ||
f: temp_f | ||
f: temp_f, | ||
}, | ||
fahrenheit: (byte5 & 0b10000000) ? true : false, | ||
fahrenheit: byte5 & 0b10000000 ? true : false, | ||
humidity: byte5 & 0b01111111, | ||
battery: (byte2 & 0b01111111) | ||
battery: byte2 & 0b01111111, | ||
}; | ||
@@ -447,4 +520,6 @@ | ||
if (buf.length !== 6) { | ||
if (onlog && typeof onlog === 'function') { | ||
onlog(`[_parseServiceDataForWoLEDStripLight] Buffer length ${buf.length} !== 6!`); | ||
if (onlog && typeof onlog === "function") { | ||
onlog( | ||
`[_parseServiceDataForWoLEDStripLight] Buffer length ${buf.length} !== 6!` | ||
); | ||
} | ||
@@ -460,4 +535,4 @@ return null; | ||
let data = { | ||
model: 'r', | ||
modelName: 'WoLEDStripLight', | ||
model: "r", | ||
modelName: "WoLEDStripLight", | ||
}; | ||
@@ -464,0 +539,0 @@ |
@@ -1,9 +0,6 @@ | ||
'use strict'; | ||
const SwitchbotDevice = require('./switchbot-device.js'); | ||
"use strict"; | ||
const SwitchbotDevice = require("./switchbot-device.js"); | ||
class SwitchbotDeviceWoContact extends SwitchbotDevice { | ||
class SwitchbotDeviceWoContact extends SwitchbotDevice {} | ||
} | ||
module.exports = SwitchbotDeviceWoContact; | ||
module.exports = SwitchbotDeviceWoContact; |
@@ -1,102 +0,117 @@ | ||
'use strict'; | ||
const SwitchbotDevice = require('./switchbot-device.js'); | ||
"use strict"; | ||
const SwitchbotDevice = require("./switchbot-device.js"); | ||
class SwitchbotDeviceWoCurtain extends SwitchbotDevice { | ||
/* ------------------------------------------------------------------ | ||
* open() | ||
* - Open the curtain | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
open() { | ||
return this._operateCurtain([0x57, 0x0f, 0x45, 0x01, 0x05, 0xff, 0x00]); | ||
} | ||
/* ------------------------------------------------------------------ | ||
* open() | ||
* - Open the curtain | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
open() { | ||
return this._operateCurtain([0x57, 0x0f, 0x45, 0x01, 0x05, 0xff, 0x00]); | ||
} | ||
/* ------------------------------------------------------------------ | ||
* close() | ||
* - close the curtain | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
close() { | ||
return this._operateCurtain([0x57, 0x0f, 0x45, 0x01, 0x05, 0xff, 0x64]); | ||
} | ||
/* ------------------------------------------------------------------ | ||
* close() | ||
* - close the curtain | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
close() { | ||
return this._operateCurtain([0x57, 0x0f, 0x45, 0x01, 0x05, 0xff, 0x64]); | ||
} | ||
/* ------------------------------------------------------------------ | ||
* pause() | ||
* - pause the curtain | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
pause() { | ||
return this._operateCurtain([0x57, 0x0f, 0x45, 0x01, 0x00, 0xff]); | ||
} | ||
/* ------------------------------------------------------------------ | ||
* pause() | ||
* - pause the curtain | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
pause() { | ||
return this._operateCurtain([0x57, 0x0f, 0x45, 0x01, 0x00, 0xff]); | ||
/* ------------------------------------------------------------------ | ||
* runToPos() | ||
* - run to the targe position | ||
* | ||
* [Arguments] | ||
* - percent | number | Required | the percentage of target position | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
runToPos(percent, mode) { | ||
if (typeof percent != "number") { | ||
return new Promise((resolve, reject) => { | ||
reject( | ||
new Error( | ||
"The type of target position percentage is incorrent: " + | ||
typeof percent | ||
) | ||
); | ||
}); | ||
} | ||
/* ------------------------------------------------------------------ | ||
* runToPos() | ||
* - run to the targe position | ||
* | ||
* [Arguments] | ||
* - percent | number | Required | the percentage of target position | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
runToPos(percent, mode) { | ||
if (typeof percent != 'number') { | ||
return new Promise((resolve, reject) => { | ||
reject(new Error('The type of target position percentage is incorrent: ' + typeof percent)); | ||
}); | ||
} | ||
if (mode == null) { | ||
mode = 0xff; | ||
} | ||
else { | ||
if (typeof mode != 'number') { | ||
return new Promise((resolve, reject) => { | ||
reject(new Error('The type of running mode is incorrent: ' + typeof mode)); | ||
}); | ||
} | ||
if (mode > 1) { mode = 0xff; } | ||
} | ||
if (percent > 100) { percent = 100; } | ||
else if (percent < 0) { percent = 0; } | ||
return this._operateCurtain([0x57, 0x0f, 0x45, 0x01, 0x05, mode, percent]); | ||
if (mode == null) { | ||
mode = 0xff; | ||
} else { | ||
if (typeof mode != "number") { | ||
return new Promise((resolve, reject) => { | ||
reject( | ||
new Error("The type of running mode is incorrent: " + typeof mode) | ||
); | ||
}); | ||
} | ||
if (mode > 1) { | ||
mode = 0xff; | ||
} | ||
} | ||
if (percent > 100) { | ||
percent = 100; | ||
} else if (percent < 0) { | ||
percent = 0; | ||
} | ||
return this._operateCurtain([0x57, 0x0f, 0x45, 0x01, 0x05, mode, percent]); | ||
} | ||
_operateCurtain(bytes) { | ||
return new Promise((resolve, reject) => { | ||
let req_buf = Buffer.from(bytes); | ||
this._command(req_buf).then((res_buf) => { | ||
let code = res_buf.readUInt8(0); | ||
if (res_buf.length === 3 && code === 0x01) { | ||
resolve(); | ||
} else { | ||
reject(new Error('The device returned an error: 0x' + res_buf.toString('hex'))); | ||
} | ||
}).catch((error) => { | ||
reject(error); | ||
}); | ||
_operateCurtain(bytes) { | ||
return new Promise((resolve, reject) => { | ||
let req_buf = Buffer.from(bytes); | ||
this._command(req_buf) | ||
.then((res_buf) => { | ||
let code = res_buf.readUInt8(0); | ||
if (res_buf.length === 3 && code === 0x01) { | ||
resolve(); | ||
} else { | ||
reject( | ||
new Error( | ||
"The device returned an error: 0x" + res_buf.toString("hex") | ||
) | ||
); | ||
} | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
} | ||
}); | ||
} | ||
} | ||
module.exports = SwitchbotDeviceWoCurtain; | ||
module.exports = SwitchbotDeviceWoCurtain; |
@@ -1,17 +0,16 @@ | ||
'use strict'; | ||
const SwitchbotDevice = require('./switchbot-device.js'); | ||
"use strict"; | ||
const SwitchbotDevice = require("./switchbot-device.js"); | ||
class SwitchbotDeviceWoHand extends SwitchbotDevice { | ||
/* ------------------------------------------------------------------ | ||
* press() | ||
* - Press | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* press() | ||
* - Press | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
press() { | ||
@@ -22,12 +21,12 @@ return this._operateBot([0x57, 0x01, 0x00]); | ||
/* ------------------------------------------------------------------ | ||
* turnOn() | ||
* - Turn on | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* turnOn() | ||
* - Turn on | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
turnOn() { | ||
@@ -38,12 +37,12 @@ return this._operateBot([0x57, 0x01, 0x01]); | ||
/* ------------------------------------------------------------------ | ||
* turnOff() | ||
* - Turn off | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* turnOff() | ||
* - Turn off | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
turnOff() { | ||
@@ -54,12 +53,12 @@ return this._operateBot([0x57, 0x01, 0x02]); | ||
/* ------------------------------------------------------------------ | ||
* down() | ||
* - Down | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* down() | ||
* - Down | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
down() { | ||
@@ -70,12 +69,12 @@ return this._operateBot([0x57, 0x01, 0x03]); | ||
/* ------------------------------------------------------------------ | ||
* up() | ||
* - Up | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* up() | ||
* - Up | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
up() { | ||
@@ -88,17 +87,22 @@ return this._operateBot([0x57, 0x01, 0x04]); | ||
let req_buf = Buffer.from(bytes); | ||
this._command(req_buf).then((res_buf) => { | ||
let code = res_buf.readUInt8(0); | ||
if (res_buf.length === 3 && (code === 0x01 || code === 0x05)) { | ||
resolve(); | ||
} else { | ||
reject(new Error('The device returned an error: 0x' + res_buf.toString('hex'))); | ||
} | ||
}).catch((error) => { | ||
reject(error); | ||
}); | ||
this._command(req_buf) | ||
.then((res_buf) => { | ||
let code = res_buf.readUInt8(0); | ||
if (res_buf.length === 3 && (code === 0x01 || code === 0x05)) { | ||
resolve(); | ||
} else { | ||
reject( | ||
new Error( | ||
"The device returned an error: 0x" + res_buf.toString("hex") | ||
) | ||
); | ||
} | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
}); | ||
} | ||
} | ||
module.exports = SwitchbotDeviceWoHand; | ||
module.exports = SwitchbotDeviceWoHand; |
@@ -1,17 +0,16 @@ | ||
'use strict'; | ||
const SwitchbotDevice = require('./switchbot-device.js'); | ||
"use strict"; | ||
const SwitchbotDevice = require("./switchbot-device.js"); | ||
class SwitchbotDeviceWoHumi extends SwitchbotDevice { | ||
/* ------------------------------------------------------------------ | ||
* press() | ||
* - Press | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* press() | ||
* - Press | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
press() { | ||
@@ -22,12 +21,12 @@ return this._operateBot([0x57, 0x01, 0x00]); | ||
/* ------------------------------------------------------------------ | ||
* turnOn() | ||
* - Turn on | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* turnOn() | ||
* - Turn on | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
turnOn() { | ||
@@ -38,12 +37,12 @@ return this._operateBot([0x57, 0x01, 0x01]); | ||
/* ------------------------------------------------------------------ | ||
* turnOff() | ||
* - Turn off | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* turnOff() | ||
* - Turn off | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
turnOff() { | ||
@@ -54,12 +53,12 @@ return this._operateBot([0x57, 0x01, 0x02]); | ||
/* ------------------------------------------------------------------ | ||
* down() | ||
* - Down | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* down() | ||
* - Down | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
down() { | ||
@@ -70,12 +69,12 @@ return this._operateBot([0x57, 0x01, 0x03]); | ||
/* ------------------------------------------------------------------ | ||
* up() | ||
* - Up | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* up() | ||
* - Up | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
up() { | ||
@@ -88,17 +87,22 @@ return this._operateBot([0x57, 0x01, 0x04]); | ||
let req_buf = Buffer.from(bytes); | ||
this._command(req_buf).then((res_buf) => { | ||
let code = res_buf.readUInt8(0); | ||
if (res_buf.length === 3 && (code === 0x01 || code === 0x05)) { | ||
resolve(); | ||
} else { | ||
reject(new Error('The device returned an error: 0x' + res_buf.toString('hex'))); | ||
} | ||
}).catch((error) => { | ||
reject(error); | ||
}); | ||
this._command(req_buf) | ||
.then((res_buf) => { | ||
let code = res_buf.readUInt8(0); | ||
if (res_buf.length === 3 && (code === 0x01 || code === 0x05)) { | ||
resolve(); | ||
} else { | ||
reject( | ||
new Error( | ||
"The device returned an error: 0x" + res_buf.toString("hex") | ||
) | ||
); | ||
} | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
}); | ||
} | ||
} | ||
module.exports = SwitchbotDeviceWoHumi; | ||
module.exports = SwitchbotDeviceWoHumi; |
@@ -1,9 +0,6 @@ | ||
'use strict'; | ||
const SwitchbotDevice = require('./switchbot-device.js'); | ||
"use strict"; | ||
const SwitchbotDevice = require("./switchbot-device.js"); | ||
class SwitchbotDeviceWoPresence extends SwitchbotDevice { | ||
class SwitchbotDeviceWoPresence extends SwitchbotDevice {} | ||
} | ||
module.exports = SwitchbotDeviceWoPresence; | ||
module.exports = SwitchbotDeviceWoPresence; |
@@ -1,9 +0,6 @@ | ||
'use strict'; | ||
const SwitchbotDevice = require('./switchbot-device.js'); | ||
"use strict"; | ||
const SwitchbotDevice = require("./switchbot-device.js"); | ||
class SwitchbotDeviceWoSensorTH extends SwitchbotDevice { | ||
class SwitchbotDeviceWoSensorTH extends SwitchbotDevice {} | ||
} | ||
module.exports = SwitchbotDeviceWoSensorTH; | ||
module.exports = SwitchbotDeviceWoSensorTH; |
@@ -1,14 +0,14 @@ | ||
'use strict'; | ||
const parameterChecker = require('./parameter-checker.js'); | ||
const switchbotAdvertising = require('./switchbot-advertising.js'); | ||
"use strict"; | ||
const parameterChecker = require("./parameter-checker.js"); | ||
const switchbotAdvertising = require("./switchbot-advertising.js"); | ||
class SwitchbotDevice { | ||
/* ------------------------------------------------------------------ | ||
* Constructor | ||
* | ||
* [Arguments] | ||
* - peripheral | Object | Required | The `peripheral` object of noble, | ||
* | | | which represents this device | ||
* - noble | Noble | Required | The Nobel object created by the noble module. | ||
* ---------------------------------------------------------------- */ | ||
* Constructor | ||
* | ||
* [Arguments] | ||
* - peripheral | Object | Required | The `peripheral` object of noble, | ||
* | | | which represents this device | ||
* - noble | Noble | Required | The Nobel object created by the noble module. | ||
* ---------------------------------------------------------------- */ | ||
constructor(peripheral, noble) { | ||
@@ -19,6 +19,6 @@ this._peripheral = peripheral; | ||
this._SERV_UUID_PRIMARY = 'cba20d00224d11e69fb80002a5d5c51b'; | ||
this._CHAR_UUID_WRITE = 'cba20002224d11e69fb80002a5d5c51b'; | ||
this._CHAR_UUID_NOTIFY = 'cba20003224d11e69fb80002a5d5c51b'; | ||
this._CHAR_UUID_DEVICE = '2a00'; | ||
this._SERV_UUID_PRIMARY = "cba20d00224d11e69fb80002a5d5c51b"; | ||
this._CHAR_UUID_WRITE = "cba20002224d11e69fb80002a5d5c51b"; | ||
this._CHAR_UUID_NOTIFY = "cba20003224d11e69fb80002a5d5c51b"; | ||
this._CHAR_UUID_DEVICE = "2a00"; | ||
@@ -39,6 +39,6 @@ this._READ_TIMEOUT_MSEC = 3000; | ||
this._onconnect = () => { }; | ||
this._ondisconnect = () => { }; | ||
this._ondisconnect_internal = () => { }; | ||
this._onnotify_internal = () => { }; | ||
this._onconnect = () => {}; | ||
this._ondisconnect = () => {}; | ||
this._ondisconnect_internal = () => {}; | ||
this._onnotify_internal = () => {}; | ||
} | ||
@@ -60,4 +60,4 @@ | ||
get connectionState() { | ||
if (!this._connected && this._peripheral.state === 'disconnecting') { | ||
return 'disconnected'; | ||
if (!this._connected && this._peripheral.state === "disconnecting") { | ||
return "disconnected"; | ||
} else { | ||
@@ -70,4 +70,4 @@ return this._peripheral.state; | ||
set onconnect(func) { | ||
if (!func || typeof (func) !== 'function') { | ||
throw new Error('The `onconnect` must be a function.'); | ||
if (!func || typeof func !== "function") { | ||
throw new Error("The `onconnect` must be a function."); | ||
} | ||
@@ -77,4 +77,4 @@ this._onconnect = func; | ||
set ondisconnect(func) { | ||
if (!func || typeof (func) !== 'function') { | ||
throw new Error('The `ondisconnect` must be a function.'); | ||
if (!func || typeof func !== "function") { | ||
throw new Error("The `ondisconnect` must be a function."); | ||
} | ||
@@ -85,12 +85,12 @@ this._ondisconnect = func; | ||
/* ------------------------------------------------------------------ | ||
* connect() | ||
* - Connect the device | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* connect() | ||
* - Connect the device | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
connect() { | ||
@@ -104,4 +104,8 @@ this._was_connected_explicitly = true; | ||
// Check the bluetooth state | ||
if (this._noble.state !== 'poweredOn') { | ||
reject(new Error('The Bluetooth status is ' + this._noble.state + ', not poweredOn.')); | ||
if (this._noble.state !== "poweredOn") { | ||
reject( | ||
new Error( | ||
"The Bluetooth status is " + this._noble.state + ", not poweredOn." | ||
) | ||
); | ||
return; | ||
@@ -112,7 +116,9 @@ } | ||
let state = this.connectionState; | ||
if (state === 'connected') { | ||
if (state === "connected") { | ||
resolve(); | ||
return; | ||
} else if (state === 'connecting' || state === 'disconnecting') { | ||
reject(new Error('Now ' + state + '. Wait for a few seconds then try again.')); | ||
} else if (state === "connecting" || state === "disconnecting") { | ||
reject( | ||
new Error("Now " + state + ". Wait for a few seconds then try again.") | ||
); | ||
return; | ||
@@ -122,3 +128,3 @@ } | ||
// Set event handlers for events fired on the `Peripheral` object | ||
this._peripheral.once('connect', () => { | ||
this._peripheral.once("connect", () => { | ||
this._connected = true; | ||
@@ -128,3 +134,3 @@ this._onconnect(); | ||
this._peripheral.once('disconnect', () => { | ||
this._peripheral.once("disconnect", () => { | ||
this._connected = false; | ||
@@ -143,11 +149,14 @@ this._chars = null; | ||
} | ||
this._getCharacteristics().then((chars) => { | ||
this._chars = chars; | ||
return this._subscribe(); | ||
}).then(() => { | ||
resolve(); | ||
}).catch((error) => { | ||
this._peripheral.disconnect(); | ||
reject(error); | ||
}); | ||
this._getCharacteristics() | ||
.then((chars) => { | ||
this._chars = chars; | ||
return this._subscribe(); | ||
}) | ||
.then(() => { | ||
resolve(); | ||
}) | ||
.catch((error) => { | ||
this._peripheral.disconnect(); | ||
reject(error); | ||
}); | ||
}); | ||
@@ -161,5 +170,7 @@ }); | ||
let timer = setTimeout(() => { | ||
this._ondisconnect_internal = () => { }; | ||
this._ondisconnect_internal = () => {}; | ||
timer = null; | ||
reject(new Error('Failed to discover services and characteristics: TIMEOUT')); | ||
reject( | ||
new Error("Failed to discover services and characteristics: TIMEOUT") | ||
); | ||
}, 5000); | ||
@@ -172,5 +183,9 @@ | ||
timer = null; | ||
this._ondisconnect_internal = () => { }; | ||
this._ondisconnect_internal = () => {}; | ||
} | ||
reject(new Error('Failed to discover services and characteristics: DISCONNECTED')); | ||
reject( | ||
new Error( | ||
"Failed to discover services and characteristics: DISCONNECTED" | ||
) | ||
); | ||
}; | ||
@@ -182,3 +197,3 @@ | ||
if (!timer) { | ||
throw new Error(''); | ||
throw new Error(""); | ||
} | ||
@@ -189,3 +204,3 @@ | ||
notify: null, | ||
device: null | ||
device: null, | ||
}; | ||
@@ -210,5 +225,4 @@ | ||
} else { | ||
reject(new Error('No characteristic was found.')); | ||
reject(new Error("No characteristic was found.")); | ||
} | ||
})().catch((error) => { | ||
@@ -218,3 +232,3 @@ if (timer) { | ||
timer = null; | ||
this._ondisconnect_internal = () => { }; | ||
this._ondisconnect_internal = () => {}; | ||
reject(error); | ||
@@ -246,3 +260,3 @@ } else { | ||
} else { | ||
reject(new Error('No service was found.')); | ||
reject(new Error("No service was found.")); | ||
} | ||
@@ -269,3 +283,3 @@ }); | ||
if (!char) { | ||
reject(new Error('No notify characteristic was found.')); | ||
reject(new Error("No notify characteristic was found.")); | ||
return; | ||
@@ -278,7 +292,7 @@ } | ||
} | ||
char.on('data', (buf) => { | ||
char.on("data", (buf) => { | ||
this._onnotify_internal(buf); | ||
}); | ||
resolve(); | ||
}) | ||
}); | ||
}); | ||
@@ -302,12 +316,12 @@ } | ||
/* ------------------------------------------------------------------ | ||
* disconnect() | ||
* - Disconnect the device | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* disconnect() | ||
* - Disconnect the device | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
disconnect() { | ||
@@ -318,7 +332,9 @@ return new Promise((resolve, reject) => { | ||
let state = this._peripheral.state; | ||
if (state === 'disconnected') { | ||
if (state === "disconnected") { | ||
resolve(); | ||
return; | ||
} else if (state === 'connecting' || state === 'disconnecting') { | ||
reject(new Error('Now ' + state + '. Wait for a few seconds then try again.')); | ||
} else if (state === "connecting" || state === "disconnecting") { | ||
reject( | ||
new Error("Now " + state + ". Wait for a few seconds then try again.") | ||
); | ||
return; | ||
@@ -348,29 +364,37 @@ } | ||
/* ------------------------------------------------------------------ | ||
* getDeviceName() | ||
* - Retrieve the device name | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* The device name will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* getDeviceName() | ||
* - Retrieve the device name | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* The device name will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
getDeviceName() { | ||
return new Promise((resolve, reject) => { | ||
let name = ''; | ||
this._connect().then(() => { | ||
if (!this._chars.device) { | ||
// Some models of Bot don't seem to support this characteristic UUID | ||
throw new Error('The device does not support the characteristic UUID 0x' + this._CHAR_UUID_DEVICE + '.'); | ||
} | ||
return this._read(this._chars.device); | ||
}).then((buf) => { | ||
name = buf.toString('utf8'); | ||
return this._disconnect(); | ||
}).then(() => { | ||
resolve(name); | ||
}).catch((error) => { | ||
reject(error); | ||
}); | ||
let name = ""; | ||
this._connect() | ||
.then(() => { | ||
if (!this._chars.device) { | ||
// Some models of Bot don't seem to support this characteristic UUID | ||
throw new Error( | ||
"The device does not support the characteristic UUID 0x" + | ||
this._CHAR_UUID_DEVICE + | ||
"." | ||
); | ||
} | ||
return this._read(this._chars.device); | ||
}) | ||
.then((buf) => { | ||
name = buf.toString("utf8"); | ||
return this._disconnect(); | ||
}) | ||
.then(() => { | ||
resolve(name); | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
}); | ||
@@ -380,19 +404,22 @@ } | ||
/* ------------------------------------------------------------------ | ||
* setDeviceName(name) | ||
* - Set the device name | ||
* | ||
* [Arguments] | ||
* - name | String | Required | Device name. The bytes length of the name | ||
* | | | must be in the range of 1 to 20 bytes. | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* setDeviceName(name) | ||
* - Set the device name | ||
* | ||
* [Arguments] | ||
* - name | String | Required | Device name. The bytes length of the name | ||
* | | | must be in the range of 1 to 20 bytes. | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
setDeviceName(name) { | ||
return new Promise((resolve, reject) => { | ||
// Check the parameters | ||
let valid = parameterChecker.check({ name: name }, { | ||
name: { required: true, type: 'string', minBytes: 1, maxBytes: 100 } | ||
}); | ||
let valid = parameterChecker.check( | ||
{ name: name }, | ||
{ | ||
name: { required: true, type: "string", minBytes: 1, maxBytes: 100 }, | ||
} | ||
); | ||
@@ -404,16 +431,24 @@ if (!valid) { | ||
let buf = Buffer.from(name, 'utf8'); | ||
this._connect().then(() => { | ||
if (!this._chars.device) { | ||
// Some models of Bot don't seem to support this characteristic UUID | ||
throw new Error('The device does not support the characteristic UUID 0x' + this._CHAR_UUID_DEVICE + '.'); | ||
} | ||
return this._write(this._chars.device, buf); | ||
}).then(() => { | ||
return this._disconnect(); | ||
}).then(() => { | ||
resolve(); | ||
}).catch((error) => { | ||
reject(error); | ||
}); | ||
let buf = Buffer.from(name, "utf8"); | ||
this._connect() | ||
.then(() => { | ||
if (!this._chars.device) { | ||
// Some models of Bot don't seem to support this characteristic UUID | ||
throw new Error( | ||
"The device does not support the characteristic UUID 0x" + | ||
this._CHAR_UUID_DEVICE + | ||
"." | ||
); | ||
} | ||
return this._write(this._chars.device, buf); | ||
}) | ||
.then(() => { | ||
return this._disconnect(); | ||
}) | ||
.then(() => { | ||
resolve(); | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
}); | ||
@@ -428,3 +463,3 @@ } | ||
if (!Buffer.isBuffer(req_buf)) { | ||
reject(new Error('The specified data is not acceptable for writing.')); | ||
reject(new Error("The specified data is not acceptable for writing.")); | ||
return; | ||
@@ -435,14 +470,19 @@ } | ||
this._connect().then(() => { | ||
return this._write(this._chars.write, req_buf); | ||
}).then(() => { | ||
return this._waitCommandResponse(); | ||
}).then((buf) => { | ||
res_buf = buf; | ||
return this._disconnect(); | ||
}).then(() => { | ||
resolve(res_buf); | ||
}).catch((error) => { | ||
reject(error); | ||
}); | ||
this._connect() | ||
.then(() => { | ||
return this._write(this._chars.write, req_buf); | ||
}) | ||
.then(() => { | ||
return this._waitCommandResponse(); | ||
}) | ||
.then((buf) => { | ||
res_buf = buf; | ||
return this._disconnect(); | ||
}) | ||
.then(() => { | ||
resolve(res_buf); | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
}); | ||
@@ -455,4 +495,4 @@ } | ||
timer = null; | ||
this._onnotify_internal = () => { }; | ||
reject(new Error('COMMAND_TIMEOUT')); | ||
this._onnotify_internal = () => {}; | ||
reject(new Error("COMMAND_TIMEOUT")); | ||
}, this._COMMAND_TIMEOUT_MSEC); | ||
@@ -465,3 +505,3 @@ | ||
} | ||
this._onnotify_internal = () => { }; | ||
this._onnotify_internal = () => {}; | ||
resolve(buf); | ||
@@ -477,3 +517,3 @@ }; | ||
let timer = setTimeout(() => { | ||
reject('READ_TIMEOUT'); | ||
reject("READ_TIMEOUT"); | ||
}, this._READ_TIMEOUT_MSEC); | ||
@@ -501,3 +541,3 @@ | ||
let timer = setTimeout(() => { | ||
reject('WRITE_TIMEOUT'); | ||
reject("WRITE_TIMEOUT"); | ||
}, this._WRITE_TIMEOUT_MSEC); | ||
@@ -519,5 +559,4 @@ | ||
} | ||
} | ||
module.exports = SwitchbotDevice; | ||
module.exports = SwitchbotDevice; |
@@ -1,24 +0,25 @@ | ||
'use strict'; | ||
const parameterChecker = require('./parameter-checker.js'); | ||
const switchbotAdvertising = require('./switchbot-advertising.js'); | ||
"use strict"; | ||
const parameterChecker = require("./parameter-checker.js"); | ||
const switchbotAdvertising = require("./switchbot-advertising.js"); | ||
const SwitchbotDevice = require('./switchbot-device.js'); | ||
const SwitchbotDeviceWoHand = require('./switchbot-device-wohand.js'); | ||
const SwitchbotDeviceWoCurtain = require('./switchbot-device-wocurtain.js'); | ||
const SwitchbotDeviceWoPresence = require('./switchbot-device-wopresence.js'); | ||
const SwitchbotDeviceWoContact = require('./switchbot-device-wocontact.js'); | ||
const SwitchbotDeviceWoSensorTH = require('./switchbot-device-wosensorth.js'); | ||
const SwitchbotDeviceWoHumi = require('./switchbot-device-wohumi.js'); | ||
const SwitchbotDevice = require("./switchbot-device.js"); | ||
const SwitchbotDeviceWoHand = require("./switchbot-device-wohand.js"); | ||
const SwitchbotDeviceWoCurtain = require("./switchbot-device-wocurtain.js"); | ||
const SwitchbotDeviceWoPresence = require("./switchbot-device-wopresence.js"); | ||
const SwitchbotDeviceWoContact = require("./switchbot-device-wocontact.js"); | ||
const SwitchbotDeviceWoSensorTH = require("./switchbot-device-wosensorth.js"); | ||
const SwitchbotDeviceWoHumi = require("./switchbot-device-wohumi.js"); | ||
const SwitchbotDeviceWoPlugMini = require("./switchbot-device-woplugmini.js"); | ||
class Switchbot { | ||
/* ------------------------------------------------------------------ | ||
* Constructor | ||
* | ||
* [Arguments] | ||
* - params | Object | Optional | | ||
* - noble | Noble | Optional | The Nobel object created by the noble module. | ||
* | | | This parameter is optional. | ||
* | | | If you don't specify this parameter, this | ||
* | | | module automatically creates it. | ||
* ---------------------------------------------------------------- */ | ||
* Constructor | ||
* | ||
* [Arguments] | ||
* - params | Object | Optional | | ||
* - noble | Noble | Optional | The Nobel object created by the noble module. | ||
* | | | This parameter is optional. | ||
* | | | If you don't specify this parameter, this | ||
* | | | module automatically creates it. | ||
* ---------------------------------------------------------------- */ | ||
constructor(params) { | ||
@@ -30,3 +31,3 @@ // Check parameters | ||
} else { | ||
noble = require('@abandonware/noble'); | ||
noble = require("@abandonware/noble"); | ||
} | ||
@@ -42,52 +43,60 @@ | ||
this._scanning = false; | ||
this._DEFAULT_DISCOVERY_DURATION = 5000 | ||
this._DEFAULT_DISCOVERY_DURATION = 5000; | ||
this._PRIMARY_SERVICE_UUID_LIST = []; | ||
}; | ||
} | ||
/* ------------------------------------------------------------------ | ||
* discover([params]) | ||
* - Discover switchbot devices | ||
* | ||
* [Arguments] | ||
* - params | Object | Optional | | ||
* - duration | Integer | Optional | Duration for discovery process (msec). | ||
* | | | The value must be in the range of 1 to 60000. | ||
* | | | The default value is 5000 (msec). | ||
* - model | String | Optional | "H", "T", "e", "s", "d", "c", "u", "g", "o", "i", or "r". | ||
* | | | If "H" is specified, this method will discover only Bots. | ||
* | | | If "T" is specified, this method will discover only Meters. | ||
* | | | If "e" is specified, this method will discover only Humidifiers. | ||
* | | | If "s" is specified, this method will discover only Motion Sensors. | ||
* | | | If "d" is specified, this method will discover only Contact Sensors. | ||
* | | | If "c" is specified, this method will discover only Curtains. | ||
* | | | If "u" is specified, this method will discover only Color Bulbs. | ||
* | | | If "g" is specified, this method will discover only Plugs. | ||
* | | | If "o" is specified, this method will discover only Locks. | ||
* | | | If "i" is specified, this method will discover only Meter Pluses. | ||
* | | | If "r" is specified, this method will discover only Locks. | ||
* - id | String | Optional | If this value is set, this method will discover | ||
* | | | only a device whose ID is as same as this value. | ||
* | | | The ID is identical to the MAC address. | ||
* | | | This parameter is case-insensitive, and | ||
* | | | colons are ignored. | ||
* - quick | Boolean | Optional | If this value is true, this method finishes | ||
* | | | the discovery process when the first device | ||
* | | | is found, then calls the resolve() function | ||
* | | | without waiting the specified duration. | ||
* | | | The default value is false. | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* An array will be passed to the `resolve()`, which includes | ||
* `SwitchbotDevice` objects representing the found devices. | ||
* ---------------------------------------------------------------- */ | ||
* discover([params]) | ||
* - Discover switchbot devices | ||
* | ||
* [Arguments] | ||
* - params | Object | Optional | | ||
* - duration | Integer | Optional | Duration for discovery process (msec). | ||
* | | | The value must be in the range of 1 to 60000. | ||
* | | | The default value is 5000 (msec). | ||
* - model | String | Optional | "H", "T", "e", "s", "d", "c", "u", "g", "o", "i", or "r". | ||
* | | | If "H" is specified, this method will discover only Bots. | ||
* | | | If "T" is specified, this method will discover only Meters. | ||
* | | | If "e" is specified, this method will discover only Humidifiers. | ||
* | | | If "s" is specified, this method will discover only Motion Sensors. | ||
* | | | If "d" is specified, this method will discover only Contact Sensors. | ||
* | | | If "c" is specified, this method will discover only Curtains. | ||
* | | | If "u" is specified, this method will discover only Color Bulbs. | ||
* | | | If "g" is specified, this method will discover only Plugs. | ||
* | | | If "o" is specified, this method will discover only Locks. | ||
* | | | If "i" is specified, this method will discover only Meter Pluses. | ||
* | | | If "r" is specified, this method will discover only Locks. | ||
* - id | String | Optional | If this value is set, this method will discover | ||
* | | | only a device whose ID is as same as this value. | ||
* | | | The ID is identical to the MAC address. | ||
* | | | This parameter is case-insensitive, and | ||
* | | | colons are ignored. | ||
* - quick | Boolean | Optional | If this value is true, this method finishes | ||
* | | | the discovery process when the first device | ||
* | | | is found, then calls the resolve() function | ||
* | | | without waiting the specified duration. | ||
* | | | The default value is false. | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* An array will be passed to the `resolve()`, which includes | ||
* `SwitchbotDevice` objects representing the found devices. | ||
* ---------------------------------------------------------------- */ | ||
discover(params = {}) { | ||
let promise = new Promise((resolve, reject) => { | ||
// Check the parameters | ||
let valid = parameterChecker.check(params, { | ||
duration: { required: false, type: 'integer', min: 1, max: 60000 }, | ||
model: { required: false, type: 'string', enum: ['H', 'T', 'e', 's', 'd', 'c', 'u', 'g', 'o', 'i', 'r'] }, | ||
id: { required: false, type: 'string', min: 12, max: 17 }, | ||
quick: { required: false, type: 'boolean' } | ||
}, false); | ||
let valid = parameterChecker.check( | ||
params, | ||
{ | ||
duration: { required: false, type: "integer", min: 1, max: 60000 }, | ||
model: { | ||
required: false, | ||
type: "string", | ||
enum: ["H", "T", "e", "s", "d", "c", "u", "g", "j", "o", "i", "r"], | ||
}, | ||
id: { required: false, type: "string", min: 12, max: 17 }, | ||
quick: { required: false, type: "boolean" }, | ||
}, | ||
false | ||
); | ||
@@ -106,56 +115,62 @@ if (!valid) { | ||
duration: params.duration || this._DEFAULT_DISCOVERY_DURATION, | ||
model: params.model || '', | ||
id: params.id || '', | ||
quick: params.quick ? true : false | ||
model: params.model || "", | ||
id: params.id || "", | ||
quick: params.quick ? true : false, | ||
}; | ||
// Initialize the noble object | ||
this._init().then(() => { | ||
let peripherals = {}; | ||
let timer = null; | ||
let finishDiscovery = () => { | ||
if (timer) { | ||
clearTimeout(timer); | ||
} | ||
this.noble.removeAllListeners('discover'); | ||
this.noble.stopScanning(); | ||
let device_list = []; | ||
for (let addr in peripherals) { | ||
device_list.push(peripherals[addr]); | ||
} | ||
resolve(device_list); | ||
}; | ||
this._init() | ||
.then(() => { | ||
let peripherals = {}; | ||
let timer = null; | ||
let finishDiscovery = () => { | ||
if (timer) { | ||
clearTimeout(timer); | ||
} | ||
this.noble.removeAllListeners("discover"); | ||
this.noble.stopScanning(); | ||
let device_list = []; | ||
for (let addr in peripherals) { | ||
device_list.push(peripherals[addr]); | ||
} | ||
resolve(device_list); | ||
}; | ||
// Set a handler for the 'discover' event | ||
this.noble.on('discover', (peripheral) => { | ||
let device = this._getDeviceObject(peripheral, p.id, p.model); | ||
if (!device) { | ||
return; | ||
} | ||
let id = device.id; | ||
peripherals[id] = device; | ||
// Set a handler for the 'discover' event | ||
this.noble.on("discover", (peripheral) => { | ||
let device = this._getDeviceObject(peripheral, p.id, p.model); | ||
if (!device) { | ||
return; | ||
} | ||
let id = device.id; | ||
peripherals[id] = device; | ||
if (this.ondiscover && typeof (this.ondiscover) === 'function') { | ||
this.ondiscover(device); | ||
} | ||
if (this.ondiscover && typeof this.ondiscover === "function") { | ||
this.ondiscover(device); | ||
} | ||
if (p.quick) { | ||
finishDiscovery(); | ||
return; | ||
} | ||
}); | ||
if (p.quick) { | ||
finishDiscovery(); | ||
return; | ||
} | ||
}); | ||
// Start scanning | ||
this.noble.startScanning(this._PRIMARY_SERVICE_UUID_LIST, false, (error) => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
timer = setTimeout(() => { | ||
finishDiscovery(); | ||
}, p.duration); | ||
// Start scanning | ||
this.noble.startScanning( | ||
this._PRIMARY_SERVICE_UUID_LIST, | ||
false, | ||
(error) => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
timer = setTimeout(() => { | ||
finishDiscovery(); | ||
}, p.duration); | ||
} | ||
); | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
}).catch((error) => { | ||
reject(error); | ||
}); | ||
}); | ||
@@ -168,15 +183,19 @@ return promise; | ||
switch (this.noble.state) { | ||
case 'poweredOn': | ||
case "poweredOn": | ||
resolve(); | ||
return; | ||
case 'unsupported', 'unauthorized', 'poweredOff': | ||
let err = new Error('Failed to initialize the Noble object: ' + this.noble.state); | ||
case ("unsupported", "unauthorized", "poweredOff"): | ||
let err = new Error( | ||
"Failed to initialize the Noble object: " + this.noble.state | ||
); | ||
reject(err); | ||
return; | ||
default: // 'resetting', 'unknown' | ||
this.noble.once('stateChange', (state) => { | ||
if (state === 'poweredOn') { | ||
this.noble.once("stateChange", (state) => { | ||
if (state === "poweredOn") { | ||
resolve(); | ||
} else { | ||
let err = new Error('Failed to initialize the Noble object: ' + state); | ||
let err = new Error( | ||
"Failed to initialize the Noble object: " + state | ||
); | ||
reject(err); | ||
@@ -195,33 +214,34 @@ } | ||
switch (ad.serviceData.model) { | ||
case 'H': | ||
case "H": | ||
device = new SwitchbotDeviceWoHand(peripheral, this.noble); | ||
break; | ||
case 'T': | ||
case "T": | ||
device = new SwitchbotDeviceWoSensorTH(peripheral, this.noble); | ||
break; | ||
case 'e': | ||
case "e": | ||
device = new SwitchbotDeviceWoHumi(peripheral, this.noble); | ||
break; | ||
case 's': | ||
case "s": | ||
device = new SwitchbotDeviceWoPresence(peripheral, this.noble); | ||
break; | ||
case 'd': | ||
case "d": | ||
device = new SwitchbotDeviceWoContact(peripheral, this.noble); | ||
break; | ||
case 'c': | ||
case "c": | ||
device = new SwitchbotDeviceWoCurtain(peripheral, this.noble); | ||
break; | ||
case 'u': | ||
case "u": | ||
device = new SwitchbotDeviceWoColorBulb(peripheral, this.noble); | ||
break; | ||
case 'g': | ||
case "g": | ||
case "j": | ||
device = new SwitchbotDeviceWoPlugMini(peripheral, this.noble); | ||
break; | ||
case 'o': | ||
case "o": | ||
device = new SwitchbotDeviceWoSmartLock(peripheral, this.noble); | ||
break; | ||
case 'i': | ||
case "i": | ||
device = new SwitchbotDeviceWoSensorTHPlus(peripheral, this.noble); | ||
break; | ||
case 'r': | ||
case "r": | ||
device = new SwitchbotDeviceWoLEDStripLight(peripheral, this.noble); | ||
@@ -243,4 +263,4 @@ break; | ||
if (id) { | ||
id = id.toLowerCase().replace(/\:/g, ''); | ||
let ad_id = ad.address.toLowerCase().replace(/[^a-z0-9]/g, ''); | ||
id = id.toLowerCase().replace(/\:/g, ""); | ||
let ad_id = ad.address.toLowerCase().replace(/[^a-z0-9]/g, ""); | ||
if (ad_id !== id) { | ||
@@ -259,60 +279,68 @@ return false; | ||
/* ------------------------------------------------------------------ | ||
* startScan([params]) | ||
* - Start to monitor advertising packets coming from switchbot devices | ||
* | ||
* [Arguments] | ||
* - params | Object | Optional | | ||
* - model | String | Optional | "H", "T", "e", "s", "d", "c", "u", "g", "o", "i", or "r". | ||
* | | | If "H" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Bots. | ||
* | | | If "T" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Meters. | ||
* | | | If "e" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Humidifiers. | ||
* | | | If "s" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Motion Sensor. | ||
* | | | If "d" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Contact Sensor. | ||
* | | | If "c" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Curtains. | ||
* | | | If "u" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Color Bulb. | ||
* | | | If "g" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Plug Mini. | ||
* | | | If "o" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Smart Lock. | ||
* | | | If "i" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Meter Plus. | ||
* | | | If "r" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from LED Strip Light. | ||
* - id | String | Optional | If this value is set, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from devices whose ID is as same as | ||
* | | | this value. | ||
* | | | The ID is identical to the MAC address. | ||
* | | | This parameter is case-insensitive, and | ||
* | | | colons are ignored. | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* startScan([params]) | ||
* - Start to monitor advertising packets coming from switchbot devices | ||
* | ||
* [Arguments] | ||
* - params | Object | Optional | | ||
* - model | String | Optional | "H", "T", "e", "s", "d", "c", "u", "g", "o", "i", or "r". | ||
* | | | If "H" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Bots. | ||
* | | | If "T" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Meters. | ||
* | | | If "e" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Humidifiers. | ||
* | | | If "s" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Motion Sensor. | ||
* | | | If "d" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Contact Sensor. | ||
* | | | If "c" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Curtains. | ||
* | | | If "u" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Color Bulb. | ||
* | | | If "g" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Plug Mini. | ||
* | | | If "o" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Smart Lock. | ||
* | | | If "i" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from Meter Plus. | ||
* | | | If "r" is specified, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from LED Strip Light. | ||
* - id | String | Optional | If this value is set, the `onadvertisement` | ||
* | | | event handler will be called only when advertising | ||
* | | | packets comes from devices whose ID is as same as | ||
* | | | this value. | ||
* | | | The ID is identical to the MAC address. | ||
* | | | This parameter is case-insensitive, and | ||
* | | | colons are ignored. | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
startScan(params) { | ||
let promise = new Promise((resolve, reject) => { | ||
// Check the parameters | ||
let valid = parameterChecker.check(params, { | ||
model: { required: false, type: 'string', enum: ['H', 'T', 'e', 's', 'd', 'c', 'u', 'g', 'o', 'i', 'r'] }, | ||
id: { required: false, type: 'string', min: 12, max: 17 }, | ||
}, false); | ||
let valid = parameterChecker.check( | ||
params, | ||
{ | ||
model: { | ||
required: false, | ||
type: "string", | ||
enum: ["H", "T", "e", "s", "d", "c", "u", "g", "j", "o", "i", "r"], | ||
}, | ||
id: { required: false, type: "string", min: 12, max: 17 }, | ||
}, | ||
false | ||
); | ||
@@ -329,31 +357,39 @@ if (!valid) { | ||
// Initialize the noble object | ||
this._init().then(() => { | ||
this._init() | ||
.then(() => { | ||
// Determine the values of the parameters | ||
let p = { | ||
model: params.model || "", | ||
id: params.id || "", | ||
}; | ||
// Determine the values of the parameters | ||
let p = { | ||
model: params.model || '', | ||
id: params.id || '' | ||
}; | ||
// Set a handler for the 'discover' event | ||
this.noble.on("discover", (peripheral) => { | ||
let ad = switchbotAdvertising.parse(peripheral, this.onlog); | ||
if (this._filterAdvertising(ad, p.id, p.model)) { | ||
if ( | ||
this.onadvertisement && | ||
typeof this.onadvertisement === "function" | ||
) { | ||
this.onadvertisement(ad); | ||
} | ||
} | ||
}); | ||
// Set a handler for the 'discover' event | ||
this.noble.on('discover', (peripheral) => { | ||
let ad = switchbotAdvertising.parse(peripheral, this.onlog); | ||
if (this._filterAdvertising(ad, p.id, p.model)) { | ||
if (this.onadvertisement && typeof (this.onadvertisement) === 'function') { | ||
this.onadvertisement(ad); | ||
// Start scanning | ||
this.noble.startScanning( | ||
this._PRIMARY_SERVICE_UUID_LIST, | ||
true, | ||
(error) => { | ||
if (error) { | ||
reject(error); | ||
} else { | ||
resolve(); | ||
} | ||
} | ||
} | ||
); | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
// Start scanning | ||
this.noble.startScanning(this._PRIMARY_SERVICE_UUID_LIST, true, (error) => { | ||
if (error) { | ||
reject(error); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
}).catch((error) => { | ||
reject(error); | ||
}); | ||
}); | ||
@@ -364,13 +400,13 @@ return promise; | ||
/* ------------------------------------------------------------------ | ||
* stopScan() | ||
* - Stop to monitor advertising packets coming from switchbot devices | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - none | ||
* ---------------------------------------------------------------- */ | ||
* stopScan() | ||
* - Stop to monitor advertising packets coming from switchbot devices | ||
* | ||
* [Arguments] | ||
* - none | ||
* | ||
* [Return value] | ||
* - none | ||
* ---------------------------------------------------------------- */ | ||
stopScan() { | ||
this.noble.removeAllListeners('discover'); | ||
this.noble.removeAllListeners("discover"); | ||
this.noble.stopScanning(); | ||
@@ -380,18 +416,21 @@ } | ||
/* ------------------------------------------------------------------ | ||
* wait(msec) { | ||
* - Wait for the specified time (msec) | ||
* | ||
* [Arguments] | ||
* - msec | Integer | Required | Msec. | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
* wait(msec) { | ||
* - Wait for the specified time (msec) | ||
* | ||
* [Arguments] | ||
* - msec | Integer | Required | Msec. | ||
* | ||
* [Return value] | ||
* - Promise object | ||
* Nothing will be passed to the `resolve()`. | ||
* ---------------------------------------------------------------- */ | ||
wait(msec) { | ||
return new Promise((resolve, reject) => { | ||
// Check the parameters | ||
let valid = parameterChecker.check({ msec: msec }, { | ||
msec: { required: true, type: 'integer', min: 0 } | ||
}); | ||
let valid = parameterChecker.check( | ||
{ msec: msec }, | ||
{ | ||
msec: { required: true, type: "integer", min: 0 }, | ||
} | ||
); | ||
@@ -406,5 +445,4 @@ if (!valid) { | ||
} | ||
} | ||
module.exports = Switchbot; |
{ | ||
"name": "node-switchbot", | ||
"version": "1.3.0", | ||
"version": "1.4.0", | ||
"description": "The node-switchbot is a Node.js module which allows you to move your Switchbot (Bot)'s arm and Switchbot Curtain(Curtain), also monitor the temperature/humidity from SwitchBot Thermometer & Hygrometer (Meter).", | ||
@@ -40,4 +40,4 @@ "main": "./lib/switchbot.js", | ||
"devDependencies": { | ||
"npm-check-updates": "^14.1.1" | ||
"npm-check-updates": "^16.0.5" | ||
} | ||
} | ||
} |
578
README.md
@@ -19,3 +19,4 @@ <span align="center"> | ||
--------------------------------------- | ||
--- | ||
## Table of Contents | ||
@@ -73,6 +74,5 @@ | ||
* [Node.js](https://nodejs.org/en/) 10 + | ||
* [@abandonware/noble](https://github.com/abandonware/noble) | ||
- [Node.js](https://nodejs.org/en/) 10 + | ||
- [@abandonware/noble](https://github.com/abandonware/noble) | ||
See the document of the [@abandonware/noble](https://github.com/abandonware/noble) for details on installing the [@abandonware/noble](https://github.com/abandonware/noble). | ||
@@ -100,3 +100,4 @@ | ||
--------------------------------------- | ||
--- | ||
## Quick Start | ||
@@ -137,3 +138,3 @@ | ||
// Load the node-switchbot and get a `Switchbot` constructor object | ||
const Switchbot = require('node-switchbot'); | ||
const Switchbot = require("node-switchbot"); | ||
// Create an `Switchbot` object | ||
@@ -143,14 +144,17 @@ let switchbot = new Switchbot(); | ||
// Start to monitor advertisement packets | ||
switchbot.startScan().then(() => { | ||
// Set an event hander | ||
switchbot.onadvertisement = (ad) => { | ||
console.log(JSON.stringify(ad, null, ' ')); | ||
}; | ||
// Wait 10 seconds | ||
return switchbot.wait(10000); | ||
}).then(() => { | ||
// Stop to monitor | ||
switchbot.stopScan(); | ||
process.exit(); | ||
}); | ||
switchbot | ||
.startScan() | ||
.then(() => { | ||
// Set an event hander | ||
switchbot.onadvertisement = (ad) => { | ||
console.log(JSON.stringify(ad, null, " ")); | ||
}; | ||
// Wait 10 seconds | ||
return switchbot.wait(10000); | ||
}) | ||
.then(() => { | ||
// Stop to monitor | ||
switchbot.stopScan(); | ||
process.exit(); | ||
}); | ||
``` | ||
@@ -212,3 +216,3 @@ | ||
// Load the node-switchbot and get a `Switchbot` constructor object | ||
const Switchbot = require('node-switchbot'); | ||
const Switchbot = require("node-switchbot"); | ||
// Create an `Switchbot` object | ||
@@ -219,5 +223,5 @@ const switchbot = new Switchbot(); | ||
// Find a Bot (WoHand) | ||
const bot_list = await switchbot.discover({ model: 'H', quick: true }); | ||
const bot_list = await switchbot.discover({ model: "H", quick: true }); | ||
if (bot_list.length === 0) { | ||
throw new Error('No device was found.'); | ||
throw new Error("No device was found."); | ||
} | ||
@@ -240,3 +244,4 @@ // The `SwitchbotDeviceWoHand` object representing the found Bot. | ||
--------------------------------------- | ||
--- | ||
## `Switchbot` object | ||
@@ -258,5 +263,5 @@ | ||
Property | Type | Required | Description | ||
:--------|:-------|:---------|:----------- | ||
`noble` | Noble | option | a Noble object of the [`@abandonware/noble`](https://github.com/abandonware/noble) module | ||
| Property | Type | Required | Description | | ||
| :------- | :---- | :------- | :---------------------------------------------------------------------------------------- | | ||
| `noble` | Noble | option | a Noble object of the [`@abandonware/noble`](https://github.com/abandonware/noble) module | | ||
@@ -282,8 +287,8 @@ The node-switchbot module uses the [`@abandonware/noble`](https://github.com/abandonware/noble) module in order to interact with BLE devices. If you want to interact other BLE devices using the `@abandonware/noble` module, you can create an `Noble` object by yourself, then pass it to this module. If you don't specify a `Noble` object to the `noble` property, this module automatically create a `Noble` object internally. | ||
Property | Type | Required | Description | ||
:------------|:--------|:---------|:------------ | ||
`duration` | Integer | Optional | Duration for discovery process (msec). The default value is 5000 (msec). | ||
`model` | String | Optional | `"H"`, `"T"` or `"c"`. If `"H"` is specified, this method will discover only Bots. If `"T"` is specified, this method will discover only Meters. If `"c"` is specified, this method will discover only Curtains. | ||
`id` | String | Optional | If this value is set, this method will discover only a device whose ID is as same as this value. The ID is identical to the MAC address. This parameter is case-insensitive, and colons are ignored. | ||
`quick` | Boolean | Optional | If this value is `true`, this method finishes the discovery process when the first device is found, then calls the `resolve()` function without waiting the specified `duration`. The default value is `false`. | ||
| Property | Type | Required | Description | | ||
| :--------- | :------ | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `duration` | Integer | Optional | Duration for discovery process (msec). The default value is 5000 (msec). | | ||
| `model` | String | Optional | `"H"`, `"T"` or `"c"`. If `"H"` is specified, this method will discover only Bots. If `"T"` is specified, this method will discover only Meters. If `"c"` is specified, this method will discover only Curtains. | | ||
| `id` | String | Optional | If this value is set, this method will discover only a device whose ID is as same as this value. The ID is identical to the MAC address. This parameter is case-insensitive, and colons are ignored. | | ||
| `quick` | Boolean | Optional | If this value is `true`, this method finishes the discovery process when the first device is found, then calls the `resolve()` function without waiting the specified `duration`. The default value is `false`. | | ||
@@ -300,3 +305,3 @@ In the code snippet below, no parameter is passed to the method: | ||
If no parameter is passed to the method as the code above, an `Array` object will be passed to the `resolve()` function in 5 seconds. The `Array` object contains [`SwitchbotDevice`](#SwitchbotDevice-object) objects representing the found devices. See the section "[`SwitchbotDevice`](#SwitchbotDevice-object) objects" for more details. | ||
If no parameter is passed to the method as the code above, an `Array` object will be passed to the `resolve()` function in 5 seconds. The `Array` object contains [`SwitchbotDevice`](#SwitchbotDevice-object) objects representing the found devices. See the section "[`SwitchbotDevice`](#SwitchbotDevice-object) objects" for more details. | ||
@@ -347,6 +352,6 @@ If you want a quick response, you can set the `quick` property to `true`. | ||
Property | Type | Required | Description | ||
:------------|:-------|:---------|:------------ | ||
`model` | String | Optional | `"H"`, `"T"` or `"c"`. If `"H"` is specified, this method will discover only Bots. If `"T"` is specified, this method will discover only Meters. If `"c"` is specified, this method will discover only Curtains. | ||
`id` | String | Optional | If this value is set, this method will discover only a device whose ID is as same as this value. The ID is identical to the MAC address. This value is case-insensitive, and colons are ignored. | ||
| Property | Type | Required | Description | | ||
| :------- | :----- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `model` | String | Optional | `"H"`, `"T"` or `"c"`. If `"H"` is specified, this method will discover only Bots. If `"T"` is specified, this method will discover only Meters. If `"c"` is specified, this method will discover only Curtains. | | ||
| `id` | String | Optional | If this value is set, this method will discover only a device whose ID is as same as this value. The ID is identical to the MAC address. This value is case-insensitive, and colons are ignored. | | ||
@@ -399,3 +404,3 @@ Whenever a packet is received, the callback function set to the [`onadvertisement`](#Switchbot-onadvertisement-event-handler) property of the [`Switchbot`](#Switchbot-object) object will be called. When a packet is received, a hash object representing the packet will be passed to the callback function. | ||
The `stopScan()` method stops to scan advertising packets coming from devices. This method returns nothing. Note that this method is *not* asynchronous but synchronous unlike the other methods. See the section "[`startScan()` method](#startscan-method)" for details. | ||
The `stopScan()` method stops to scan advertising packets coming from devices. This method returns nothing. Note that this method is _not_ asynchronous but synchronous unlike the other methods. See the section "[`startScan()` method](#startscan-method)" for details. | ||
@@ -414,3 +419,4 @@ ### `onadvertisement` event handler | ||
--------------------------------------- | ||
--- | ||
## `SwitchbotDevice` object | ||
@@ -428,11 +434,11 @@ | ||
Property | Type | Description | ||
:----------------|:---------|:----------- | ||
`id` | String | ID of the device. (e.g., `"cb4eb903c96d"`) | ||
`address` | String | MAC address of the device. Basically it is as same as the value of the `id` except that this value includes `:` in the string. (e.g., `"cb:4e:b9:03:c9:6d"`) | ||
`model` | String | This value is `"H"` "Bot (WoHand)", `"T"` "Meter (WoSensorTH)", `"c"` "Curtain (WoCurtain)", `"d"` "Contact (WoContact)" or `"s"` "Motion (WoMotion)". | ||
`modelName` | String | This value is `"WoHand"`, `"WoSensorTH"`, `WoCurtain`, `WoContect` or `WoMotion`. | ||
`connectionState` | String | This value indicates the BLE connection state. `"connecting"`, `"connected"`, `"disconnecting"`, or `"disconnected"`. | ||
`onconnect` | Function | See the section "[`onconnect` event handler](#SwitchbotDevice-onconnect-event-handler)" for details. | ||
`ondisconnect` | Function | See the section "[`ondisconnect` event handler](#SwitchbotDevice-ondisconnect-event-handler)" for details. | ||
| Property | Type | Description | | ||
| :---------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `id` | String | ID of the device. (e.g., `"cb4eb903c96d"`) | | ||
| `address` | String | MAC address of the device. Basically it is as same as the value of the `id` except that this value includes `:` in the string. (e.g., `"cb:4e:b9:03:c9:6d"`) | | ||
| `model` | String | This value is `"H"` "Bot (WoHand)", `"T"` "Meter (WoSensorTH)", `"c"` "Curtain (WoCurtain)", `"d"` "Contact (WoContact)" or `"s"` "Motion (WoMotion)". | | ||
| `modelName` | String | This value is `"WoHand"`, `"WoSensorTH"`, `WoCurtain`, `WoContect` or `WoMotion`. | | ||
| `connectionState` | String | This value indicates the BLE connection state. `"connecting"`, `"connected"`, `"disconnecting"`, or `"disconnected"`. | | ||
| `onconnect` | Function | See the section "[`onconnect` event handler](#SwitchbotDevice-onconnect-event-handler)" for details. | | ||
| `ondisconnect` | Function | See the section "[`ondisconnect` event handler](#SwitchbotDevice-ondisconnect-event-handler)" for details. | | ||
@@ -448,11 +454,15 @@ ### `getDeviceName()` method | ||
```javascript | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
return device_list[0].getDeviceName(); | ||
}).then((name) => { | ||
console.log(name); | ||
process.exit(); | ||
}).catch((error) => { | ||
console.error(error); | ||
process.exit(); | ||
}); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].getDeviceName(); | ||
}) | ||
.then((name) => { | ||
console.log(name); | ||
process.exit(); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
process.exit(); | ||
}); | ||
``` | ||
@@ -463,3 +473,3 @@ | ||
```javascript | ||
WoHand | ||
WoHand; | ||
``` | ||
@@ -476,11 +486,15 @@ | ||
```javascript | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
return device_list[0].setDeviceName('Bot in kitchen'); | ||
}).then(() => { | ||
console.log('Done.'); | ||
process.exit(); | ||
}).catch((error) => { | ||
console.error(error); | ||
process.exit(); | ||
}); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].setDeviceName("Bot in kitchen"); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
process.exit(); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
process.exit(); | ||
}); | ||
``` | ||
@@ -501,30 +515,38 @@ | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
device = device_list[0]; | ||
if (!device) { | ||
console.log('No device was found.'); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
device = device_list[0]; | ||
if (!device) { | ||
console.log("No device was found."); | ||
process.exit(); | ||
} | ||
console.log(device.modelName + " (" + device.address + ") was found."); | ||
console.log("Connecting..."); | ||
return device.connect(); | ||
}) | ||
.then(() => { | ||
console.log("Putting the arm down..."); | ||
return device.down(); | ||
}) | ||
.then(() => { | ||
console.log("Waiting for 5 seconds..."); | ||
return switchbot.wait(5000); | ||
}) | ||
.then(() => { | ||
console.log("Putting the arm up..."); | ||
return device.up(); | ||
}) | ||
.then(() => { | ||
console.log("Disconnecting..."); | ||
return device.disconnect(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
process.exit(); | ||
} | ||
console.log(device.modelName + ' (' + device.address + ') was found.'); | ||
console.log('Connecting...'); | ||
return device.connect(); | ||
}).then(() => { | ||
console.log('Putting the arm down...'); | ||
return device.down(); | ||
}).then(() => { | ||
console.log('Waiting for 5 seconds...'); | ||
return switchbot.wait(5000); | ||
}).then(() => { | ||
console.log('Putting the arm up...'); | ||
return device.up(); | ||
}).then(() => { | ||
console.log('Disconnecting...'); | ||
return device.disconnect(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
process.exit(); | ||
}).catch((error) => { | ||
console.error(error); | ||
process.exit(); | ||
}); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
process.exit(); | ||
}); | ||
``` | ||
@@ -557,27 +579,31 @@ | ||
```javascript | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
let device = device_list[0]; | ||
if (!device) { | ||
console.log('No device was found.'); | ||
process.exit(); | ||
} | ||
console.log(device.modelName + ' (' + device.address + ') was found.'); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
let device = device_list[0]; | ||
if (!device) { | ||
console.log("No device was found."); | ||
process.exit(); | ||
} | ||
console.log(device.modelName + " (" + device.address + ") was found."); | ||
// Set event handers | ||
device.onconnect = () => { | ||
console.log('Connected.'); | ||
}; | ||
device.ondisconnect = () => { | ||
console.log('Disconnected.'); | ||
}; | ||
// Set event handers | ||
device.onconnect = () => { | ||
console.log("Connected."); | ||
}; | ||
device.ondisconnect = () => { | ||
console.log("Disconnected."); | ||
}; | ||
console.log('Pressing the switch...'); | ||
return device.press(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
process.exit(); | ||
}).catch((error) => { | ||
console.error(error); | ||
process.exit(); | ||
}); | ||
console.log("Pressing the switch..."); | ||
return device.press(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
process.exit(); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
process.exit(); | ||
}); | ||
``` | ||
@@ -601,3 +627,4 @@ | ||
--------------------------------------- | ||
--- | ||
## `SwitchbotDeviceWoHand` object | ||
@@ -616,9 +643,13 @@ | ||
```javascript | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
return device_list[0].press(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].press(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
@@ -636,16 +667,20 @@ | ||
Mode | Inverse the on/off direction | Physical position of the arm | ||
:-------------------|:-----------------------------|:---------------------------- | ||
Press mode | N/A | Down (stretched), then Up (retracted) | ||
Switch mode | Disabled | Down (stretched) | ||
| Enabled | Up (retracted) | ||
| Mode | Inverse the on/off direction | Physical position of the arm | | ||
| :---------- | :--------------------------- | :------------------------------------ | | ||
| Press mode | N/A | Down (stretched), then Up (retracted) | | ||
| Switch mode | Disabled | Down (stretched) | | ||
| | Enabled | Up (retracted) | | ||
```javascript | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
return device_list[0].turnOn(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].turnOn(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
@@ -661,16 +696,20 @@ | ||
Mode | Inverse the on/off direction | Physical position of the arm | ||
:-------------------|:-----------------------------|:---------------------------- | ||
Press mode | N/A | Down (stretched), then Up (retracted) | ||
Switch mode | Disabled | Up (retracted) | ||
| Enabled | Down (stretched) | ||
| Mode | Inverse the on/off direction | Physical position of the arm | | ||
| :---------- | :--------------------------- | :------------------------------------ | | ||
| Press mode | N/A | Down (stretched), then Up (retracted) | | ||
| Switch mode | Disabled | Up (retracted) | | ||
| | Enabled | Down (stretched) | | ||
```javascript | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
return device_list[0].turnOff(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].turnOff(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
@@ -687,9 +726,13 @@ | ||
```javascript | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
return device_list[0].down(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].down(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
@@ -706,12 +749,17 @@ | ||
```javascript | ||
switchbot.discover({ model: 'H', quick: true }).then((device_list) => { | ||
return device_list[0].up(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "H", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].up(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
--------------------------------------- | ||
--- | ||
## `SwitchbotDeviceWoCurtain` object | ||
@@ -726,9 +774,13 @@ | ||
```javascript | ||
switchbot.discover({ model: 'c', quick: true }).then((device_list) => { | ||
return device_list[0].open(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "c", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].open(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
@@ -745,9 +797,13 @@ | ||
```javascript | ||
switchbot.discover({ model: 'c', quick: true }).then((device_list) => { | ||
return device_list[0].close(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "c", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].close(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
@@ -764,9 +820,13 @@ | ||
```javascript | ||
switchbot.discover({ model: 'c', quick: true }).then((device_list) => { | ||
return device_list[0].pause(); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "c", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].pause(); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
@@ -782,3 +842,2 @@ | ||
The `open()` method sends an open command to the Curtain. This method returns a `Promise` object. Nothing will be passed to the `resove()`. | ||
@@ -790,18 +849,23 @@ | ||
Property | Type | Required | Description | ||
:------------|:--------|:---------|:------------ | ||
`percent` | Integer | Required | The percentage of target position (`0-100`). (e.g., `50`) | ||
`mode` | Integer | Optional | The running mode of Curtain. <br/>`0x00` - Performance mode.<br/> `0x01` - Silent mode. <br/>`0xff` - Default. Unspecified, from Curtain's settings. | ||
| Property | Type | Required | Description | | ||
| :-------- | :------ | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `percent` | Integer | Required | The percentage of target position (`0-100`). (e.g., `50`) | | ||
| `mode` | Integer | Optional | The running mode of Curtain. <br/>`0x00` - Performance mode.<br/> `0x01` - Silent mode. <br/>`0xff` - Default. Unspecified, from Curtain's settings. | | ||
```javascript | ||
switchbot.discover({ model: 'c', quick: true }).then((device_list) => { | ||
return device_list[0].runToPos(50); | ||
}).then(() => { | ||
console.log('Done.'); | ||
}).catch((error) => { | ||
console.error(error); | ||
}); | ||
switchbot | ||
.discover({ model: "c", quick: true }) | ||
.then((device_list) => { | ||
return device_list[0].runToPos(50); | ||
}) | ||
.then(() => { | ||
console.log("Done."); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
``` | ||
--------------------------------------- | ||
--- | ||
## Advertisement data | ||
@@ -811,8 +875,8 @@ | ||
Property | Type | Description | ||
:-------------|:--------|:----------- | ||
`id` | String | ID of the device. (e.g., `"cb4eb903c96d"`) | ||
`address` | String | MAC address of the device. Basically it is as same as the value of the `id` except that this value includes `:` in the string. (e.g., `"cb:4e:b9:03:c9:6d"`) | ||
`rssi` | Integer | RSSI. (e.g., `-62`) | ||
`serviceData` | Object | An object including the device-specific data. | ||
| Property | Type | Description | | ||
| :------------ | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `id` | String | ID of the device. (e.g., `"cb4eb903c96d"`) | | ||
| `address` | String | MAC address of the device. Basically it is as same as the value of the `id` except that this value includes `:` in the string. (e.g., `"cb:4e:b9:03:c9:6d"`) | | ||
| `rssi` | Integer | RSSI. (e.g., `-62`) | | ||
| `serviceData` | Object | An object including the device-specific data. | | ||
@@ -842,24 +906,23 @@ The structures of the `serviceData` are described in the following sections. | ||
Property | Type | Description | ||
:-----------|:--------|:----------- | ||
`model` | String | This value is always `"H"`, which means "Bot (WoHand)". | ||
`modelName` | String | This value is always `"WoHand"`, which means "Bot". | ||
`mode` | Boolean | This indicates the mode setting. When the mode is "Switch mode", this value is `true`. When the mode is "Press mode", this value is `false`. | ||
`state` | Boolean | This value indicates whether the switch status is ON or OFF. | ||
`battery` | Integer | (**experimental**) This value indicates the battery level (`%`). | ||
| Property | Type | Description | | ||
| :---------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `model` | String | This value is always `"H"`, which means "Bot (WoHand)". | | ||
| `modelName` | String | This value is always `"WoHand"`, which means "Bot". | | ||
| `mode` | Boolean | This indicates the mode setting. When the mode is "Switch mode", this value is `true`. When the mode is "Press mode", this value is `false`. | | ||
| `state` | Boolean | This value indicates whether the switch status is ON or OFF. | | ||
| `battery` | Integer | (**experimental**) This value indicates the battery level (`%`). | | ||
The `mode` can be changed only using the official smartphone app. The node-switchbot does not support changing the mode because the BLE protocol is non-public. | ||
If the `mode` is `false`, which means the "Press mode" is selected, the `state` is always `false`. If the `mode` is `true`, which means the "Switch mode" is selected, the `state` represents the logical state (ON or OFF). Note that it does *not* mean the physical arm position. The physical arm position depends on the setting "Inverse the on/off direction" on the official smartphone app. | ||
If the `mode` is `false`, which means the "Press mode" is selected, the `state` is always `false`. If the `mode` is `true`, which means the "Switch mode" is selected, the `state` represents the logical state (ON or OFF). Note that it does _not_ mean the physical arm position. The physical arm position depends on the setting "Inverse the on/off direction" on the official smartphone app. | ||
"Inverse the on/off direction" | Value of the `state` | Logical state | Physical arm position | ||
:------------------------------|:---------------------|:--------------|:------------ | ||
disabled | `true` | OFF | Up (retracted) | ||
| `false` | ON | Down (stretched) | ||
enabled | `true` | OFF | Down (stretched) | ||
| `false` | ON | Up (retracted) | ||
| "Inverse the on/off direction" | Value of the `state` | Logical state | Physical arm position | | ||
| :----------------------------- | :------------------- | :------------ | :-------------------- | | ||
| disabled | `true` | OFF | Up (retracted) | | ||
| | `false` | ON | Down (stretched) | | ||
| enabled | `true` | OFF | Down (stretched) | | ||
| | `false` | ON | Up (retracted) | | ||
The `battery` is *experimental* for now. I'm not sure whether the value is correct or not. Never trust this value for now. | ||
The `battery` is _experimental_ for now. I'm not sure whether the value is correct or not. Never trust this value for now. | ||
### Meter (WoSensorTH) | ||
@@ -890,16 +953,16 @@ | ||
Property | Type | Description | ||
:-------------|:--------|:----------- | ||
`model` | String | This value is always `"T"`, which means "Meter (WoSensorTH)". | ||
`modelName` | String | This value is always `"WoSensorTH"`, which means "Meter". | ||
`temperature` | Object | | ||
`c` | Float | Temperature (degree Celsius/°C) | ||
`f` | Float | Temperature (degree Fahrenheit/℉) | ||
`fahrenheit` | Boolean | The flag whether the Meter shows Fahrenheit (`true`) or Celsius (`false`) for the temperature on the display | ||
`humidity` | Integer | Humidity (`%`) | ||
`battery` | Integer | (**experimental**) This value indicates the battery level (`%`). | ||
| Property | Type | Description | | ||
| :--------------- | :------ | :----------------------------------------------------------------------------------------------------------- | | ||
| `model` | String | This value is always `"T"`, which means "Meter (WoSensorTH)". | | ||
| `modelName` | String | This value is always `"WoSensorTH"`, which means "Meter". | | ||
| `temperature` | Object | | ||
| `c` | Float | Temperature (degree Celsius/°C) | | ||
| `f` | Float | Temperature (degree Fahrenheit/℉) | | ||
| `fahrenheit` | Boolean | The flag whether the Meter shows Fahrenheit (`true`) or Celsius (`false`) for the temperature on the display | | ||
| `humidity` | Integer | Humidity (`%`) | | ||
| `battery` | Integer | (**experimental**) This value indicates the battery level (`%`). | | ||
The `fahrenheit` indicates the setting on the device. Note that it does *not* indicate the setting on the official smartphone app. The setting of the temperature unit on the device and the setting on the app are independent. | ||
The `fahrenheit` indicates the setting on the device. Note that it does _not_ indicate the setting on the official smartphone app. The setting of the temperature unit on the device and the setting on the app are independent. | ||
The `battery` is *experimental* for now. I'm not sure whether the value is correct or not. Never trust this value for now. | ||
The `battery` is _experimental_ for now. I'm not sure whether the value is correct or not. Never trust this value for now. | ||
@@ -928,10 +991,10 @@ ### Curtain (WoCurtain) | ||
Property | Type | Description | ||
:-------------|:--------|:----------- | ||
`model` | String | This value is always `"c"`, which means "Curtain (WoCurtain)". | ||
`modelName` | String | This value is always `"WoCurtain"`, which means "Curtain". | ||
`calibration` | Boolean | This value indicates the calibration status (`true` or `false`). | ||
`battery` | Integer | This value indicates the battery level (`1-100`, `%`). | ||
`position` | Integer | This value indicates the percentage of current position (`0-100`, 0 is open, `%`). | ||
`lightLevel` | Integer | This value indicates the light level of the light source currently set (`1-10`). | ||
| Property | Type | Description | | ||
| :------------ | :------ | :--------------------------------------------------------------------------------- | | ||
| `model` | String | This value is always `"c"`, which means "Curtain (WoCurtain)". | | ||
| `modelName` | String | This value is always `"WoCurtain"`, which means "Curtain". | | ||
| `calibration` | Boolean | This value indicates the calibration status (`true` or `false`). | | ||
| `battery` | Integer | This value indicates the battery level (`1-100`, `%`). | | ||
| `position` | Integer | This value indicates the percentage of current position (`0-100`, 0 is open, `%`). | | ||
| `lightLevel` | Integer | This value indicates the light level of the light source currently set (`1-10`). | | ||
@@ -960,10 +1023,10 @@ ### Contact (WoContact) | ||
Property | Type | Description | ||
:-------------|:--------|:----------- | ||
`model` | String | This value is always `"c"`, which means "Contact (WoContact)". | ||
`modelName` | String | This value is always `"WoContact"`, which means "Contact". | ||
`movement` | Boolean | This value indicates the motion status (`true` or `false`). | ||
`battery` | Integer | This value indicates the battery level (`1-100`, `%`). | ||
`doorState` | String | This value indicates the door Status (`close`, `open`, `timeout no closed`). | ||
`lightLevel` | String | This value indicates the light level (`dark`, `bright`). | ||
| Property | Type | Description | | ||
| :----------- | :------ | :--------------------------------------------------------------------------- | | ||
| `model` | String | This value is always `"c"`, which means "Contact (WoContact)". | | ||
| `modelName` | String | This value is always `"WoContact"`, which means "Contact". | | ||
| `movement` | Boolean | This value indicates the motion status (`true` or `false`). | | ||
| `battery` | Integer | This value indicates the battery level (`1-100`, `%`). | | ||
| `doorState` | String | This value indicates the door Status (`close`, `open`, `timeout no closed`). | | ||
| `lightLevel` | String | This value indicates the light level (`dark`, `bright`). | | ||
@@ -991,14 +1054,15 @@ ### Motion (WoMotion) | ||
Property | Type | Description | ||
:-------------|:--------|:----------- | ||
`model` | String | This value is always `"s"`, which means "Motion (WoMotion)". | ||
`modelName` | String | This value is always `"WoMotion"`, which means "Motion". | ||
`movement` | Boolean | This value indicates the motion status (`true` or `false`). | ||
`battery` | Integer | This value indicates the battery level (`1-100`, `%`). | ||
`lightLevel` | String | This value indicates the light level (`dark`, `bright`). | ||
| Property | Type | Description | | ||
| :----------- | :------ | :----------------------------------------------------------- | | ||
| `model` | String | This value is always `"s"`, which means "Motion (WoMotion)". | | ||
| `modelName` | String | This value is always `"WoMotion"`, which means "Motion". | | ||
| `movement` | Boolean | This value indicates the motion status (`true` or `false`). | | ||
| `battery` | Integer | This value indicates the battery level (`1-100`, `%`). | | ||
| `lightLevel` | String | This value indicates the light level (`dark`, `bright`). | | ||
--------------------------------------- | ||
--- | ||
## References | ||
* [Switchbot official global site](https://www.switch-bot.com/) | ||
* [GitHub - OpenWonderLabs/SwitchBotAPI-BLE](https://github.com/OpenWonderLabs/SwitchBotAPI-BLE) | ||
- [Switchbot official global site](https://www.switch-bot.com/) | ||
- [GitHub - OpenWonderLabs/SwitchBotAPI-BLE](https://github.com/OpenWonderLabs/SwitchBotAPI-BLE) |
Sorry, the diff of this file is not supported yet
125527
15
2239
1032