@lumiastream/tapo-cove 0.0.1-alpha.2 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +146 -0
- package/dist/index.js +554 -0
- package/dist/index.mjs +521 -0
- package/package.json +25 -37
- package/lib/cjs/discovery.d.ts +0 -9
- package/lib/cjs/discovery.d.ts.map +0 -1
- package/lib/cjs/discovery.js +0 -114
- package/lib/cjs/index.d.ts +0 -6
- package/lib/cjs/index.d.ts.map +0 -1
- package/lib/cjs/index.js +0 -37
- package/lib/cjs/lightstate.d.ts +0 -44
- package/lib/cjs/lightstate.d.ts.map +0 -1
- package/lib/cjs/lightstate.js +0 -123
- package/lib/cjs/shared/cipher.d.ts +0 -12
- package/lib/cjs/shared/cipher.d.ts.map +0 -1
- package/lib/cjs/shared/cipher.js +0 -74
- package/lib/cjs/shared/helpers.d.ts +0 -6
- package/lib/cjs/shared/helpers.d.ts.map +0 -1
- package/lib/cjs/shared/helpers.js +0 -99
- package/lib/cjs/tapo-api.d.ts +0 -43
- package/lib/cjs/tapo-api.d.ts.map +0 -1
- package/lib/cjs/tapo-api.js +0 -114
- package/lib/cjs/types/interfaces.d.ts +0 -4
- package/lib/cjs/types/interfaces.d.ts.map +0 -1
- package/lib/cjs/types/interfaces.js +0 -2
- package/lib/cjs/types/raw-types.d.ts +0 -85
- package/lib/cjs/types/raw-types.d.ts.map +0 -1
- package/lib/cjs/types/raw-types.js +0 -2
- package/lib/cjs/types/tapo.constants.d.ts +0 -22
- package/lib/cjs/types/tapo.constants.d.ts.map +0 -1
- package/lib/cjs/types/tapo.constants.js +0 -25
- package/lib/esm/discovery.d.ts +0 -9
- package/lib/esm/discovery.d.ts.map +0 -1
- package/lib/esm/discovery.js +0 -109
- package/lib/esm/index.d.ts +0 -6
- package/lib/esm/index.d.ts.map +0 -1
- package/lib/esm/index.js +0 -5
- package/lib/esm/lightstate.d.ts +0 -44
- package/lib/esm/lightstate.d.ts.map +0 -1
- package/lib/esm/lightstate.js +0 -119
- package/lib/esm/shared/cipher.d.ts +0 -12
- package/lib/esm/shared/cipher.d.ts.map +0 -1
- package/lib/esm/shared/cipher.js +0 -61
- package/lib/esm/shared/helpers.d.ts +0 -6
- package/lib/esm/shared/helpers.d.ts.map +0 -1
- package/lib/esm/shared/helpers.js +0 -89
- package/lib/esm/tapo-api.d.ts +0 -43
- package/lib/esm/tapo-api.d.ts.map +0 -1
- package/lib/esm/tapo-api.js +0 -108
- package/lib/esm/types/interfaces.d.ts +0 -4
- package/lib/esm/types/interfaces.d.ts.map +0 -1
- package/lib/esm/types/interfaces.js +0 -1
- package/lib/esm/types/raw-types.d.ts +0 -85
- package/lib/esm/types/raw-types.d.ts.map +0 -1
- package/lib/esm/types/raw-types.js +0 -1
- package/lib/esm/types/tapo.constants.d.ts +0 -22
- package/lib/esm/types/tapo.constants.d.ts.map +0 -1
- package/lib/esm/types/tapo.constants.js +0 -22
- package/test/auth.test.ts +0 -22
- package/test/discover.test.ts +0 -9
- package/test/light.test.ts +0 -17
- package/test/plug.test.ts +0 -16
- package/tslint.json +0 -53
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { ILumiaDeviceType } from '@lumiastream/lumia-rgb-types';
|
|
2
|
+
|
|
3
|
+
declare const discover: (config: {
|
|
4
|
+
token?: string;
|
|
5
|
+
email?: string;
|
|
6
|
+
password?: string;
|
|
7
|
+
types?: ILumiaDeviceType;
|
|
8
|
+
}) => Promise<any>;
|
|
9
|
+
|
|
10
|
+
declare class SuperState {
|
|
11
|
+
protected _values: Record<string, boolean | string | number>;
|
|
12
|
+
transition: (value: number) => this;
|
|
13
|
+
duration: (value: number) => this;
|
|
14
|
+
getValues: () => Record<string, string | number | boolean>;
|
|
15
|
+
}
|
|
16
|
+
declare class LightState extends SuperState {
|
|
17
|
+
constructor(values?: {
|
|
18
|
+
on: boolean;
|
|
19
|
+
});
|
|
20
|
+
create: (values?: {
|
|
21
|
+
[x: string]: any;
|
|
22
|
+
} | undefined) => this;
|
|
23
|
+
on: (value?: boolean) => this;
|
|
24
|
+
turnOn: (value?: boolean) => this;
|
|
25
|
+
off: () => this;
|
|
26
|
+
turnOff: () => this;
|
|
27
|
+
mode: (value?: string) => this;
|
|
28
|
+
hue: (value: number) => this;
|
|
29
|
+
bri: (value: number) => this;
|
|
30
|
+
brightness: (value: number) => this;
|
|
31
|
+
sat: (value: number) => this;
|
|
32
|
+
saturation: (value: number) => this;
|
|
33
|
+
temp: (value: number) => this;
|
|
34
|
+
colorTemperature: (value: number) => this;
|
|
35
|
+
hsv: (value: [
|
|
36
|
+
number,
|
|
37
|
+
number,
|
|
38
|
+
number
|
|
39
|
+
] | {
|
|
40
|
+
h: number;
|
|
41
|
+
s: number;
|
|
42
|
+
v: number;
|
|
43
|
+
}) => this;
|
|
44
|
+
rgb: (value: [
|
|
45
|
+
number,
|
|
46
|
+
number,
|
|
47
|
+
number
|
|
48
|
+
] | {
|
|
49
|
+
r: number;
|
|
50
|
+
g: number;
|
|
51
|
+
b: number;
|
|
52
|
+
}) => this;
|
|
53
|
+
color: (value: {
|
|
54
|
+
ct?: number;
|
|
55
|
+
r?: number;
|
|
56
|
+
g?: number;
|
|
57
|
+
b?: number;
|
|
58
|
+
}) => this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type TapoDeviceKey = {
|
|
62
|
+
key: string | Buffer;
|
|
63
|
+
iv: string | Buffer;
|
|
64
|
+
deviceIp: string;
|
|
65
|
+
sessionCookie: string;
|
|
66
|
+
token?: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
declare class TapoApi {
|
|
70
|
+
_baseUrl: string;
|
|
71
|
+
_baseTapoCareUrl: string;
|
|
72
|
+
private axiosInstance;
|
|
73
|
+
private _config;
|
|
74
|
+
private _token;
|
|
75
|
+
private _devices;
|
|
76
|
+
constructor(config?: {
|
|
77
|
+
token?: string;
|
|
78
|
+
timeout?: number;
|
|
79
|
+
httpTimeout?: number;
|
|
80
|
+
});
|
|
81
|
+
auth: ({ email, password }: {
|
|
82
|
+
email: string;
|
|
83
|
+
password: string;
|
|
84
|
+
}) => Promise<string>;
|
|
85
|
+
setup: ({ email, password, devices }: {
|
|
86
|
+
email: string;
|
|
87
|
+
password: string;
|
|
88
|
+
devices: Array<{
|
|
89
|
+
id: string;
|
|
90
|
+
ip: string;
|
|
91
|
+
}>;
|
|
92
|
+
}) => Promise<Map<string, TapoDeviceKey>>;
|
|
93
|
+
sendState: (config: {
|
|
94
|
+
device: {
|
|
95
|
+
id: string;
|
|
96
|
+
};
|
|
97
|
+
state: LightState;
|
|
98
|
+
fetchConfig?: {
|
|
99
|
+
shouldWait: boolean;
|
|
100
|
+
};
|
|
101
|
+
}) => Promise<any>;
|
|
102
|
+
sendPower: (config: {
|
|
103
|
+
device: {
|
|
104
|
+
id: string;
|
|
105
|
+
};
|
|
106
|
+
power: boolean;
|
|
107
|
+
}) => void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
declare const TapoDeviceTypes: {
|
|
111
|
+
ALL: string;
|
|
112
|
+
BULBS: string;
|
|
113
|
+
LIGHTSTRIPS: string;
|
|
114
|
+
PLUGS: string;
|
|
115
|
+
};
|
|
116
|
+
declare enum ETapoDeviceTypes {
|
|
117
|
+
ALL = "all",
|
|
118
|
+
BULBS = "bulb",
|
|
119
|
+
LIGHTSTRIPS = "lightstrip",
|
|
120
|
+
PLUGS = "plug"
|
|
121
|
+
}
|
|
122
|
+
declare const DeviceResTypes: {
|
|
123
|
+
BULB: string;
|
|
124
|
+
PLUG: string;
|
|
125
|
+
};
|
|
126
|
+
declare const DeviceSendValues: {
|
|
127
|
+
BULB: string;
|
|
128
|
+
LIGHTSTRIP: string;
|
|
129
|
+
PLUG: string;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
declare const tapo_constants_DeviceResTypes: typeof DeviceResTypes;
|
|
133
|
+
declare const tapo_constants_DeviceSendValues: typeof DeviceSendValues;
|
|
134
|
+
type tapo_constants_ETapoDeviceTypes = ETapoDeviceTypes;
|
|
135
|
+
declare const tapo_constants_ETapoDeviceTypes: typeof ETapoDeviceTypes;
|
|
136
|
+
declare const tapo_constants_TapoDeviceTypes: typeof TapoDeviceTypes;
|
|
137
|
+
declare namespace tapo_constants {
|
|
138
|
+
export {
|
|
139
|
+
tapo_constants_DeviceResTypes as DeviceResTypes,
|
|
140
|
+
tapo_constants_DeviceSendValues as DeviceSendValues,
|
|
141
|
+
tapo_constants_ETapoDeviceTypes as ETapoDeviceTypes,
|
|
142
|
+
tapo_constants_TapoDeviceTypes as TapoDeviceTypes,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { LightState, TapoApi, tapo_constants as TapoConstants, discover };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
var __export = (target, all) => {
|
|
23
|
+
for (var name in all)
|
|
24
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
25
|
+
};
|
|
26
|
+
var __copyProps = (to, from, except, desc) => {
|
|
27
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
28
|
+
for (let key of __getOwnPropNames(from))
|
|
29
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
30
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
31
|
+
}
|
|
32
|
+
return to;
|
|
33
|
+
};
|
|
34
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
35
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
36
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
37
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
38
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
39
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
40
|
+
mod
|
|
41
|
+
));
|
|
42
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
43
|
+
var __async = (__this, __arguments, generator) => {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
var fulfilled = (value) => {
|
|
46
|
+
try {
|
|
47
|
+
step(generator.next(value));
|
|
48
|
+
} catch (e) {
|
|
49
|
+
reject(e);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var rejected = (value) => {
|
|
53
|
+
try {
|
|
54
|
+
step(generator.throw(value));
|
|
55
|
+
} catch (e) {
|
|
56
|
+
reject(e);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
60
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/index.ts
|
|
65
|
+
var src_exports = {};
|
|
66
|
+
__export(src_exports, {
|
|
67
|
+
LightState: () => LightState,
|
|
68
|
+
TapoApi: () => TapoApi,
|
|
69
|
+
TapoConstants: () => tapo_constants_exports,
|
|
70
|
+
discover: () => discovery_default
|
|
71
|
+
});
|
|
72
|
+
module.exports = __toCommonJS(src_exports);
|
|
73
|
+
|
|
74
|
+
// src/discovery.ts
|
|
75
|
+
var import_lumia_rgb_types = require("@lumiastream/lumia-rgb-types");
|
|
76
|
+
var import_axios3 = __toESM(require("axios"));
|
|
77
|
+
var import_local_devices = __toESM(require("local-devices"));
|
|
78
|
+
|
|
79
|
+
// src/shared/cipher.ts
|
|
80
|
+
var import_crypto = __toESM(require("crypto"));
|
|
81
|
+
var import_util = __toESM(require("util"));
|
|
82
|
+
var RSA_CIPHER_ALGORITHM = "rsa";
|
|
83
|
+
var AES_CIPHER_ALGORITHM = "aes-128-cbc";
|
|
84
|
+
var PASSPHRASE = "top secret";
|
|
85
|
+
var generateKeyPair = () => __async(void 0, null, function* () {
|
|
86
|
+
const RSA_OPTIONS = {
|
|
87
|
+
modulusLength: 1024,
|
|
88
|
+
publicKeyEncoding: {
|
|
89
|
+
type: "spki",
|
|
90
|
+
format: "pem"
|
|
91
|
+
},
|
|
92
|
+
privateKeyEncoding: {
|
|
93
|
+
type: "pkcs1",
|
|
94
|
+
format: "pem",
|
|
95
|
+
cipher: "aes-256-cbc",
|
|
96
|
+
passphrase: PASSPHRASE
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const generateKeyPair2 = import_util.default.promisify(import_crypto.default.generateKeyPair);
|
|
100
|
+
return generateKeyPair2(RSA_CIPHER_ALGORITHM, RSA_OPTIONS);
|
|
101
|
+
});
|
|
102
|
+
var encrypt = (data, deviceKey) => {
|
|
103
|
+
var cipher = import_crypto.default.createCipheriv(AES_CIPHER_ALGORITHM, deviceKey.key, deviceKey.iv);
|
|
104
|
+
var ciphertext = cipher.update(Buffer.from(JSON.stringify(data)));
|
|
105
|
+
return Buffer.concat([ciphertext, cipher.final()]).toString("base64");
|
|
106
|
+
};
|
|
107
|
+
var decrypt = (data, deviceKey) => {
|
|
108
|
+
var cipher = import_crypto.default.createDecipheriv(AES_CIPHER_ALGORITHM, deviceKey.key, deviceKey.iv);
|
|
109
|
+
var ciphertext = cipher.update(Buffer.from(data, "base64"));
|
|
110
|
+
return JSON.parse(Buffer.concat([ciphertext, cipher.final()]).toString());
|
|
111
|
+
};
|
|
112
|
+
var readDeviceKey = (pemKey, privateKey) => {
|
|
113
|
+
const keyBytes = Buffer.from(pemKey, "base64");
|
|
114
|
+
const deviceKey = import_crypto.default.privateDecrypt(
|
|
115
|
+
{
|
|
116
|
+
key: privateKey,
|
|
117
|
+
padding: import_crypto.default.constants.RSA_PKCS1_PADDING,
|
|
118
|
+
passphrase: PASSPHRASE
|
|
119
|
+
},
|
|
120
|
+
keyBytes
|
|
121
|
+
);
|
|
122
|
+
return deviceKey;
|
|
123
|
+
};
|
|
124
|
+
var base64Encode = (data) => {
|
|
125
|
+
return Buffer.from(data).toString("base64");
|
|
126
|
+
};
|
|
127
|
+
var base64Decode = (data) => {
|
|
128
|
+
return Buffer.from(data, "base64").toString();
|
|
129
|
+
};
|
|
130
|
+
var shaDigest = (data) => {
|
|
131
|
+
var shasum = import_crypto.default.createHash("sha1");
|
|
132
|
+
shasum.update(data);
|
|
133
|
+
return shasum.digest("hex");
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/shared/helpers.ts
|
|
137
|
+
var import_axios = __toESM(require("axios"));
|
|
138
|
+
var throwErrorIfFound = (responseData) => {
|
|
139
|
+
const errorCode = responseData["error_code"];
|
|
140
|
+
if (errorCode) {
|
|
141
|
+
switch (errorCode) {
|
|
142
|
+
case 0:
|
|
143
|
+
return;
|
|
144
|
+
case -1010:
|
|
145
|
+
throw new Error("Invalid public key length");
|
|
146
|
+
case -1012:
|
|
147
|
+
throw new Error("Invalid terminal UUID");
|
|
148
|
+
case -1501:
|
|
149
|
+
throw new Error("Invalid request or credentials");
|
|
150
|
+
case -1002:
|
|
151
|
+
throw new Error("Incorrect request");
|
|
152
|
+
case -1003:
|
|
153
|
+
throw new Error("JSON format error");
|
|
154
|
+
case -20601:
|
|
155
|
+
throw new Error("Incorrect email or password");
|
|
156
|
+
case -20675:
|
|
157
|
+
throw new Error("Cloud token expired or invalid");
|
|
158
|
+
case 9999:
|
|
159
|
+
throw new Error("Device token expired or invalid");
|
|
160
|
+
default:
|
|
161
|
+
throw new Error(`Unexpected Error Code: ${errorCode} (${responseData["msg"]})`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
var handshake = (deviceIp) => __async(void 0, null, function* () {
|
|
166
|
+
const keyPair = yield generateKeyPair();
|
|
167
|
+
const handshakeRequest = {
|
|
168
|
+
method: "handshake",
|
|
169
|
+
params: {
|
|
170
|
+
key: keyPair.publicKey
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const response = yield (0, import_axios.default)({
|
|
174
|
+
method: "post",
|
|
175
|
+
url: `http://${deviceIp}/app`,
|
|
176
|
+
data: handshakeRequest
|
|
177
|
+
});
|
|
178
|
+
throwErrorIfFound(response.data);
|
|
179
|
+
const setCookieHeader = response.headers["set-cookie"][0];
|
|
180
|
+
const sessionCookie = setCookieHeader.substring(0, setCookieHeader.indexOf(";"));
|
|
181
|
+
const deviceKey = readDeviceKey(response.data.result.key, keyPair.privateKey);
|
|
182
|
+
return {
|
|
183
|
+
key: deviceKey.subarray(0, 16),
|
|
184
|
+
iv: deviceKey.subarray(16, 32),
|
|
185
|
+
deviceIp,
|
|
186
|
+
sessionCookie
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
var securePassthrough = (deviceRequest, deviceKey) => __async(void 0, null, function* () {
|
|
190
|
+
const encryptedRequest = encrypt(deviceRequest, deviceKey);
|
|
191
|
+
const securePassthroughRequest = {
|
|
192
|
+
method: "securePassthrough",
|
|
193
|
+
params: {
|
|
194
|
+
request: encryptedRequest
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const response = yield (0, import_axios.default)({
|
|
198
|
+
method: "post",
|
|
199
|
+
url: `http://${deviceKey.deviceIp}/app?token=${deviceKey.token}`,
|
|
200
|
+
data: securePassthroughRequest,
|
|
201
|
+
headers: {
|
|
202
|
+
Cookie: deviceKey.sessionCookie
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
throwErrorIfFound(response.data);
|
|
206
|
+
const decryptedResponse = decrypt(response.data.result.response, deviceKey);
|
|
207
|
+
throwErrorIfFound(decryptedResponse);
|
|
208
|
+
return decryptedResponse.result;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// src/tapo-api.ts
|
|
212
|
+
var import_axios2 = __toESM(require("axios"));
|
|
213
|
+
|
|
214
|
+
// src/lightstate.ts
|
|
215
|
+
var import_lumia_rgb_utils = require("@lumiastream/lumia-rgb-utils");
|
|
216
|
+
var SuperState = class {
|
|
217
|
+
constructor() {
|
|
218
|
+
this._values = {};
|
|
219
|
+
// In Milliseconds
|
|
220
|
+
// 0.0 – infinity seconds
|
|
221
|
+
this.transition = (value) => {
|
|
222
|
+
this._values["transition_period"] = value;
|
|
223
|
+
return this;
|
|
224
|
+
};
|
|
225
|
+
this.duration = this.transition;
|
|
226
|
+
this.getValues = () => {
|
|
227
|
+
return this._values;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var LightState = class extends SuperState {
|
|
232
|
+
constructor(values) {
|
|
233
|
+
super();
|
|
234
|
+
this.create = (values) => {
|
|
235
|
+
if (values) {
|
|
236
|
+
Object.keys(values).forEach((value) => {
|
|
237
|
+
let fn;
|
|
238
|
+
if (this.hasOwnProperty(value)) {
|
|
239
|
+
fn = this[value];
|
|
240
|
+
if ((0, import_lumia_rgb_utils.isFunction)(fn) && (0, import_lumia_rgb_utils.isTruly)(values[value])) {
|
|
241
|
+
fn.apply(this, [values[value]]);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
return this;
|
|
247
|
+
};
|
|
248
|
+
this.on = (value = true) => {
|
|
249
|
+
this._values["device_on"] = value;
|
|
250
|
+
return this;
|
|
251
|
+
};
|
|
252
|
+
this.turnOn = this.on;
|
|
253
|
+
this.off = () => {
|
|
254
|
+
this.on(false);
|
|
255
|
+
return this;
|
|
256
|
+
};
|
|
257
|
+
this.turnOff = this.off;
|
|
258
|
+
this.mode = (value = "normal") => {
|
|
259
|
+
if (value === "normal") {
|
|
260
|
+
this._values["color_temp"] = 0;
|
|
261
|
+
}
|
|
262
|
+
this._values["mode"] = value;
|
|
263
|
+
return this;
|
|
264
|
+
};
|
|
265
|
+
// 0 - 360
|
|
266
|
+
this.hue = (value) => {
|
|
267
|
+
this._values["hue"] = value;
|
|
268
|
+
this._values["color_temp"] = 0;
|
|
269
|
+
return this;
|
|
270
|
+
};
|
|
271
|
+
// Bri 0-100
|
|
272
|
+
this.bri = (value) => {
|
|
273
|
+
this._values["brightness"] = value;
|
|
274
|
+
return this;
|
|
275
|
+
};
|
|
276
|
+
this.brightness = this.bri;
|
|
277
|
+
this.sat = (value) => {
|
|
278
|
+
this._values["saturation"] = value;
|
|
279
|
+
return this;
|
|
280
|
+
};
|
|
281
|
+
this.saturation = this.sat;
|
|
282
|
+
// Color Temperature 2500 - 9000 in kelvin
|
|
283
|
+
// Will override hue if passed
|
|
284
|
+
this.temp = (value) => {
|
|
285
|
+
this._values["color_temp"] = value;
|
|
286
|
+
return this;
|
|
287
|
+
};
|
|
288
|
+
this.colorTemperature = this.temp;
|
|
289
|
+
// Can take in an array or object
|
|
290
|
+
this.hsv = (value) => {
|
|
291
|
+
let convertedHsv = [];
|
|
292
|
+
if (Array.isArray(value)) {
|
|
293
|
+
convertedHsv = value;
|
|
294
|
+
} else {
|
|
295
|
+
convertedHsv = [value.h, value.s, value.v];
|
|
296
|
+
}
|
|
297
|
+
this.hue(convertedHsv[0]);
|
|
298
|
+
this.sat(convertedHsv[1]);
|
|
299
|
+
this.bri(convertedHsv[2]);
|
|
300
|
+
return this;
|
|
301
|
+
};
|
|
302
|
+
// Can take in an array or object
|
|
303
|
+
this.rgb = (value) => {
|
|
304
|
+
let hsv;
|
|
305
|
+
if (Array.isArray(value)) {
|
|
306
|
+
hsv = (0, import_lumia_rgb_utils.rgb2hsv)(value);
|
|
307
|
+
} else {
|
|
308
|
+
hsv = (0, import_lumia_rgb_utils.rgb2hsv)([value.r, value.g, value.b]);
|
|
309
|
+
}
|
|
310
|
+
return this.hsv(hsv);
|
|
311
|
+
};
|
|
312
|
+
// Can take in an object will first check for ct and if it exists and the light will worki with it it'll use color temp
|
|
313
|
+
this.color = (value) => {
|
|
314
|
+
if (Array.isArray(value)) {
|
|
315
|
+
value = { r: value[0], g: value[1], b: value[2] };
|
|
316
|
+
}
|
|
317
|
+
if (value.ct) {
|
|
318
|
+
return this.temp(value.ct);
|
|
319
|
+
} else {
|
|
320
|
+
return this.rgb({ r: value.r, g: value.g, b: value.b });
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
if (values)
|
|
324
|
+
this.create(values);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// src/tapo-api.ts
|
|
329
|
+
var import_fetch_cove = require("@lumiastream/fetch-cove");
|
|
330
|
+
var import_lumia_rgb_utils2 = require("@lumiastream/lumia-rgb-utils");
|
|
331
|
+
var TapoApi = class {
|
|
332
|
+
constructor(config) {
|
|
333
|
+
this._baseUrl = "https://eu-wap.tplinkcloud.com/";
|
|
334
|
+
// https://n-euw1-wap-gw.tplinkcloud.com
|
|
335
|
+
this._baseTapoCareUrl = "https://euw1-app-tapo-care.i.tplinknbu.com";
|
|
336
|
+
this._config = {
|
|
337
|
+
authToken: null,
|
|
338
|
+
timeout: 1e4,
|
|
339
|
+
httpTimeout: 4e3
|
|
340
|
+
};
|
|
341
|
+
this._token = "";
|
|
342
|
+
this._devices = /* @__PURE__ */ new Map();
|
|
343
|
+
this.auth = (_0) => __async(this, [_0], function* ({ email, password }) {
|
|
344
|
+
const loginRequest = {
|
|
345
|
+
method: "login",
|
|
346
|
+
params: {
|
|
347
|
+
appType: "Tapo_Android",
|
|
348
|
+
cloudPassword: password,
|
|
349
|
+
cloudUserName: email,
|
|
350
|
+
terminalUUID: (0, import_lumia_rgb_utils2.uuidv4)()
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
const response = yield (0, import_axios2.default)({
|
|
354
|
+
method: "post",
|
|
355
|
+
url: this._baseUrl,
|
|
356
|
+
data: loginRequest
|
|
357
|
+
});
|
|
358
|
+
throwErrorIfFound(response.data);
|
|
359
|
+
this._token = response.data.result.token;
|
|
360
|
+
return this._token;
|
|
361
|
+
});
|
|
362
|
+
this.setup = (_0) => __async(this, [_0], function* ({ email, password, devices }) {
|
|
363
|
+
const promises = devices.map((device) => __async(this, null, function* () {
|
|
364
|
+
const deviceKey = yield handshake(device.ip);
|
|
365
|
+
const loginDeviceRequest = {
|
|
366
|
+
method: "login_device",
|
|
367
|
+
params: {
|
|
368
|
+
username: base64Encode(shaDigest(email)),
|
|
369
|
+
password: base64Encode(password)
|
|
370
|
+
},
|
|
371
|
+
requestTimeMils: 0
|
|
372
|
+
};
|
|
373
|
+
const loginDeviceResponse = yield securePassthrough(loginDeviceRequest, deviceKey);
|
|
374
|
+
deviceKey.token = loginDeviceResponse.token;
|
|
375
|
+
this._devices.set(device.id, deviceKey);
|
|
376
|
+
return;
|
|
377
|
+
}));
|
|
378
|
+
yield Promise.all(promises);
|
|
379
|
+
return this._devices;
|
|
380
|
+
});
|
|
381
|
+
// Change state using lightstate
|
|
382
|
+
this.sendState = (config) => {
|
|
383
|
+
const deviceKey = this._devices.get(config.device.id);
|
|
384
|
+
let shouldWait = false;
|
|
385
|
+
if (config.fetchConfig && config.fetchConfig.shouldWait) {
|
|
386
|
+
shouldWait = true;
|
|
387
|
+
}
|
|
388
|
+
if (!deviceKey) {
|
|
389
|
+
return Promise.resolve(false);
|
|
390
|
+
}
|
|
391
|
+
const values = config.state.getValues();
|
|
392
|
+
const deviceRequest = {
|
|
393
|
+
method: "set_device_info",
|
|
394
|
+
params: values,
|
|
395
|
+
terminalUUID: (0, import_lumia_rgb_utils2.uuidv4)()
|
|
396
|
+
};
|
|
397
|
+
const encryptedRequest = encrypt(deviceRequest, deviceKey);
|
|
398
|
+
const securePassthroughRequest = {
|
|
399
|
+
method: "securePassthrough",
|
|
400
|
+
params: {
|
|
401
|
+
request: encryptedRequest
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
return (0, import_fetch_cove.fetchWork)({
|
|
405
|
+
url: `http://${deviceKey.deviceIp}/app?token=${deviceKey.token}`,
|
|
406
|
+
data: {
|
|
407
|
+
method: "POST",
|
|
408
|
+
body: securePassthroughRequest,
|
|
409
|
+
headers: {
|
|
410
|
+
Cookie: deviceKey.sessionCookie
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
shouldWait
|
|
414
|
+
});
|
|
415
|
+
};
|
|
416
|
+
this.sendPower = (config) => {
|
|
417
|
+
this.sendState({ device: config.device, state: new LightState({ on: config.power }) });
|
|
418
|
+
};
|
|
419
|
+
this.axiosInstance = import_axios2.default.create();
|
|
420
|
+
this.axiosInstance.defaults.timeout = (config == null ? void 0 : config.httpTimeout) || 4e3;
|
|
421
|
+
this._config = __spreadValues(__spreadValues({}, this._config), config);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// src/discovery.ts
|
|
426
|
+
var discover = (config) => __async(void 0, null, function* () {
|
|
427
|
+
var _a;
|
|
428
|
+
const api = new TapoApi(config);
|
|
429
|
+
let token = config.token;
|
|
430
|
+
if (!token && config.email && config.password) {
|
|
431
|
+
token = yield api.auth({ email: config.email, password: config.password });
|
|
432
|
+
}
|
|
433
|
+
const getDeviceRequest = {
|
|
434
|
+
method: "getDeviceList"
|
|
435
|
+
};
|
|
436
|
+
const response = yield (0, import_axios3.default)({
|
|
437
|
+
method: "post",
|
|
438
|
+
url: `${api._baseUrl}?token=${token}`,
|
|
439
|
+
data: getDeviceRequest
|
|
440
|
+
});
|
|
441
|
+
throwErrorIfFound(response.data);
|
|
442
|
+
const devices = [];
|
|
443
|
+
const localDevices = yield (0, import_local_devices.default)({ skipNameResolution: true });
|
|
444
|
+
(_a = response.data.result) == null ? void 0 : _a.deviceList.map((deviceInfo) => __async(void 0, null, function* () {
|
|
445
|
+
var _a2, _b;
|
|
446
|
+
if (!deviceInfo.ip) {
|
|
447
|
+
const findableMac = deviceInfo.deviceMac.replace(/:/g, "").toUpperCase();
|
|
448
|
+
const found = localDevices.find((device) => findableMac == device.mac.replace(/:/g, "").toUpperCase());
|
|
449
|
+
if (found) {
|
|
450
|
+
deviceInfo.ip = found.ip;
|
|
451
|
+
} else {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
switch (deviceInfo.deviceType) {
|
|
456
|
+
case "IOT.SMARTBULB":
|
|
457
|
+
case "SMART.TAPOBULB": {
|
|
458
|
+
if (config.types && config.types !== import_lumia_rgb_types.ILumiaDeviceType.LIGHT) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const isTapo = deviceInfo.deviceType === "SMART.TAPOBULB";
|
|
462
|
+
const alias = (_a2 = isTapo ? base64Decode(deviceInfo.alias) : deviceInfo.alias) != null ? _a2 : deviceInfo.deviceName;
|
|
463
|
+
devices.push({
|
|
464
|
+
name: alias,
|
|
465
|
+
id: deviceInfo.deviceMac,
|
|
466
|
+
address: `http://${deviceInfo.ip}`,
|
|
467
|
+
host: deviceInfo.ip,
|
|
468
|
+
lumiaInfo: {
|
|
469
|
+
alias,
|
|
470
|
+
identifier: deviceInfo.deviceMac,
|
|
471
|
+
serial: deviceInfo.hwId,
|
|
472
|
+
lumiaType: import_lumia_rgb_types.ILumiaDeviceType.LIGHT,
|
|
473
|
+
zonable: false,
|
|
474
|
+
maxZones: 1,
|
|
475
|
+
zones: [],
|
|
476
|
+
rgb: true,
|
|
477
|
+
white: true,
|
|
478
|
+
connectionType: import_lumia_rgb_types.ILumiaDeviceConnectionType.WIFI,
|
|
479
|
+
brand: isTapo ? import_lumia_rgb_types.ILumiaDeviceBrands.TPLINK : import_lumia_rgb_types.ILumiaDeviceBrands.TPLINK,
|
|
480
|
+
product: deviceInfo == null ? void 0 : deviceInfo.deviceModel
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
case "IOT.SMARTPLUGSWITCH":
|
|
485
|
+
case "SMART.TAPOPLUG": {
|
|
486
|
+
if (config.types && config.types !== import_lumia_rgb_types.ILumiaDeviceType.PLUG) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const isTapo = deviceInfo.deviceType === "SMART.TAPOPLUG";
|
|
490
|
+
const alias = (_b = isTapo ? base64Decode(deviceInfo.alias) : deviceInfo.alias) != null ? _b : deviceInfo.deviceName;
|
|
491
|
+
devices.push({
|
|
492
|
+
name: alias,
|
|
493
|
+
id: deviceInfo.deviceMac,
|
|
494
|
+
address: `http://${deviceInfo.ip}`,
|
|
495
|
+
host: deviceInfo.ip,
|
|
496
|
+
lumiaInfo: {
|
|
497
|
+
alias,
|
|
498
|
+
identifier: deviceInfo.deviceMac,
|
|
499
|
+
serial: deviceInfo.hwId,
|
|
500
|
+
lumiaType: import_lumia_rgb_types.ILumiaDeviceType.PLUG,
|
|
501
|
+
zonable: false,
|
|
502
|
+
maxZones: 0,
|
|
503
|
+
zones: [],
|
|
504
|
+
rgb: false,
|
|
505
|
+
white: false,
|
|
506
|
+
connectionType: import_lumia_rgb_types.ILumiaDeviceConnectionType.WIFI,
|
|
507
|
+
brand: isTapo ? import_lumia_rgb_types.ILumiaDeviceBrands.TPLINK : import_lumia_rgb_types.ILumiaDeviceBrands.TPLINK,
|
|
508
|
+
product: deviceInfo == null ? void 0 : deviceInfo.deviceModel
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}));
|
|
514
|
+
return devices;
|
|
515
|
+
});
|
|
516
|
+
var discovery_default = discover;
|
|
517
|
+
|
|
518
|
+
// src/types/tapo.constants.ts
|
|
519
|
+
var tapo_constants_exports = {};
|
|
520
|
+
__export(tapo_constants_exports, {
|
|
521
|
+
DeviceResTypes: () => DeviceResTypes,
|
|
522
|
+
DeviceSendValues: () => DeviceSendValues,
|
|
523
|
+
ETapoDeviceTypes: () => ETapoDeviceTypes,
|
|
524
|
+
TapoDeviceTypes: () => TapoDeviceTypes
|
|
525
|
+
});
|
|
526
|
+
var TapoDeviceTypes = {
|
|
527
|
+
ALL: "all",
|
|
528
|
+
BULBS: "bulb",
|
|
529
|
+
LIGHTSTRIPS: "lightstrip",
|
|
530
|
+
PLUGS: "plug"
|
|
531
|
+
};
|
|
532
|
+
var ETapoDeviceTypes = /* @__PURE__ */ ((ETapoDeviceTypes2) => {
|
|
533
|
+
ETapoDeviceTypes2["ALL"] = "all";
|
|
534
|
+
ETapoDeviceTypes2["BULBS"] = "bulb";
|
|
535
|
+
ETapoDeviceTypes2["LIGHTSTRIPS"] = "lightstrip";
|
|
536
|
+
ETapoDeviceTypes2["PLUGS"] = "plug";
|
|
537
|
+
return ETapoDeviceTypes2;
|
|
538
|
+
})(ETapoDeviceTypes || {});
|
|
539
|
+
var DeviceResTypes = {
|
|
540
|
+
BULB: "IOT.SMARTPLUGSWITCH",
|
|
541
|
+
PLUG: "IOT.SMARTPLUGSWITCH"
|
|
542
|
+
};
|
|
543
|
+
var DeviceSendValues = {
|
|
544
|
+
BULB: "smartlife.iot.smartbulb.lightingservice",
|
|
545
|
+
LIGHTSTRIP: "smartlife.iot.lightStrip",
|
|
546
|
+
PLUG: "system"
|
|
547
|
+
};
|
|
548
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
549
|
+
0 && (module.exports = {
|
|
550
|
+
LightState,
|
|
551
|
+
TapoApi,
|
|
552
|
+
TapoConstants,
|
|
553
|
+
discover
|
|
554
|
+
});
|