From 0c3f1b9f3fb45fb6e3c24b97b7765245238e11ac Mon Sep 17 00:00:00 2001 From: Koen Kanters Date: Thu, 8 Jun 2023 14:51:42 +0200 Subject: [PATCH] fix(ignore): TS refactor (#5850) * fix(ignore): TS refactor * updates --- package-lock.json | 22 ++++- package.json | 2 + src/devices/bosch.ts | 2 +- src/devices/danalock.ts | 2 +- src/devices/danfoss.ts | 2 +- src/devices/dresden_elektronik.ts | 2 +- src/devices/ecodim.ts | 2 +- src/devices/eurotronic.ts | 2 +- src/devices/heimgard_technologies.ts | 2 +- src/devices/iluminize.ts | 2 +- src/devices/innr.ts | 2 +- src/devices/insta.ts | 2 +- src/devices/ledvance.ts | 2 +- src/devices/lupus.ts | 2 +- src/devices/lutron.ts | 2 +- src/devices/nodon.ts | 2 +- src/devices/osram.ts | 2 +- src/devices/salus_controls.ts | 2 +- src/devices/securifi.ts | 2 +- src/devices/sengled.ts | 2 +- src/devices/sylvania.ts | 2 +- src/devices/tuya.ts | 2 +- src/devices/uhome.ts | 2 +- src/lib/ota/{common.js => common.ts} | 102 ++++++++++++--------- src/lib/ota/index.js | 10 -- src/lib/ota/index.ts | 28 ++++++ src/lib/ota/{inovelli.js => inovelli.ts} | 22 +++-- src/lib/ota/{ledvance.js => ledvance.ts} | 17 ++-- src/lib/ota/{lixee.js => lixee.ts} | 22 ++--- src/lib/ota/{salus.js => salus.ts} | 30 +++--- src/lib/ota/{securifi.js => securifi.ts} | 15 ++- src/lib/ota/{tradfri.js => tradfri.ts} | 21 ++--- src/lib/ota/{ubisys.js => ubisys.ts} | 19 ++-- src/lib/ota/{zigbeeOTA.js => zigbeeOTA.ts} | 57 ++++++------ src/lib/philips.ts | 2 +- src/lib/types.ts | 55 ++++++++++- 36 files changed, 280 insertions(+), 186 deletions(-) rename src/lib/ota/{common.js => common.ts} (82%) delete mode 100644 src/lib/ota/index.js create mode 100644 src/lib/ota/index.ts rename src/lib/ota/{inovelli.js => inovelli.ts} (69%) rename src/lib/ota/{ledvance.js => ledvance.ts} (74%) rename src/lib/ota/{lixee.js => lixee.ts} (63%) rename src/lib/ota/{salus.js => salus.ts} (64%) rename src/lib/ota/{securifi.js => securifi.ts} (65%) rename src/lib/ota/{tradfri.js => tradfri.ts} (58%) rename src/lib/ota/{ubisys.js => ubisys.ts} (78%) rename src/lib/ota/{zigbeeOTA.js => zigbeeOTA.ts} (73%) diff --git a/package-lock.json b/package-lock.json index a70ccb0bb61ea..19669038efed8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "zigbee-herdsman-converters", - "version": "15.16.0", + "version": "15.18.0", "license": "MIT", "dependencies": { "axios": "^1.4.0", @@ -16,8 +16,10 @@ "zigbee-herdsman": "^0.14.117" }, "devDependencies": { + "@types/buffer-crc32": "^0.2.2", "@types/jest": "^29.5.2", "@types/node": "^20.2.5", + "@types/tar-stream": "^2.2.2", "@typescript-eslint/eslint-plugin": "^5.59.8", "@typescript-eslint/parser": "^5.59.8", "eslint": "^8.42.0", @@ -1453,6 +1455,15 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/buffer-crc32": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@types/buffer-crc32/-/buffer-crc32-0.2.2.tgz", + "integrity": "sha512-UpJyUKgG33LVehYtv9k2x4HUEY5ThV62YNGFBbQNBgtoky/0tQCceh8BPI9r3XL5hQ1tGmq34jGWNRBKf2P1UQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", @@ -1526,6 +1537,15 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/tar-stream": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@types/tar-stream/-/tar-stream-2.2.2.tgz", + "integrity": "sha512-1AX+Yt3icFuU6kxwmPakaiGrJUwG44MpuiqPg4dSolRFk6jmvs4b3IbUol9wKDLIgU76gevn3EwE8y/DkSJCZQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz", diff --git a/package.json b/package.json index 7140b557d348b..a6187ec04dc6a 100644 --- a/package.json +++ b/package.json @@ -45,8 +45,10 @@ "zigbee-herdsman": "^0.14.117" }, "devDependencies": { + "@types/buffer-crc32": "^0.2.2", "@types/jest": "^29.5.2", "@types/node": "^20.2.5", + "@types/tar-stream": "^2.2.2", "@typescript-eslint/eslint-plugin": "^5.59.8", "@typescript-eslint/parser": "^5.59.8", "eslint": "^8.42.0", diff --git a/src/devices/bosch.ts b/src/devices/bosch.ts index f8ef9a1917e53..892198a15f376 100644 --- a/src/devices/bosch.ts +++ b/src/devices/bosch.ts @@ -5,7 +5,7 @@ import tz from '../converters/toZigbee'; import * as reporting from '../lib/reporting'; import * as utils from '../lib/utils'; import * as constants from '../lib/constants'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import {Tz, Fz, Definition, KeyValue} from '../lib/types'; const e = exposes.presets; const ea = exposes.access; diff --git a/src/devices/danalock.ts b/src/devices/danalock.ts index 3a578736c7a04..f3b8947232f3a 100644 --- a/src/devices/danalock.ts +++ b/src/devices/danalock.ts @@ -3,7 +3,7 @@ import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import tz from '../converters/toZigbee'; import * as reporting from '../lib/reporting'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; const e = exposes.presets; const definitions: Definition[] = [ diff --git a/src/devices/danfoss.ts b/src/devices/danfoss.ts index ed37ae0e70c41..89a1c203ac99e 100644 --- a/src/devices/danfoss.ts +++ b/src/devices/danfoss.ts @@ -2,7 +2,7 @@ import {Definition} from '../lib/types'; import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import tz from '../converters/toZigbee'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as constants from '../lib/constants'; import * as reporting from '../lib/reporting'; const e = exposes.presets; diff --git a/src/devices/dresden_elektronik.ts b/src/devices/dresden_elektronik.ts index 8f0e32d353f46..a8aab1668d2fe 100644 --- a/src/devices/dresden_elektronik.ts +++ b/src/devices/dresden_elektronik.ts @@ -1,6 +1,6 @@ import {Definition} from '../lib/types'; import * as exposes from '../lib/exposes'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import extend from '../lib/extend'; const e = exposes.presets; diff --git a/src/devices/ecodim.ts b/src/devices/ecodim.ts index c0a43f8d0b0b8..7c7e1969eb20c 100644 --- a/src/devices/ecodim.ts +++ b/src/devices/ecodim.ts @@ -4,7 +4,7 @@ import fz from '../converters/fromZigbee'; const e = exposes.presets; import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as tuya from '../lib/tuya'; const definitions: Definition[] = [ diff --git a/src/devices/eurotronic.ts b/src/devices/eurotronic.ts index ed97b02a0ca3d..7f4520109c0f2 100644 --- a/src/devices/eurotronic.ts +++ b/src/devices/eurotronic.ts @@ -3,7 +3,7 @@ import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import * as legacy from '../lib/legacy'; import tz from '../converters/toZigbee'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as constants from '../lib/constants'; import * as reporting from '../lib/reporting'; const e = exposes.presets; diff --git a/src/devices/heimgard_technologies.ts b/src/devices/heimgard_technologies.ts index b9d845217f7db..3c1fe8c3d8272 100644 --- a/src/devices/heimgard_technologies.ts +++ b/src/devices/heimgard_technologies.ts @@ -4,7 +4,7 @@ import fz from '../converters/fromZigbee'; import tz from '../converters/toZigbee'; import * as reporting from '../lib/reporting'; const e = exposes.presets; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; const definitions: Definition[] = [ { diff --git a/src/devices/iluminize.ts b/src/devices/iluminize.ts index a99b1a957308a..bcb9c75a4b160 100644 --- a/src/devices/iluminize.ts +++ b/src/devices/iluminize.ts @@ -4,7 +4,7 @@ import fz from '../converters/fromZigbee'; import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; import tz from '../converters/toZigbee'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; const e = exposes.presets; const ea = exposes.access; diff --git a/src/devices/innr.ts b/src/devices/innr.ts index 496b26ec4250b..403952d3e7150 100644 --- a/src/devices/innr.ts +++ b/src/devices/innr.ts @@ -5,7 +5,7 @@ import tz from '../converters/toZigbee'; import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; const e = exposes.presets; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; const definitions: Definition[] = [ { diff --git a/src/devices/insta.ts b/src/devices/insta.ts index 107d267cc0d06..fe8b46599951c 100644 --- a/src/devices/insta.ts +++ b/src/devices/insta.ts @@ -3,7 +3,7 @@ import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import * as legacy from '../lib/legacy'; import tz from '../converters/toZigbee'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as reporting from '../lib/reporting'; import * as utils from '../lib/utils'; const e = exposes.presets; diff --git a/src/devices/ledvance.ts b/src/devices/ledvance.ts index f83f6fb6c8134..813f6d4c9f081 100644 --- a/src/devices/ledvance.ts +++ b/src/devices/ledvance.ts @@ -1,5 +1,5 @@ import {Definition} from '../lib/types'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import extend from '../lib/extend'; import * as ledvance from '../lib/ledvance'; import * as reporting from '../lib/reporting'; diff --git a/src/devices/lupus.ts b/src/devices/lupus.ts index 28aa35a8727fe..b17957e3304e0 100644 --- a/src/devices/lupus.ts +++ b/src/devices/lupus.ts @@ -6,7 +6,7 @@ import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; const e = exposes.presets; const ea = exposes.access; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; const definitions: Definition[] = [ { diff --git a/src/devices/lutron.ts b/src/devices/lutron.ts index e39d12f860189..ae93055499ff5 100644 --- a/src/devices/lutron.ts +++ b/src/devices/lutron.ts @@ -1,7 +1,7 @@ import {Definition} from '../lib/types'; import * as exposes from '../lib/exposes'; import * as legacy from '../lib/legacy'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as reporting from '../lib/reporting'; const e = exposes.presets; const ea = exposes.access; diff --git a/src/devices/nodon.ts b/src/devices/nodon.ts index cb3c7c2586a43..cbed42a8d8010 100644 --- a/src/devices/nodon.ts +++ b/src/devices/nodon.ts @@ -2,7 +2,7 @@ import {Definition} from '../lib/types'; import * as exposes from '../lib/exposes'; import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; const e = exposes.presets; import tz from '../converters/toZigbee'; import fz from '../converters/fromZigbee'; diff --git a/src/devices/osram.ts b/src/devices/osram.ts index 170d076ac3618..48a1bef1cae43 100644 --- a/src/devices/osram.ts +++ b/src/devices/osram.ts @@ -1,7 +1,7 @@ import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import * as legacy from '../lib/legacy'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; import * as utils from '../lib/utils'; diff --git a/src/devices/salus_controls.ts b/src/devices/salus_controls.ts index 868cb24c15ed6..e9d2972f8dfdf 100644 --- a/src/devices/salus_controls.ts +++ b/src/devices/salus_controls.ts @@ -2,7 +2,7 @@ import {Definition} from '../lib/types'; import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import tz from '../converters/toZigbee'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; const e = exposes.presets; diff --git a/src/devices/securifi.ts b/src/devices/securifi.ts index b2c385e3bb41c..093686aabdf07 100644 --- a/src/devices/securifi.ts +++ b/src/devices/securifi.ts @@ -2,7 +2,7 @@ import {Definition} from '../lib/types'; import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import tz from '../converters/toZigbee'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as reporting from '../lib/reporting'; const e = exposes.presets; diff --git a/src/devices/sengled.ts b/src/devices/sengled.ts index 8fb7e165b2eec..7f58186be320a 100644 --- a/src/devices/sengled.ts +++ b/src/devices/sengled.ts @@ -2,7 +2,7 @@ import {Definition} from '../lib/types'; import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import tz from '../converters/toZigbee'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as reporting from '../lib/reporting'; const e = exposes.presets; diff --git a/src/devices/sylvania.ts b/src/devices/sylvania.ts index 5f286e834e189..2a330168c30fb 100644 --- a/src/devices/sylvania.ts +++ b/src/devices/sylvania.ts @@ -2,7 +2,7 @@ import {Definition} from '../lib/types'; import * as exposes from '../lib/exposes'; import fz from '../converters/fromZigbee'; import * as legacy from '../lib/legacy'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; import * as ledvance from '../lib/ledvance'; diff --git a/src/devices/tuya.ts b/src/devices/tuya.ts index 3627c9ff50d7b..b87aba2f5274b 100644 --- a/src/devices/tuya.ts +++ b/src/devices/tuya.ts @@ -1,7 +1,7 @@ import * as exposes from '../lib/exposes'; import * as legacy from '../lib/legacy'; import * as tuya from '../lib/tuya'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; import * as reporting from '../lib/reporting'; import extend from '../lib/extend'; import * as libColor from '../lib/color'; diff --git a/src/devices/uhome.ts b/src/devices/uhome.ts index 1d07e805ae4fd..bab734a69a60d 100644 --- a/src/devices/uhome.ts +++ b/src/devices/uhome.ts @@ -3,7 +3,7 @@ import fz from '../converters/fromZigbee'; import tz from '../converters/toZigbee'; import * as exposes from '../lib/exposes'; import * as reporting from '../lib/reporting'; -import ota from '../lib/ota'; +import * as ota from '../lib/ota'; const e = exposes.presets; const definitions: Definition[] = [ diff --git a/src/lib/ota/common.js b/src/lib/ota/common.ts similarity index 82% rename from src/lib/ota/common.js rename to src/lib/ota/common.ts index c26567b171cd3..1b8ff8bb9b5a0 100644 --- a/src/lib/ota/common.js +++ b/src/lib/ota/common.ts @@ -1,11 +1,12 @@ -const crypto = require('crypto'); -const upgradeFileIdentifier = Buffer.from([0x1E, 0xF1, 0xEE, 0x0B]); -const {HttpsProxyAgent} = require('https-proxy-agent'); -const assert = require('assert'); -const crc32 = require('buffer-crc32'); +import crypto from 'crypto'; +import {HttpsProxyAgent} from 'https-proxy-agent'; +import {Zh, Ota, Logger, KeyValueAny, KeyValue, KeyValueNumberString} from '../types'; +import assert from 'assert'; +import crc32 from 'buffer-crc32'; +import axios from 'axios'; const maxTimeout = 2147483647; // +- 24 days const imageBlockResponseDelay = 250; -const endRequestCodeLookup = { +const endRequestCodeLookup: KeyValueNumberString = { 0x00: 'success', 0x95: 'aborted by device', 0x7E: 'not authorized', @@ -16,6 +17,15 @@ const endRequestCodeLookup = { 0x81: 'unsupported cluster command', 0x99: 'requires more image files', }; +export const upgradeFileIdentifier = Buffer.from([0x1E, 0xF1, 0xEE, 0x0B]); + +interface Request {cancel: () => void, promise: Promise<{header: KeyValue, payload: KeyValue}>} +interface Waiters {imageBlockOrPageRequest?: Request, nextImageRequest?: Request, upgradeEndRequest?: Request} +type IsNewImageAvailable = (current: Ota.ImageInfo, logger: Logger, device: Zh.Device, getImageMeta: Ota.GetImageMeta) => + Promise<{available: number, currentFileVersion: number, otaFileVersion: number}> +type DownloadImage = (meta: Ota.ImageMeta, logger: Logger) => Promise<{data: Buffer}>; +type GetNewImage = (current: Ota.Version, logger: Logger, device: Zh.Device, getImageMeta: Ota.GetImageMeta, downloadImage: DownloadImage) + => Promise; const validSilabsCrc = 0x2144DF1C; @@ -28,19 +38,19 @@ const eblImageSignature = 0xe350; const gblTagHeader = 0xeb17a603; const gblTagEnd = 0xfc0404fc; -function getOTAEndpoint(device) { +function getOTAEndpoint(device: Zh.Device) { return device.endpoints.find((e) => e.supportsOutputCluster('genOta')); } -function parseSubElement(buffer, position) { +function parseSubElement(buffer: Buffer, position: number): Ota.ImageElement { const tagID = buffer.readUInt16LE(position); const length = buffer.readUInt32LE(position + 2); const data = buffer.slice(position + 6, position + 6 + length); return {tagID, length, data}; } -function parseImage(buffer) { - const header = { +export function parseImage(buffer: Buffer): Ota.Image { + const header: Ota.ImageHeader = { otaUpgradeFileIdentifier: buffer.subarray(0, 4), otaHeaderVersion: buffer.readUInt16LE(4), otaHeaderLength: buffer.readUInt16LE(6), @@ -84,7 +94,7 @@ function parseImage(buffer) { return {header, elements, raw}; } -function validateImageData(image) { +function validateImageData(image: Ota.Image) { for (const element of image.elements) { const {data} = element; @@ -100,7 +110,7 @@ function validateImageData(image) { } } -function validateSilabsEbl(data) { +function validateSilabsEbl(data: Buffer) { const dataLength = data.length; let position = 0; @@ -129,7 +139,7 @@ function validateSilabsEbl(data) { throw new Error(`Image is truncated: not long enough to contain a valid tag`); } -function validateSilabsGbl(data) { +function validateSilabsGbl(data: Buffer) { const dataLength = data.length; let position = 0; @@ -154,7 +164,7 @@ function validateSilabsGbl(data) { throw new Error(`Image is truncated: not long enough to contain a valid tag`); } -function cancelWaiters(waiters) { +function cancelWaiters(waiters: Waiters) { for (const waiter of Object.values(waiters)) { if (waiter) { waiter.cancel(); @@ -162,7 +172,7 @@ function cancelWaiters(waiters) { } } -function sendQueryNextImageResponse(endpoint, image, logger) { +function sendQueryNextImageResponse(endpoint: Zh.Endpoint, image: Ota.Image, logger: Logger) { const payload = { status: 0, manufacturerCode: image.header.manufacturerCode, @@ -176,15 +186,16 @@ function sendQueryNextImageResponse(endpoint, image, logger) { }); } -function imageNotify(endpoint) { +function imageNotify(endpoint: Zh.Endpoint) { return endpoint.commandResponse('genOta', 'imageNotify', {payloadType: 0, queryJitter: 100}, {sendWhen: 'immediate'}); } -async function requestOTA(endpoint) { +async function requestOTA(endpoint: Zh.Endpoint): Promise<{payload: Ota.ImageInfo}> { // Some devices (e.g. Insta) take very long trying to discover the correct coordinator EP for OTA. const queryNextImageRequest = endpoint.waitForCommand('genOta', 'queryNextImageRequest', null, 60000); try { await imageNotify(endpoint); + // @ts-expect-error return await queryNextImageRequest.promise; } catch (e) { queryNextImageRequest.cancel(); @@ -192,7 +203,7 @@ async function requestOTA(endpoint) { } } -function getImageBlockResponsePayload(image, imageBlockRequest, pageOffset, pageSize) { +function getImageBlockResponsePayload(image: Ota.Image, imageBlockRequest: KeyValueAny, pageOffset: number, pageSize: number) { const start = imageBlockRequest.payload.fileOffset + pageOffset; // When the data size is too big, OTA gets unstable, so default it to 50 bytes maximum. // For Insta devices, OTA only works for data sizes 40 and smaller (= manufacturerCode 4474). @@ -217,7 +228,8 @@ function getImageBlockResponsePayload(image, imageBlockRequest, pageOffset, page }; } -function callOnProgress(startTime, lastUpdate, imageBlockRequest, image, logger, onProgress) { +function callOnProgress(startTime: number, lastUpdate: number, imageBlockRequest: KeyValueAny, + image: Ota.Image, logger: Logger, onProgress: Ota.OnProgress) { const now = Date.now(); // Call on progress every +- 30 seconds @@ -235,7 +247,8 @@ function callOnProgress(startTime, lastUpdate, imageBlockRequest, image, logger, } } -async function isUpdateAvailable(device, logger, isNewImageAvailable, requestPayload, getImageMeta = null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, isNewImageAvailable: IsNewImageAvailable, requestPayload: Ota.ImageInfo, + getImageMeta: Ota.GetImageMeta = null) { logger.debug(`Check if update available for '${device.ieeeAddr}' (${device.modelID})`); if (requestPayload === null) { @@ -256,7 +269,7 @@ async function isUpdateAvailable(device, logger, isNewImageAvailable, requestPay return {...availableResult, available: availableResult.available < 0}; } -async function isNewImageAvailable(current, logger, device, getImageMeta) { +export async function isNewImageAvailable(current: Ota.ImageInfo, logger: Logger, device: Zh.Device, getImageMeta: Ota.GetImageMeta) { const meta = await getImageMeta(current, logger, device); const [currentS, metaS] = [JSON.stringify(current), JSON.stringify(meta)]; logger.debug(`Is new image available for '${device.ieeeAddr}', current '${currentS}', latest meta '${metaS}'`); @@ -269,7 +282,8 @@ async function isNewImageAvailable(current, logger, device, getImageMeta) { }; } -async function updateToLatest(device, logger, onProgress, getNewImage, getImageMeta = null, downloadImage = null) { +export async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress, getNewImage: GetNewImage, + getImageMeta: Ota.GetImageMeta = null, downloadImage: DownloadImage = null): Promise { logger.debug(`Updating to latest '${device.ieeeAddr}' (${device.modelID})`); const endpoint = getOTAEndpoint(device); @@ -282,9 +296,9 @@ async function updateToLatest(device, logger, onProgress, getNewImage, getImageM const image = await getNewImage(request.payload, logger, device, getImageMeta, downloadImage); logger.debug(`Got new image for '${device.ieeeAddr}'`); - const waiters = {}; - let lastUpdate = null; - let lastImageBlockResponse = null; + const waiters: Waiters = {}; + let lastUpdate: number = null; + let lastImageBlockResponse: number = null; const startTime = Date.now(); return new Promise((resolve, reject) => { @@ -304,7 +318,7 @@ async function updateToLatest(device, logger, onProgress, getNewImage, getImageM let pageOffset = 0; let pageSize = 0; - const sendImageBlockResponse = (imageBlockRequest, thenCallback, transactionSequenceNumber) => { + const sendImageBlockResponse = (imageBlockRequest: KeyValueAny, thenCallback: () => void, transactionSequenceNumber: number) => { const payload = getImageBlockResponsePayload(image, imageBlockRequest, pageOffset, pageSize); const now = Date.now(); const timeSinceLastImageBlockResponse = now - lastImageBlockResponse; @@ -335,11 +349,11 @@ async function updateToLatest(device, logger, onProgress, getNewImage, getImageM if ('pageSize' in imageBlockOrPageRequest.payload) { // imagePageRequest - pageSize = imageBlockOrPageRequest.payload.pageSize; - const handleImagePageRequestBlocks = (imagePageRequest) => { + pageSize = imageBlockOrPageRequest.payload.pageSize as number; + const handleImagePageRequestBlocks = (imagePageRequest: KeyValueAny) => { if (pageOffset < pageSize) { sendImageBlockResponse(imagePageRequest, - () => handleImagePageRequestBlocks(imagePageRequest)); + () => handleImagePageRequestBlocks(imagePageRequest), imagePageRequest.header.transactionSequenceNumber); } else { answerNextImageBlockOrPageRequest(); } @@ -348,6 +362,7 @@ async function updateToLatest(device, logger, onProgress, getNewImage, getImageM } else { // imageBlockRequest sendImageBlockResponse(imageBlockOrPageRequest, answerNextImageBlockOrPageRequest, + // @ts-expect-error imageBlockOrPageRequest.header.transactionSequenceNumber); } }, @@ -384,7 +399,7 @@ async function updateToLatest(device, logger, onProgress, getNewImage, getImageM logger.debug(`Update succeeded, waiting for device announce`); onProgress(100, null); - let timer = null; + let timer: NodeJS.Timer = null; const cb = () => { logger.debug('Got device announce or timed out, call resolve'); clearInterval(timer); @@ -401,6 +416,7 @@ async function updateToLatest(device, logger, onProgress, getNewImage, getImageM }, ); } else { + // @ts-expect-error const error = `Update failed with reason: '${endRequestCodeLookup[data.payload.status]}'`; logger.debug(error); reject(new Error(error)); @@ -416,7 +432,8 @@ async function updateToLatest(device, logger, onProgress, getNewImage, getImageM }); } -async function getNewImage(current, logger, device, getImageMeta, downloadImage) { +export async function getNewImage(current: Ota.ImageInfo, logger: Logger, device: Zh.Device, + getImageMeta: Ota.GetImageMeta, downloadImage: DownloadImage): Promise { const meta = await getImageMeta(current, logger, device); logger.debug(`getNewImage for '${device.ieeeAddr}', meta ${JSON.stringify(meta)}`); assert(meta.fileVersion > current.fileVersion || meta.force, 'No new image available'); @@ -447,7 +464,7 @@ async function getNewImage(current, logger, device, getImageMeta, downloadImage) return image; } -function getAxios() { +export function getAxios() { let config = {}; const proxy = process.env.HTTPS_PROXY; if (proxy) { @@ -460,17 +477,14 @@ function getAxios() { }; } - const axios = require('axios').create(config); - return axios; + return axios.create(config); } -module.exports = { - upgradeFileIdentifier, - isUpdateAvailable, - parseImage, - validateImageData, - isNewImageAvailable, - updateToLatest, - getNewImage, - getAxios, -}; +exports.upgradeFileIdentifier = upgradeFileIdentifier; +exports.isUpdateAvailable = isUpdateAvailable; +exports.parseImage = parseImage; +exports.validateImageData = validateImageData; +exports.isNewImageAvailable = isNewImageAvailable; +exports.updateToLatest = updateToLatest; +exports.getNewImage = getNewImage; +exports.getAxios = getAxios; diff --git a/src/lib/ota/index.js b/src/lib/ota/index.js deleted file mode 100644 index f6ffbce034096..0000000000000 --- a/src/lib/ota/index.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - inovelli: require('./inovelli'), - ledvance: require('./ledvance'), - salus: require('./salus'), - lixee: require('./lixee'), - securifi: require('./securifi'), - tradfri: require('./tradfri'), - ubisys: require('./ubisys'), - zigbeeOTA: require('./zigbeeOTA'), -}; diff --git a/src/lib/ota/index.ts b/src/lib/ota/index.ts new file mode 100644 index 0000000000000..e56ed2b6ab383 --- /dev/null +++ b/src/lib/ota/index.ts @@ -0,0 +1,28 @@ +import * as inovelli from './inovelli'; +import * as ledvance from './ledvance'; +import * as salus from './salus'; +import * as lixee from './lixee'; +import * as securifi from './securifi'; +import * as tradfri from './tradfri'; +import * as ubisys from './ubisys'; +import * as zigbeeOTA from './zigbeeOTA'; + +export { + inovelli, + ledvance, + salus, + lixee, + securifi, + tradfri, + ubisys, + zigbeeOTA, +}; + +exports.inovelli = inovelli; +exports.ledvance = ledvance; +exports.salus = salus; +exports.lixee = lixee; +exports.securifi = securifi; +exports.tradfri = tradfri; +exports.ubisys = ubisys; +exports.zigbeeOTA = zigbeeOTA; diff --git a/src/lib/ota/inovelli.js b/src/lib/ota/inovelli.ts similarity index 69% rename from src/lib/ota/inovelli.js rename to src/lib/ota/inovelli.ts index 9fe8872bbfa28..51dff4e627cc8 100644 --- a/src/lib/ota/inovelli.js +++ b/src/lib/ota/inovelli.ts @@ -1,10 +1,11 @@ -const common = require('./common'); +import * as common from './common'; +import {Zh, Logger, Ota, KeyValueAny} from '../types'; const axios = common.getAxios(); /* * Helper functions */ -async function getImageMeta(current, logger, device) { +export async function getImageMeta(current: Ota.ImageInfo, logger: Logger, device: Zh.Device): Promise { const images = (await axios.get('https://files.inovelli.com/firmware/firmware.json')).data; if (Object.keys(images).indexOf(device.modelID) === -1) { @@ -14,11 +15,13 @@ async function getImageMeta(current, logger, device) { // Force for now. There is only beta firmware at the moment. const useBetaChannel = true; const image = images[device.modelID] - .filter((i) => (useBetaChannel ? true : i.channel === 'production')) - .sort((a, b) => { + .filter((i: KeyValueAny) => (useBetaChannel ? true : i.channel === 'production')) + .sort((a: KeyValueAny, b: KeyValueAny) => { const aRadix = a.version.match(/[A-F]/) ? 16 : 10; const bRadix = b.version.match(/[A-F]/) ? 16 : 10; + // @ts-expect-error const aVersion = parseFloat(a.version, aRadix); + // @ts-expect-error const bVersion = parseFloat(b.version, bRadix); // doesn't matter which order they are in if (aVersion < bVersion) { @@ -37,6 +40,7 @@ async function getImageMeta(current, logger, device) { } // version in the firmare removes the zero padding and support hex versioning return { + // @ts-expect-error fileVersion: parseFloat(image.version, image.version.match(/[A-F]/) ? 16 : 10), url: image.firmware, }; @@ -46,7 +50,7 @@ async function getImageMeta(current, logger, device) { * Interface implementation */ -async function isUpdateAvailable(device, logger, requestPayload = null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo=null) { return common.isUpdateAvailable( device, logger, @@ -56,7 +60,7 @@ async function isUpdateAvailable(device, logger, requestPayload = null) { ); } -async function updateToLatest(device, logger, onProgress) { +export async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) { return common.updateToLatest( device, logger, @@ -66,7 +70,5 @@ async function updateToLatest(device, logger, onProgress) { ); } -module.exports = { - isUpdateAvailable, - updateToLatest, -}; +exports.isUpdateAvailable = isUpdateAvailable; +exports.updateToLatest = updateToLatest; diff --git a/src/lib/ota/ledvance.js b/src/lib/ota/ledvance.ts similarity index 74% rename from src/lib/ota/ledvance.js rename to src/lib/ota/ledvance.ts index d8d706c7f8924..70f088666f386 100644 --- a/src/lib/ota/ledvance.js +++ b/src/lib/ota/ledvance.ts @@ -1,14 +1,15 @@ const updateCheckUrl = 'https://api.update.ledvance.com/v1/zigbee/firmwares/newer'; const updateDownloadUrl = 'https://api.update.ledvance.com/v1/zigbee/firmwares/download'; -const assert = require('assert'); -const common = require('./common'); +import assert from 'assert'; +import * as common from './common'; +import {Zh, Logger, Ota} from '../types'; const axios = common.getAxios(); /** * Helper functions */ -async function getImageMeta(current, logger, device) { +export async function getImageMeta(current: Ota.ImageInfo, logger: Logger, device: Zh.Device): Promise { const manufacturerCode = current.manufacturerCode; const imageType = current.imageType; const {data} = await axios.get(updateCheckUrl + @@ -38,15 +39,13 @@ async function getImageMeta(current, logger, device) { * Interface implementation */ -async function isUpdateAvailable(device, logger, requestPayload=null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo=null) { return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, getImageMeta); } -async function updateToLatest(device, logger, onProgress) { +export async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) { return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta); } -module.exports = { - isUpdateAvailable, - updateToLatest, -}; +exports.isUpdateAvailable = isUpdateAvailable; +exports.updateToLatest = updateToLatest; diff --git a/src/lib/ota/lixee.js b/src/lib/ota/lixee.ts similarity index 63% rename from src/lib/ota/lixee.js rename to src/lib/ota/lixee.ts index 3b4e599773938..83affc2ce62da 100644 --- a/src/lib/ota/lixee.js +++ b/src/lib/ota/lixee.ts @@ -1,13 +1,14 @@ const firmwareOrigin = 'https://api.github.com/repos/fairecasoimeme/Zlinky_TIC/releases'; -const assert = require('assert'); -const common = require('./common'); +import {Zh, Logger, Ota, KeyValueAny} from '../types'; +import assert from 'assert'; +import * as common from './common'; const axios = common.getAxios(); /** * Helper functions */ -async function getImageMeta(current, logger, device) { +export async function getImageMeta(current: Ota.ImageInfo, logger: Logger, device: Zh.Device): Promise { const manufacturerCode = current.manufacturerCode; const imageType = current.imageType; const releasesLIST = (await axios.get(firmwareOrigin)).data; @@ -15,10 +16,9 @@ async function getImageMeta(current, logger, device) { let firmURL; // Find the most recent OTA file available - for (const e of releasesLIST.sort((a, b) => a.published_at - a.published_at)) { + for (const e of releasesLIST.sort((a: KeyValueAny, b: KeyValueAny) => a.published_at - a.published_at)) { if (e.assets) { - const targetObj = e.assets - .find((a) => a.name.endsWith('.ota')); + const targetObj = e.assets.find((a: KeyValueAny) => a.name.endsWith('.ota')); if (targetObj && targetObj.browser_download_url) { firmURL = targetObj; break; @@ -43,15 +43,13 @@ async function getImageMeta(current, logger, device) { * Interface implementation */ -async function isUpdateAvailable(device, logger, requestPayload=null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo=null) { return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, getImageMeta); } -async function updateToLatest(device, logger, onProgress) { +export async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) { return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta); } -module.exports = { - isUpdateAvailable, - updateToLatest, -}; +exports.isUpdateAvailable = isUpdateAvailable; +exports.updateToLatest = updateToLatest; diff --git a/src/lib/ota/salus.js b/src/lib/ota/salus.ts similarity index 64% rename from src/lib/ota/salus.js rename to src/lib/ota/salus.ts index 66375d5a2bdfc..a25532331716b 100644 --- a/src/lib/ota/salus.js +++ b/src/lib/ota/salus.ts @@ -1,17 +1,18 @@ const url = 'https://eu.salusconnect.io/demo/default/status/firmware?timestamp=0'; -const assert = require('assert'); -const common = require('./common'); -const tar = require('tar-stream'); +import assert from 'assert'; +import * as common from './common'; +import tar from 'tar-stream'; +import {Zh, Logger, Ota, KeyValue, KeyValueAny} from '../types'; const axios = common.getAxios(); /** * Helper functions */ -async function getImageMeta(current, logger, device) { +export async function getImageMeta(current: Ota.ImageInfo, logger: Logger, device: Zh.Device): Promise { const modelID = device.modelID; const images = (await axios.get(url)).data.versions; - const image = images.find((i) => i.model === modelID); + const image = images.find((i: KeyValue) => i.model === modelID); assert(image, `No image available for modelID '${modelID}'`); return { fileVersion: parseInt(image.version, 16), @@ -19,16 +20,16 @@ async function getImageMeta(current, logger, device) { }; } -async function untar(tarStream) { +async function untar(tarStream: NodeJS.ReadStream) { return new Promise((resolve, reject) => { const extract = tar.extract(); - const result = []; + const result: KeyValue[] = []; extract.on('error', reject); extract.on('entry', (headers, stream, next) => { - const buffers = []; + const buffers: Buffer[] = []; stream.on('data', function(data) { buffers.push(data); @@ -54,11 +55,12 @@ async function untar(tarStream) { }); } -async function downloadImage(meta, logger) { +async function downloadImage(meta: KeyValueAny, logger: Logger) { const download = await axios.get(meta.url, {responseType: 'stream'}); const files = await untar(download.data); + // @ts-expect-error const imageFile = files.find((file) => file.headers.name.endsWith('.ota')); return imageFile; @@ -68,15 +70,13 @@ async function downloadImage(meta, logger) { * Interface implementation */ -async function isUpdateAvailable(device, logger, requestPayload=null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo=null) { return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, getImageMeta); } -async function updateToLatest(device, logger, onProgress) { +export async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) { return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta, downloadImage); } -module.exports = { - isUpdateAvailable, - updateToLatest, -}; +exports.isUpdateAvailable = isUpdateAvailable; +exports.updateToLatest = updateToLatest; diff --git a/src/lib/ota/securifi.js b/src/lib/ota/securifi.ts similarity index 65% rename from src/lib/ota/securifi.js rename to src/lib/ota/securifi.ts index fb47a3b13f1a5..4d2f0991e4f48 100644 --- a/src/lib/ota/securifi.js +++ b/src/lib/ota/securifi.ts @@ -1,7 +1,8 @@ -const common = require('./common'); -const zigbeeOTA = require('./zigbeeOTA'); +import {Zh, Logger, Ota} from '../types'; +import * as common from './common'; +import * as zigbeeOTA from './zigbeeOTA'; -async function isUpdateAvailable(device, logger, requestPayload=null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo=null) { if (device.modelID === 'PP-WHT-US') { // see https://github.com/Koenkk/zigbee-OTA/pull/14 const scenesEndpoint = device.endpoints.find((e) => e.supportsOutputCluster('genScenes')); @@ -10,7 +11,7 @@ async function isUpdateAvailable(device, logger, requestPayload=null) { return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, zigbeeOTA.getImageMeta); } -async function updateToLatest(device, logger, onProgress) { +export async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) { if (device.modelID === 'PP-WHT-US') { // see https://github.com/Koenkk/zigbee-OTA/pull/14 const scenesEndpoint = device.endpoints.find((e) => e.supportsOutputCluster('genScenes')); @@ -19,7 +20,5 @@ async function updateToLatest(device, logger, onProgress) { return common.updateToLatest(device, logger, onProgress, common.getNewImage, zigbeeOTA.getImageMeta); } -module.exports = { - isUpdateAvailable, - updateToLatest, -}; +exports.isUpdateAvailable = isUpdateAvailable; +exports.updateToLatest = updateToLatest; diff --git a/src/lib/ota/tradfri.js b/src/lib/ota/tradfri.ts similarity index 58% rename from src/lib/ota/tradfri.js rename to src/lib/ota/tradfri.ts index 2d52f5dd877cd..7d5f15fff29a2 100644 --- a/src/lib/ota/tradfri.js +++ b/src/lib/ota/tradfri.ts @@ -1,6 +1,7 @@ const productionURL = 'http://fw.ota.homesmart.ikea.net/feed/version_info.json'; const testURL = 'http://fw.test.ota.homesmart.ikea.net/feed/version_info.json'; -const common = require('./common'); +import {Ota, Logger, Zh, KeyValue} from '../types'; +import * as common from './common'; const axios = common.getAxios(); let useTestURL = false; @@ -8,11 +9,11 @@ let useTestURL = false; * Helper functions */ -async function getImageMeta(current, logger, device) { +export async function getImageMeta(current: Ota.ImageInfo, logger: Logger, device: Zh.Device): Promise { const url = useTestURL ? testURL : productionURL; const imageType = current.imageType; const images = (await axios.get(url)).data; - const image = images.find((i) => i.fw_image_type === imageType); + const image = images.find((i: KeyValue) => i.fw_image_type === imageType); if (!image) { throw new Error(`No image available for imageType '${imageType}'`); @@ -28,18 +29,16 @@ async function getImageMeta(current, logger, device) { * Interface implementation */ -async function isUpdateAvailable(device, logger, requestPayload=null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo=null) { return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, getImageMeta); } -async function updateToLatest(device, logger, onProgress) { +async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) { return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta); } -module.exports = { - isUpdateAvailable, - updateToLatest, - useTestURL: () => { - useTestURL = true; - }, +exports.isUpdateAvailable = isUpdateAvailable; +exports.updateToLatest = updateToLatest; +exports.useTestURL = () => { + useTestURL = true; }; diff --git a/src/lib/ota/ubisys.js b/src/lib/ota/ubisys.ts similarity index 78% rename from src/lib/ota/ubisys.js rename to src/lib/ota/ubisys.ts index f861b65c694e7..517e2d9df04b4 100644 --- a/src/lib/ota/ubisys.js +++ b/src/lib/ota/ubisys.ts @@ -1,15 +1,16 @@ const firmwareHtmlPageUrl = 'http://fwu.ubisys.de/smarthome/OTA/release/index'; const imageRegex = /10F2-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{8})\S*ota1?\.zigbee/gi; -const assert = require('assert'); -const url = require('url'); -const common = require('./common'); +import assert from 'assert'; +import url from 'url'; +import * as common from './common'; +import {Ota, Logger, Zh} from '../types'; const axios = common.getAxios(); /** * Helper functions */ -async function getImageMeta(current, logger, device) { +export async function getImageMeta(current: Ota.ImageInfo, logger: Logger, device: Zh.Device): Promise { const imageType = current.imageType; const hardwareVersion = device.hardwareVersion; @@ -47,15 +48,13 @@ async function getImageMeta(current, logger, device) { * Interface implementation */ -async function isUpdateAvailable(device, logger, requestPayload=null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo=null) { return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, getImageMeta); } -async function updateToLatest(device, logger, onProgress) { +export async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) { return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta); } -module.exports = { - isUpdateAvailable, - updateToLatest, -}; +exports.isUpdateAvailable = isUpdateAvailable; +exports.updateToLatest = updateToLatest; diff --git a/src/lib/ota/zigbeeOTA.js b/src/lib/ota/zigbeeOTA.ts similarity index 73% rename from src/lib/ota/zigbeeOTA.js rename to src/lib/ota/zigbeeOTA.ts index ecaf5ce45a812..9c60167688140 100644 --- a/src/lib/ota/zigbeeOTA.js +++ b/src/lib/ota/zigbeeOTA.ts @@ -1,19 +1,20 @@ const url = 'https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/index.json'; -const common = require('./common'); +import * as common from './common'; +import {Logger, Zh, Ota, KeyValueAny} from '../types'; const axios = common.getAxios(); -const fs = require('fs'); -const URI = require('uri-js'); -const path = require('path'); +import fs from 'fs'; +import URI from 'uri-js'; +import path from 'path'; -let overrideIndexFileName = null; -let dataDir = null; +let overrideIndexFileName: string = null; +let dataDir: string = null; /** * Helper functions */ -function isValidUrl(url) { +function isValidUrl(url: string) { let parsed; try { parsed = URI.parse(url); @@ -23,15 +24,15 @@ function isValidUrl(url) { return parsed.scheme === 'http' || parsed.scheme === 'https'; } -async function getIndexFile(urlOrName) { +async function getIndexFile(urlOrName: string) { if (isValidUrl(urlOrName)) { return (await axios.get(urlOrName)).data; } - return JSON.parse(fs.readFileSync(urlOrName)); + return JSON.parse(fs.readFileSync(urlOrName, 'utf-8')); } -function readLocalFile(fileName, logger) { +function readLocalFile(fileName: string, logger: Logger) { // If the file name is not a full path, then treat it as a relative to the data directory if (!path.isAbsolute(fileName) && dataDir) { fileName = path.join(dataDir, fileName); @@ -41,7 +42,7 @@ function readLocalFile(fileName, logger) { return fs.readFileSync(fileName); } -async function getFirmwareFile(image, logger) { +async function getFirmwareFile(image: KeyValueAny, logger: Logger) { const urlOrName = image.url; // First try to download firmware file with the URL provided @@ -53,7 +54,7 @@ async function getFirmwareFile(image, logger) { return {data: readLocalFile(urlOrName, logger)}; } -function fillImageInfo(meta, logger) { +function fillImageInfo(meta: KeyValueAny, logger: Logger) { // Web-hosted images must come with all fields filled already if (isValidUrl(meta.url)) { return meta; @@ -78,7 +79,7 @@ function fillImageInfo(meta, logger) { return meta; } -async function getIndex(logger) { +async function getIndex(logger: Logger) { const index = (await axios.get(url)).data; logger.debug(`ZigbeeOTA: downloaded main index`); @@ -88,13 +89,13 @@ async function getIndex(logger) { const localIndex = await getIndexFile(overrideIndexFileName); // Resulting index will have overriden items first - return localIndex.concat(index).map((item) => isValidUrl(item.url) ? item : fillImageInfo(item, logger)); + return localIndex.concat(index).map((item: KeyValueAny) => isValidUrl(item.url) ? item : fillImageInfo(item, logger)); } return index; } -async function getImageMeta(current, logger, device) { +export async function getImageMeta(current: Ota.ImageInfo, logger: Logger, device: Zh.Device): Promise { const modelId = device.modelID; const imageType = current.imageType; const manufacturerCode = current.manufacturerCode; @@ -105,7 +106,7 @@ async function getImageMeta(current, logger, device) { // However Gledopto pro products use the same imageType (0) for every device while the image is different. // For this case additional identification through the modelId is done. // In the case of Tuya and Moes, additional identification is carried out through the manufacturerName. - const image = images.find((i) => i.imageType === imageType && i.manufacturerCode === manufacturerCode && + const image = images.find((i: KeyValueAny) => i.imageType === imageType && i.manufacturerCode === manufacturerCode && (!i.minFileVersion || current.fileVersion >= i.minFileVersion) && (!i.maxFileVersion || current.fileVersion <= i.maxFileVersion) && (!i.modelId || i.modelId === modelId) && (!i.manufacturerName || i.manufacturerName.includes(manufacturerName))); @@ -122,7 +123,7 @@ async function getImageMeta(current, logger, device) { }; } -async function isNewImageAvailable(current, logger, device, getImageMeta) { +async function isNewImageAvailable(current: Ota.ImageInfo, logger: Logger, device: Zh.Device, getImageMeta: Ota.GetImageMeta) { if (device.modelID === 'lumi.airrtc.agl001') { // The current.fileVersion which comes from the device is wrong. // Use the `aqaraFileVersion` which comes from the aqaraOpple.attributeReport instead. @@ -140,22 +141,20 @@ async function isNewImageAvailable(current, logger, device, getImageMeta) { * Interface implementation */ -async function isUpdateAvailable(device, logger, requestPayload=null) { +export async function isUpdateAvailable(device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo=null) { return common.isUpdateAvailable(device, logger, isNewImageAvailable, requestPayload, getImageMeta); } -async function updateToLatest(device, logger, onProgress) { +export async function updateToLatest(device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) { return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta, getFirmwareFile); } -module.exports = { - getImageMeta, - isUpdateAvailable, - updateToLatest, - useIndexOverride: (indexFileName) => { - overrideIndexFileName = indexFileName; - }, - setDataDir: (dir) => { - dataDir = dir; - }, +exports.getImageMeta = getImageMeta; +exports.isUpdateAvailable = isUpdateAvailable; +exports.updateToLatest = updateToLatest; +exports.useIndexOverride = (indexFileName: string) => { + overrideIndexFileName = indexFileName; +}, +exports.setDataDir = (dir: string) => { + dataDir = dir; }; diff --git a/src/lib/philips.ts b/src/lib/philips.ts index 5f5685024b139..a7c316829968f 100644 --- a/src/lib/philips.ts +++ b/src/lib/philips.ts @@ -1,6 +1,6 @@ import {ColorXY, ColorRGB} from './color'; import extendDontUse from './extend'; -import ota from './ota'; +import * as ota from './ota'; import * as exposes from './exposes'; import tz from '../converters/toZigbee'; import * as libColor from './color'; diff --git a/src/lib/types.ts b/src/lib/types.ts index 1b9c70da0531c..da0b3a5019edf 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -18,7 +18,7 @@ export interface Logger { export type Range = [number, number]; export interface KeyValue {[s: string]: unknown} export interface KeyValueString {[s: string]: string} -export interface KeyValueNumberString {[s: string]: string} +export interface KeyValueNumberString {[s: number]: string} // eslint-disable-next-line export interface KeyValueAny {[s: string]: any} export type Publish = (payload: KeyValue) => void; @@ -89,10 +89,8 @@ export type Definition = { meta?: DefinitionMeta, onEvent?: OnEvent, ota?: { - isUpdateAvailable: (device: Zh.Device, logger: Logger, data?: KeyValue) - => Promise; - updateToLatest: (device: Zh.Device, logger: Logger, - onProgress: (progress: number, remaining: number) => void) => Promise; + isUpdateAvailable: (device: Zh.Device, logger: Logger, requestPayload:Ota.ImageInfo) => Promise; + updateToLatest: (device: Zh.Device, logger: Logger, onProgress: Ota.OnProgress) => Promise; } } & ({ zigbeeModel: string[] } | { fingerprint: Fingerprint[] }) & ({ extend: Extend } | @@ -175,3 +173,50 @@ export namespace Extend { preferHueAndSaturation?: boolean, supportsHueAndSaturation?: boolean, } } + +export namespace Ota { + export type OnProgress = (progress: number, remaining: number) => void; + export interface Version {imageType: number, manufacturerCode: number, fileVersion: number} + export interface ImageHeader { + otaUpgradeFileIdentifier: Buffer, + otaHeaderVersion: number, + otaHeaderLength: number, + otaHeaderFieldControl: number, + manufacturerCode: number, + imageType: number, + fileVersion: number, + zigbeeStackVersion: number, + otaHeaderString: string, + totalImageSize: number, + securityCredentialVersion?: number, + upgradeFileDestination?: Buffer + minimumHardwareVersion?: number, + maximumHardwareVersion?: number, + } + export interface ImageElement { + tagID: number, + length: number, + data: Buffer, + } + export interface Image { + header: ImageHeader, + elements: ImageElement[], + raw: Buffer, + } + export interface ImageInfo { + imageType: number, + fileVersion: number, + manufacturerCode: number, + } + export interface ImageMeta { + fileVersion: number; + fileSize?: number; + url: string; + sha256?: string; + force?: boolean; + sha512?: string; + hardwareVersionMin?: number, + hardwareVersionMax?: number, + } + export type GetImageMeta = (current: ImageInfo, logger: Logger, device: Zh.Device) => Promise; +}