UNPKG

home-assistant-matter-hub

Version:

!["Home-Assistant-Matter-Hub"](./packages/docs/assets/hamh-logo-small.png)

1,398 lines (1,340 loc) 158 kB
#!/usr/bin/env node // src/cli.ts import "./bootstrap.js"; import * as path4 from "node:path"; import * as url from "node:url"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; // src/commands/start/start-command.ts import "@matter/nodejs"; // src/commands/start/start-handler.ts import { Environment as Environment2, VendorId as VendorId2 } from "@matter/main"; import AsyncLock3 from "async-lock"; import * as ws from "ws"; // src/api/web-api.ts import express3 from "express"; import basicAuth from "express-basic-auth"; import AccessControl from "express-ip-access-control"; import nocache from "nocache"; // src/environment/logger.ts import { LogFormat, Logger, LogLevel as MatterLogLevel } from "@matter/general"; function logging(environment, level, disableColors) { const loggerService = new LoggerService(); loggerService.level = logLevelFromString(level ?? "info"); if (disableColors) { loggerService.format = LogFormat.PLAIN; } environment.set(LoggerService, loggerService); } function logLevelFromString(level) { const customNames = { SILLY: -1 /* SILLY */ }; if (level.toUpperCase() in customNames) { return customNames[level.toUpperCase()]; } return MatterLogLevel(level); } var LoggerService = class { _level = MatterLogLevel.INFO; customLogLevelMapping = { [-1 /* SILLY */]: MatterLogLevel.DEBUG }; set level(value) { this._level = value ?? this._level; Logger.level = this.customLogLevelMapping[this._level] ?? this._level; } set format(format) { Logger.format = format; } get(name) { return new BetterLogger(name, this._level); } }; var BetterLogger = class extends Logger { constructor(name, _level) { super(name); this._level = _level; } silly(...values) { if (this._level <= -1 /* SILLY */) { this.debug(...["SILLY", ...values]); } } }; // src/environment/register.ts function register(environment, factory, service) { const logger = environment.get(LoggerService).get("Environment"); environment.set(factory, service); const close = async () => { logger.debug(`Disposing ${factory.name}`); await service[Symbol.asyncDispose]?.(); logger.debug(`Disposed ${factory.name}`); }; environment.runtime.add({ [Symbol.asyncDispose]: close }); } // src/api/access-log.ts function accessLogger(logger) { return (req, res, next) => { res.on("finish", () => { logger.debug( `${req.method} ${decodeURI(req.originalUrl)} ${res.statusCode} ${res.statusMessage} from ${req.socket.remoteAddress}` ); }); next(); }; } // ../common/dist/bridge-data.js var BridgeStatus; (function(BridgeStatus2) { BridgeStatus2["Starting"] = "starting"; BridgeStatus2["Running"] = "running"; BridgeStatus2["Stopped"] = "stopped"; BridgeStatus2["Failed"] = "failed"; })(BridgeStatus || (BridgeStatus = {})); // ../common/dist/home-assistant-filter.js var HomeAssistantMatcherType; (function(HomeAssistantMatcherType2) { HomeAssistantMatcherType2["Pattern"] = "pattern"; HomeAssistantMatcherType2["Domain"] = "domain"; HomeAssistantMatcherType2["Platform"] = "platform"; HomeAssistantMatcherType2["Label"] = "label"; HomeAssistantMatcherType2["Area"] = "area"; HomeAssistantMatcherType2["EntityCategory"] = "entity_category"; })(HomeAssistantMatcherType || (HomeAssistantMatcherType = {})); // ../common/dist/home-assistant-domain.js var HomeAssistantDomain; (function(HomeAssistantDomain2) { HomeAssistantDomain2["automation"] = "automation"; HomeAssistantDomain2["button"] = "button"; HomeAssistantDomain2["binary_sensor"] = "binary_sensor"; HomeAssistantDomain2["climate"] = "climate"; HomeAssistantDomain2["cover"] = "cover"; HomeAssistantDomain2["fan"] = "fan"; HomeAssistantDomain2["humidifier"] = "humidifier"; HomeAssistantDomain2["input_boolean"] = "input_boolean"; HomeAssistantDomain2["input_button"] = "input_button"; HomeAssistantDomain2["light"] = "light"; HomeAssistantDomain2["lock"] = "lock"; HomeAssistantDomain2["media_player"] = "media_player"; HomeAssistantDomain2["scene"] = "scene"; HomeAssistantDomain2["script"] = "script"; HomeAssistantDomain2["sensor"] = "sensor"; HomeAssistantDomain2["switch"] = "switch"; HomeAssistantDomain2["vacuum"] = "vacuum"; })(HomeAssistantDomain || (HomeAssistantDomain = {})); // ../common/dist/domains/binary-sensor.js var BinarySensorDeviceClass; (function(BinarySensorDeviceClass2) { BinarySensorDeviceClass2["Battery"] = "battery"; BinarySensorDeviceClass2["BatteryCharging"] = "battery_charging"; BinarySensorDeviceClass2["CarbonMonoxide"] = "carbon_monoxide"; BinarySensorDeviceClass2["Cold"] = "cold"; BinarySensorDeviceClass2["Connectivity"] = "connectivity"; BinarySensorDeviceClass2["Door"] = "door"; BinarySensorDeviceClass2["GarageDoor"] = "garage_door"; BinarySensorDeviceClass2["Gas"] = "gas"; BinarySensorDeviceClass2["Heat"] = "heat"; BinarySensorDeviceClass2["Light"] = "light"; BinarySensorDeviceClass2["Lock"] = "lock"; BinarySensorDeviceClass2["Moisture"] = "moisture"; BinarySensorDeviceClass2["Motion"] = "motion"; BinarySensorDeviceClass2["Moving"] = "moving"; BinarySensorDeviceClass2["Occupancy"] = "occupancy"; BinarySensorDeviceClass2["Opening"] = "opening"; BinarySensorDeviceClass2["Plug"] = "plug"; BinarySensorDeviceClass2["Power"] = "power"; BinarySensorDeviceClass2["Presence"] = "presence"; BinarySensorDeviceClass2["Problem"] = "problem"; BinarySensorDeviceClass2["Running"] = "running"; BinarySensorDeviceClass2["Safety"] = "safety"; BinarySensorDeviceClass2["Smoke"] = "smoke"; BinarySensorDeviceClass2["Sound"] = "sound"; BinarySensorDeviceClass2["Tamper"] = "tamper"; BinarySensorDeviceClass2["Update"] = "update"; BinarySensorDeviceClass2["Vibration"] = "vibration"; BinarySensorDeviceClass2["Window"] = "window"; })(BinarySensorDeviceClass || (BinarySensorDeviceClass = {})); // ../common/dist/domains/climate.js var ClimateHvacMode; (function(ClimateHvacMode2) { ClimateHvacMode2["off"] = "off"; ClimateHvacMode2["heat"] = "heat"; ClimateHvacMode2["cool"] = "cool"; ClimateHvacMode2["heat_cool"] = "heat_cool"; ClimateHvacMode2["auto"] = "auto"; ClimateHvacMode2["dry"] = "dry"; ClimateHvacMode2["fan_only"] = "fan_only"; })(ClimateHvacMode || (ClimateHvacMode = {})); var ClimateHvacAction; (function(ClimateHvacAction2) { ClimateHvacAction2["off"] = "off"; ClimateHvacAction2["preheating"] = "preheating"; ClimateHvacAction2["heating"] = "heating"; ClimateHvacAction2["cooling"] = "cooling"; ClimateHvacAction2["drying"] = "drying"; ClimateHvacAction2["fan"] = "fan"; ClimateHvacAction2["idle"] = "idle"; ClimateHvacAction2["defrosting"] = "defrosting"; })(ClimateHvacAction || (ClimateHvacAction = {})); var ClimateDeviceFeature; (function(ClimateDeviceFeature2) { ClimateDeviceFeature2[ClimateDeviceFeature2["TARGET_TEMPERATURE"] = 1] = "TARGET_TEMPERATURE"; ClimateDeviceFeature2[ClimateDeviceFeature2["TARGET_TEMPERATURE_RANGE"] = 2] = "TARGET_TEMPERATURE_RANGE"; ClimateDeviceFeature2[ClimateDeviceFeature2["TARGET_HUMIDITY"] = 4] = "TARGET_HUMIDITY"; ClimateDeviceFeature2[ClimateDeviceFeature2["FAN_MODE"] = 8] = "FAN_MODE"; ClimateDeviceFeature2[ClimateDeviceFeature2["PRESET_MODE"] = 16] = "PRESET_MODE"; ClimateDeviceFeature2[ClimateDeviceFeature2["SWING_MODE"] = 32] = "SWING_MODE"; ClimateDeviceFeature2[ClimateDeviceFeature2["AUX_HEAT"] = 64] = "AUX_HEAT"; ClimateDeviceFeature2[ClimateDeviceFeature2["TURN_OFF"] = 128] = "TURN_OFF"; ClimateDeviceFeature2[ClimateDeviceFeature2["TURN_ON"] = 256] = "TURN_ON"; ClimateDeviceFeature2[ClimateDeviceFeature2["SWING_HORIZONTAL_MODE"] = 512] = "SWING_HORIZONTAL_MODE"; })(ClimateDeviceFeature || (ClimateDeviceFeature = {})); // ../common/dist/domains/cover.js var CoverDeviceState; (function(CoverDeviceState2) { CoverDeviceState2["closed"] = "closed"; CoverDeviceState2["open"] = "open"; CoverDeviceState2["closing"] = "closing"; CoverDeviceState2["opening"] = "opening"; })(CoverDeviceState || (CoverDeviceState = {})); var CoverSupportedFeatures = { support_open: 1, support_close: 2, support_set_position: 4, support_stop: 8, support_open_tilt: 16, support_close_tilt: 32, support_stop_tilt: 64, support_set_tilt_position: 128 }; // ../common/dist/domains/fan.js var FanDeviceDirection; (function(FanDeviceDirection2) { FanDeviceDirection2["FORWARD"] = "forward"; FanDeviceDirection2["REVERSE"] = "reverse"; })(FanDeviceDirection || (FanDeviceDirection = {})); var FanDeviceFeature; (function(FanDeviceFeature2) { FanDeviceFeature2[FanDeviceFeature2["SET_SPEED"] = 1] = "SET_SPEED"; FanDeviceFeature2[FanDeviceFeature2["OSCILLATE"] = 2] = "OSCILLATE"; FanDeviceFeature2[FanDeviceFeature2["DIRECTION"] = 4] = "DIRECTION"; FanDeviceFeature2[FanDeviceFeature2["PRESET_MODE"] = 8] = "PRESET_MODE"; FanDeviceFeature2[FanDeviceFeature2["TURN_OFF"] = 16] = "TURN_OFF"; FanDeviceFeature2[FanDeviceFeature2["TURN_ON"] = 32] = "TURN_ON"; })(FanDeviceFeature || (FanDeviceFeature = {})); // ../common/dist/domains/light.js var LightDeviceColorMode; (function(LightDeviceColorMode2) { LightDeviceColorMode2["UNKNOWN"] = "unknown"; LightDeviceColorMode2["ONOFF"] = "onoff"; LightDeviceColorMode2["BRIGHTNESS"] = "brightness"; LightDeviceColorMode2["COLOR_TEMP"] = "color_temp"; LightDeviceColorMode2["HS"] = "hs"; LightDeviceColorMode2["XY"] = "xy"; LightDeviceColorMode2["RGB"] = "rgb"; LightDeviceColorMode2["RGBW"] = "rgbw"; LightDeviceColorMode2["RGBWW"] = "rgbww"; LightDeviceColorMode2["WHITE"] = "white"; })(LightDeviceColorMode || (LightDeviceColorMode = {})); // ../common/dist/domains/sensor.js var SensorDeviceClass; (function(SensorDeviceClass2) { SensorDeviceClass2["None"] = "None"; SensorDeviceClass2["apparent_power"] = "apparent_power"; SensorDeviceClass2["aqi"] = "aqi"; SensorDeviceClass2["atmospheric_pressure"] = "atmospheric_pressure"; SensorDeviceClass2["battery"] = "battery"; SensorDeviceClass2["carbon_dioxide"] = "carbon_dioxide"; SensorDeviceClass2["carbon_monoxide"] = "carbon_monoxide"; SensorDeviceClass2["current"] = "current"; SensorDeviceClass2["data_rate"] = "data_rate"; SensorDeviceClass2["data_size"] = "data_size"; SensorDeviceClass2["date"] = "date"; SensorDeviceClass2["distance"] = "distance"; SensorDeviceClass2["duration"] = "duration"; SensorDeviceClass2["energy"] = "energy"; SensorDeviceClass2["energy_storage"] = "energy_storage"; SensorDeviceClass2["enum"] = "enum"; SensorDeviceClass2["frequency"] = "frequency"; SensorDeviceClass2["gas"] = "gas"; SensorDeviceClass2["humidity"] = "humidity"; SensorDeviceClass2["illuminance"] = "illuminance"; SensorDeviceClass2["irradiance"] = "irradiance"; SensorDeviceClass2["moisture"] = "moisture"; SensorDeviceClass2["monetary"] = "monetary"; SensorDeviceClass2["nitrogen_dioxide"] = "nitrogen_dioxide"; SensorDeviceClass2["nitrogen_monoxide"] = "nitrogen_monoxide"; SensorDeviceClass2["nitrous_oxide"] = "nitrous_oxide"; SensorDeviceClass2["ozone"] = "ozone"; SensorDeviceClass2["ph"] = "ph"; SensorDeviceClass2["pm1"] = "pm1"; SensorDeviceClass2["pm25"] = "pm25"; SensorDeviceClass2["pm10"] = "pm10"; SensorDeviceClass2["power_factor"] = "power_factor"; SensorDeviceClass2["power"] = "power"; SensorDeviceClass2["precipitation"] = "precipitation"; SensorDeviceClass2["precipitation_intensity"] = "precipitation_intensity"; SensorDeviceClass2["pressure"] = "pressure"; SensorDeviceClass2["reactive_power"] = "reactive_power"; SensorDeviceClass2["signal_strength"] = "signal_strength"; SensorDeviceClass2["sound_pressure"] = "sound_pressure"; SensorDeviceClass2["speed"] = "speed"; SensorDeviceClass2["sulphur_dioxide"] = "sulphur_dioxide"; SensorDeviceClass2["temperature"] = "temperature"; SensorDeviceClass2["timestamp"] = "timestamp"; SensorDeviceClass2["volatile_organic_compounds"] = "volatile_organic_compounds"; SensorDeviceClass2["volatile_organic_compounds_parts"] = "volatile_organic_compounds_parts"; SensorDeviceClass2["voltage"] = "voltage"; SensorDeviceClass2["volume"] = "volume"; SensorDeviceClass2["volume_flow_rate"] = "volume_flow_rate"; SensorDeviceClass2["volume_storage"] = "volume_storage"; SensorDeviceClass2["water"] = "water"; SensorDeviceClass2["weight"] = "weight"; SensorDeviceClass2["wind_speed"] = "wind_speed"; })(SensorDeviceClass || (SensorDeviceClass = {})); // ../common/dist/domains/media-player.js var MediaPlayerDeviceClass; (function(MediaPlayerDeviceClass2) { MediaPlayerDeviceClass2["Tv"] = "tv"; MediaPlayerDeviceClass2["Speaker"] = "speaker"; MediaPlayerDeviceClass2["Receiver"] = "receiver"; })(MediaPlayerDeviceClass || (MediaPlayerDeviceClass = {})); var MediaPlayerDeviceFeature; (function(MediaPlayerDeviceFeature2) { MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["PAUSE"] = 1] = "PAUSE"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["SEEK"] = 2] = "SEEK"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["VOLUME_SET"] = 4] = "VOLUME_SET"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["VOLUME_MUTE"] = 8] = "VOLUME_MUTE"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["PREVIOUS_TRACK"] = 16] = "PREVIOUS_TRACK"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["NEXT_TRACK"] = 32] = "NEXT_TRACK"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["TURN_ON"] = 128] = "TURN_ON"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["TURN_OFF"] = 256] = "TURN_OFF"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["PLAY_MEDIA"] = 512] = "PLAY_MEDIA"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["VOLUME_STEP"] = 1024] = "VOLUME_STEP"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["SELECT_SOURCE"] = 2048] = "SELECT_SOURCE"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["STOP"] = 4096] = "STOP"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["CLEAR_PLAYLIST"] = 8192] = "CLEAR_PLAYLIST"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["PLAY"] = 16384] = "PLAY"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["SHUFFLE_SET"] = 32768] = "SHUFFLE_SET"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["SELECT_SOUND_MODE"] = 65536] = "SELECT_SOUND_MODE"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["BROWSE_MEDIA"] = 131072] = "BROWSE_MEDIA"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["REPEAT_SET"] = 262144] = "REPEAT_SET"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["GROUPING"] = 524288] = "GROUPING"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["MEDIA_ANNOUNCE"] = 1048576] = "MEDIA_ANNOUNCE"; MediaPlayerDeviceFeature2[MediaPlayerDeviceFeature2["MEDIA_ENQUEUE"] = 2097152] = "MEDIA_ENQUEUE"; })(MediaPlayerDeviceFeature || (MediaPlayerDeviceFeature = {})); // ../common/dist/domains/vacuum.js var VacuumState; (function(VacuumState2) { VacuumState2["cleaning"] = "cleaning"; VacuumState2["docked"] = "docked"; VacuumState2["returning"] = "returning"; VacuumState2["error"] = "error"; VacuumState2["idle"] = "idle"; VacuumState2["paused"] = "paused"; })(VacuumState || (VacuumState = {})); var VacuumDeviceFeature; (function(VacuumDeviceFeature2) { VacuumDeviceFeature2[VacuumDeviceFeature2["TURN_ON"] = 1] = "TURN_ON"; VacuumDeviceFeature2[VacuumDeviceFeature2["TURN_OFF"] = 2] = "TURN_OFF"; VacuumDeviceFeature2[VacuumDeviceFeature2["PAUSE"] = 4] = "PAUSE"; VacuumDeviceFeature2[VacuumDeviceFeature2["STOP"] = 8] = "STOP"; VacuumDeviceFeature2[VacuumDeviceFeature2["RETURN_HOME"] = 16] = "RETURN_HOME"; VacuumDeviceFeature2[VacuumDeviceFeature2["FAN_SPEED"] = 32] = "FAN_SPEED"; VacuumDeviceFeature2[VacuumDeviceFeature2["BATTERY"] = 64] = "BATTERY"; VacuumDeviceFeature2[VacuumDeviceFeature2["STATUS"] = 128] = "STATUS"; VacuumDeviceFeature2[VacuumDeviceFeature2["SEND_COMMAND"] = 256] = "SEND_COMMAND"; VacuumDeviceFeature2[VacuumDeviceFeature2["LOCATE"] = 512] = "LOCATE"; VacuumDeviceFeature2[VacuumDeviceFeature2["CLEAN_SPOT"] = 1024] = "CLEAN_SPOT"; VacuumDeviceFeature2[VacuumDeviceFeature2["MAP"] = 2048] = "MAP"; VacuumDeviceFeature2[VacuumDeviceFeature2["STATE"] = 4096] = "STATE"; VacuumDeviceFeature2[VacuumDeviceFeature2["START"] = 8192] = "START"; })(VacuumDeviceFeature || (VacuumDeviceFeature = {})); var VacuumFanSpeed; (function(VacuumFanSpeed2) { VacuumFanSpeed2["off"] = "off"; VacuumFanSpeed2["low"] = "low"; VacuumFanSpeed2["medium"] = "medium"; VacuumFanSpeed2["high"] = "high"; VacuumFanSpeed2["turbo"] = "turbo"; VacuumFanSpeed2["auto"] = "auto"; VacuumFanSpeed2["max"] = "max"; })(VacuumFanSpeed || (VacuumFanSpeed = {})); // ../common/dist/utils/color-converter.js import Color from "color"; var ColorConverter = class _ColorConverter { /** * Create a color object from `hs_color` value * @param hue Hue, Values between 0 and 360 * @param saturation Saturation, Values between 0 and 100 * @return Color */ static fromHomeAssistantHS(hue, saturation) { return Color.hsv(hue, saturation, 100); } /** * Create a color object from `hue` and `saturation` values set via Matter * @param hue Hue, Values between 0 and 255 * @param saturation Saturation, Values between 0 and 255 * @return Color */ static fromMatterHS(hue, saturation) { return Color.hsv(Math.round(hue / 254 * 360), Math.round(saturation / 254 * 100), 100); } /** * Create a color object from `x` and `y` values set via Matter. * This function was inspired by color utils of Home Assistant Core (`homeassistant.util.color.color_xy_brightness_to_RGB`). * @param x X, Values between 0 and 1 * @param y Y, Values between 0 and 1 * @return Color */ static fromXY(x, y) { function toXYZ(x2, y2) { const Y = 1; const X = Y / y2 * x2; const Z = Y / y2 * (1 - x2 - y2); return [X, Y, Z]; } function toRGB_D65(X, Y, Z) { const r2 = X * 1.656492 - Y * 0.354851 - Z * 0.255038; const g2 = -X * 0.707196 + Y * 1.655397 + Z * 0.036152; const b2 = X * 0.051713 - Y * 0.121364 + Z * 1.01153; return [r2, g2, b2]; } function applyReverseGammaCorrection(x2) { if (x2 <= 31308e-7) { return 12.92 * x2; } return (1 + 0.055) * x2 ** (1 / 2.4) - 0.055; } const XYZ = toXYZ(x, y); let rgb = toRGB_D65(...XYZ).map(applyReverseGammaCorrection).map((v) => Math.max(v, 0)); const maxValue = Math.max(...rgb); if (maxValue > 1) { rgb = rgb.map((v) => v / maxValue); } const [r, g, b] = rgb.map((v) => Math.round(v * 255)); return _ColorConverter.fromRGB(r, g, b); } /** * Create a color object from `rgb_color` value * @param r Red, 0-255 * @param g Green, 0-255 * @param b Blue, 0-255 * @return Color */ static fromRGB(r, g, b) { return Color.rgb(r, g, b); } /** * Create a color object from `rgbw_color` value * @param r Red, 0-255 * @param g Green, 0-255 * @param b Blue, 0-255 * @param w White, 0-255 * @return Color */ static fromRGBW(r, g, b, w) { return _ColorConverter.fromRGB(Math.min(255, r + w), Math.min(255, g + w), Math.min(255, b + w)); } /** * Create a color object from `rgbww_color` value * @param r Red, 0-255 * @param g Green, 0-255 * @param b Blue, 0-255 * @param cw Cold White, 0-255 * @param ww Warm White, 0-255 * @returns */ static fromRGBWW(r, g, b, cw, ww) { return _ColorConverter.fromRGBW(r, g, b, (cw + ww) / 2); } /** * Extract Hue and Saturation compatible with Home Assistant * @param color The Color * @return [hue, saturation] */ static toHomeAssistantHS(color) { const [h, s] = color.hsv().array(); return [h, s]; } /** * Extract Hue and Saturation compatible with Matter * @param color The Color * @return [hue, saturation] */ static toMatterHS(color) { const [h, s] = color.hsv().array(); return [Math.round(h / 360 * 254), Math.round(s / 100 * 254)]; } /** * Convert Color Tempareture from Mireds to Kelvin * @param temperatureMireds Temperature in Mireds * @return Temperature in Kelvin */ static temperatureMiredsToKelvin(temperatureMireds) { return 1e6 / temperatureMireds; } /** * Convert Color Tempareture from Kelvin to Mireds * @param temperatureKelvin Temperature in Kelvin * @param rounding Whether to floor or to ceil after conversion * @param boundaries Min and Max Boundaries to apply * @return Temperature in Mireds */ static temperatureKelvinToMireds(temperatureKelvin, boundaries = [0, 65279]) { const result = 1e6 / temperatureKelvin; const [min, max] = boundaries; return Math.min(Math.max(result, min), max); } }; // ../common/dist/clusters/door-lock.js var DoorLockStatus; (function(DoorLockStatus2) { DoorLockStatus2[DoorLockStatus2["locked"] = 1] = "locked"; DoorLockStatus2[DoorLockStatus2["unlocked"] = 2] = "unlocked"; })(DoorLockStatus || (DoorLockStatus = {})); // ../common/dist/clusters/fan-control.js var FanControlFanMode; (function(FanControlFanMode2) { FanControlFanMode2[FanControlFanMode2["Off"] = 0] = "Off"; FanControlFanMode2[FanControlFanMode2["Low"] = 1] = "Low"; FanControlFanMode2[FanControlFanMode2["Medium"] = 2] = "Medium"; FanControlFanMode2[FanControlFanMode2["High"] = 3] = "High"; FanControlFanMode2[FanControlFanMode2["On"] = 4] = "On"; FanControlFanMode2[FanControlFanMode2["Auto"] = 5] = "Auto"; FanControlFanMode2[FanControlFanMode2["Smart"] = 6] = "Smart"; })(FanControlFanMode || (FanControlFanMode = {})); var FanControlAirflowDirection; (function(FanControlAirflowDirection2) { FanControlAirflowDirection2[FanControlAirflowDirection2["Forward"] = 0] = "Forward"; FanControlAirflowDirection2[FanControlAirflowDirection2["Reverse"] = 1] = "Reverse"; })(FanControlAirflowDirection || (FanControlAirflowDirection = {})); var FanControlFanModeSequence; (function(FanControlFanModeSequence2) { FanControlFanModeSequence2[FanControlFanModeSequence2["OffLowMedHigh"] = 0] = "OffLowMedHigh"; FanControlFanModeSequence2[FanControlFanModeSequence2["OffLowHigh"] = 1] = "OffLowHigh"; FanControlFanModeSequence2[FanControlFanModeSequence2["OffLowMedHighAuto"] = 2] = "OffLowMedHighAuto"; FanControlFanModeSequence2[FanControlFanModeSequence2["OffLowHighAuto"] = 3] = "OffLowHighAuto"; FanControlFanModeSequence2[FanControlFanModeSequence2["OffHighAuto"] = 4] = "OffHighAuto"; FanControlFanModeSequence2[FanControlFanModeSequence2["OffHigh"] = 5] = "OffHigh"; })(FanControlFanModeSequence || (FanControlFanModeSequence = {})); // ../common/dist/clusters/thermostat.js var ThermostatSystemMode; (function(ThermostatSystemMode2) { ThermostatSystemMode2[ThermostatSystemMode2["Off"] = 0] = "Off"; ThermostatSystemMode2[ThermostatSystemMode2["Auto"] = 1] = "Auto"; ThermostatSystemMode2[ThermostatSystemMode2["Cool"] = 3] = "Cool"; ThermostatSystemMode2[ThermostatSystemMode2["Heat"] = 4] = "Heat"; ThermostatSystemMode2[ThermostatSystemMode2["EmergencyHeat"] = 5] = "EmergencyHeat"; ThermostatSystemMode2[ThermostatSystemMode2["Precooling"] = 6] = "Precooling"; ThermostatSystemMode2[ThermostatSystemMode2["FanOnly"] = 7] = "FanOnly"; ThermostatSystemMode2[ThermostatSystemMode2["Dry"] = 8] = "Dry"; ThermostatSystemMode2[ThermostatSystemMode2["Sleep"] = 9] = "Sleep"; })(ThermostatSystemMode || (ThermostatSystemMode = {})); // ../common/dist/clusters/rvc-operational-state.js var RvcOperationalState; (function(RvcOperationalState4) { RvcOperationalState4[RvcOperationalState4["Stopped"] = 0] = "Stopped"; RvcOperationalState4[RvcOperationalState4["Running"] = 1] = "Running"; RvcOperationalState4[RvcOperationalState4["Paused"] = 2] = "Paused"; RvcOperationalState4[RvcOperationalState4["Error"] = 3] = "Error"; RvcOperationalState4[RvcOperationalState4["SeekingCharger"] = 64] = "SeekingCharger"; RvcOperationalState4[RvcOperationalState4["Charging"] = 65] = "Charging"; RvcOperationalState4[RvcOperationalState4["Docked"] = 66] = "Docked"; })(RvcOperationalState || (RvcOperationalState = {})); // ../common/dist/clusters/index.js var ClusterId; (function(ClusterId2) { ClusterId2["homeAssistantEntity"] = "homeAssistantEntity"; ClusterId2["descriptor"] = "descriptor"; ClusterId2["identify"] = "identify"; ClusterId2["groups"] = "groups"; ClusterId2["bridgedDeviceBasicInformation"] = "bridgedDeviceBasicInformation"; ClusterId2["booleanState"] = "booleanState"; ClusterId2["colorControl"] = "colorControl"; ClusterId2["doorLock"] = "doorLock"; ClusterId2["levelControl"] = "levelControl"; ClusterId2["fanControl"] = "fanControl"; ClusterId2["illuminanceMeasurement"] = "illuminanceMeasurement"; ClusterId2["occupancySensing"] = "occupancySensing"; ClusterId2["onOff"] = "onOff"; ClusterId2["relativeHumidityMeasurement"] = "relativeHumidityMeasurement"; ClusterId2["temperatureMeasurement"] = "temperatureMeasurement"; ClusterId2["thermostat"] = "thermostat"; ClusterId2["windowCovering"] = "windowCovering"; ClusterId2["mediaInput"] = "mediaInput"; ClusterId2["rvcRunMode"] = "rvcRunMode"; ClusterId2["rvcOperationalState"] = "rvcOperationalState"; })(ClusterId || (ClusterId = {})); // ../common/dist/schemas/bridge-config-schema.js var homeAssistantMatcherSchema = { type: "object", default: { type: "", value: "" }, properties: { type: { title: "Type", type: "string", enum: Object.values(HomeAssistantMatcherType) }, value: { title: "Value", type: "string", minLength: 1 } }, required: ["type", "value"], additionalProperties: false }; var homeAssistantFilterSchema = { title: "Include or exclude entities", type: "object", properties: { include: { title: "Include", type: "array", items: homeAssistantMatcherSchema }, exclude: { title: "Exclude", type: "array", items: homeAssistantMatcherSchema } }, required: ["include", "exclude"], additionalProperties: false }; var featureFlagSchema = { title: "Feature Flags", type: "object", properties: { coverDoNotInvertPercentage: { title: "Do not invert Percentages for Covers", description: "Do not invert the percentage of covers to match Home Assistant (not Matter compliant)", type: "boolean", default: false }, includeHiddenEntities: { title: "Include Hidden Entities", description: "Include entities that are marked as hidden in Home Assistant", type: "boolean", default: false } }, additionalProperties: false }; var bridgeConfigSchema = { type: "object", title: "Bridge Config", properties: { name: { title: "Name", type: "string", minLength: 1, maxLength: 32 }, port: { title: "Port", type: "number", minimum: 1 }, countryCode: { title: "Country Code", type: "string", description: "An ISO 3166-1 alpha-2 code to represent the country in which the Node is located. Only needed if the commissioning fails due to missing country code.", minLength: 2, maxLength: 3 }, filter: homeAssistantFilterSchema, featureFlags: featureFlagSchema }, required: ["name", "port", "filter"], additionalProperties: false }; // ../common/dist/schemas/create-bridge-request-schema.js var createBridgeRequestSchema = bridgeConfigSchema; // ../common/dist/schemas/update-bridge-request-schema.js var updateBridgeRequestSchema = { ...bridgeConfigSchema, properties: { ...bridgeConfigSchema.properties, id: { type: "string" } }, required: [...bridgeConfigSchema?.required ?? [], "id"] }; // src/api/matter-api.ts import { Ajv } from "ajv"; import express from "express"; // src/errors/port-already-in-use-error.ts var PortAlreadyInUseError = class extends Error { constructor(port) { super(`Port ${port} is already in use`); } }; // src/matter/bridge-service.ts import crypto4 from "node:crypto"; import { asyncNew } from "@matter/main"; // src/storage/bridge-storage.ts import { Environmental as Environmental2 } from "@matter/main"; // src/storage/app-storage.ts import { Environmental, StorageService } from "@matter/main"; var AppStorage = class _AppStorage { constructor(environment) { this.environment = environment; register(environment, _AppStorage, this); this.construction = this.initialize(); } static [Environmental.create](environment) { return new _AppStorage(environment); } construction; storageManager; async initialize() { this.storageManager = await this.environment.load(StorageService).then((s) => s.open("app")); } async [Symbol.asyncDispose]() { await this.storageManager.close(); } createContext(context) { return this.storageManager.createContext(context); } }; // src/storage/migrations/bridge/v1-to-v2.ts async function migrateBridgeV1ToV2(storage2) { const bridgeIds = JSON.parse(await storage2.get("ids", "[]")); await storage2.set("ids", bridgeIds); for (const bridgeId of bridgeIds) { const bridgeValue = await storage2.get(bridgeId); if (bridgeValue === void 0) { continue; } const bridge = JSON.parse(bridgeValue); bridge.compatibility = void 0; await storage2.set(bridgeId, bridge); } return 2; } // src/storage/migrations/bridge/v2-to-v3.ts async function migrateBridgeV2ToV3(storage2) { const bridgeIdsValue = await storage2.get("ids", []); let bridgeIds; if (typeof bridgeIdsValue === "string") { bridgeIds = JSON.parse(bridgeIdsValue); await storage2.set("ids", bridgeIds); } else { bridgeIds = bridgeIdsValue; } for (const bridgeId of bridgeIds) { const bridgeValue = await storage2.get(bridgeId); if (bridgeValue === void 0) { continue; } let bridge; if (typeof bridgeValue === "string") { bridge = JSON.parse(bridgeValue); } else { bridge = bridgeValue; } await storage2.set(bridgeId, bridge); } return 3; } // src/storage/migrations/bridge/v3-to-v4.ts async function migrateBridgeV3ToV4(storage2) { const bridgeIds = await storage2.get("ids", []); for (const bridgeId of bridgeIds) { const bridgeValue = await storage2.get(bridgeId); if (bridgeValue === void 0) { continue; } const bridge = bridgeValue; const featureFlags = bridge.featureFlags; if (featureFlags) { featureFlags.coverDoNotInvertPercentage = featureFlags.mimicHaCoverPercentage ?? false; featureFlags.coverSwapOpenClose = featureFlags.mimicHaCoverPercentage ?? false; featureFlags.mimicHaCoverPercentage = void 0; await storage2.set(bridgeId, bridgeValue); } } return 4; } // src/storage/migrations/bridge/v4-to-v5.ts async function migrateBridgeV4ToV5(storage2) { const bridgeIds = await storage2.get("ids", []); for (const bridgeId of bridgeIds) { const bridgeValue = await storage2.get(bridgeId); if (bridgeValue === void 0) { continue; } const bridge = bridgeValue; const featureFlags = bridge.featureFlags; if (featureFlags) { featureFlags.coverSwapOpenClose = void 0; featureFlags.matterFans = void 0; featureFlags.matterSpeakers = void 0; featureFlags.useOnOffSensorAsDefaultForBinarySensors = void 0; await storage2.set(bridgeId, bridgeValue); } } return 5; } // src/storage/bridge-storage.ts var BridgeStorage = class _BridgeStorage { constructor(environment) { this.environment = environment; register(environment, _BridgeStorage, this); this.construction = this.initialize(); } static [Environmental2.create](environment) { return new _BridgeStorage(environment); } construction; storage; _bridges = []; async initialize() { const appStorage = await this.environment.load(AppStorage); this.storage = appStorage.createContext("bridges"); await this.migrate(); const bridgeIds = await this.storage.get("ids", []); const bridges = await Promise.all( bridgeIds.map( async (bridgeId) => this.storage.get(bridgeId) ) ); this._bridges = bridges.filter((b) => b !== void 0).map((bridge) => bridge); } get bridges() { return this._bridges; } async add(bridge) { const idx = this._bridges.findIndex((b) => b.id === bridge.id); if (idx !== -1) { this._bridges[idx] = bridge; } else { this._bridges.push(bridge); } await this.storage.set(bridge.id, bridge); await this.persistIds(); } async remove(bridgeId) { const idx = this._bridges.findIndex((b) => b.id === bridgeId); if (idx >= 0) { this._bridges.splice(idx, 1); } await this.storage.delete(bridgeId); await this.persistIds(); } async persistIds() { await this.storage.set( "ids", this._bridges.map((b) => b.id) ); } async migrate() { const version = await this.storage.get("version", 1); let migratedVersion = version; if (version === 1) { migratedVersion = await migrateBridgeV1ToV2(this.storage); } else if (version === 2) { migratedVersion = await migrateBridgeV2ToV3(this.storage); } else if (version === 3) { migratedVersion = await migrateBridgeV3ToV4(this.storage); } else if (version === 4) { migratedVersion = await migrateBridgeV4ToV5(this.storage); } if (migratedVersion !== version) { await this.storage.set("version", migratedVersion); return this.migrate(); } } }; // src/matter/bridge/bridge-server-node.ts import { Endpoint as Endpoint2, Environment } from "@matter/main"; import { AggregatorEndpoint as AggregatorEndpoint2 } from "@matter/main/endpoints"; import { ServerNode as ServerNode2 } from "@matter/main/node"; import _4 from "lodash"; // src/utils/json/create-bridge-server-config.ts import crypto from "node:crypto"; import { AggregatorEndpoint } from "@matter/main/endpoints"; import { ServerNode } from "@matter/main/node"; // src/utils/trim-to-length.ts function trimToLength(value, maxLength, suffix) { const stringValue = value?.toString(); if (!stringValue?.trim().length) { return void 0; } if (stringValue.length <= maxLength) { return stringValue; } return stringValue.substring(0, maxLength - suffix.length) + suffix; } // src/utils/json/create-bridge-server-config.ts function createBridgeServerConfig(environment, data) { return { type: ServerNode.RootEndpoint, environment, id: data.id, network: { port: data.port }, productDescription: { name: data.name, deviceType: AggregatorEndpoint.deviceType }, basicInformation: { uniqueId: data.id, nodeLabel: trimToLength(data.name, 32, "..."), vendorId: data.basicInformation.vendorId, vendorName: data.basicInformation.vendorName, productId: data.basicInformation.productId, productName: data.basicInformation.productName, productLabel: data.basicInformation.productLabel, serialNumber: crypto.createHash("md5").update(`serial-${data.id}`).digest("hex").substring(0, 32), hardwareVersion: data.basicInformation.hardwareVersion, softwareVersion: data.basicInformation.softwareVersion, ...data.countryCode ? { location: data.countryCode } : {} } }; } // src/matter/bridge/bridge-data-provider.ts var BridgeDataProvider = class _BridgeDataProvider { constructor(environment, getBridgeData) { this.getBridgeData = getBridgeData; environment.set(_BridgeDataProvider, this); } get basicInformation() { return this.getBridgeData().basicInformation; } get featureFlags() { return this.getBridgeData().featureFlags ?? {}; } }; // src/matter/bridge/bridge-device-manager.ts import { Endpoint } from "@matter/main"; import AsyncLock2 from "async-lock"; import _3 from "lodash"; // src/home-assistant/home-assistant-registry.ts import { Environmental as Environmental3 } from "@matter/main"; import { getStates } from "home-assistant-js-websocket"; import _ from "lodash"; // src/home-assistant/api/get-registry.ts async function getRegistry(connection) { return connection.sendMessagePromise({ type: "config/entity_registry/list" }); } async function getDeviceRegistry(connection) { return connection.sendMessagePromise({ type: "config/device_registry/list" }); } // src/home-assistant/api/subscribe-entities.ts import crypto2 from "node:crypto"; import { getCollection } from "home-assistant-js-websocket"; import { atLeastHaVersion } from "home-assistant-js-websocket/dist/util.js"; function processEvent(store, updates) { const state = { ...store.state }; if (updates.a) { for (const entityId in updates.a) { const newState = updates.a[entityId]; const last_changed = new Date(newState.lc * 1e3).toISOString(); state[entityId] = { entity_id: entityId, state: newState.s, attributes: newState.a, context: typeof newState.c === "string" ? { id: newState.c, parent_id: null, user_id: null } : newState.c, last_changed, last_updated: newState.lu ? new Date(newState.lu * 1e3).toISOString() : last_changed }; } } if (updates.r) { for (const entityId of updates.r) { delete state[entityId]; } } if (updates.c) { for (const entityId in updates.c) { let entityState = state[entityId]; if (!entityState) { console.warn("Received state update for unknown entity", entityId); continue; } entityState = { ...entityState }; const { "+": toAdd, "-": toRemove } = updates.c[entityId]; const attributesChanged = toAdd?.a || toRemove?.a; const attributes4 = attributesChanged ? { ...entityState.attributes } : entityState.attributes; if (toAdd) { if (toAdd.s !== void 0) { entityState.state = toAdd.s; } if (toAdd.c) { if (typeof toAdd.c === "string") { entityState.context = { ...entityState.context, id: toAdd.c }; } else { entityState.context = { ...entityState.context, ...toAdd.c }; } } if (toAdd.lc) { entityState.last_updated = entityState.last_changed = new Date( toAdd.lc * 1e3 ).toISOString(); } else if (toAdd.lu) { entityState.last_updated = new Date(toAdd.lu * 1e3).toISOString(); } if (toAdd.a) { Object.assign(attributes4, toAdd.a); } } if (toRemove?.a) { for (const key of toRemove.a) { delete attributes4[key]; } } if (attributesChanged) { entityState.attributes = attributes4; } state[entityId] = entityState; } } store.setState(state, true); } var subscribeUpdates = (conn, store, entityIds) => { if (entityIds && entityIds.length === 0) { return Promise.resolve(async () => { }); } return conn.subscribeMessage((ev) => processEvent(store, ev), { type: "subscribe_entities", entity_ids: entityIds }); }; function createEntitiesHash(entityIds) { return crypto2.createHash("sha256").update(entityIds.join(",")).digest("hex").substring(0, 16); } var entitiesColl = (conn, entityIds) => { if (atLeastHaVersion(conn.haVersion, 2022, 4, 0)) { return getCollection( conn, `_ent_${createEntitiesHash(entityIds)}`, void 0, (conn2, store) => subscribeUpdates(conn2, store, entityIds) ); } throw new Error(`Home Assistant version ${conn.haVersion} is not supported`); }; var subscribeEntities = (conn, onChange, entityIds) => entitiesColl(conn, entityIds).subscribe(onChange); // src/home-assistant/home-assistant-client.ts import { ERR_CANNOT_CONNECT, ERR_INVALID_AUTH, createConnection, createLongLivedTokenAuth, getConfig } from "home-assistant-js-websocket"; var HomeAssistantClient = class _HomeAssistantClient { constructor(environment, props) { this.props = props; register(environment, _HomeAssistantClient, this); this.log = environment.get(LoggerService).get("HomeAssistantClient"); this.construction = this.initialize(); } log; construction; connection; disposed = false; async initialize() { if (this.disposed) { return; } try { this.connection?.close(); this.connection = await createConnection({ auth: createLongLivedTokenAuth( this.props.url.replace(/\/$/, ""), this.props.accessToken ) }); await this.waitForHomeAssistantToBeUpAndRunning(); } catch (reason) { return this.handleInitializationError(reason); } } async handleInitializationError(reason) { if (reason === ERR_CANNOT_CONNECT) { this.log.error( `Unable to connect to home assistant with url: ${this.props.url}. Retrying in 5 seconds...` ); await new Promise((resolve) => setTimeout(resolve, 5e3)); return this.initialize(); } if (reason === ERR_INVALID_AUTH) { throw new Error( "Authentication failed while connecting to home assistant" ); } throw new Error(`Unable to connect to home assistant: ${reason}`); } async waitForHomeAssistantToBeUpAndRunning() { this.log.info("Waiting for Home Assistant to be up and running"); const getState = async () => { const s = await getConfig(this.connection).then((config6) => config6.state); this.log.debug( `Got an update from Home Assistant. System state is '${s}'.` ); return s; }; let state = "NOT_RUNNING"; while (state !== "RUNNING") { await new Promise((resolve) => setTimeout(resolve, 5e3)); state = await getState(); } } async [Symbol.asyncDispose]() { this.disposed = true; this.connection?.close(); } }; // src/home-assistant/home-assistant-registry.ts var HomeAssistantRegistry = class _HomeAssistantRegistry { constructor(environment) { this.environment = environment; environment.set(_HomeAssistantRegistry, this); } static [Environmental3.create](environment) { return new _HomeAssistantRegistry(environment); } async allEntities() { const client = await this.environment.load(HomeAssistantClient); const entityRegistry = _.keyBy( await getRegistry(client.connection), (r) => r.entity_id ); const deviceRegistry = _.keyBy( await getDeviceRegistry(client.connection), (r) => r.id ); const entityStates = _.keyBy( await getStates(client.connection), (s) => s.entity_id ); const entityIds = _.uniq( _.keys(entityRegistry).concat(_.keys(entityStates)) ); const deviceByEntityId = _.fromPairs( entityIds.map((entityId) => [entityId, entityRegistry[entityId]?.device_id]).filter(([, deviceId]) => !!deviceId).map(([entityId, deviceId]) => [entityId, deviceRegistry[deviceId]]) ); return entityIds.map((entity_id) => ({ entity_id, registry: entityRegistry[entity_id], deviceRegistry: deviceByEntityId[entity_id], state: entityStates[entity_id] })); } async subscribeStates(entityIds, onChange) { const client = await this.environment.load(HomeAssistantClient); return subscribeEntities(client.connection, onChange, entityIds); } }; // src/utils/errors/invalid-device-error.ts var InvalidDeviceError = class extends Error { constructor(reason) { super(`Invalid device detected. Reason: ${reason}`); } }; // src/matter/custom-behaviors/home-assistant-entity-behavior.ts import { Behavior, EventEmitter } from "@matter/main"; import AsyncLock from "async-lock"; // src/home-assistant/home-assistant-actions.ts import { Environmental as Environmental4 } from "@matter/main"; import { callService } from "home-assistant-js-websocket"; var HomeAssistantActions = class _HomeAssistantActions { constructor(environment) { this.environment = environment; environment.set(_HomeAssistantActions, this); this.log = environment.get(LoggerService).get("HomeAssistantActions"); } log; static [Environmental4.create](environment) { return new _HomeAssistantActions(environment); } call(action, target, returnResponse) { const [domain, actionName] = action.action.split("."); return this.callAction( domain, actionName, action.data, target, returnResponse ); } async callAction(domain, action, data, target, returnResponse) { const client = await this.environment.load(HomeAssistantClient); this.log.debug( `Calling action '${domain}.${action}' for target ${JSON.stringify(target)} with data ${JSON.stringify(data ?? {})}` ); const result = await callService( client.connection, domain, action, data, target, returnResponse ); return result; } }; // src/home-assistant/home-assistant-config.ts import { Environmental as Environmental5 } from "@matter/main"; import { getConfig as getConfig2 } from "home-assistant-js-websocket"; var HomeAssistantConfig = class _HomeAssistantConfig { static [Environmental5.create](environment) { return new _HomeAssistantConfig(environment); } environment; logger; construction; config; get unitSystem() { return this.config.unit_system; } constructor(environment) { register(environment, _HomeAssistantConfig, this); this.environment = environment; this.logger = environment.get(LoggerService).get("HomeAssistantConfig"); this.construction = this.initialize(); } async initialize() { const { connection } = await this.environment.load(HomeAssistantClient); this.config = await getConfig2(connection); } }; // src/utils/async-observable.ts import { AsyncObservable as Base } from "@matter/main"; var AsyncObservable = () => Base(); // src/matter/custom-behaviors/home-assistant-entity-behavior.ts var HomeAssistantEntityBehavior = class extends Behavior { static id = ClusterId.homeAssistantEntity; async initialize() { await this.env.load(HomeAssistantConfig); this.internal.logger = this.env.get(LoggerService).get(`HomeAssistant / ${this.entityId}`); } get entityId() { return this.entity.entity_id; } get entity() { return this.state.entity; } get onChange() { return this.events.entity$Changed; } get isAvailable() { return this.entity.state.state !== "unavailable"; } callAction(action) { const actions = this.env.get(HomeAssistantActions); const lock = this.env.get(AsyncLock); const lockKey2 = this.state.lockKey; const log = this.internal.logger; const target = { entity_id: this.entityId }; setTimeout( async () => lock.acquire( lockKey2, () => actions.call(action, target, false).catch((error) => log.error(error)) ), 0 ); } }; ((HomeAssistantEntityBehavior2) => { class Internal { logger; } HomeAssistantEntityBehavior2.Internal = Internal; class State { lockKey; entity; } HomeAssistantEntityBehavior2.State = State; class Events extends EventEmitter { entity$Changed = AsyncObservable(); } HomeAssistantEntityBehavior2.Events = Events; })(HomeAssistantEntityBehavior || (HomeAssistantEntityBehavior = {})); // src/matter/devices/automation/index.ts import { OnOffPlugInUnitDevice } from "@matter/main/devices"; // src/matter/behaviors/basic-information-server.ts import crypto3 from "node:crypto"; import { VendorId } from "@matter/main"; import { BridgedDeviceBasicInformationServer as Base2 } from "@matter/main/behaviors"; // src/utils/apply-patch-state.ts import _2 from "lodash"; function applyPatchState(state, patch) { const actualPatch = {}; const keys = Object.keys(patch); for (const key of keys) { const patchValue = patch[key]; if (patchValue !== void 0 && !deepEqual(state[key], patchValue)) { actualPatch[key] = patchValue; } } if (_2.size(actualPatch) > 0) { try { Object.assign(state, actualPatch); } catch (e) { throw new Error( `Failed to patch the following properties: ${JSON.stringify(actualPatch, null, 2)}`, { cause: e } ); } } return actualPatch; } function deepEqual(a, b) { if (a == null || b == null) { return a === b; } if (typeof a !== typeof b || Array.isArray(a) !== Array.isArray(b)) { return false; } if (Array.isArray(a) && Array.isArray(b)) { return a.length === b.length && a.every((vA, idx) => deepEqual(vA, b[idx])); } if (typeof a === "object" && typeof b === "object") { const keys = Object.keys({ ...a, ...b }); return keys.every((key) => deepEqual(a[key], b[key])); } return a === b; } // src/matter/behaviors/basic-information-server.ts var BasicInformationServer = class extends Base2 { async initialize() { await super.initialize(); const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior); this.update(homeAssistant.entity); this.reactTo(homeAssistant.onChange, this.update); } update(entity) { const { basicInformation: basicInformation2 } = this.env.get(BridgeDataProvider); const device = entity.deviceRegistry; applyPatchState(this.state, { vendorId: VendorId(basicInformation2.vendorId), vendorName: ellipse(32, device?.manufacturer) ?? hash(32, basicInformation2.vendorName), productName: ellipse(32, device?.model_id) ?? ellipse(32, device?.model) ?? hash(32, basicInformation2.productName), productLabel: ellipse(64, device?.model) ?? hash(64, basicInformation2.productLabel), hardwareVersion: basicInformation2.hardwareVersion, softwareVersion: basicInformation2.softwareVersion, hardwareVersionString: ellipse(64, device?.hw_version), softwareVersionString: ellipse(64,