UNPKG

home-assistant-matter-hub

Version:

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

1,467 lines (1,410 loc) 164 kB
#!/usr/bin/env node // src/cli.ts import "./bootstrap.js"; // src/polyfills.ts var JSON_SPECIAL_KEY_TYPE = "__object__"; var JSON_SPECIAL_KEY_VALUE = "__value__"; BigInt.prototype.toJSON = function() { return `{"${JSON_SPECIAL_KEY_TYPE}":"BigInt","${JSON_SPECIAL_KEY_VALUE}":"${this.toString()}"}`; }; // src/cli.ts 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 * 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/core/ioc/service.ts var Service = class { constructor(serviceName) { this.serviceName = serviceName; } construction = new Promise((resolve) => { setImmediate(() => { const init = this.initialize?.bind(this) ?? (async () => { }); init().then(resolve); }); }); }; // 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/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/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/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/index.js var ClusterId; (function(ClusterId2) { ClusterId2["homeAssistantEntity"] = "homeAssistantEntity"; 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/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/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/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/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/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/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/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"] }; // ../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); } }; // src/api/matter-api.ts import { Ajv } from "ajv"; import express from "express"; // src/utils/json/endpoint-to-json.ts function endpointToJson(endpoint, parentId) { const globalId = [parentId, endpoint.id].filter((i) => !!i).join("."); return { id: { global: globalId, local: endpoint.id }, type: { name: endpoint.type.name, id: `0x${endpoint.type.deviceType.toString(16).padStart(4, "0")}` }, endpoint: endpoint.number, state: endpoint.state, parts: endpoint.parts.map((p) => endpointToJson(p, globalId)) }; } // src/api/matter-api.ts var ajv = new Ajv(); function matterApi(bridgeService) { const router = express.Router(); router.get("/", (_, res) => { res.status(200).json({}); }); router.get("/bridges", async (_, res) => { res.status(200).json(bridgeService.bridges.map((b) => b.data)); }); router.post("/bridges", async (req, res) => { const body = req.body; const isValid = ajv.validate(createBridgeRequestSchema, body); if (!isValid) { res.status(400).json(ajv.errors); } else { const bridge = await bridgeService.create(body); res.status(200).json(bridge.data); } }); router.get("/bridges/:bridgeId", async (req, res) => { const bridgeId = req.params.bridgeId; const bridge = bridgeService.get(bridgeId); if (bridge) { res.status(200).json(bridge.data); } else { res.status(404).send("Not Found"); } }); router.put("/bridges/:bridgeId", async (req, res) => { const bridgeId = req.params.bridgeId; const body = req.body; const isValid = ajv.validate(updateBridgeRequestSchema, body); if (!isValid) { res.status(400).json(ajv.errors); } else if (bridgeId !== body.id) { res.status(400).send("Path variable `bridgeId` does not match `body.id`"); } else { const bridge = await bridgeService.update(body); if (!bridge) { res.status(404).send("Not Found"); } else { res.status(200).json(bridge.data); } } }); router.delete("/bridges/:bridgeId", async (req, res) => { const bridgeId = req.params.bridgeId; await bridgeService.delete(bridgeId); res.status(204).send(); }); router.get("/bridges/:bridgeId/actions/factory-reset", async (req, res) => { const bridgeId = req.params.bridgeId; const bridge = bridgeService.bridges.find((b) => b.id === bridgeId); if (bridge) { await bridge.factoryReset(); await bridge.start(); res.status(200).json(bridge.data); } else { res.status(404).send("Not Found"); } }); router.get("/bridges/:bridgeId/devices", async (req, res) => { const bridgeId = req.params.bridgeId; const bridge = bridgeService.bridges.find((b) => b.id === bridgeId); if (bridge) { res.status(200).json(endpointToJson(bridge.server)); } else { res.status(404).send("Not Found"); } }); return router; } // src/api/proxy-support.ts import path from "node:path"; var ingressPath = "x-ingress-path"; var forwardedPrefix = "x-forwarded-prefix"; function supportIngress(req, _, next) { if (!(ingressPath in req.headers)) { return next(); } const prefix = req.header(ingressPath); if (!prefix) { return next(); } const baseUrl = buildPath(prefix, req.baseUrl); req.baseUrl = baseUrl; req.originalUrl = buildPath(baseUrl, req.url); req.url = buildPath(req.url); next(); } function supportProxyLocation(req, res, next) { if (!(forwardedPrefix in req.headers)) { return next(); } const prefix = req.header(forwardedPrefix); if (!prefix) { return next(); } let baseUrl = buildPath(prefix, req.baseUrl); if (baseUrl.endsWith("/")) { baseUrl = baseUrl.slice(0, -1); } if (!req.url.startsWith(`${baseUrl}/`)) { res.status(400).contentType("text/plain").send(`URL ${req.url} does not match base url ${baseUrl}`); return; } req.baseUrl = baseUrl; req.originalUrl = req.url; req.url = req.url.slice(baseUrl.length); next(); } function buildPath(...paths) { let result = path.posix.join(...paths); if (!result.startsWith("/")) { result = `/${result}`; } return result; } // src/api/web-ui.ts import fs from "node:fs"; import path2 from "node:path"; import express2 from "express"; function webUi(dist) { const router = express2.Router(); if (dist) { const index = replaceBase(dist); router.get("/", index); router.get("/index.html", index); router.use(express2.static(dist)); router.get(/.*/, index); } return router; } function replaceBase(dist) { return (req, res) => { let baseUrl = req.baseUrl; if (!baseUrl.endsWith("/")) { baseUrl += "/"; } const content = fs.readFileSync(path2.join(dist, "index.html"), "utf8").replace( /<!-- BASE -->[\s\S]*<!-- \/BASE -->/, `<base href='${baseUrl}' />` ); res.status(200).contentType("text/html").send(content); }; } // src/api/web-api.ts var WebApi = class extends Service { constructor(logger, bridgeService, props) { super("WebApi"); this.bridgeService = bridgeService; this.props = props; this.log = logger.get(this); this.accessLogger = accessLogger(this.log.createChild("Access Log")); } log; accessLogger; app; server; async initialize() { const api = express3.Router(); api.use(express3.json()).use(nocache()).use("/matter", matterApi(this.bridgeService)); const middlewares = [ this.accessLogger, supportIngress, supportProxyLocation ]; if (this.props.auth) { middlewares.push( basicAuth({ users: { [this.props.auth.username]: this.props.auth.password }, challenge: true, realm: "Home Assistant Matter Hub" }) ); this.log.info("Basic authentication enabled"); } if (this.props.whitelist && this.props.whitelist.length > 0) { middlewares.push( AccessControl({ log: (clientIp, access) => { this.log.silly( `Client ${clientIp} was ${access ? "granted" : "denied"}` ); }, mode: "allow", allows: this.props.whitelist }) ); } this.app = express3().use(...middlewares).use("/api", api).use(webUi(this.props.webUiDist)); } async dispose() { await new Promise((resolve, reject) => { this.server?.close((error) => { if (error) { reject(error); } else { resolve(); } }); }); } async start() { if (this.server) { return; } this.server = await new Promise((resolve) => { const server = this.app.listen(this.props.port, () => { this.log.info( `HTTP server (API ${this.props.webUiDist ? "& Web App" : "only"}) listening on port ${this.props.port}` ); resolve(server); }); }); } }; // src/core/app/configure-default-environment.ts import { Environment, VariableService } from "@matter/main"; import AsyncLock from "async-lock"; // src/core/app/logger.ts import { LogFormat, Logger, LogLevel as MatterLogLevel } from "@matter/general"; 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 }; constructor(options) { this._level = logLevelFromString(options.level ?? "info"); Logger.level = this.customLogLevelMapping[this._level] ?? this._level; Logger.format = options.disableColors ? LogFormat.PLAIN : LogFormat.ANSI; } get(nameOrService) { let name; if (typeof nameOrService === "string") { name = nameOrService; } else { name = nameOrService.serviceName; } return new BetterLogger(name, this._level); } }; var BetterLogger = class _BetterLogger extends Logger { constructor(name, _level) { super(name); this.name = name; this._level = _level; } createChild(name) { return new _BetterLogger(`${this.name} / ${name}`, this._level); } silly(...values4) { if (this._level <= -1 /* SILLY */) { this.debug(...["SILLY", ...values4]); } } }; // src/core/app/mdns.ts import { MdnsService } from "@matter/main/protocol"; function mdns(env, options) { new MdnsService(env, { ipv4: options.ipv4, networkInterface: options.networkInterface }); } // src/core/app/storage.ts import fs3 from "node:fs"; import os from "node:os"; import path3 from "node:path"; import { StorageService } from "@matter/main"; // src/core/app/storage/custom-storage.ts import fs2 from "node:fs"; import { StorageBackendDisk } from "@matter/nodejs"; import { forEach } from "lodash-es"; // src/core/app/storage/legacy-custom-storage.ts import { StorageBackendJsonFile } from "@matter/nodejs"; import { pickBy } from "lodash-es"; var LegacyCustomStorage = class extends StorageBackendJsonFile { constructor(log, path5) { super(path5); this.log = log; const parser = this; const serialize = parser.toJson.bind(parser); const deserialize = parser.fromJson.bind(parser); parser.fromJson = (json) => { if (json.trim().length === 0) { return {}; } try { const object = deserialize(json); return this.removeClusters(object, Object.values(ClusterId)); } catch (e) { this.log.error( `Failed to parse json file '${path5}' with content: ${json} ` ); throw e; } }; parser.toJson = (object) => { const json = serialize( this.removeClusters(object, [ClusterId.homeAssistantEntity]) ); if (json.trim().length === 0) { throw new Error(`Tried to write empty storage to ${path5}`); } return json; }; } removeClusters(object, clusters) { if (clusters.length === 0) { return object; } const keys3 = Object.keys(object).filter( (key) => key.startsWith("root.parts.") && clusters.some((cluster) => key.endsWith(`.${cluster}`)) ); return pickBy(object, (_, key) => !keys3.includes(key)); } }; // src/core/app/storage/custom-storage.ts var CustomStorage = class extends StorageBackendDisk { constructor(log, path5) { super(path5); this.log = log; this.path = path5; } async initialize() { await super.initialize(); if (fs2.existsSync(`${this.path}.json`)) { await this.migrateLegacyStorage(); } } async keys(contexts) { const key = this.getContextBaseKey(contexts); const clusters = Object.values(ClusterId); if (key.startsWith("root.parts.aggregator.parts.") && clusters.some((cluster) => key.endsWith(cluster))) { return []; } return await super.keys(contexts); } async migrateLegacyStorage() { const path5 = this.path; this.log.warn( `Migrating legacy storage (JSON file) to new storage (directory): ${path5}` ); const legacyStorage = new LegacyCustomStorage(this.log, `${path5}.json`); legacyStorage.initialize(); forEach(legacyStorage.data, (values4, context) => { forEach(values4, (value, key) => { this.set([context], key, value); }); }); await legacyStorage.close(); fs2.renameSync(`${path5}.json`, `${path5}/backup.alpha-69.json`); } }; // src/core/app/storage.ts function storage(environment, options) { const logger = environment.get(LoggerService).get("CustomStorage"); const location = resolveStorageLocation(options.location); fs3.mkdirSync(location, { recursive: true }); const storageService = environment.get(StorageService); storageService.location = location; storageService.factory = (ns) => new CustomStorage(logger, path3.resolve(location, ns)); } function resolveStorageLocation(storageLocation) { const homedir = os.homedir(); return storageLocation ? path3.resolve(storageLocation.replace(/^~\//, `${homedir}/`)) : path3.join(homedir, ".home-assistant-matter-hub"); } // src/core/app/configure-default-environment.ts function configureDefaultEnvironment(options) { const env = Environment.default; env.runtime; new VariableService(env); env.set(AsyncLock, new AsyncLock()); env.set(LoggerService, new LoggerService(options.logging)); mdns(env, options.mdns); storage(env, options.storage); return env; } // src/core/app/options.ts import { VendorId } from "@matter/main"; var Options = class { constructor(startOptions) { this.startOptions = startOptions; } get mdns() { return { ipv4: true, networkInterface: notEmpty(this.startOptions.mdnsNetworkInterface) }; } get logging() { return { level: this.startOptions.logLevel, disableColors: this.startOptions.disableLogColors ?? false }; } get storage() { return { location: notEmpty(this.startOptions.storageLocation) }; } get homeAssistant() { return { url: this.startOptions.homeAssistantUrl, accessToken: this.startOptions.homeAssistantAccessToken, refreshInterval: this.startOptions.homeAssistantRefreshInterval }; } get webApi() { const auth = this.startOptions.httpAuthUsername && this.startOptions.httpAuthPassword ? { username: this.startOptions.httpAuthUsername, password: this.startOptions.httpAuthPassword } : void 0; return { port: this.startOptions.httpPort, whitelist: this.startOptions.httpIpWhitelist?.map( (item) => item.toString() ), webUiDist: this.startOptions.webUiDist, auth }; } get bridgeService() { return { basicInformation: { vendorId: VendorId(65521), vendorName: "t0bst4r", productId: 32768, productName: "MatterHub", productLabel: "Home Assistant Matter Hub", hardwareVersion: (/* @__PURE__ */ new Date()).getFullYear(), softwareVersion: (/* @__PURE__ */ new Date()).getFullYear() } }; } }; function notEmpty(val) { const value = val?.trim(); if (value == null || value.length === 0) { return void 0; } return value; } // src/core/ioc/app-environment.ts import { StorageService as StorageService2 } from "@matter/main"; // src/services/bridges/bridge-factory.ts var BridgeFactory = class extends Service { }; // src/services/bridges/bridge-service.ts import crypto from "node:crypto"; var BridgeService = class extends Service { constructor(bridgeStorage, bridgeFactory, props) { super("BridgeService"); this.bridgeStorage = bridgeStorage; this.bridgeFactory = bridgeFactory; this.props = props; } bridges = []; async initialize() { for (const data of this.bridgeStorage.bridges) { await this.addBridge(data); } } async dispose() { await Promise.all(this.bridges.map((bridge) => bridge.dispose())); } async startAll() { for (const bridge of this.bridges) { await bridge.start(); } } async refreshAll() { for (const bridge of this.bridges) { await bridge.refreshDevices(); } } get(id) { return this.bridges.find((bridge) => bridge.id === id); } async create(request) { if (this.portUsed(request.port)) { throw new Error(`Port already in use: ${request.port}`); } const bridge = await this.addBridge({ ...request, id: crypto.randomUUID().replace(/-/g, ""), basicInformation: this.props.basicInformation }); await this.bridgeStorage.add(bridge.data); await bridge.start(); return bridge; } async update(request) { if (this.portUsed(request.port, [request.id])) { throw new Error(`Port already in use: ${request.port}`); } const bridge = this.get(request.id); if (!bridge) { return; } await bridge.update(request); await this.bridgeStorage.add(bridge.data); return bridge; } async delete(bridgeId) { const bridge = this.bridges.find((bridge2) => bridge2.id === bridgeId); if (!bridge) { return; } await bridge.stop(); await bridge.delete(); await bridge.dispose(); this.bridges.splice(this.bridges.indexOf(bridge), 1); await this.bridgeStorage.remove(bridgeId); } async addBridge(bridgeData) { const bridge = await this.bridgeFactory.create(bridgeData); this.bridges.push(bridge); return bridge; } portUsed(port, notId) { return this.bridges.filter((bridge) => notId == null || !notId.includes(bridge.id)).some((bridge) => bridge.data.port === port); } }; // src/services/home-assistant/home-assistant-actions.ts import { callService } from "home-assistant-js-websocket"; var HomeAssistantActions = class extends Service { constructor(logger, client) { super("HomeAssistantActions"); this.client = client; this.log = logger.get(this); } log; 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) { this.log.debug( `Calling action '${domain}.${action}' for target ${JSON.stringify(target)} with data ${JSON.stringify(data ?? {})}` ); const result = await callService( this.client.connection, domain, action, data, target, returnResponse ); return result; } }; // src/services/home-assistant/home-assistant-client.ts import { createConnection, createLongLivedTokenAuth, ERR_CANNOT_CONNECT, ERR_INVALID_AUTH, getConfig } from "home-assistant-js-websocket"; var HomeAssistantClient = class extends Service { constructor(logger, options) { super("HomeAssistantClient"); this.options = options; this.log = logger.get(this); } static Options = Symbol.for("HomeAssistantClientProps"); _connection; log; get connection() { return this._connection; } async initialize() { this._connection = await this.createConnection(this.options); } async dispose() { this.connection?.close(); } async createConnection(props) { try { const connection = await createConnection({ auth: createLongLivedTokenAuth( props.url.replace(/\/$/, ""), props.accessToken ) }); await this.waitForHomeAssistantToBeUpAndRunning(connection); return connection; } catch (reason) { return this.handleInitializationError(reason, props); } } async handleInitializationError(reason, props) { if (reason === ERR_CANNOT_CONNECT) { this.log.error( `Unable to connect to home assistant with url: ${props.url}. Retrying in 5 seconds...` ); await new Promise((resolve) => setTimeout(resolve, 5e3)); return this.createConnection(props); } 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(connection) { this.log.info( "Waiting for Home Assistant to be up and running - the application will be available once a connection to Home Assistant could be established." ); const getState = async () => { const s = await getConfig(connection).then((config6) => config6.state); this.log.debug( `Got an update from Home Assistant. System state is '${s}'.` ); return s; }; let state; while (state !== "RUNNING") { await new Promise((resolve) => setTimeout(resolve, 5e3)); state = await getState(); } this.log.info("Home assistant reported to be up and running"); } }; // src/services/home-assistant/home-assistant-config.ts import { getConfig as getConfig2 } from "home-assistant-js-websocket"; var HomeAssistantConfig = class extends Service { constructor(client) { super("HomeAssistantConfig"); this.client = client; } config; get unitSystem() { return this.config.unit_system; } async initialize() { this.config = await getConfig2(this.client.connection); } }; // src/services/home-assistant/home-assistant-registry.ts import { createHash } from "node:crypto"; import { getStates } from "home-assistant-js-websocket"; import { fromPairs, keyBy, keys, uniq, values } from "lodash-es"; // src/services/home-assistant/api/get-registry.ts async function getRegistry(connection) { return await connection.sendMessagePromise({ type: "config/entity_registry/list" }); } async function getDeviceRegistry(connection) { return connection.sendMessagePromise({ type: "config/device_registry/list" }); } // src/services/home-assistant/home-assistant-registry.ts var HomeAssistantRegistry = class extends Service { constructor(client, options) { super("HomeAssistantRegistry"); this.client = client; this.options = options; } autoRefresh; _devices = {}; get devices() { return this._devices; } _entities = {}; get entities() { return this._entities; } _states = {}; get states() { return this._states; } async initialize() { await this.reload(); } async dispose() { this.disableAutoRefresh(); } enableAutoRefresh(onRefresh) { this.disableAutoRefresh(); this.autoRefresh = setInterval(async () => { await this.reload(); onRefresh(); }, this.options.refreshInterval * 1e3); } disableAutoRefresh() { if (this.autoRefresh != null) { clearInterval(this.autoRefresh); } this.autoRefresh = void 0; } async reload() { const connection = this.client.connection; const entityRegistry = await getRegistry(connection); entityRegistry.forEach((e) => { e.device_id = e.device_id ?? mockDeviceId(e.entity_id); }); const entities = keyBy(entityRegistry, "entity_id"); const states = keyBy( await getStates(connection), "entity_id" ); const entityIds = uniq(keys(entities).concat(keys(states))); const allEntities = keyBy( entityIds.map((id) => entities[id] ?? { entity_id: id, device_id: id }), "entity_id" ); const deviceIds = values(allEntities).map( (e) => e.device_id ?? e.entity_id ); const realDevices = keyBy(await getDeviceRegistry(connection), "id"); const missingDeviceIds = uniq(deviceIds.filter((d) => !realDevices[d])); const missingDevices = fromPairs(missingDeviceIds.map((d) => [d, { id: d }])); this._devices = { ...missingDevices, ...realDevices }; this._entities = entities; this._states = states; } }; function mockDeviceId(entityId) { const hash2 = createHash("sha256").update(entityId).digest("hex").substring(0, 29); return `e__${hash2}`; } // src/services/storage/app-storage.ts var AppStorage = class extends Service { constructor(storageService) { super("AppStorage"); this.storageService = storageService; } storageManager; async initialize() { this.storageManager = await this.storageService.open("app"); } async dispose() { await this.storageManager.close(); } createContext(context) { return this.storageManager.createContext(context); } }; // src/services/storage/m