fix(M06-E-R1): 校正智慧插座协议
This commit is contained in:
@@ -73,17 +73,69 @@ assert.throws(
|
||||
);
|
||||
|
||||
const socket = new JilianSmartSocketAdapter();
|
||||
assert.deepEqual(socket.read('workInfo'), { read: 'workInfo' });
|
||||
assert.deepEqual(socket.read('workInfo'), { read: 'workInfo', slotNum: 1 });
|
||||
assert.deepEqual(socket.read({ target: 'localtask', slotNum: 2, taskNum: 7 }), {
|
||||
read: 'localtask', slotNum: 2, taskNum: 7
|
||||
});
|
||||
assert.deepEqual(socket.read({ target: 'localtask', taskNum: 0 }), {
|
||||
read: 'localtask', slotNum: 1, taskNum: 0
|
||||
});
|
||||
assert.deepEqual(socket.read({ target: 'mqttConfig' }), { read: 'mqttConfig' });
|
||||
assert.deepEqual(socket.switch({ id: '123', on: true }), {
|
||||
action: 'on', id: '123', slotNum: 1
|
||||
});
|
||||
assert.equal(socket.localTask({
|
||||
assert.deepEqual(socket.localTask({
|
||||
id: '123', taskNum: 20, action: 'off', mode: 'weekly',
|
||||
time: '23:00', weekdays: [1, 5]
|
||||
}).action, 'localtask');
|
||||
assert.deepEqual(socket.clearTask({ id: '123', taskNum: 0 }), {
|
||||
action: 'clearTask', id: '123', taskNum: 0
|
||||
}), {
|
||||
action: 'localtask', slotNum: 1, taskNum: 20, tasktype: 'off',
|
||||
cyctype: 'weekcyc', weekdata: '1000100', actiontime: '2300', id: '123'
|
||||
});
|
||||
assert.throws(() => socket.localTask({
|
||||
id: '123', taskNum: 0, action: 'on', mode: 'once', time: '12:00'
|
||||
}), /greater than or equal to 1/);
|
||||
assert.throws(() => socket.localTask({
|
||||
id: '123', taskNum: 1, action: 'on', mode: 'weekly', time: '24:00',
|
||||
weekdays: [1]
|
||||
}), /Invalid/);
|
||||
assert.throws(() => socket.localTask({
|
||||
id: '123', taskNum: 1, action: 'on', mode: 'weekly', time: '12:00',
|
||||
weekdays: [1, 1]
|
||||
}), /weekdays must be unique/);
|
||||
assert.deepEqual(socket.clearTask({ id: '123', taskNum: 0 }), {
|
||||
action: 'clearTask', id: '123', slotNum: 1, taskNum: 0
|
||||
});
|
||||
assert.deepEqual(socket.parameter({
|
||||
id: '123', resetHold: 1, keyLock: 0, keyOff: 1
|
||||
}), { setting: 'parameter', resetHold: 1, keyLock: 0, KeyOff: 1, id: '123' });
|
||||
assert.equal(socket.reboot('123').action, 'reboot');
|
||||
assert.equal(socket.emptyPower('123').action, 'emptyPower');
|
||||
assert.deepEqual(socket.returnFactory({ id: '123', password: 'secret' }), {
|
||||
action: 'returnFactory', password: 'secret', id: '123'
|
||||
});
|
||||
assert.deepEqual(socket.setReturnKey({ id: '123', old: 'old-key', new: 'new-key' }), {
|
||||
action: 'setReturnKey', old: 'old-key', new: 'new-key', id: '123'
|
||||
});
|
||||
const protection = socket.protectOff({
|
||||
id: '123', ratedAmps: 16, maxPower: 3500, maxCurrent: 15,
|
||||
maxTemperature: 80, pullOutStop: 1, pullOutPower: 5, pullOutSec: 10,
|
||||
chargeFullStop: 1, chargeFullPower: 8, chargeFullSec: 30
|
||||
});
|
||||
assert.equal(protection.setting, 'protectOff');
|
||||
assert.equal('ratedAmps' in protection, false);
|
||||
for (const ratedAmps of [10, 63]) {
|
||||
const ratedProtection = socket.protectOff({
|
||||
id: '123', ratedAmps, maxPower: ratedAmps * 250, maxCurrent: ratedAmps,
|
||||
maxTemperature: 100, pullOutStop: 0, pullOutPower: 0, pullOutSec: 1,
|
||||
chargeFullStop: 0, chargeFullPower: 0, chargeFullSec: 1
|
||||
});
|
||||
assert.equal(ratedProtection.maxCurrent, ratedAmps);
|
||||
}
|
||||
assert.throws(() => socket.protectOff({
|
||||
id: '123', ratedAmps: 10, maxPower: 2600, maxCurrent: 11,
|
||||
maxTemperature: 80, pullOutStop: 1, pullOutPower: 5, pullOutSec: 10,
|
||||
chargeFullStop: 1, chargeFullPower: 8, chargeFullSec: 30
|
||||
}), /10A capability/);
|
||||
|
||||
const ack = control.parseUplink({
|
||||
DeviceID: 'BOX_001', id: '123', result: 'unconfirm', action: 'task'
|
||||
@@ -116,6 +168,39 @@ for (const eventType of ['magstate', 'taskfinish', 'Poweron', 'connected']) {
|
||||
assert.equal(fixture.kind, 'EVENT');
|
||||
assert.equal(fixture.eventType, eventType);
|
||||
}
|
||||
const workInfo = socket.parseUplink({
|
||||
DeviceID: 'SOCKET_001', read: 'workInfo', onOff: 'on',
|
||||
vol: '0220.5000', cur: '01.250', pow: '00275.000', temp: '45.50', energy: '0012.3400'
|
||||
});
|
||||
assert.deepEqual({
|
||||
switchOn: workInfo.payload.switchOn,
|
||||
voltage: workInfo.payload.voltage,
|
||||
current: workInfo.payload.current,
|
||||
power: workInfo.payload.power,
|
||||
temperature: workInfo.payload.temperature,
|
||||
energy: workInfo.payload.energy
|
||||
}, {
|
||||
switchOn: true, voltage: '220.5', current: '1.25', power: '275',
|
||||
temperature: '45.5', energy: '12.34'
|
||||
});
|
||||
const temperatureVariant = socket.parseUplink({
|
||||
DeviceID: 'SOCKET_001', read: 'workInfo', temperature: '46.0', KeyOff: 1
|
||||
});
|
||||
assert.equal(temperatureVariant.payload.temperature, '46');
|
||||
assert.equal(temperatureVariant.payload.keyOff, 1);
|
||||
const conflictingTemperature = socket.parseUplink({
|
||||
DeviceID: 'SOCKET_001', read: 'workInfo', temp: '45', temperature: '46'
|
||||
});
|
||||
assert.deepEqual(conflictingTemperature.protocolIssues, ['SOCKET_TEMPERATURE_CONFLICT']);
|
||||
for (const [action, result] of [
|
||||
['off', 'update'], ['off', 'paraerror'], ['localtask', 'error']
|
||||
]) {
|
||||
const response = socket.parseUplink({
|
||||
DeviceID: 'SOCKET_001', id: `20${result.length}`, action, result
|
||||
});
|
||||
assert.equal(response.kind, 'ACK');
|
||||
assert.equal(response.result, result);
|
||||
}
|
||||
|
||||
assert.equal(generateCommandId(1782120000000, 7), '1782120000007');
|
||||
assert.match(generateCommandId(), /^\d{13}$/);
|
||||
@@ -240,21 +325,68 @@ assert.equal(calls.some((item) =>
|
||||
&& item.params[2] === 'UNKNOWN_VENDOR_RESULT'
|
||||
), true);
|
||||
|
||||
const socketAlertsBeforeFakeFields = calls.filter((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_device_alerts')
|
||||
).length;
|
||||
await service.handle('/devicesend/SOCKET_001', Buffer.from(JSON.stringify({
|
||||
DeviceID: 'SOCKET_001', event: 'workInfo', switch: 'on',
|
||||
powerW: 3601, temperature: 82, overLoad: true
|
||||
})));
|
||||
assert.equal(calls.some((item) =>
|
||||
assert.equal(calls.filter((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_device_alerts')
|
||||
&& item.params.includes('SOCKET_OVERLOAD')
|
||||
), true);
|
||||
).length, socketAlertsBeforeFakeFields, 'legacy fake fields must not generate alerts');
|
||||
|
||||
await service.handle('/devicesend/SOCKET_001', Buffer.from(JSON.stringify({
|
||||
DeviceID: 'SOCKET_001', read: 'workInfo', onOff: 'on',
|
||||
vol: '220.50', cur: '1.250', pow: '275.000', temp: '45.5', energy: '12.340'
|
||||
})));
|
||||
const workInfoInsert = calls.findLast((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_iot_device_events')
|
||||
);
|
||||
const persistedWorkInfo = JSON.parse(workInfoInsert.params[9]).payload;
|
||||
assert.deepEqual({
|
||||
switchOn: persistedWorkInfo.switchOn,
|
||||
voltage: persistedWorkInfo.voltage,
|
||||
current: persistedWorkInfo.current,
|
||||
power: persistedWorkInfo.power,
|
||||
temperature: persistedWorkInfo.temperature,
|
||||
energy: persistedWorkInfo.energy
|
||||
}, {
|
||||
switchOn: true, voltage: '220.5', current: '1.25', power: '275',
|
||||
temperature: '45.5', energy: '12.34'
|
||||
});
|
||||
|
||||
const protectionAlerts = new Map([
|
||||
[3, 'SOCKET_PULL_OUT_STOP'], [4, 'SOCKET_CHARGE_FULL_STOP'],
|
||||
[5, 'SOCKET_OVERPOWER'], [6, 'SOCKET_OVERCURRENT'],
|
||||
[7, 'SOCKET_OVERTEMPERATURE']
|
||||
]);
|
||||
for (let closeReason = 1; closeReason <= 7; closeReason += 1) {
|
||||
const alertCountBefore = calls.filter((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_device_alerts')
|
||||
).length;
|
||||
await service.handle('/devicesend/SOCKET_001', Buffer.from(JSON.stringify({
|
||||
DeviceID: 'SOCKET_001', event: 'special', slotNum: 1,
|
||||
state: 'off', closeReason, energy: `${closeReason}.0`
|
||||
})));
|
||||
const alertType = protectionAlerts.get(closeReason);
|
||||
if (alertType) {
|
||||
assert.equal(calls.some((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_device_alerts') && item.params.includes(alertType)
|
||||
), true);
|
||||
} else {
|
||||
assert.equal(calls.filter((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_device_alerts')
|
||||
).length, alertCountBefore, `closeReason ${closeReason} is only a local operation`);
|
||||
}
|
||||
}
|
||||
|
||||
await service.handle('/devicesend/SOCKET_001', Buffer.from(JSON.stringify({
|
||||
DeviceID: 'SOCKET_001', read: 'workInfo', temp: '45', temperature: '46'
|
||||
})));
|
||||
assert.equal(calls.some((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_device_alerts')
|
||||
&& item.params.includes('SOCKET_POWER_LIMIT')
|
||||
), true);
|
||||
assert.equal(calls.some((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_device_alerts')
|
||||
&& item.params.includes('SOCKET_TEMPERATURE_LIMIT')
|
||||
item.sql.includes('qipai_iot_dead_letters')
|
||||
&& item.params.includes('SOCKET_TEMPERATURE_CONFLICT')
|
||||
), true);
|
||||
|
||||
await assert.rejects(
|
||||
@@ -280,6 +412,19 @@ const secretCommandInsert = calls.findLast((item) =>
|
||||
assert.equal(secretCommandInsert.params[7].includes('123456'), false);
|
||||
assert.equal(secretCommandInsert.params[7].includes('valueHash'), true);
|
||||
|
||||
await service.createCommand({
|
||||
tenantId: '7', assetId: '52', storeId: '11', commandId: '129',
|
||||
commandType: 'setReturnKey', traceId: 'socket-secret-test',
|
||||
payload: { action: 'setReturnKey', id: '129', old: 'old-secret', new: 'new-secret' }
|
||||
});
|
||||
const socketSecretInsert = calls.findLast((item) =>
|
||||
item.sql.includes('INSERT INTO qipai_iot_commands')
|
||||
);
|
||||
assert.equal(socketSecretInsert.params[7].includes('old-secret'), false);
|
||||
assert.equal(socketSecretInsert.params[7].includes('new-secret'), false);
|
||||
assert.equal(socketSecretInsert.params[7].includes('oldHash'), true);
|
||||
assert.equal(socketSecretInsert.params[7].includes('newHash'), true);
|
||||
|
||||
const commandCalls = [];
|
||||
const commandService = new DeviceCommandService({
|
||||
async createCommand(input) { commandCalls.push(['create', input]); },
|
||||
@@ -304,4 +449,4 @@ assert.equal(commandCalls[0][0], 'create');
|
||||
assert.equal(commandCalls[1][0], 'mqtt');
|
||||
assert.equal(commandCalls[2][0], 'published');
|
||||
|
||||
console.log('PASS: M06-C adapters, vendor spelling, command IDs, ACKs and QoS 1 dedup work.');
|
||||
console.log('PASS: M06-C/E-R1 exact wire, normalized events, ACKs and QoS 1 dedup work.');
|
||||
|
||||
Reference in New Issue
Block a user