Skip to content

Commit

Permalink
add support zuno
Browse files Browse the repository at this point in the history
  • Loading branch information
amatilda committed Jun 19, 2024
1 parent a5c9cc3 commit 8714dfc
Show file tree
Hide file tree
Showing 9 changed files with 938 additions and 17 deletions.
16 changes: 16 additions & 0 deletions src/lang/ui_lang_define.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,22 @@ enum ControllerUiLangClassId
DETECTION_PROCESS_QUEST_SYNC,
DETECTION_PROCESS_BUTTON_RE_SYNC,
DETECTION_PROCESS_BUTTON_RE_SYNC_TITLE,
SLAVE_INFO_HEADER,
SLAVE_MESSAGE_READ_BOARD_INFO,
SLAVE_TABLE_NAME_VERSION,
SLAVE_TABLE_NAME_VERSION_TITLE,
SLAVE_TABLE_NAME_BUILD_TIME_STAMP,
SLAVE_TABLE_NAME_BUILD_TIME_STAMP_TITLE,
SLAVE_TABLE_NAME_UUID,
SLAVE_TABLE_NAME_UUID_TITLE,
SLAVE_TABLE_NAME_HOME,
SLAVE_TABLE_NAME_HOME_TITLE,
SLAVE_TABLE_NAME_NODE,
SLAVE_TABLE_NAME_NODE_TITLE,
SLAVE_TABLE_NAME_DSK,
SLAVE_TABLE_NAME_DSK_TITLE,
SLAVE_TABLE_NAME_QR_CODE,
SLAVE_TABLE_NAME_QR_CODE_TITLE,
}

type ControllerUiLangClassList =
Expand Down
16 changes: 16 additions & 0 deletions src/lang/ui_lang_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,20 @@ const controller_lang_en:ControllerUiLangClassList =
[ControllerUiLangClassId.DETECTION_PROCESS_QUEST_SYNC]: "Use reset and confirm",
[ControllerUiLangClassId.DETECTION_PROCESS_BUTTON_RE_SYNC]: "re sync",
[ControllerUiLangClassId.DETECTION_PROCESS_BUTTON_RE_SYNC_TITLE]: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
[ControllerUiLangClassId.SLAVE_INFO_HEADER]: "Slave Info",
[ControllerUiLangClassId.SLAVE_MESSAGE_READ_BOARD_INFO]: "Read board info the slave",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_VERSION]: "Version:",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_VERSION_TITLE]: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_BUILD_TIME_STAMP]: "Build data time:",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_BUILD_TIME_STAMP_TITLE]: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_UUID]: "Uuid:",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_UUID_TITLE]: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_HOME]: "Home:",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_HOME_TITLE]: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_NODE]: "Node:",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_NODE_TITLE]: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_DSK]: "Dsk:",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_DSK_TITLE]: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_QR_CODE]: "QR-Code:",
[ControllerUiLangClassId.SLAVE_TABLE_NAME_QR_CODE_TITLE]: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
};
54 changes: 51 additions & 3 deletions src/other/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
export {sleep, checksum, calcSigmaCRC16, costruct_int, hexToBytes, arrayToStringHex, versionNumberToString, intToBytearrayLsbMsb, intToBytearrayMsbLsb};
export { sleep, checksum, calcSigmaCRC16, costruct_int, hexToBytes, arrayToStringHex, versionNumberToString, intToBytearrayLsbMsb, intToBytearrayMsbLsb, versionNumberToStringSlave, numberToStringHex, conv2Decimal, toString,
conv2DecimalPadding
};

function toString(array:Array<number>): string {
let result:string;

result = "";
for (let i = 0; i < array.length; i++) {
result += String.fromCharCode(array[i]);
}
return result;
}

function numberToStringHex(num:number): string {
return (((num >> 24) & 0xFF).toString(0x10).padStart(2, '0') + ((num >> 16) & 0xFF).toString(0x10).padStart(2, '0') + ((num >> 8) & 0xFF).toString(0x10).padStart(2, '0') + ((num) & 0xFF).toString(0x10).padStart(2, '0'));
}

