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,392 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ===== Types =====
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.internalCodeToBase64 = internalCodeToBase64;
|
|
5
|
+
exports.base64ToInternalCode = base64ToInternalCode;
|
|
6
|
+
exports.internalCodeToPulsesMicros = internalCodeToPulsesMicros;
|
|
7
|
+
exports.decodeInternalCode = decodeInternalCode;
|
|
8
|
+
// ===== Utilities =====
|
|
9
|
+
function cleanHex(hex) {
|
|
10
|
+
return hex.replace(/[\s:,|_-]/g, '').toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
function hexToBytes(hex) {
|
|
13
|
+
const h = cleanHex(hex);
|
|
14
|
+
if (!h) {
|
|
15
|
+
throw new Error('empty hex string');
|
|
16
|
+
}
|
|
17
|
+
if (h.length % 2 !== 0) {
|
|
18
|
+
throw new Error('hex string length must be even');
|
|
19
|
+
}
|
|
20
|
+
const out = new Array(h.length / 2);
|
|
21
|
+
for (let i = 0; i < h.length; i += 2) {
|
|
22
|
+
const b = parseInt(h.slice(i, i + 2), 16);
|
|
23
|
+
if (Number.isNaN(b)) {
|
|
24
|
+
throw new Error(`invalid hex at ${i}: ${h.slice(i, i + 2)}`);
|
|
25
|
+
}
|
|
26
|
+
out[i / 2] = b;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
function bytesToHex(bytes) {
|
|
31
|
+
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
32
|
+
}
|
|
33
|
+
function trimPaddingFF(bytes, ffRunLength) {
|
|
34
|
+
if (ffRunLength <= 0) {
|
|
35
|
+
return bytes.slice();
|
|
36
|
+
}
|
|
37
|
+
let run = 0;
|
|
38
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
39
|
+
if (bytes[i] === 0xff) {
|
|
40
|
+
run++;
|
|
41
|
+
if (run >= ffRunLength) {
|
|
42
|
+
return bytes.slice(0, i - run + 1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
run = 0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return bytes.slice();
|
|
50
|
+
}
|
|
51
|
+
function bytesToBase64(bytes) {
|
|
52
|
+
if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
|
|
53
|
+
return Buffer.from(bytes).toString('base64');
|
|
54
|
+
}
|
|
55
|
+
// Browser
|
|
56
|
+
let bin = '';
|
|
57
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
58
|
+
bin += String.fromCharCode(bytes[i]);
|
|
59
|
+
}
|
|
60
|
+
// eslint-disable-next-line no-undef
|
|
61
|
+
return btoa(bin);
|
|
62
|
+
}
|
|
63
|
+
function base64ToBytes(b64) {
|
|
64
|
+
if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
|
|
65
|
+
return Array.from(Buffer.from(b64, 'base64'));
|
|
66
|
+
}
|
|
67
|
+
// eslint-disable-next-line no-undef
|
|
68
|
+
const bin = atob(b64);
|
|
69
|
+
const out = new Array(bin.length);
|
|
70
|
+
for (let i = 0; i < bin.length; i++) {
|
|
71
|
+
out[i] = bin.charCodeAt(i);
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
function u16FromBytes(bytes, endian) {
|
|
76
|
+
if (bytes.length % 2 !== 0) {
|
|
77
|
+
bytes = bytes.slice(0, bytes.length - 1);
|
|
78
|
+
}
|
|
79
|
+
const out = [];
|
|
80
|
+
for (let i = 0; i < bytes.length; i += 2) {
|
|
81
|
+
const val = endian === 'LE'
|
|
82
|
+
? (bytes[i] | (bytes[i + 1] << 8))
|
|
83
|
+
: ((bytes[i] << 8) | bytes[i + 1]);
|
|
84
|
+
out.push(val);
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
function toPulsesMicrosFromU16(ticks, tickMicros) {
|
|
89
|
+
return ticks.map(t => t * tickMicros);
|
|
90
|
+
}
|
|
91
|
+
function near(x, target, tolPct) {
|
|
92
|
+
const lo = target * (1 - tolPct);
|
|
93
|
+
const hi = target * (1 + tolPct);
|
|
94
|
+
return x >= lo && x <= hi;
|
|
95
|
+
}
|
|
96
|
+
function median(arr) {
|
|
97
|
+
const a = arr.slice().sort((x, y) => x - y);
|
|
98
|
+
const m = Math.floor(a.length / 2);
|
|
99
|
+
return a.length % 2 ? a[m] : (a[m - 1] + a[m]) / 2;
|
|
100
|
+
}
|
|
101
|
+
function packBitsLSB(bits) {
|
|
102
|
+
const out = [];
|
|
103
|
+
let cur = 0, count = 0;
|
|
104
|
+
for (const bit of bits) {
|
|
105
|
+
cur |= (bit & 1) << count;
|
|
106
|
+
count++;
|
|
107
|
+
if (count === 8) {
|
|
108
|
+
out.push(cur);
|
|
109
|
+
cur = 0;
|
|
110
|
+
count = 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (count > 0) {
|
|
114
|
+
out.push(cur);
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
function packBitsMSB(bits) {
|
|
119
|
+
const out = [];
|
|
120
|
+
let cur = 0, count = 0;
|
|
121
|
+
for (const bit of bits) {
|
|
122
|
+
cur = (cur << 1) | (bit & 1);
|
|
123
|
+
count++;
|
|
124
|
+
if (count === 8) {
|
|
125
|
+
out.push(cur);
|
|
126
|
+
cur = 0;
|
|
127
|
+
count = 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (count > 0) {
|
|
131
|
+
out.push(cur);
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
// ===== Core conversions =====
|
|
136
|
+
function internalCodeToBase64(hex, opts = {}) {
|
|
137
|
+
const { trimPadding = true, ffRunLength = 8, } = opts;
|
|
138
|
+
const bytes = hexToBytes(hex);
|
|
139
|
+
const body = trimPadding ? trimPaddingFF(bytes, ffRunLength) : bytes;
|
|
140
|
+
return bytesToBase64(body);
|
|
141
|
+
}
|
|
142
|
+
function base64ToInternalCode(b64) {
|
|
143
|
+
const bytes = base64ToBytes(b64);
|
|
144
|
+
return bytesToHex(bytes);
|
|
145
|
+
}
|
|
146
|
+
function internalCodeToPulsesMicros(hex, opts = {}) {
|
|
147
|
+
const { tickMicros = 25, endian = 'LE', trimPadding = true, ffRunLength = 8, } = opts;
|
|
148
|
+
const bytes = hexToBytes(hex);
|
|
149
|
+
const body = trimPadding ? trimPaddingFF(bytes, ffRunLength) : bytes;
|
|
150
|
+
const ticks = u16FromBytes(body, endian);
|
|
151
|
+
return toPulsesMicrosFromU16(ticks, tickMicros);
|
|
152
|
+
}
|
|
153
|
+
function decodeNEC(pulses) {
|
|
154
|
+
const notes = [];
|
|
155
|
+
// Find leader (mark ~9000, space ~4500)
|
|
156
|
+
let i = 0;
|
|
157
|
+
const m0 = pulses[0] ?? 0;
|
|
158
|
+
const s0 = pulses[1] ?? 0;
|
|
159
|
+
if (!(m0 > 6000 && s0 > 2000)) {
|
|
160
|
+
// sometimes extra gap precedes; try to skip small first element
|
|
161
|
+
if (pulses.length > 3 && pulses[1] > 6000 && pulses[2] > 2000) {
|
|
162
|
+
i = 1;
|
|
163
|
+
notes.push('shifted by 1 to align leader');
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
return { ok: false, score: 0 };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const leader = { mark: pulses[i], space: pulses[i + 1] };
|
|
170
|
+
i += 2;
|
|
171
|
+
// Estimate unit T from marks following leader (expect ~560µs)
|
|
172
|
+
const markSamples = [];
|
|
173
|
+
for (let k = i; k < Math.min(i + 100, pulses.length); k += 2) {
|
|
174
|
+
const mk = pulses[k];
|
|
175
|
+
if (mk > 200 && mk < 1200) {
|
|
176
|
+
markSamples.push(mk);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (markSamples.length < 4) {
|
|
180
|
+
return { ok: false, score: 0 };
|
|
181
|
+
}
|
|
182
|
+
const T = median(markSamples);
|
|
183
|
+
const bits = [];
|
|
184
|
+
while (i + 1 < pulses.length) {
|
|
185
|
+
const mark = pulses[i];
|
|
186
|
+
const space = pulses[i + 1];
|
|
187
|
+
i += 2;
|
|
188
|
+
if (mark < T * 0.5 || mark > T * 1.8) {
|
|
189
|
+
break;
|
|
190
|
+
} // mark drifted → end
|
|
191
|
+
// classify by space length (0 ~T, 1 ~3T)
|
|
192
|
+
const bit = (space > T * 2) ? 1 : 0;
|
|
193
|
+
bits.push(bit);
|
|
194
|
+
if (bits.length > 64) {
|
|
195
|
+
break;
|
|
196
|
+
} // NEC32(最大で拡張48)
|
|
197
|
+
}
|
|
198
|
+
// Expect 32 bits (allow 28..40)
|
|
199
|
+
if (bits.length < 28) {
|
|
200
|
+
return { ok: false, score: 0.2 };
|
|
201
|
+
}
|
|
202
|
+
const bytesLSB = packBitsLSB(bits);
|
|
203
|
+
const res = {
|
|
204
|
+
protocol: 'NEC',
|
|
205
|
+
confidence: 0.6,
|
|
206
|
+
leader,
|
|
207
|
+
unitMicros: T,
|
|
208
|
+
pulsesMicros: pulses,
|
|
209
|
+
bits: {
|
|
210
|
+
bits,
|
|
211
|
+
bitCount: bits.length,
|
|
212
|
+
bytesLSB,
|
|
213
|
+
bytesMSB: packBitsMSB(bits),
|
|
214
|
+
},
|
|
215
|
+
notes,
|
|
216
|
+
};
|
|
217
|
+
// Validate inverted bytes if 32 bits available
|
|
218
|
+
if (bytesLSB.length >= 4) {
|
|
219
|
+
const addr = bytesLSB[0] & 0xff;
|
|
220
|
+
const addrInv = bytesLSB[1] & 0xff;
|
|
221
|
+
const cmd = bytesLSB[2] & 0xff;
|
|
222
|
+
const cmdInv = bytesLSB[3] & 0xff;
|
|
223
|
+
const addrOk = ((addr ^ addrInv) & 0xff) === 0xff;
|
|
224
|
+
const cmdOk = ((cmd ^ cmdInv) & 0xff) === 0xff;
|
|
225
|
+
res.address = addr;
|
|
226
|
+
res.command = cmd;
|
|
227
|
+
res.addressInvertedOk = addrOk;
|
|
228
|
+
res.commandInvertedOk = cmdOk;
|
|
229
|
+
res.confidence = 0.75 + (addrOk && cmdOk ? 0.2 : 0);
|
|
230
|
+
}
|
|
231
|
+
// Score leader closeness
|
|
232
|
+
const leaderScore = (near(leader.mark, 9000, 0.35) ? 0.1 : 0) +
|
|
233
|
+
(near(leader.space, 4500, 0.35) ? 0.1 : 0);
|
|
234
|
+
res.confidence = Math.min(1, res.confidence + leaderScore);
|
|
235
|
+
return { ok: true, score: res.confidence, result: res };
|
|
236
|
+
}
|
|
237
|
+
function decodeAEHA(pulses) {
|
|
238
|
+
const notes = [];
|
|
239
|
+
// Leader ~3400/1700
|
|
240
|
+
let i = 0;
|
|
241
|
+
const m0 = pulses[0] ?? 0;
|
|
242
|
+
const s0 = pulses[1] ?? 0;
|
|
243
|
+
if (!(m0 > 2200 && m0 < 5200 && s0 > 900 && s0 < 3000)) {
|
|
244
|
+
return { ok: false, score: 0 };
|
|
245
|
+
}
|
|
246
|
+
const leader = { mark: m0, space: s0 };
|
|
247
|
+
i += 2;
|
|
248
|
+
// Unit ~425
|
|
249
|
+
const markSamples = [];
|
|
250
|
+
for (let k = i; k < Math.min(i + 200, pulses.length); k += 2) {
|
|
251
|
+
const mk = pulses[k];
|
|
252
|
+
if (mk > 200 && mk < 900) {
|
|
253
|
+
markSamples.push(mk);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (markSamples.length < 6) {
|
|
257
|
+
return { ok: false, score: 0 };
|
|
258
|
+
}
|
|
259
|
+
const T = median(markSamples);
|
|
260
|
+
const bits = [];
|
|
261
|
+
while (i + 1 < pulses.length) {
|
|
262
|
+
const mark = pulses[i];
|
|
263
|
+
const space = pulses[i + 1];
|
|
264
|
+
i += 2;
|
|
265
|
+
if (mark < T * 0.5 || mark > T * 1.8) {
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
const bit = (space > T * 2.0) ? 1 : 0; // 0: ~T, 1: ~3T
|
|
269
|
+
bits.push(bit);
|
|
270
|
+
if (bits.length > 256) {
|
|
271
|
+
break;
|
|
272
|
+
} // AEHA長尺上限
|
|
273
|
+
}
|
|
274
|
+
if (bits.length < 32) {
|
|
275
|
+
return { ok: false, score: 0.2 };
|
|
276
|
+
}
|
|
277
|
+
const bytesLSB = packBitsLSB(bits);
|
|
278
|
+
const res = {
|
|
279
|
+
protocol: 'AEHA',
|
|
280
|
+
confidence: 0.6,
|
|
281
|
+
leader,
|
|
282
|
+
unitMicros: T,
|
|
283
|
+
pulsesMicros: pulses,
|
|
284
|
+
bits: {
|
|
285
|
+
bits, bitCount: bits.length, bytesLSB, bytesMSB: packBitsMSB(bits),
|
|
286
|
+
},
|
|
287
|
+
notes,
|
|
288
|
+
};
|
|
289
|
+
// Heuristic score: leader closeness + reasonable length
|
|
290
|
+
let score = 0.6;
|
|
291
|
+
score += (near(leader.mark, 3400, 0.35) ? 0.15 : 0);
|
|
292
|
+
score += (near(leader.space, 1700, 0.35) ? 0.15 : 0);
|
|
293
|
+
score += Math.min(0.1, bits.length / 2560); // longer frames slightly up
|
|
294
|
+
res.confidence = Math.min(1, score);
|
|
295
|
+
return { ok: true, score: res.confidence, result: res };
|
|
296
|
+
}
|
|
297
|
+
function decodeSIRC(pulses) {
|
|
298
|
+
const notes = [];
|
|
299
|
+
// Start: mark ~2400, space ~600
|
|
300
|
+
let i = 0;
|
|
301
|
+
const m0 = pulses[0] ?? 0;
|
|
302
|
+
const s0 = pulses[1] ?? 0;
|
|
303
|
+
if (!(near(m0, 2400, 0.35) && near(s0, 600, 0.35))) {
|
|
304
|
+
return { ok: false, score: 0 };
|
|
305
|
+
}
|
|
306
|
+
i += 2;
|
|
307
|
+
// Bits: mark 600(0) or 1200(1), space ~600
|
|
308
|
+
const bits = [];
|
|
309
|
+
const T = 600;
|
|
310
|
+
while (i + 1 < pulses.length) {
|
|
311
|
+
const mark = pulses[i];
|
|
312
|
+
const space = pulses[i + 1];
|
|
313
|
+
i += 2;
|
|
314
|
+
if (!near(space, 600, 0.4)) {
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
const bit = near(mark, 1200, 0.35) ? 1 : (near(mark, 600, 0.35) ? 0 : -1);
|
|
318
|
+
if (bit < 0) {
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
bits.push(bit);
|
|
322
|
+
if (bits.length > 24) {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// Valid lengths: 12/15/20 bits
|
|
327
|
+
const validLens = [12, 15, 20];
|
|
328
|
+
const len = bits.length;
|
|
329
|
+
if (!validLens.includes(len)) {
|
|
330
|
+
return { ok: false, score: 0.2 };
|
|
331
|
+
}
|
|
332
|
+
const bytesLSB = packBitsLSB(bits);
|
|
333
|
+
const res = {
|
|
334
|
+
protocol: 'SIRC',
|
|
335
|
+
confidence: 0.7,
|
|
336
|
+
unitMicros: T,
|
|
337
|
+
pulsesMicros: pulses,
|
|
338
|
+
bits: {
|
|
339
|
+
bits, bitCount: bits.length, bytesLSB, bytesMSB: packBitsMSB(bits),
|
|
340
|
+
},
|
|
341
|
+
sircBits: len,
|
|
342
|
+
notes,
|
|
343
|
+
};
|
|
344
|
+
return { ok: true, score: res.confidence, result: res };
|
|
345
|
+
}
|
|
346
|
+
// ===== Auto detect & decode =====
|
|
347
|
+
function decodeInternalCode(hex, opts = {}) {
|
|
348
|
+
const { tickMicros = 25, endian = 'LE', trimPadding = true, ffRunLength = 8, } = opts;
|
|
349
|
+
// 1) 内部コード → µsパルス
|
|
350
|
+
const bytes = hexToBytes(hex);
|
|
351
|
+
const body = trimPadding ? trimPaddingFF(bytes, ffRunLength) : bytes.slice();
|
|
352
|
+
const ticks = u16FromBytes(body, endian);
|
|
353
|
+
const pulses = toPulsesMicrosFromU16(ticks, tickMicros);
|
|
354
|
+
// 2) 先頭がスペースだった場合の簡易補正
|
|
355
|
+
let pulsesUse = pulses.slice();
|
|
356
|
+
if (pulsesUse.length >= 2 && pulsesUse[0] < 200) {
|
|
357
|
+
pulsesUse = pulsesUse.slice(1); // 先頭の極小値を捨てる
|
|
358
|
+
}
|
|
359
|
+
// 3) 各方式で試す
|
|
360
|
+
const tries = [
|
|
361
|
+
decodeNEC(pulsesUse),
|
|
362
|
+
decodeAEHA(pulsesUse),
|
|
363
|
+
decodeSIRC(pulsesUse),
|
|
364
|
+
].filter(t => t.ok);
|
|
365
|
+
if (tries.length === 0) {
|
|
366
|
+
return {
|
|
367
|
+
protocol: 'UNKNOWN',
|
|
368
|
+
confidence: 0,
|
|
369
|
+
pulsesMicros: pulsesUse,
|
|
370
|
+
notes: ['Failed to match NEC/AEHA/SIRC'],
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
// 4) スコア最大の方式を採用
|
|
374
|
+
tries.sort((a, b) => b.score - a.score);
|
|
375
|
+
const best = tries[0].result;
|
|
376
|
+
return best;
|
|
377
|
+
}
|
|
378
|
+
/*
|
|
379
|
+
// 1) 内部コード → Base64
|
|
380
|
+
const b64 = internalCodeToBase64(internalHex, { trimPadding: true, endian: 'LE' });
|
|
381
|
+
|
|
382
|
+
// 2) Base64 → 内部コード
|
|
383
|
+
const hex = base64ToInternalCode(b64);
|
|
384
|
+
|
|
385
|
+
// 3) パルス配列(µs)
|
|
386
|
+
const pulses = internalCodeToPulsesMicros(internalHex, { tickMicros: 25, endian: 'LE' });
|
|
387
|
+
|
|
388
|
+
// 4) 自動デコード(NEC/AEHA/SIRC) TODO:上手くいかないことがある
|
|
389
|
+
const decoded = decodeInternalCode(internalHex, { tickMicros: 25, endian: 'LE' });
|
|
390
|
+
console.log(decoded.protocol, decoded.address, decoded.command, decoded.bits?.bytesLSB);
|
|
391
|
+
*/
|
|
392
|
+
//# sourceMappingURL=InfraredTool.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PrefixLogger = void 0;
|
|
5
|
+
function isExLogger(obj) {
|
|
6
|
+
return typeof obj.success === 'function';
|
|
7
|
+
}
|
|
8
|
+
class PrefixLogger {
|
|
9
|
+
constructor(log, prefix, debugMode = false) {
|
|
10
|
+
this.log = log;
|
|
11
|
+
this.prefix = prefix;
|
|
12
|
+
this.debugMode = debugMode;
|
|
13
|
+
this.debugMode = this.debugMode || process.argv.includes('-D') || process.argv.includes('--debug');
|
|
14
|
+
}
|
|
15
|
+
debug(message, ...args) {
|
|
16
|
+
if (this.debugMode) {
|
|
17
|
+
this.log.info((this.prefix ? `[${this.prefix}] ` : '') + message, ...args);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
this.log.debug((this.prefix ? `[${this.prefix}] ` : '') + message, ...args);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
info(message, ...args) {
|
|
24
|
+
this.log.info((this.prefix ? `[${this.prefix}] ` : '') + message, ...args);
|
|
25
|
+
}
|
|
26
|
+
warn(message, ...args) {
|
|
27
|
+
this.log.warn((this.prefix ? `[${this.prefix}] ` : '') + message, ...args);
|
|
28
|
+
}
|
|
29
|
+
error(message, ...args) {
|
|
30
|
+
this.log.error((this.prefix ? `[${this.prefix}] ` : '') + message, ...args);
|
|
31
|
+
}
|
|
32
|
+
success(message, ...args) {
|
|
33
|
+
if (isExLogger(this.log)) {
|
|
34
|
+
this.log.success((this.prefix ? `[${this.prefix}] ` : '') + message, ...args);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.log.info((this.prefix ? `[${this.prefix}] ` : '') + message, ...args);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.PrefixLogger = PrefixLogger;
|
|
42
|
+
//# sourceMappingURL=Logger.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TuyaRecordingDelegate = void 0;
|
|
4
|
+
class TuyaRecordingDelegate {
|
|
5
|
+
updateRecordingActive(active) {
|
|
6
|
+
throw new Error('Method not implemented.');
|
|
7
|
+
}
|
|
8
|
+
updateRecordingConfiguration(configuration) {
|
|
9
|
+
throw new Error('Method not implemented.');
|
|
10
|
+
}
|
|
11
|
+
handleRecordingStreamRequest(streamId) {
|
|
12
|
+
throw new Error('Method not implemented.');
|
|
13
|
+
}
|
|
14
|
+
acknowledgeStream(streamId) {
|
|
15
|
+
throw new Error('Method not implemented.');
|
|
16
|
+
}
|
|
17
|
+
closeRecordingStream(streamId, reason) {
|
|
18
|
+
throw new Error('Method not implemented.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.TuyaRecordingDelegate = TuyaRecordingDelegate;
|
|
22
|
+
//# sourceMappingURL=TuyaRecordingDelegate.js.map
|