UNPKG

zigbee-herdsman-converters

Version:

Collection of device converters to be used with zigbee-herdsman

918 lines • 35.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.definitions = void 0; const zigbee_herdsman_1 = require("zigbee-herdsman"); const fz = __importStar(require("../converters/fromZigbee")); const exposes = __importStar(require("../lib/exposes")); const m = __importStar(require("../lib/modernExtend")); const reporting = __importStar(require("../lib/reporting")); const e = exposes.presets; const ea = exposes.access; const HALO_MANUFACTURER_CODE = 0x1201; const CLUSTER_HALO_STATUS = 0xfd00; const CLUSTER_HALO_CONTROL = 0xfd01; const CLUSTER_HALO_SENSORS = 0xfd02; const CLUSTER_HALO_WEATHER = 0xfd03; const HALO_COMMAND_TEST = 0x00; const HALO_COMMAND_HUSH = 0x01; const HALO_TEST_START = 0x01; const HALO_TEST_CANCEL = 0x00; const HALO_HUSH_START = 0x00; const HALO_HUSH_STOP = 0x01; const WEATHER_ALERT_CODES = [ "NONE", "AVA", "AVW", "BZW", "CFA", "CFW", "DSW", "EQW", "FFA", "FFS", "FFW", "FLA", "FLS", "FLW", "FRW", "FSW", "FZW", "HLS", "HUA", "HUW", "HWA", "HWW", "SPS", "SVA", "SVR", "SVS", "TOA", "TOR", "TRA", "TRW", "TSA", "TSW", "VOW", "WSA", "WSW", "ADR", "NIC", "NMN", "CAE", "LAE", "TOE", "DMO", "NAT", "NPT", "NST", "RMT", "RWT", "CDW", "CEM", "EAN", "EAT", "EVI", "HMW", "NUW", "RHW", "SPW", "LEW", "SMW", "??S", "??M", "??E", "??A", "??W", "BHW", "BWW", "CHW", "CWW", "DBA", "DBW", "DEW", "EVA", "FCW", "IBW", "IFW", "LSW", "POS", "WFA", "WFW", "EWW", "SSA", "SSW", ]; const WEATHER_ALERT_STATE_CODES = [...WEATHER_ALERT_CODES, "UNKNOWN"]; const WEATHER_ALERT_CODE_SET = new Set(WEATHER_ALERT_CODES); const WEATHER_ALERT_TO_ID = WEATHER_ALERT_CODES.reduce((acc, code, index) => { acc[code] = index; return acc; }, {}); const WEATHER_ID_TO_ALERT = WEATHER_ALERT_CODES.reduce((acc, code, index) => { acc[index] = code; return acc; }, {}); const WEATHER_PLAYING_STATE_VALUES = ["quiet", "playing"]; const WEATHER_PLAYBACK_ACTIONS = ["play", "stop"]; const HALO_WEATHER_COMMAND_SCAN = 0x00; const HALO_WEATHER_COMMAND_PLAY = 0x03; const WEATHER_STATION_MIN = 1; const WEATHER_STATION_MAX = 7; const WEATHER_STATION_FREQUENCIES = { 1: "162.400 MHz", 2: "162.425 MHz", 3: "162.450 MHz", 4: "162.475 MHz", 5: "162.500 MHz", 6: "162.525 MHz", 7: "162.550 MHz", }; const HALO_ALERT_STATES = [ "safe", "pre_smoke", "smoke", "carbon_monoxide", "weather", "interconnect_smoke", "interconnect_carbon_monoxide", "silenced", "low_battery", "very_low_battery", "failed_battery", "end_of_life", "other", "co_test", "smoke_test", ]; const HALO_TEST_STATES = [ "idle", "running", "success", "fail_ion", "fail_photo", "fail_co", "fail_temperature", "fail_weather", "fail_other", ]; const HALO_HUSH_STATES = ["ready", "success", "timeout", "disabled"]; const HALO_ROOMS = [ "none", "basement", "bedroom", "den", "dining_room", "downstairs", "entryway", "family_room", "game_room", "guest_bedroom", "hallway", "kids_bedroom", "living_room", "master_bedroom", "office", "study", "upstairs", "workout_room", ]; const HALO_ROOM_ID_BY_NAME = { none: 0xff, basement: 0x00, bedroom: 0x01, den: 0x02, dining_room: 0x03, downstairs: 0x04, entryway: 0x05, family_room: 0x06, game_room: 0x07, guest_bedroom: 0x08, hallway: 0x09, kids_bedroom: 0x0a, living_room: 0x0b, master_bedroom: 0x0c, office: 0x0d, study: 0x0e, upstairs: 0x0f, workout_room: 0x10, }; const HALO_ROOM_NAME_BY_ID = Object.entries(HALO_ROOM_ID_BY_NAME).reduce((acc, [name, id]) => { acc[id] = name; return acc; }, {}); function ensureHaloWeatherMetaState(meta) { if (!meta.state.haloWeather) { meta.state.haloWeather = {}; } return meta.state.haloWeather; } function normalizeWeatherStation(value) { const parsed = Number(value); if (!Number.isInteger(parsed)) { throw new Error("weather_station must be an integer between 1 and 7"); } if (parsed < WEATHER_STATION_MIN || parsed > WEATHER_STATION_MAX) { throw new Error(`weather_station must be between ${WEATHER_STATION_MIN} and ${WEATHER_STATION_MAX}`); } return parsed; } function normalizeWeatherLocation(value) { const raw = typeof value === "number" ? value.toString() : String(value ?? "").trim(); if (!/^\d{1,6}$/.test(raw)) { throw new Error("weather_location must be a numeric SAME code up to 6 digits"); } const padded = raw.padStart(6, "0"); return { numeric: Number(padded), padded }; } function normalizeWeatherAlertCode(value) { const upper = value.toUpperCase(); if (!WEATHER_ALERT_CODE_SET.has(upper)) { throw new Error(`Unsupported weather alert code: ${value}`); } return upper; } function normalizeHaloRoom(value) { if (value === undefined || value === null) { throw new Error("room value is required"); } const normalized = String(value).trim().toLowerCase().replace(/\s+/g, "_"); if (!(normalized in HALO_ROOM_ID_BY_NAME)) { throw new Error(`Unsupported room value '${value}'`); } return normalized; } function haloRoomIdToName(id) { if (id === undefined) return "none"; return HALO_ROOM_NAME_BY_ID[id] ?? "none"; } function parseWeatherAlertList(value) { if (value === undefined || value === null) { return []; } let rawList; if (Array.isArray(value)) { rawList = value.map((entry) => (typeof entry === "string" ? entry : String(entry ?? ""))); } else if (typeof value === "string") { const trimmed = value.trim(); if (trimmed.startsWith("[") && trimmed.endsWith("]")) { try { const parsed = JSON.parse(trimmed); if (Array.isArray(parsed)) { rawList = parsed.map((entry) => (typeof entry === "string" ? entry : String(entry ?? ""))); } } catch { // fall back to simple splitting } } if (!rawList) { rawList = trimmed .split(/[,\s]+/) .map((entry) => entry.trim()) .filter((entry) => entry.length > 0); } } else { rawList = [String(value)]; } const unique = []; for (const entry of rawList) { const code = normalizeWeatherAlertCode(entry); if (!unique.includes(code)) { unique.push(code); } } return unique; } function encodeWeatherAlertBitmaps(alerts) { let event1 = 0; let event2 = 0; let event3 = 0; for (const alert of alerts) { const id = WEATHER_ALERT_TO_ID[alert]; if (id <= 0) continue; if (id <= 32) { event1 = (event1 | (1 << (id - 1))) >>> 0; } else if (id <= 62) { event2 = (event2 | (1 << (id - 33))) >>> 0; } else if (id <= 80) { event3 = (event3 | (1 << (id - 63))) >>> 0; } } return { event1, event2, event3 }; } function decodeWeatherAlertBitmaps(state) { const result = new Set(); const addFromBitmap = (bitmap, startId) => { if (bitmap === undefined) return; const unsigned = bitmap >>> 0; for (let bit = 0; bit < 32; bit += 1) { if ((unsigned & (1 << bit)) !== 0) { const id = startId + bit; const code = WEATHER_ID_TO_ALERT[id]; if (code) { result.add(code); } } } }; addFromBitmap(state.event1, 1); addFromBitmap(state.event2, 33); addFromBitmap(state.event3, 63); return [...result].sort((a, b) => WEATHER_ALERT_TO_ID[a] - WEATHER_ALERT_TO_ID[b]); } const DEVICE_STATUS_LOOKUP = { 0: "safe", 1: "low_battery", 2: "end_of_life", 4: "pre_smoke", 5: "weather", 6: "carbon_monoxide", 7: "smoke", 8: "other", 9: "silenced", 10: "very_low_battery", 11: "failed_battery", 14: "co_test", 16: "smoke_test", 18: "interconnect_carbon_monoxide", 19: "interconnect_smoke", }; const TEST_STATUS_LOOKUP = { 0: "success", 1: "fail_ion", 2: "fail_photo", 3: "fail_co", 4: "fail_temperature", 5: "fail_weather", 6: "fail_other", 7: "running", }; const HUSH_STATUS_LOOKUP = { 0: "success", 1: "timeout", 2: "ready", 3: "disabled", }; function haloZoneStatus() { return { exposes: [ e.smoke(), e.carbon_monoxide(), e.tamper(), e.battery_low(), e.test(), e .binary("mains_power_connected", ea.STATE, true, false) .withDescription("Indicates whether mains power is currently available") .withCategory("diagnostic"), ], fromZigbee: [ fz.ias_enroll, { cluster: "ssIasZone", type: ["commandStatusChangeNotification", "attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const zoneStatus = "zonestatus" in msg.data ? msg.data.zonestatus : msg.data.zoneStatus; if (zoneStatus === undefined) return; const payload = {}; if (msg.endpoint.ID === 1) { payload.smoke = (zoneStatus & 1) > 0; payload.tamper = (zoneStatus & (1 << 2)) > 0; payload.battery_low = (zoneStatus & (1 << 3)) > 0; payload.test = (zoneStatus & (1 << 8)) > 0; const acFault = (zoneStatus & (1 << 7)) > 0; payload.mains_power_connected = !acFault; } else if (msg.endpoint.ID === 3) { payload.carbon_monoxide = (zoneStatus & 1) > 0; } if (Object.keys(payload).length > 0) { return payload; } }, }, ], configure: [ async (device, coordinatorEndpoint) => { const endpoint1 = device.getEndpoint(1); if (endpoint1) { await reporting.bind(endpoint1, coordinatorEndpoint, ["ssIasZone"]); await endpoint1.read("ssIasZone", ["zoneStatus"]); } const endpoint3 = device.getEndpoint(3); if (endpoint3) { await reporting.bind(endpoint3, coordinatorEndpoint, ["ssIasZone"]); await endpoint3.read("ssIasZone", ["zoneStatus"]); } }, ], isModernExtend: true, }; } function haloDeviceStatus() { return { exposes: [ e .enum("halo_alert_state", ea.STATE, [...HALO_ALERT_STATES]) .withDescription("Device state reported by the manufacturer cluster") .withCategory("diagnostic"), e.enum("room", ea.STATE_SET, [...HALO_ROOMS]).withDescription("Spoken room name used in Halo announcements"), ], fromZigbee: [ { cluster: "haloDeviceStatus", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const payload = {}; const status = msg.data.deviceStatus; if (status !== undefined) { const mapped = DEVICE_STATUS_LOOKUP[status] ?? "other"; payload.halo_alert_state = mapped; payload.weather_alert = mapped === "weather"; } if (msg.data.room !== undefined) { payload.room = haloRoomIdToName(msg.data.room); } if (Object.keys(payload).length > 0) { return payload; } }, }, ], toZigbee: [ { key: ["room"], convertSet: async (entity, key, value, meta) => { const roomName = normalizeHaloRoom(value); const roomId = HALO_ROOM_ID_BY_NAME[roomName]; const endpoint = meta.device.getEndpoint(4); if (!endpoint) { throw new Error(`Failed to find endpoint 4 on Halo device ${meta.device.ieeeAddr}`); } await endpoint.write("haloDeviceStatus", { room: roomId }, { manufacturerCode: HALO_MANUFACTURER_CODE }); return { state: { room: roomName } }; }, }, ], configure: [ async (device, coordinatorEndpoint) => { const endpoint4 = device.getEndpoint(4); if (endpoint4) { await reporting.bind(endpoint4, coordinatorEndpoint, [CLUSTER_HALO_STATUS]); await endpoint4.read("haloDeviceStatus", ["deviceStatus", "room"], { manufacturerCode: HALO_MANUFACTURER_CODE, }); } }, ], isModernExtend: true, }; } function haloControl() { return { exposes: [ e .enum("halo_test_result", ea.STATE, [...HALO_TEST_STATES]) .withDescription("Result of the most recent self-test") .withCategory("diagnostic"), e.binary("test_in_progress", ea.STATE, true, false).withDescription("Indicates whether a test is running").withCategory("diagnostic"), e .enum("halo_hush_state", ea.STATE, [...HALO_HUSH_STATES]) .withDescription("Current hush status") .withCategory("diagnostic"), e.binary("hush_active", ea.STATE, true, false).withDescription("Indicates whether the alarm is hushed").withCategory("diagnostic"), e.enum("hush", ea.SET, ["start", "stop"]).withDescription("Start or cancel hush mode"), e.enum("test_cycle", ea.SET, ["start", "cancel"]).withDescription("Start or cancel the built-in test"), ], fromZigbee: [ { cluster: "haloControl", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const payload = {}; if (msg.data.testStatus !== undefined) { const mapped = TEST_STATUS_LOOKUP[msg.data.testStatus] ?? "idle"; payload.halo_test_result = mapped; payload.test_in_progress = mapped === "running"; } if (msg.data.hushStatus !== undefined) { const mapped = HUSH_STATUS_LOOKUP[msg.data.hushStatus] ?? "ready"; payload.halo_hush_state = mapped; payload.hush_active = mapped === "success"; } if (Object.keys(payload).length > 0) { return payload; } }, }, ], toZigbee: [ { key: ["hush"], convertSet: async (entity, key, value, meta) => { if (value === undefined) throw new Error("Value must be provided"); const normalized = String(value).toLowerCase(); if (!["start", "stop"].includes(normalized)) { throw new Error("Value must be one of 'start' or 'stop'"); } const payloadValue = normalized === "start" ? HALO_HUSH_START : HALO_HUSH_STOP; const endpoint = meta.device.getEndpoint(4); if (!endpoint) { throw new Error(`Failed to find endpoint 4 on Halo device ${meta.device.ieeeAddr}`); } await endpoint.command("haloControl", "haloHush", { value: payloadValue }, { manufacturerCode: HALO_MANUFACTURER_CODE, disableDefaultResponse: true }); return { state: { hush: normalized } }; }, }, { key: ["test_cycle"], convertSet: async (entity, key, value, meta) => { if (value === undefined) throw new Error("Value must be provided"); const normalized = String(value).toLowerCase(); if (!["start", "cancel"].includes(normalized)) { throw new Error("Value must be one of 'start' or 'cancel'"); } const payloadValue = normalized === "start" ? HALO_TEST_START : HALO_TEST_CANCEL; const endpoint = meta.device.getEndpoint(4); if (!endpoint) { throw new Error(`Failed to find endpoint 4 on Halo device ${meta.device.ieeeAddr}`); } await endpoint.command("haloControl", "haloTest", { value: payloadValue }, { manufacturerCode: HALO_MANUFACTURER_CODE, disableDefaultResponse: true }); return { state: { test_cycle: normalized } }; }, }, ], configure: [ async (device, coordinatorEndpoint) => { const endpoint4 = device.getEndpoint(4); if (endpoint4) { await reporting.bind(endpoint4, coordinatorEndpoint, [CLUSTER_HALO_CONTROL]); await endpoint4.read("haloControl", ["testStatus", "hushStatus"], { manufacturerCode: HALO_MANUFACTURER_CODE, }); } }, ], isModernExtend: true, }; } function haloSensors() { return { exposes: [ e .numeric("co_ppm", ea.STATE) .withDescription("Current CO concentration reported by the detector") .withUnit("ppm") .withCategory("diagnostic"), ], fromZigbee: [ { cluster: "haloSensors", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { if (msg.data.coPpm !== undefined) { return { co_ppm: msg.data.coPpm }; } }, }, ], configure: [ async (device, coordinatorEndpoint) => { const endpoint4 = device.getEndpoint(4); if (endpoint4) { await reporting.bind(endpoint4, coordinatorEndpoint, [CLUSTER_HALO_SENSORS]); await endpoint4.read("haloSensors", ["coPpm"], { manufacturerCode: HALO_MANUFACTURER_CODE, }); await m.setupAttributes(endpoint4, coordinatorEndpoint, "haloSensors", [ { attribute: { ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.INT16 }, min: "10_SECONDS", max: "1_HOUR", change: 1 }, ]); } }, ], isModernExtend: true, }; } function haloWeather() { return { exposes: [ e .binary("weather_alert", ea.STATE, true, false) .withDescription("Indicates an active NOAA/SAME weather alert") .withCategory("diagnostic"), e .enum("current_weather_alert", ea.STATE, [...WEATHER_ALERT_STATE_CODES]) .withDescription("Current NOAA/SAME event code reported by the detector") .withCategory("diagnostic"), e .list("weather_alerts_interest", ea.STATE_SET, e.enum("alert", ea.STATE_SET, [...WEATHER_ALERT_CODES]).withDescription("Alert code")) .withDescription("NOAA/SAME event codes that should trigger the Halo+ speaker") .withCategory("config"), e .text("weather_location", ea.STATE_SET) .withDescription("SAME location code (000000-999999) announced by the weather radio") .withCategory("config"), e .numeric("weather_station", ea.STATE_SET) .withValueMin(WEATHER_STATION_MIN) .withValueMax(WEATHER_STATION_MAX) .withDescription("Weather radio preset (1-7)") .withCategory("config"), e.text("weather_frequency", ea.STATE).withDescription("Frequency of the selected weather radio preset").withCategory("diagnostic"), e .enum("weather_playing_state", ea.STATE, [...WEATHER_PLAYING_STATE_VALUES]) .withDescription("Current playback state of the weather radio") .withCategory("diagnostic"), e.enum("weather_playback", ea.SET, [...WEATHER_PLAYBACK_ACTIONS]).withDescription("Start or stop the weather radio audio"), ], fromZigbee: [ { cluster: "haloWeather", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const payload = {}; const weatherState = ensureHaloWeatherMetaState(meta); if (msg.data.weatherAlertStatus !== undefined) { const code = (WEATHER_ID_TO_ALERT[msg.data.weatherAlertStatus] ?? "UNKNOWN"); payload.current_weather_alert = code; payload.weather_alert = msg.data.weatherAlertStatus !== 0; } if (msg.data.weatherMute !== undefined) { const playing = Number(msg.data.weatherMute) === 1; payload.weather_playing_state = playing ? "playing" : "quiet"; } if (msg.data.weatherLocation !== undefined) { payload.weather_location = msg.data.weatherLocation.toString().padStart(6, "0"); } if (msg.data.weatherStation !== undefined) { const station = msg.data.weatherStation; if (station >= WEATHER_STATION_MIN && station <= WEATHER_STATION_MAX) { payload.weather_station = station; payload.weather_frequency = WEATHER_STATION_FREQUENCIES[station]; } } let eventsChanged = false; if (msg.data.weatherEvent1 !== undefined) { weatherState.event1 = msg.data.weatherEvent1; eventsChanged = true; } if (msg.data.weatherEvent2 !== undefined) { weatherState.event2 = msg.data.weatherEvent2; eventsChanged = true; } if (msg.data.weatherEvent3 !== undefined) { weatherState.event3 = msg.data.weatherEvent3; eventsChanged = true; } if (eventsChanged) { payload.weather_alerts_interest = decodeWeatherAlertBitmaps(weatherState); } if (Object.keys(payload).length > 0) { return payload; } }, }, ], toZigbee: [ { key: ["weather_playback"], convertSet: async (entity, key, value, meta) => { if (value === undefined) throw new Error("Value must be provided"); const normalized = typeof value === "boolean" ? (value ? "play" : "stop") : String(value).toLowerCase(); if (!WEATHER_PLAYBACK_ACTIONS.includes(normalized)) { throw new Error("Value must be 'play' or 'stop'"); } const commandValue = normalized === "play" ? 0x01 : 0x00; const endpoint = meta.device.getEndpoint(5); if (!endpoint) { throw new Error(`Failed to find endpoint 5 on Halo device ${meta.device.ieeeAddr}`); } await endpoint.command("haloWeather", "weatherRadioPlay", { value: commandValue }, { manufacturerCode: HALO_MANUFACTURER_CODE, disableDefaultResponse: true }); return { state: { weather_playback: normalized, weather_playing_state: normalized === "play" ? "playing" : "quiet" } }; }, }, { key: ["weather_station"], convertSet: async (entity, key, value, meta) => { if (value === undefined) throw new Error("Value must be provided"); const station = normalizeWeatherStation(value); const endpoint = meta.device.getEndpoint(5); if (!endpoint) { throw new Error(`Failed to find endpoint 5 on Halo device ${meta.device.ieeeAddr}`); } await endpoint.write("haloWeather", { weatherStation: station }, { manufacturerCode: HALO_MANUFACTURER_CODE }); return { state: { weather_station: station, weather_frequency: WEATHER_STATION_FREQUENCIES[station] } }; }, }, { key: ["weather_location"], convertSet: async (entity, key, value, meta) => { if (value === undefined) throw new Error("Value must be provided"); const location = normalizeWeatherLocation(value); const endpoint = meta.device.getEndpoint(5); if (!endpoint) { throw new Error(`Failed to find endpoint 5 on Halo device ${meta.device.ieeeAddr}`); } await endpoint.write("haloWeather", { weatherLocation: location.numeric }, { manufacturerCode: HALO_MANUFACTURER_CODE }); return { state: { weather_location: location.padded } }; }, }, { key: ["weather_alerts_interest"], convertSet: async (entity, key, value, meta) => { if (value === undefined) throw new Error("Value must be provided"); const alerts = parseWeatherAlertList(value); const { event1, event2, event3 } = encodeWeatherAlertBitmaps(alerts); const endpoint = meta.device.getEndpoint(5); if (!endpoint) { throw new Error(`Failed to find endpoint 5 on Halo device ${meta.device.ieeeAddr}`); } await endpoint.write("haloWeather", { weatherEvent1: event1, weatherEvent2: event2, weatherEvent3: event3 }, { manufacturerCode: HALO_MANUFACTURER_CODE }); return { state: { weather_alerts_interest: alerts } }; }, }, ], configure: [ async (device, coordinatorEndpoint) => { const endpoint5 = device.getEndpoint(5); if (endpoint5) { await reporting.bind(endpoint5, coordinatorEndpoint, [CLUSTER_HALO_WEATHER]); await endpoint5.read("haloWeather", ["weatherAlertStatus", "weatherMute", "weatherLocation", "weatherEvent1", "weatherEvent2", "weatherEvent3", "weatherStation"], { manufacturerCode: HALO_MANUFACTURER_CODE }); } }, ], isModernExtend: true, }; } const haloCommonExtend = [ m.deviceEndpoints({ endpoints: { default: 1, light: 2 } }), m.deviceAddCustomCluster("haloDeviceStatus", { name: "haloDeviceStatus", ID: CLUSTER_HALO_STATUS, manufacturerCode: HALO_MANUFACTURER_CODE, attributes: { deviceStatus: { name: "deviceStatus", ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 }, room: { name: "room", ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 }, }, commands: {}, commandsResponse: {}, }), m.deviceAddCustomCluster("haloControl", { name: "haloControl", ID: CLUSTER_HALO_CONTROL, manufacturerCode: HALO_MANUFACTURER_CODE, attributes: { testStatus: { name: "testStatus", ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 }, hushStatus: { name: "hushStatus", ID: 0x0001, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 }, }, commands: { haloTest: { name: "haloTest", ID: HALO_COMMAND_TEST, parameters: [{ name: "value", type: zigbee_herdsman_1.Zcl.DataType.UINT8 }] }, haloHush: { name: "haloHush", ID: HALO_COMMAND_HUSH, parameters: [{ name: "value", type: zigbee_herdsman_1.Zcl.DataType.UINT8 }] }, }, commandsResponse: {}, }), m.deviceAddCustomCluster("haloSensors", { name: "haloSensors", ID: CLUSTER_HALO_SENSORS, manufacturerCode: HALO_MANUFACTURER_CODE, attributes: { coPpm: { name: "coPpm", ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.INT16 }, }, commands: {}, commandsResponse: {}, }), haloZoneStatus(), haloDeviceStatus(), haloControl(), haloSensors(), m.light({ color: { modes: ["hs"], enhancedHue: false }, configureReporting: true, effect: false, powerOnBehavior: false, endpointNames: ["light"], }), m.battery({ percentage: true, voltage: true, voltageReporting: true }), m.temperature(), m.humidity(), m.pressure(), ]; exports.definitions = [ { zigbeeModel: ["halo"], model: "HALO", vendor: "Halo Smart Labs", description: "Halo smart smoke & CO detector", extend: [...haloCommonExtend], meta: { disableDefaultResponse: true }, configure: (device) => { const endpoint2 = device.getEndpoint(2); if (endpoint2) { endpoint2.saveClusterAttributeKeyValue("lightingColorCtrl", { colorCapabilities: 0x09, colorTempPhysicalMin: 153, colorTempPhysicalMax: 500, }); } }, }, { zigbeeModel: ["halo+", "haloWX", "SABDA1"], model: "HALO+", vendor: "Halo Smart Labs", description: "Halo+ smart smoke & CO detector with weather radio", extend: [ ...haloCommonExtend, m.deviceAddCustomCluster("haloWeather", { name: "haloWeather", ID: CLUSTER_HALO_WEATHER, manufacturerCode: HALO_MANUFACTURER_CODE, attributes: { weatherAlertStatus: { name: "weatherAlertStatus", ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 }, weatherMute: { name: "weatherMute", ID: 0x0001, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN }, weatherLocation: { name: "weatherLocation", ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.UINT32 }, weatherEvent1: { name: "weatherEvent1", ID: 0x0003, type: zigbee_herdsman_1.Zcl.DataType.BITMAP32 }, weatherEvent2: { name: "weatherEvent2", ID: 0x0004, type: zigbee_herdsman_1.Zcl.DataType.BITMAP32 }, weatherEvent3: { name: "weatherEvent3", ID: 0x0005, type: zigbee_herdsman_1.Zcl.DataType.BITMAP32 }, weatherStation: { name: "weatherStation", ID: 0x0006, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, weatherStationRssi1: { name: "weatherStationRssi1", ID: 0x0007, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, weatherStationRssi2: { name: "weatherStationRssi2", ID: 0x0008, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, weatherStationRssi3: { name: "weatherStationRssi3", ID: 0x0009, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, weatherStationRssi4: { name: "weatherStationRssi4", ID: 0x000a, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, weatherStationRssi5: { name: "weatherStationRssi5", ID: 0x000b, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, weatherStationRssi6: { name: "weatherStationRssi6", ID: 0x000c, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, weatherStationRssi7: { name: "weatherStationRssi7", ID: 0x000d, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, }, commands: { weatherScan: { name: "weatherScan", ID: HALO_WEATHER_COMMAND_SCAN, parameters: [] }, weatherRadioPlay: { name: "weatherRadioPlay", ID: HALO_WEATHER_COMMAND_PLAY, parameters: [{ name: "value", type: zigbee_herdsman_1.Zcl.DataType.UINT8 }], }, }, commandsResponse: {}, }), haloWeather(), ], meta: { disableDefaultResponse: true }, configure: (device) => { const endpoint2 = device.getEndpoint(2); if (endpoint2) { endpoint2.saveClusterAttributeKeyValue("lightingColorCtrl", { colorCapabilities: 0x09, colorTempPhysicalMin: 153, colorTempPhysicalMax: 500, }); } }, }, ]; //# sourceMappingURL=halo_smart_labs.js.map