function versionNumberToString(version:number): string {
const txt:string = String((version >> 24) & 0xFF).padStart(2, '0') + "." + String((version >> 16) & 0xFF).padStart(2, '0') + "." + String((version >> 0x8) & 0xFF).padStart(2, '0') + "." + String((version) & 0xFF).padStart(2, '0')
return (txt)
}

function arrayToStringHex(data:Array<number>):string {
function versionNumberToStringSlave(version:number): string {
const txt:string = String((version >> 24) & 0xFF).padStart(2, '0') + "." + String((version >> 16) & 0xFF).padStart(2, '0') + "." + String((version) & 0xFFFF)
return (txt)
}

function arrayToStringHex(data:Array<number>|Uint8Array):string {
let str_hex:string, i:number;

str_hex = "";
Expand Down Expand Up @@ -125,4 +146,31 @@ function intToBytearrayMsbLsb(data:number, size:number = 0x4):Uint8Array {
i++;
}
return (array);
}
}

function conv2DecimalPadding(num:number, max:number): string {
let num_str = num.toString(0xA);

while (num_str.length < max)
num_str = '0' + num_str;
return (num_str);
}


function conv2Decimal(buff:Uint8Array, separator:string = "-"): string {
let i:number, text:string, v:number;

text = "";
i = 0x0;
while (i < (buff.length / 2)) {
v = buff[ (i * 2)];
v <<= 8;
v += buff[ (i * 2) + 1];
if(i != 0)
text += separator;
text += conv2DecimalPadding(v, 5);
i = i + 1;
}
return (text)
}

20 changes: 20 additions & 0 deletions src/qr_code/qrcode.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

export const enum QRErrorCorrectLevel {
M = 0x0,
L = 0x1,
H = 0x2,
Q = 0x3,
}

export type QRCodeOption = {
"text" : string,
"width" : number,
"height" : number,
"colorDark" : string,
"colorLight" : string,
"correctLevel" : QRErrorCorrectLevel,
}

