diff --git a/README.md b/README.md index 22348230..b587c0c6 100644 --- a/README.md +++ b/README.md @@ -78,18 +78,18 @@ Any field not in this list will be ignored. Any field not matching the required ###### To place restrictions -- "force": boolean _(ignore `fileVersion` and always present as 'available')_ -- "hardwareVersionMax": number -- "hardwareVersionMin": number -- "manufacturerName": array of strings _(target only devices with one of these manufacturer names)_ -- "maxFileVersion": number _(target only devices with this version or below)_ -- "minFileVersion": number _(target only devices with this version or above)_ -- "modelId": string _(target only devices with this model ID)_ +- "force": boolean _(ignore `fileVersion` and always present as 'available')_ +- "hardwareVersionMax": number +- "hardwareVersionMin": number +- "manufacturerName": array of strings _(target only devices with one of these manufacturer names)_ +- "maxFileVersion": number _(target only devices with this version or below)_ +- "minFileVersion": number _(target only devices with this version or above)_ +- "modelId": string _(target only devices with this model ID)_ ###### For record purpose -- "originalUrl": string -- "releaseNotes": string +- "originalUrl": string +- "releaseNotes": string If the pull request contains multiple files, the metadata is added for all files. If some files require different metadata, add the matching `fileName` to the JSON using an encompassing array instead. It will be used to assign metadata as directed. @@ -114,8 +114,8 @@ Example: ### Notes for maintainers & developers -- `images` and `index.json` contain added (PR or auto download) "upgrade" images. -- `images1` and `index1.json` contain automatically archived "downgrade" images (automatically moved from `images`/`index.json` after a merged PR introduced a newer version, or during auto download). +- `images` and `index.json` contain added (PR or auto download) "upgrade" images. +- `images1` and `index1.json` contain automatically archived "downgrade" images (automatically moved from `images`/`index.json` after a merged PR introduced a newer version, or during auto download). If a manual modification of the manifests is necessary, it should be done in a PR that does not trigger the `update_ota_pr` workflow (no changes in `images/**` directory). As a last resort, the label `ignore-ota-workflow` can be added to prevent the workflow from running. diff --git a/package.json b/package.json index 150b34bd..49a27320 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zigbee-ota", - "version": "1.1.1", + "version": "1.2.0", "repository": { "type": "git", "url": "git+https://github.com/Koenkk/zigbee-OTA.git" diff --git a/src/autodl/hue.ts b/src/autodl/hue.ts new file mode 100644 index 00000000..4e3af6db --- /dev/null +++ b/src/autodl/hue.ts @@ -0,0 +1,105 @@ +import {getJson, getLatestImage, readCacheJson, writeCacheJson} from '../common.js'; +import {processFirmwareImage} from '../process_firmware_image.js'; + +type ImageJson = { + createdAt: string; + updatedAt: string; + fileSize: number; + md5: string; + binaryUrl: string; + version: number; + versionName: string; + releaseNotes: string; +}; +type PageJson = {updates: ImageJson[]}; + +const NAME = 'Hue'; +const BASE_URL = 'https://firmware.meethue.com/v1/checkupdate?version=0&deviceTypeId='; +const DEVICE_TYPE_IDS: string[] = [ + '100b-111', + '100b-112', + // '100b-113', + '100b-114', + '100b-115', + // '100b-116', + '100b-117', + '100b-118', + // '100b-119', + '100b-11a', + // '100b-11b', + // '100b-11c', + '100b-11d', + '100b-11e', + '100b-11f', + '100b-120', + // '100b-121', + // '100b-122', + '100b-123', + // '100b-124', + '100b-125', + // '100b-126', + '100b-127', + // '100b-128', + '100b-129', + // '100b-12a', + // '100b-12b', + // '100b-12c', + // '100b-12d', + // '100b-12e', + // '100b-12f', +]; + +function sortByVersion(a: ImageJson, b: ImageJson): number { + return a.version < b.version ? -1 : a.version > b.version ? 1 : 0; +} + +function isDifferent(newData: PageJson, cachedData?: PageJson): boolean { + return ( + Boolean(process.env.IGNORE_CACHE) || + !cachedData?.updates.length || + getLatestImage(cachedData.updates, sortByVersion)?.version !== getLatestImage(newData.updates, sortByVersion)?.version + ); +} + +export async function writeCache(): Promise { + for (const deviceTypeId of DEVICE_TYPE_IDS) { + const url = `${BASE_URL}${deviceTypeId}`; + const page = await getJson(NAME, url); + + if (page?.updates.length) { + writeCacheJson(`${NAME}_${deviceTypeId}`, page); + } + } +} + +export async function download(): Promise { + for (const deviceTypeId of DEVICE_TYPE_IDS) { + const logPrefix = `[${NAME}:${deviceTypeId}]`; + const url = `${BASE_URL}${deviceTypeId}`; + const page = await getJson(NAME, url); + + if (!page?.updates.length) { + console.error(`${logPrefix} No image data.`); + continue; + } + + const cacheFileName = `${NAME}_${deviceTypeId}`; + + if (!isDifferent(page, readCacheJson(cacheFileName))) { + console.log(`${logPrefix} No change from last run.`); + continue; + } + + writeCacheJson(cacheFileName, page); + + const image = getLatestImage(page.updates, sortByVersion); + + if (!image) { + continue; + } + + const firmwareFileName = image.binaryUrl.split('/').pop()!; + + await processFirmwareImage(NAME, firmwareFileName, image.binaryUrl, {releaseNotes: image.releaseNotes || undefined}); + } +} diff --git a/src/common.ts b/src/common.ts index 53f62853..a084d20d 100644 --- a/src/common.ts +++ b/src/common.ts @@ -31,6 +31,7 @@ export const PR_ARTIFACT_NUMBER_FILEPATH = path.join(PR_ARTIFACT_DIR, PR_NUMBER_ */ export const ALL_AUTODL_MANUFACTURERS = [ 'gammatroniques', + 'hue', 'ikea_new', 'ikea', 'inovelli', diff --git a/tests/ghw_reprocess_all_images.test.ts b/tests/ghw_reprocess_all_images.test.ts index c53a3462..823b9708 100644 --- a/tests/ghw_reprocess_all_images.test.ts +++ b/tests/ghw_reprocess_all_images.test.ts @@ -462,6 +462,7 @@ describe('Github Workflow: Re-Process All Images', () => { ok: fetchReturnedStatus.ok, status: fetchReturnedStatus.status, body: fetchReturnedStatus.body, + // @ts-expect-error Buffer <> ArrayBuffer (props not used) arrayBuffer: (): ArrayBuffer => readFileSync(getImageOriginalDirPath((input as string).split('/').pop()!)), }; }, @@ -762,6 +763,7 @@ describe('Github Workflow: Re-Process All Images', () => { ok: fetchReturnedStatus.ok, status: fetchReturnedStatus.status, body: fetchReturnedStatus.body, + // @ts-expect-error Buffer <> ArrayBuffer (props not used) arrayBuffer: (): ArrayBuffer => readFileSync(getImageOriginalDirPath(fileName)), }; }, diff --git a/tests/process_firmware_image.test.ts b/tests/process_firmware_image.test.ts index 4b9cbf79..875f7ec2 100644 --- a/tests/process_firmware_image.test.ts +++ b/tests/process_firmware_image.test.ts @@ -134,6 +134,7 @@ describe('Process Firmware Image', () => { ok: fetchReturnedStatus.ok, status: fetchReturnedStatus.status, body: fetchReturnedStatus.body, + // @ts-expect-error Buffer <> ArrayBuffer (props not used) arrayBuffer: (): ArrayBuffer => readFileSync(getImageOriginalDirPath(input as string)), }; },