diff --git a/src/index.ts b/src/index.ts index 79c28a3089711..60bf02428d25d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -523,49 +523,51 @@ export async function findDefinition(device: Zh.Device, generateForUnknown = fal } } - if (!candidates) { - if (!generateForUnknown || device.type === "Coordinator") { - return undefined; + if (candidates) { + if (candidates.length === 1 && candidates[0].zigbeeModel) { + return candidates[0]; } - // Do not add this definition to cache, as device configuration might change. - const {definition} = await generateDefinition(device); + logger.debug(() => `Candidates for ${device.ieeeAddr}/${device.modelID}: ${candidates.map((c) => `${c.model}/${c.vendor}`)}`, NS); - return definition; - } - if (candidates.length === 1 && candidates[0].zigbeeModel) { - return candidates[0]; - } - logger.debug(() => `Candidates for ${device.ieeeAddr}/${device.modelID}: ${candidates.map((c) => `${c.model}/${c.vendor}`)}`, NS); + // First try to match based on fingerprint, return the first matching one. + const fingerprintMatch: {priority?: number; definition?: DefinitionWithExtend} = {priority: undefined, definition: undefined}; - // First try to match based on fingerprint, return the first matching one. - const fingerprintMatch: {priority?: number; definition?: DefinitionWithExtend} = {priority: undefined, definition: undefined}; + for (const candidate of candidates) { + if (candidate.fingerprint) { + for (const fingerprint of candidate.fingerprint) { + const priority = fingerprint.priority ?? 0; - for (const candidate of candidates) { - if (candidate.fingerprint) { - for (const fingerprint of candidate.fingerprint) { - const priority = fingerprint.priority ?? 0; - - if (isFingerprintMatch(fingerprint, device) && (fingerprintMatch.priority === undefined || priority > fingerprintMatch.priority)) { - fingerprintMatch.definition = candidate; - fingerprintMatch.priority = priority; + if ( + isFingerprintMatch(fingerprint, device) && + (fingerprintMatch.priority === undefined || priority > fingerprintMatch.priority) + ) { + fingerprintMatch.definition = candidate; + fingerprintMatch.priority = priority; + } } } } - } - if (fingerprintMatch.definition) { - return fingerprintMatch.definition; - } + if (fingerprintMatch.definition) { + return fingerprintMatch.definition; + } - // Match based on fingerprint failed, return first matching definition based on zigbeeModel - for (const candidate of candidates) { - if (candidate.zigbeeModel && device.modelID && candidate.zigbeeModel.includes(device.modelID)) { - return candidate; + // Match based on fingerprint failed, return first matching definition based on zigbeeModel + for (const candidate of candidates) { + if (candidate.zigbeeModel && device.modelID && candidate.zigbeeModel.includes(device.modelID)) { + return candidate; + } } } - return undefined; + if (!generateForUnknown || device.type === "Coordinator") { + return undefined; + } + + const {definition} = await generateDefinition(device); + + return definition; } export async function generateExternalDefinitionSource(device: Zh.Device): Promise { diff --git a/test/index.test.ts b/test/index.test.ts index 27ad1274dd243..8a8ffb50d7db4 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -261,6 +261,41 @@ describe("ZHC", () => { expect(definition.model).toStrictEqual("BHT-002/BHT-006"); }); + it("finds definition by fingerprint - GP", async () => { + const device = mockDevice( + { + modelID: "GreenPower_7", + endpoints: [{ID: 242, profileID: undefined, deviceID: undefined, inputClusters: [], outputClusters: []}], + }, + "GreenPower", + { + ieeeAddr: "0x0000000001511223", + }, + ); + const definition = await findByDevice(device); + + expect(definition.model).toStrictEqual("PTM 216Z"); + }); + + it("generates definition - GP - with no matching fingerprint from candidates", async () => { + const device = mockDevice( + { + modelID: "GreenPower_2", + endpoints: [{ID: 242, profileID: undefined, deviceID: undefined, inputClusters: [], outputClusters: []}], + }, + "GreenPower", + { + ieeeAddr: "0x0000000052373160", + }, + ); + + const definition = await findByDevice(device, true); + expect(definition.model).toStrictEqual("GreenPower_2"); + expect(definition.vendor).toStrictEqual(""); + expect(definition.description).toStrictEqual("Automatically generated definition for Green Power"); + expect(definition.generated).toStrictEqual(true); + }); + it("allows definition with both modern extend and exposes as function", async () => { const device = mockDevice({modelID: "MOSZB-140", endpoints: []}); const MOSZB140 = await findByDevice(device);