homebridge-tuya-without-developer-account 1.0.0
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/CHANGELOG.md +10 -0
- package/LICENSE +20 -0
- package/PUBLISHING.md +80 -0
- package/README.md +233 -0
- package/SUPPORTED_DEVICES.md +206 -0
- package/config.schema.json +131 -0
- package/dist/cloud/api/TuyaHACloudAPI.js +286 -0
- package/dist/cloud/api/TuyaHASharingMQ.js +114 -0
- package/dist/cloud/device/TuyaDevice.js +50 -0
- package/dist/cloud/device/TuyaHADeviceManager.js +355 -0
- package/dist/index.js +7 -0
- package/dist/platform.js +397 -0
- package/dist/settings.js +18 -0
- package/dist/shared/AccessoryFactory.js +276 -0
- package/dist/shared/accessories/AccessoryFactory.js +305 -0
- package/dist/shared/accessories/AirConditionerAccessory.js +307 -0
- package/dist/shared/accessories/AirPurifierAccessory.js +90 -0
- package/dist/shared/accessories/AirQualitySensorAccessory.js +30 -0
- package/dist/shared/accessories/BaseAccessory.js +406 -0
- package/dist/shared/accessories/BlindsAccessory.js +199 -0
- package/dist/shared/accessories/CameraAccessory.js +121 -0
- package/dist/shared/accessories/CarbonDioxideSensorAccessory.js +52 -0
- package/dist/shared/accessories/CarbonMonoxideSensorAccessory.js +52 -0
- package/dist/shared/accessories/ContactSensorAccessory.js +30 -0
- package/dist/shared/accessories/DehumidifierAccessory.js +68 -0
- package/dist/shared/accessories/DiffuserAccessory.js +55 -0
- package/dist/shared/accessories/DimmerAccessory.js +94 -0
- package/dist/shared/accessories/DoorbellAccessory.js +91 -0
- package/dist/shared/accessories/ExtractionHoodAccessory.js +120 -0
- package/dist/shared/accessories/FanAccessory.js +129 -0
- package/dist/shared/accessories/GarageDoorAccessory.js +69 -0
- package/dist/shared/accessories/HeaterAccessory.js +102 -0
- package/dist/shared/accessories/HeaterAccessory_old.js +96 -0
- package/dist/shared/accessories/HumanPresenceSensorAccessory.js +20 -0
- package/dist/shared/accessories/HumidifierAccessory.js +137 -0
- package/dist/shared/accessories/IRAirConditionerAccessory.js +278 -0
- package/dist/shared/accessories/IRControlHubAccessory.js +49 -0
- package/dist/shared/accessories/IRControlHubSubAccessory.js +52 -0
- package/dist/shared/accessories/IRGenericAccessory.js +49 -0
- package/dist/shared/accessories/LeakSensorAccessory.js +36 -0
- package/dist/shared/accessories/LightAccessory.js +36 -0
- package/dist/shared/accessories/LightSensorAccessory.js +32 -0
- package/dist/shared/accessories/LocationWeatherAccessory.js +72 -0
- package/dist/shared/accessories/LockAccessory.js +56 -0
- package/dist/shared/accessories/MotionSensorAccessory.js +20 -0
- package/dist/shared/accessories/OutletAccessory.js +23 -0
- package/dist/shared/accessories/PetFeederAccessory.js +139 -0
- package/dist/shared/accessories/SceneAccessory.js +32 -0
- package/dist/shared/accessories/SceneSwitchAccessory.js +44 -0
- package/dist/shared/accessories/SecuritySystemAccessory.js +30 -0
- package/dist/shared/accessories/SmokeSensorAccessory.js +35 -0
- package/dist/shared/accessories/SwitchAccessory.js +148 -0
- package/dist/shared/accessories/TemperatureHumiditySensorAccessory.js +24 -0
- package/dist/shared/accessories/ThermostatAccessory.js +192 -0
- package/dist/shared/accessories/TowerRackAccessory.js +157 -0
- package/dist/shared/accessories/ValveAccessory.js +45 -0
- package/dist/shared/accessories/VibrationSensorAccessory.js +46 -0
- package/dist/shared/accessories/WeatherStationAccessory.js +58 -0
- package/dist/shared/accessories/WetBulbGlobeTemperatureAccessory.js +23 -0
- package/dist/shared/accessories/WhiteNoiseLightAccessory.js +59 -0
- package/dist/shared/accessories/WindowAccessory.js +14 -0
- package/dist/shared/accessories/WindowCoveringAccessory.js +156 -0
- package/dist/shared/accessories/WirelessSwitchAccessory.js +42 -0
- package/dist/shared/accessories/characteristic/Active.js +22 -0
- package/dist/shared/accessories/characteristic/AirQuality.js +74 -0
- package/dist/shared/accessories/characteristic/CurrentRelativeHumidity.js +23 -0
- package/dist/shared/accessories/characteristic/CurrentTemperature.js +23 -0
- package/dist/shared/accessories/characteristic/CurrentWeather.js +49 -0
- package/dist/shared/accessories/characteristic/CurrentWeatherByOpenMeteo.js +49 -0
- package/dist/shared/accessories/characteristic/CurrentWetBulbGlobeTemperature.js +48 -0
- package/dist/shared/accessories/characteristic/EnergyUsage.js +98 -0
- package/dist/shared/accessories/characteristic/Light.js +268 -0
- package/dist/shared/accessories/characteristic/LightSensor.js +23 -0
- package/dist/shared/accessories/characteristic/LockPhysicalControls.js +21 -0
- package/dist/shared/accessories/characteristic/MotionDetected.js +22 -0
- package/dist/shared/accessories/characteristic/Name.js +15 -0
- package/dist/shared/accessories/characteristic/OccupancyDetected.js +19 -0
- package/dist/shared/accessories/characteristic/On.js +25 -0
- package/dist/shared/accessories/characteristic/OutletInUse.js +14 -0
- package/dist/shared/accessories/characteristic/ProgrammableSwitchEvent.js +89 -0
- package/dist/shared/accessories/characteristic/RelativeHumidityDehumidifierThreshold.js +28 -0
- package/dist/shared/accessories/characteristic/RotationSpeed.js +78 -0
- package/dist/shared/accessories/characteristic/SecuritySystemState.js +74 -0
- package/dist/shared/accessories/characteristic/SwingMode.js +21 -0
- package/dist/shared/accessories/characteristic/TargetTemperature.js +29 -0
- package/dist/shared/accessories/characteristic/TemperatureDisplayUnits.js +25 -0
- package/dist/shared/util/ConfigHash.js +79 -0
- package/dist/shared/util/FfmpegStreamingProcess.js +126 -0
- package/dist/shared/util/InfraredTool.js +392 -0
- package/dist/shared/util/Logger.js +42 -0
- package/dist/shared/util/TuyaRecordingDelegate.js +22 -0
- package/dist/shared/util/TuyaStreamDelegate.js +329 -0
- package/dist/shared/util/color.js +23 -0
- package/dist/shared/util/util.js +135 -0
- package/homebridge-ui/public/index.html +329 -0
- package/homebridge-ui/server.js +224 -0
- package/package.json +61 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
/* eslint-disable max-len */
|
|
40
|
+
const events_1 = __importDefault(require("events"));
|
|
41
|
+
const Logger_1 = require("../../shared/util/Logger");
|
|
42
|
+
const TuyaDevice_1 = __importStar(require("./TuyaDevice"));
|
|
43
|
+
const TuyaHASharingMQ_1 = __importDefault(require("../api/TuyaHASharingMQ"));
|
|
44
|
+
var Events;
|
|
45
|
+
(function (Events) {
|
|
46
|
+
Events["DEVICE_ADD"] = "DEVICE_ADD";
|
|
47
|
+
Events["DEVICE_INFO_UPDATE"] = "DEVICE_INFO_UPDATE";
|
|
48
|
+
Events["DEVICE_STATUS_UPDATE"] = "DEVICE_STATUS_UPDATE";
|
|
49
|
+
Events["DEVICE_DELETE"] = "DEVICE_DELETE";
|
|
50
|
+
})(Events || (Events = {}));
|
|
51
|
+
class TuyaHADeviceManager extends events_1.default {
|
|
52
|
+
static { this.Events = Events; }
|
|
53
|
+
constructor(api, debug = false) {
|
|
54
|
+
super();
|
|
55
|
+
this.api = api;
|
|
56
|
+
this.debug = debug;
|
|
57
|
+
this.ownerIDs = [];
|
|
58
|
+
this.devices = [];
|
|
59
|
+
const baseLog = this.api.log.log || this.api.log;
|
|
60
|
+
this.log = new Logger_1.PrefixLogger(baseLog, TuyaHADeviceManager.name, debug);
|
|
61
|
+
}
|
|
62
|
+
createVirtualDevice(baseDevice, uuid) {
|
|
63
|
+
const cloneDevice = new TuyaDevice_1.default(baseDevice);
|
|
64
|
+
const uniqueId = uuid || Date.now().toString(36) + Math.random().toString(36).substring(2);
|
|
65
|
+
cloneDevice.id = `${uniqueId}`;
|
|
66
|
+
cloneDevice.uuid = `${uniqueId}`;
|
|
67
|
+
cloneDevice.name = 'Virtual Device';
|
|
68
|
+
cloneDevice.product_id = `${uniqueId}`;
|
|
69
|
+
cloneDevice.product_name = 'virtual product';
|
|
70
|
+
cloneDevice.sub = true;
|
|
71
|
+
cloneDevice.ip = '';
|
|
72
|
+
cloneDevice.parent_id = baseDevice.id;
|
|
73
|
+
cloneDevice.remote_keys = undefined;
|
|
74
|
+
return cloneDevice;
|
|
75
|
+
}
|
|
76
|
+
getDevice(deviceID) {
|
|
77
|
+
return Array.from(this.devices).find(device => device.id === deviceID);
|
|
78
|
+
}
|
|
79
|
+
async getHomeList() {
|
|
80
|
+
const res = await this.api.get('/v1.0/m/life/users/homes');
|
|
81
|
+
if (res.success && Array.isArray(res.result)) {
|
|
82
|
+
res.result = res.result.map(home => ({
|
|
83
|
+
home_id: home.ownerId || home.home_id || home.id,
|
|
84
|
+
name: home.name,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
return res;
|
|
88
|
+
}
|
|
89
|
+
async getHomeDeviceList(homeID) {
|
|
90
|
+
return this.api.get('/v1.0/m/life/ha/home/devices', { homeId: homeID });
|
|
91
|
+
}
|
|
92
|
+
async updateDevices(homeIDList) {
|
|
93
|
+
const devices = [];
|
|
94
|
+
for (const homeID of homeIDList) {
|
|
95
|
+
const res = await this.getHomeDeviceList(String(homeID));
|
|
96
|
+
if (!res.success) {
|
|
97
|
+
this.log.warn('Fetching HA QR device list failed for homeId=%s. code=%s, msg=%s', homeID, res.code, res.msg);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const rawDevices = Array.isArray(res.result) ? res.result : [];
|
|
101
|
+
for (const rawDevice of rawDevices) {
|
|
102
|
+
const device = await this.convertHADevice(rawDevice, String(homeID));
|
|
103
|
+
devices.push(device);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
this.devices = devices;
|
|
107
|
+
return devices;
|
|
108
|
+
}
|
|
109
|
+
async updateDevice(deviceID) {
|
|
110
|
+
const devices = await this.queryDevicesByIds([deviceID]);
|
|
111
|
+
const device = devices[0];
|
|
112
|
+
if (!device) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const oldDevice = this.getDevice(deviceID);
|
|
116
|
+
if (oldDevice) {
|
|
117
|
+
this.devices.splice(this.devices.indexOf(oldDevice), 1);
|
|
118
|
+
}
|
|
119
|
+
this.devices.push(device);
|
|
120
|
+
return device;
|
|
121
|
+
}
|
|
122
|
+
async queryDevicesByIds(ids) {
|
|
123
|
+
const res = await this.api.get('/v1.0/m/life/ha/devices/detail', { devIds: ids.join(',') });
|
|
124
|
+
if (!res.success || !Array.isArray(res.result)) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
return Promise.all(res.result.map(raw => this.convertHADevice(raw, raw.owner_id || raw.ownerId || '')));
|
|
128
|
+
}
|
|
129
|
+
async convertHADevice(rawDevice, homeID) {
|
|
130
|
+
const status = Array.isArray(rawDevice.status)
|
|
131
|
+
? rawDevice.status.filter(item => item && item.code !== undefined).map(item => ({ code: item.code, value: item.value }))
|
|
132
|
+
: Object.entries(rawDevice.status || {}).map(([code, value]) => ({ code, value }));
|
|
133
|
+
const device = new TuyaDevice_1.default({
|
|
134
|
+
id: rawDevice.id || rawDevice.devId || rawDevice.device_id,
|
|
135
|
+
uuid: rawDevice.uuid || rawDevice.id || rawDevice.devId || rawDevice.device_id,
|
|
136
|
+
name: rawDevice.name || rawDevice.product_name || rawDevice.productName || rawDevice.id,
|
|
137
|
+
online: rawDevice.online !== undefined ? rawDevice.online : true,
|
|
138
|
+
owner_id: String(rawDevice.owner_id || rawDevice.ownerId || homeID),
|
|
139
|
+
product_id: rawDevice.product_id || rawDevice.productId || rawDevice.productKey || '',
|
|
140
|
+
product_name: rawDevice.product_name || rawDevice.productName || rawDevice.name || '',
|
|
141
|
+
model: rawDevice.model,
|
|
142
|
+
icon: rawDevice.icon || '',
|
|
143
|
+
category: rawDevice.category || rawDevice.categoryCode || rawDevice.productCategory || '',
|
|
144
|
+
schema: [],
|
|
145
|
+
status,
|
|
146
|
+
ip: rawDevice.ip || '',
|
|
147
|
+
lat: rawDevice.lat || '',
|
|
148
|
+
lon: rawDevice.lon || '',
|
|
149
|
+
time_zone: rawDevice.time_zone || rawDevice.timeZone || '',
|
|
150
|
+
create_time: rawDevice.create_time || rawDevice.createTime || 0,
|
|
151
|
+
active_time: rawDevice.active_time || rawDevice.activeTime || 0,
|
|
152
|
+
update_time: rawDevice.update_time || rawDevice.updateTime || 0,
|
|
153
|
+
sub: rawDevice.sub || false,
|
|
154
|
+
parent_id: rawDevice.parent_id || rawDevice.parentId,
|
|
155
|
+
remote_keys: rawDevice.remote_keys,
|
|
156
|
+
});
|
|
157
|
+
device.schema = await this.getDeviceSchema(device.id);
|
|
158
|
+
return device;
|
|
159
|
+
}
|
|
160
|
+
async getDeviceSchema(deviceID) {
|
|
161
|
+
const res = await this.api.get(`/v1.1/m/life/${deviceID}/specifications`);
|
|
162
|
+
if (!res.success) {
|
|
163
|
+
this.log.warn('Get HA QR device specification failed. devId = %s, code = %s, msg = %s', deviceID, res.code, res.msg);
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
const result = res.result || {};
|
|
167
|
+
const status = Array.isArray(result.status) ? result.status : [];
|
|
168
|
+
const functions = Array.isArray(result.functions) ? result.functions : [];
|
|
169
|
+
const schemas = new Map();
|
|
170
|
+
for (const { code, type, values } of [...status, ...functions]) {
|
|
171
|
+
if (!code || schemas.has(code)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const read = status.find(schema => schema.code === code) !== undefined;
|
|
175
|
+
const write = functions.find(schema => schema.code === code) !== undefined;
|
|
176
|
+
let mode = TuyaDevice_1.TuyaDeviceSchemaMode.UNKNOWN;
|
|
177
|
+
if (read && write) {
|
|
178
|
+
mode = TuyaDevice_1.TuyaDeviceSchemaMode.READ_WRITE;
|
|
179
|
+
}
|
|
180
|
+
else if (read && !write) {
|
|
181
|
+
mode = TuyaDevice_1.TuyaDeviceSchemaMode.READ_ONLY;
|
|
182
|
+
}
|
|
183
|
+
else if (!read && write) {
|
|
184
|
+
mode = TuyaDevice_1.TuyaDeviceSchemaMode.WRITE_ONLY;
|
|
185
|
+
}
|
|
186
|
+
let property;
|
|
187
|
+
try {
|
|
188
|
+
property = typeof values === 'string' ? JSON.parse(values) : values;
|
|
189
|
+
schemas.set(code, { code, mode, type, property });
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Ignore invalid schema data, matching the original plugin behavior.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return Array.from(schemas.values()).sort((a, b) => a.code > b.code ? 1 : -1);
|
|
196
|
+
}
|
|
197
|
+
async getDeviceDetails(deviceID) {
|
|
198
|
+
const res = await this.api.get('/v1.0/m/life/ha/devices/detail', { devIds: deviceID });
|
|
199
|
+
if (res.success && Array.isArray(res.result)) {
|
|
200
|
+
res.result = res.result[0];
|
|
201
|
+
}
|
|
202
|
+
return res;
|
|
203
|
+
}
|
|
204
|
+
async getInfraredRemotes(_infraredID) {
|
|
205
|
+
return { success: true, result: [] };
|
|
206
|
+
}
|
|
207
|
+
async getInfraredKeys(_infraredID, _remoteID) {
|
|
208
|
+
return { success: true, result: [] };
|
|
209
|
+
}
|
|
210
|
+
async getInfraredACStatus(_infraredID, _remoteID) {
|
|
211
|
+
return { success: true, result: {} };
|
|
212
|
+
}
|
|
213
|
+
async getInfraredDIYKeys(_infraredID, _remoteID) {
|
|
214
|
+
return { success: true, result: [] };
|
|
215
|
+
}
|
|
216
|
+
async updateInfraredRemotes(_allDevices) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
async sendCommands(deviceID, commands) {
|
|
220
|
+
const device = this.getDevice(deviceID);
|
|
221
|
+
const deviceName = device?.name || deviceID;
|
|
222
|
+
const commandStr = commands.map(c => `${c.code}=${c.value}`).join(', ');
|
|
223
|
+
this.log.info(`[${deviceName}] Sending command (Tuya HA QR cloud): ${commandStr}`);
|
|
224
|
+
const res = await this.api.post(`/v1.1/m/thing/${deviceID}/commands`, null, { commands });
|
|
225
|
+
if (!res.success) {
|
|
226
|
+
this.log.warn('Send HA QR command failed. devId=%s, code=%s, msg=%s', deviceID, res.code, res.msg);
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
const target = this.getDevice(deviceID);
|
|
230
|
+
if (target) {
|
|
231
|
+
for (const command of commands) {
|
|
232
|
+
const current = target.status.find(status => status.code === command.code);
|
|
233
|
+
if (current) {
|
|
234
|
+
current.value = command.value;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
target.status.push({ ...command });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
this.emit(Events.DEVICE_STATUS_UPDATE, target, commands);
|
|
241
|
+
}
|
|
242
|
+
return res.result;
|
|
243
|
+
}
|
|
244
|
+
async getSceneList(homeID) {
|
|
245
|
+
const res = await this.api.get('/v1.0/m/scene/ha/home/scenes', { homeId: homeID });
|
|
246
|
+
if (!res.success) {
|
|
247
|
+
this.log.warn('Get HA QR scene list failed. homeId = %s, code = %s, msg = %s', homeID, res.code, res.msg);
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
const scenes = [];
|
|
251
|
+
for (const scene of (Array.isArray(res.result) ? res.result : [])) {
|
|
252
|
+
if (scene.enabled === false) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
scenes.push(new TuyaDevice_1.default({
|
|
256
|
+
id: scene.scene_id || scene.sceneId,
|
|
257
|
+
uuid: scene.scene_id || scene.sceneId,
|
|
258
|
+
name: scene.name,
|
|
259
|
+
owner_id: String(homeID),
|
|
260
|
+
product_id: 'scene',
|
|
261
|
+
product_name: 'scene',
|
|
262
|
+
category: 'scene',
|
|
263
|
+
schema: [],
|
|
264
|
+
status: [],
|
|
265
|
+
online: true,
|
|
266
|
+
}));
|
|
267
|
+
}
|
|
268
|
+
return scenes;
|
|
269
|
+
}
|
|
270
|
+
async executeScene(homeID, sceneID) {
|
|
271
|
+
return this.api.post('/v1.0/m/scene/ha/trigger', null, { homeId: homeID, sceneId: sceneID });
|
|
272
|
+
}
|
|
273
|
+
async getCurrentWeather(_lat, _lon) {
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
async startMQ(ownerIDs) {
|
|
277
|
+
this.ownerIDs = ownerIDs.map(String);
|
|
278
|
+
this.mq = new TuyaHASharingMQ_1.default(this.api, this.ownerIDs, this.devices, this.log, this.debug);
|
|
279
|
+
this.mq.addMessageListener(this.onMQTTMessage.bind(this));
|
|
280
|
+
await this.mq.start();
|
|
281
|
+
}
|
|
282
|
+
stopMQ() {
|
|
283
|
+
this.mq?.stop();
|
|
284
|
+
}
|
|
285
|
+
onMQTTMessage(message) {
|
|
286
|
+
try {
|
|
287
|
+
const protocol = message.protocol || 0;
|
|
288
|
+
const data = message.data || {};
|
|
289
|
+
if (protocol === 4) {
|
|
290
|
+
this.onDeviceReport(data.devId, data.status || []);
|
|
291
|
+
}
|
|
292
|
+
else if (protocol === 20 && data.bizData?.devId) {
|
|
293
|
+
this.onDeviceOther(data.bizData.devId, data.bizCode, data);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
298
|
+
this.log.warn('Tuya HA MQTT processing error: %s', msg);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
onDeviceReport(deviceID, status) {
|
|
302
|
+
const device = this.getDevice(deviceID);
|
|
303
|
+
if (!device) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const updated = [];
|
|
307
|
+
for (const item of status) {
|
|
308
|
+
if (item.code !== undefined && item.value !== undefined) {
|
|
309
|
+
const current = device.status.find(s => s.code === item.code);
|
|
310
|
+
if (current) {
|
|
311
|
+
current.value = item.value;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
device.status.push({ code: item.code, value: item.value });
|
|
315
|
+
}
|
|
316
|
+
updated.push({ code: item.code, value: item.value });
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (updated.length > 0) {
|
|
320
|
+
this.emit(Events.DEVICE_STATUS_UPDATE, device, updated);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async onDeviceOther(deviceID, bizCode, data) {
|
|
324
|
+
let device = this.getDevice(deviceID);
|
|
325
|
+
if (bizCode === 'delete') {
|
|
326
|
+
this.devices = this.devices.filter(item => item.id !== deviceID);
|
|
327
|
+
this.mq?.unSubscribeDevice(deviceID, device?.['support_local'] || false);
|
|
328
|
+
this.emit(Events.DEVICE_DELETE, deviceID);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (bizCode === 'bindUser') {
|
|
332
|
+
const newDevice = await this.updateDevice(deviceID);
|
|
333
|
+
if (newDevice) {
|
|
334
|
+
this.mq?.subscribeDevice(newDevice.id, newDevice['support_local'] || false);
|
|
335
|
+
this.emit(Events.DEVICE_ADD, newDevice);
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!device) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (bizCode === 'online') {
|
|
343
|
+
device.online = true;
|
|
344
|
+
}
|
|
345
|
+
else if (bizCode === 'offline') {
|
|
346
|
+
device.online = false;
|
|
347
|
+
}
|
|
348
|
+
else if (bizCode === 'nameUpdate') {
|
|
349
|
+
device.name = data.bizData?.name || device.name;
|
|
350
|
+
}
|
|
351
|
+
this.emit(Events.DEVICE_INFO_UPDATE, device, []);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
exports.default = TuyaHADeviceManager;
|
|
355
|
+
//# sourceMappingURL=TuyaHADeviceManager.js.map
|
package/dist/index.js
ADDED