export declare class QRCode {
constructor(id:HTMLElement|string, text:string|QRCodeOption)
}
615 changes: 615 additions & 0 deletions src/qr_code/qrcode.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/sapi/sapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ class SapiClass {
out.status = await this._open(baudrate_array[i]);
if (out.status != SapiClassStatus.OK)
return ;
res = await this._recvIncomingRequest(600);
res = await this._recvIncomingRequest(1000);
if (res.status != SapiClassStatus.OK && func != null) {
await this._clear();
if (await func() == false) {
Expand Down
184 changes: 172 additions & 12 deletions src/sapi/zuno_sapi.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

import {SapiClass, SapiClassStatus, SapiClassFuncId, SapiClassRet} from "./sapi";
import {costruct_int, toString, conv2Decimal, conv2DecimalPadding} from "../other/utilities";

export {ZunoSapiClass, ZunoSapiClassStatus, ZunoSapiClassBoardInfo};
export {ZunoSapiClass, ZunoSapiClassStatus, ZunoSapiClassBoardInfo, ZunoSapiClassParamInfo};

enum ZunoSapiClassStatus
{
Expand All @@ -10,42 +11,201 @@ enum ZunoSapiClassStatus
WRONG_LENGTH_CMD,
}

interface ZunoSapiClassBoardInfoZwDataProt
{
s2_keys:number;
device_type:number;
device_icon:number;
vendor:number;
product_type:number;
product_id:number;
version:number;
LR:boolean;
}

interface ZunoSapiClassParamInfo
{
status:ZunoSapiClassStatus;
raw:Array<number>;
freq_i:number;
freq_str:string;
bLR:boolean;
}

interface ZunoSapiClassBoardInfo
{
status:ZunoSapiClassStatus;
version:number;
build_number:number;
build_ts:number;
hw_rev:number;
code_size:number;
ram_size:number
custom_code_offset:number;
chip_uuid:Uint8Array;
s2_pub:Uint8Array;
zwdata?:ZunoSapiClassBoardInfoZwDataProt,
smart_qr?:string;
dbg_lock:number;
home_id?:number;
node_id?:number;
}


// ------------------------------------------------------------------------------------------------------

interface FreqTableProt {
name: string;
id: number;
}


class ZunoSapiClass {
private readonly sapi:SapiClass;
private board_info:ZunoSapiClassBoardInfo = {status:ZunoSapiClassStatus.NOT_INIT, version:0x0, build_number:0x0};
private board_info:ZunoSapiClassBoardInfo = { status:ZunoSapiClassStatus.NOT_INIT, version:0x0, build_number:0x0, build_ts:0x0, hw_rev:0x0, code_size:0x0, ram_size:0x0,
dbg_lock:0x0, custom_code_offset:0x30000, chip_uuid: new Uint8Array(), s2_pub: new Uint8Array()};
private param_info:ZunoSapiClassParamInfo = { status:ZunoSapiClassStatus.NOT_INIT, freq_i:0x0, freq_str:"", bLR:false, raw:[]};

private readonly FREQ_TABLE_U7:FreqTableProt[] =
[
{ name: "EU",id: 0x00},
{ name: "US",id: 0x01},
{ name: "ANZ",id: 0x02},
{ name: "HK",id: 0x03},
// { name: "MY",id: 0x04},
{ name: "IN",id: 0x05},
{ name: "IL",id: 0x06},
{ name: "RU",id: 0x07},
{ name: "CN",id: 0x08},
{ name: "US_LR",id: 0x09},
// { name: "US_LR_BK",id: 0x0A},
{ name: "JP",id: 0x20},
{ name: "KR",id: 0x21},
// { name: "FK",id: 0xFE},
];

private _freq_int_to_str(val:number): string {
let i = 0x0;
while (i < this.FREQ_TABLE_U7.length) {
if (this.FREQ_TABLE_U7[i].id == val)
return (this.FREQ_TABLE_U7[i].name);
i++;
}
return ("UNK");
}

private async compile_zwave_qrcode(product_data:ZunoSapiClassBoardInfoZwDataProt, dsk:Uint8Array, version:number): Promise<string> {
let protocol_map:number, text:string;

text = conv2DecimalPadding(product_data["s2_keys"], 3);
text = text + conv2Decimal(dsk, "");
// #ProductType
text = text + "0010" + conv2DecimalPadding(product_data["device_type"], 5) + conv2DecimalPadding(product_data["device_icon"], 5);
// #ProductID
text = text + "0220" + conv2DecimalPadding(product_data["vendor"], 5) + conv2DecimalPadding(product_data["product_type"], 5) + conv2DecimalPadding(product_data["product_id"], 5) + conv2DecimalPadding(version, 5);
// # Supported Protocols
protocol_map = 0x01;
if (product_data["LR"] == true)
protocol_map = protocol_map | 0x02;
text += "0803" + conv2DecimalPadding(protocol_map, 3);
// # MaxInclusionInterval
text = text + "0403005";// # ==5*128=640
const buf:ArrayBuffer = Uint8Array.from(unescape(encodeURIComponent(text)), c=>c.charCodeAt(0)).buffer;
const digest:Uint8Array = new Uint8Array(await crypto.subtle.digest('SHA-1', buf));
text = "9001" + conv2DecimalPadding((digest[0x0] << 0x8) | digest[0x1], 5) + text;
return (text);
}


private async _readNVM(addr:number, size:number): Promise<SapiClassRet> {
return (await this.sapi.sendCommandUnSz(SapiClassFuncId.FUNC_ID_NVM_EXT_READ_LONG_BUFFER, [(addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, (size >> 8) & 0xFF, size & 0xFF]));
}

private async _get_param_info(out:ZunoSapiClassParamInfo): Promise<void> {
out.status = ZunoSapiClassStatus.NOT_INIT;
const param_info:SapiClassRet = await this._readNVM(0xFFE000, 0x09);
if (param_info.status != SapiClassStatus.OK) {
out.status = ((param_info.status as unknown) as ZunoSapiClassStatus);
return ;
}
const param:Array<number> = param_info.data;
if (param.length < 0x2) {
out.status = ZunoSapiClassStatus.WRONG_LENGTH_CMD;
return ;
}
out.raw = param;
out.freq_i = param_info.data[1];
out.freq_str = this._freq_int_to_str(out.freq_i);
if ((out.freq_str == "US_LR") || (out.freq_str == "US") || (out.freq_str == "US_LR_BK"))
out.bLR = true;
}

private async _get_board_info(out:ZunoSapiClassBoardInfo): Promise<void> {
let code_sz_shift:number, shift_smrt:number, bLR:boolean;

out.status = ZunoSapiClassStatus.NOT_INIT;
out.smart_qr = undefined;
out.zwdata = undefined;
out.home_id = undefined;
out.node_id = undefined;
bLR = false;
if (this.param_info.status == ZunoSapiClassStatus.OK)
bLR = this.param_info.bLR;
const board_info:SapiClassRet = await this._readNVM(0xFFFF00, 0x01);
if (board_info.status != SapiClassStatus.OK) {
out.status = ((board_info.status as unknown) as ZunoSapiClassStatus);
return ;
}
if (board_info.data.length < 10) {
const info:Array<number> = board_info.data;
if (info.length < 42) {
out.status = ZunoSapiClassStatus.WRONG_LENGTH_CMD;
return ;
}
const info:Array<number> = board_info.data;
out.status = ZunoSapiClassStatus.OK;
const version:number = ((info[0] << 8) | (info[1]));
out.build_number = (info[2] << 24) | (info[3] << 16) | (info[4] << 8) | (info[5]);
out.version = (((info[0] << 8) | (info[1])) << 16 | (out.build_number & 0xFFFF))
// const param_info:SapiClassRet = await this._readNVM(0xFFE000, 0x09);
// if (param_info.data.length < 10) {
// out.status = ZunoSapiClassStatus.WRONG_LENGTH_CMD;
// return ;
// }
out.version = (version << 16 | (out.build_number & 0xFFFF));
out.build_ts = (info[6] << 24) | (info[7] << 16) | (info[8] << 8) | (info[9]);
out.hw_rev = (info[10] << 8) | (info[11]);
if (out.build_number > 1116) {
code_sz_shift = 0x1;
out.code_size = costruct_int(info.slice(12,12+3), 3, false);
}
else {
code_sz_shift = 0x0;
out.code_size = (info[12] << 8) | (info[13]);
}
out.ram_size = (info[14+code_sz_shift] << 8) | (info[15+code_sz_shift]);
out.chip_uuid = new Uint8Array(info.slice(16+code_sz_shift,16+code_sz_shift+8));
out.s2_pub = new Uint8Array(info.slice(24+code_sz_shift,24+code_sz_shift+16));
out.dbg_lock = info[40+code_sz_shift];
const offset_base:number = 46;
if (info.length < offset_base)
return ;
out.home_id = costruct_int(info.slice(41+code_sz_shift,41+code_sz_shift+4), 4, false);
out.node_id = info[45+code_sz_shift];
if (out.build_number < 1669) {
shift_smrt = 90;
if (info.length < (offset_base + code_sz_shift + shift_smrt))
return ;
out.smart_qr = toString(info.slice(46+code_sz_shift,46+code_sz_shift+90));
}
else {
shift_smrt = 11;
if (info.length < (offset_base + code_sz_shift + shift_smrt))
return ;
out.zwdata =
{
s2_keys: info[46+code_sz_shift],
device_type: costruct_int(info.slice(47+code_sz_shift, 47+code_sz_shift+2), 2, false),
device_icon: costruct_int(info.slice(49+code_sz_shift, 49+code_sz_shift+2), 2, false),
vendor: costruct_int(info.slice(51+code_sz_shift, 51+code_sz_shift+2), 2, false),
product_type: costruct_int(info.slice(53+code_sz_shift, 53+code_sz_shift+2), 2, false),
product_id: costruct_int(info.slice(55+code_sz_shift, 55+code_sz_shift+2), 2, false),
version: version,
LR: bLR,
};
out.smart_qr = await this.compile_zwave_qrcode(out.zwdata, out.s2_pub, version);
}
}

public getBoardInfo(): ZunoSapiClassBoardInfo {
Expand All @@ -70,7 +230,7 @@ class ZunoSapiClass {
}

public async connect(): Promise<void> {
this.board_info.status = ZunoSapiClassStatus.NOT_INIT;
await this._get_param_info(this.param_info);
await this._get_board_info(this.board_info);
// await this._begin(true);
}
Expand Down
Loading

0 comments on commit 8714dfc

Please sign in to comment.