[External Converter]: ZG-IR01 from HOBEIAN
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.7k
- Forks
- 2k
- Avg merge
- 18h 55m
- Merged PRs (30d)
- 35
Description
Link
https://aliexpress.com/item/1005012528513279.html
Database entry
{"id":7,"type":"EndDevice","ieeeAddr":"0xa4c138f8b889a2eb","nwkAddr":50491,"manufId":4742,"manufName":"HOBEIAN","powerSource":"Battery","modelId":"ZG-IR01","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":256,"inClusterList":[0,3,4,5,61184,57348,60672,6,1026,1029,1],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{"manufacturerName":"HOBEIAN","zclVersion":3,"appVersion":151,"modelId":"ZG-IR01","powerSource":3}},"msTemperatureMeasurement":{"attributes":{"measuredValue":2600}},"msRelativeHumidity":{"attributes":{"measuredValue":5500}},"genPowerCfg":{"attributes":{"batteryPercentageRemaining":200,"batteryVoltage":26}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":151,"stackVersion":2,"hwVersion":1,"dateCode":"15062026","swBuildId":"0115062026","zclVersion":3,"interviewCompleted":true,"interviewState":"SUCCESSFUL","meta":{"configured":"0.0.0"},"lastSeen":1788369992914}
Zigbee2MQTT version
2.14.0 (unknown)
External converter
import deviceIndexImport from "zigbee-herdsman-converters/devices/index";
import {tzZosung} from "zigbee-herdsman-converters/lib/zosung";
let definitions = deviceIndexImport;
for (let i = 0; i < 5 && !Array.isArray(definitions); i++) {
definitions = definitions?.default;
}
if (!Array.isArray(definitions)) {
throw new Error("Could not load built-in Zigbee device definitions");
}
const original = definitions.find(
(definition) =>
definition.model === "ZG-IR01" &&
definition.vendor === "HOBEIAN",
);
if (!original) {
throw new Error("Built-in HOBEIAN ZG-IR01 definition not found");
}
//
// Convert HA raw IR timings in microseconds to a Broadlink IR packet.
//
function timingsToBroadlinkBase64(timings, repeatCount = 0) {
if (!Array.isArray(timings) || timings.length === 0) {
throw new Error("IR timings must be a non-empty array");
}
const encoded = [];
for (const timing of timings) {
const duration = Math.abs(Number(timing));
if (!Number.isFinite(duration) || duration <= 0) {
throw new Error(`Invalid IR timing: ${timing}`);
}
// Broadlink tick = 32.84 µs approximately.
const ticks = Math.max(
1,
Math.round((duration * 269) / 8192),
);
if (ticks > 0xffff) {
throw new Error(`IR timing too long: ${timing}`);
}
if (ticks < 0x100) {
encoded.push(ticks);
} else {
encoded.push(
0x00,
(ticks >> 8) & 0xff,
ticks & 0xff,
);
}
}
const repeats = Math.max(
0,
Math.min(255, Math.trunc(Number(repeatCount) || 0)),
);
const packet = [
0x26, // IR packet
repeats,
encoded.length & 0xff, // data length, little endian
(encoded.length >> 8) & 0xff,
...encoded,
0x0d,
0x05,
];
// Broadlink packets are padded to a 16-byte boundary
// including the four-byte transport header.
while ((packet.length + 4) % 16 !== 0) {
packet.push(0x00);
}
return Buffer.from(packet).toString("base64");
}
//
// Override only native HA ir_emitter handling.
//
const zgIr01NativeInfrared = {
key: ["ir_emitter"],
convertSet: async (entity, key, value, meta) => {
if (
value &&
typeof value === "object" &&
Array.isArray(value.timings)
) {
const broadlinkCode = timingsToBroadlinkBase64(
value.timings,
value.repeat_count ?? 0,
);
return await tzZosung.zosung_ir_code_to_send.convertSet(
entity,
"ir_emitter",
broadlinkCode,
meta,
);
}
// Preserve support for directly supplied IR codes.
return await tzZosung.zosung_ir_code_to_send.convertSet(
entity,
key,
value,
meta,
);
},
};
export default {
...original,
// Our ir_emitter converter must come before the built-in one.
toZigbee: [
zgIr01NativeInfrared,
...(original.toZigbee ?? []).filter(
(converter) =>
!converter.key?.includes("ir_emitter"),
),
],
};
What does/doesn't work with the external definition?
Home assistant's new infrared.* entity type doesn't work.
WARNING: It was vibe coded, but it's so simple so I don't think it's too much of a problem, but I know how some people (including me sometimes) can get a bit annoyed with AI use.
Notes
software_build_id: 0115062026
date_code: 15062026
endpoints:
{"1":{"clusters":{"input":["genBasic","genIdentify","genGroups","genScenes","manuSpecificTuya","zosungIRControl","zosungIRTransmit","genOnOff","msTemperatureMeasurement","msRelativeHumidity","genPowerCfg"],"output":["genIdentify"]}}}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the built-in definition loaded through zigbee-herdsman-converters/devices/index and the existing tzZosung converter used by the external definition. Compare the native ir_emitter handling with the supplied timingsToBroadlinkBase64 path. Done means Home Assistant's infrared entity works for ZG-IR01 while direct IR codes remain supported.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100