UNPKG

matterbridge-hass

Version:
1,059 lines 53.9 kB
import { EventEmitter } from 'node:events'; import { readFileSync } from 'node:fs'; import { AnsiLogger, CYAN, db, debugStringify, er, rs } from 'matterbridge/logger'; import { hasParameter } from 'matterbridge/utils'; import WebSocket from 'ws'; export var SelectService; (function (SelectService) { SelectService["SELECT_FIRST"] = "select_first"; SelectService["SELECT_LAST"] = "select_last"; SelectService["SELECT_NEXT"] = "select_next"; SelectService["SELECT_OPTION"] = "select_option"; SelectService["SELECT_PREVIOUS"] = "select_previous"; })(SelectService || (SelectService = {})); export var SelectAttribute; (function (SelectAttribute) { SelectAttribute["CYCLE"] = "cycle"; SelectAttribute["OPTIONS"] = "options"; SelectAttribute["OPTION"] = "option"; })(SelectAttribute || (SelectAttribute = {})); export var MediaPlayerService; (function (MediaPlayerService) { MediaPlayerService["BROWSE_MEDIA"] = "browse_media"; MediaPlayerService["CLEAR_PLAYLIST"] = "clear_playlist"; MediaPlayerService["JOIN"] = "join"; MediaPlayerService["MEDIA_NEXT_TRACK"] = "media_next_track"; MediaPlayerService["MEDIA_PAUSE"] = "media_pause"; MediaPlayerService["MEDIA_PLAY"] = "media_play"; MediaPlayerService["MEDIA_PLAY_PAUSE"] = "media_play_pause"; MediaPlayerService["MEDIA_PREVIOUS_TRACK"] = "media_previous_track"; MediaPlayerService["MEDIA_SEEK"] = "media_seek"; MediaPlayerService["MEDIA_STOP"] = "media_stop"; MediaPlayerService["PLAY_MEDIA"] = "play_media"; MediaPlayerService["REPEAT_SET"] = "repeat_set"; MediaPlayerService["SEARCH_MEDIA"] = "search_media"; MediaPlayerService["SELECT_SOUND_MODE"] = "select_sound_mode"; MediaPlayerService["SELECT_SOURCE"] = "select_source"; MediaPlayerService["SHUFFLE_SET"] = "shuffle_set"; MediaPlayerService["TOGGLE"] = "toggle"; MediaPlayerService["TURN_OFF"] = "turn_off"; MediaPlayerService["TURN_ON"] = "turn_on"; MediaPlayerService["UNJOIN"] = "unjoin"; MediaPlayerService["VOLUME_DOWN"] = "volume_down"; MediaPlayerService["VOLUME_MUTE"] = "volume_mute"; MediaPlayerService["VOLUME_SET"] = "volume_set"; MediaPlayerService["VOLUME_UP"] = "volume_up"; })(MediaPlayerService || (MediaPlayerService = {})); export var MediaPlayerAttribute; (function (MediaPlayerAttribute) { MediaPlayerAttribute["APP_ID"] = "app_id"; MediaPlayerAttribute["APP_NAME"] = "app_name"; MediaPlayerAttribute["ENTITY_PICTURE_LOCAL"] = "entity_picture_local"; MediaPlayerAttribute["GROUP_MEMBERS"] = "group_members"; MediaPlayerAttribute["INPUT_SOURCE"] = "source"; MediaPlayerAttribute["INPUT_SOURCE_LIST"] = "source_list"; MediaPlayerAttribute["MEDIA_ALBUM_ARTIST"] = "media_album_artist"; MediaPlayerAttribute["MEDIA_ALBUM_NAME"] = "media_album_name"; MediaPlayerAttribute["MEDIA_ANNOUNCE"] = "announce"; MediaPlayerAttribute["MEDIA_ARTIST"] = "media_artist"; MediaPlayerAttribute["MEDIA_CHANNEL"] = "media_channel"; MediaPlayerAttribute["MEDIA_CONTENT_ID"] = "media_content_id"; MediaPlayerAttribute["MEDIA_CONTENT_TYPE"] = "media_content_type"; MediaPlayerAttribute["MEDIA_DURATION"] = "media_duration"; MediaPlayerAttribute["MEDIA_ENQUEUE"] = "enqueue"; MediaPlayerAttribute["MEDIA_EPISODE"] = "media_episode"; MediaPlayerAttribute["MEDIA_EXTRA"] = "extra"; MediaPlayerAttribute["MEDIA_FILTER_CLASSES"] = "media_filter_classes"; MediaPlayerAttribute["MEDIA_IMAGE_REMOTELY_ACCESSIBLE"] = "media_image_remotely_accessible"; MediaPlayerAttribute["MEDIA_IMAGE_URL"] = "media_image_url"; MediaPlayerAttribute["MEDIA_PLAYLIST"] = "media_playlist"; MediaPlayerAttribute["MEDIA_POSITION"] = "media_position"; MediaPlayerAttribute["MEDIA_POSITION_UPDATED_AT"] = "media_position_updated_at"; MediaPlayerAttribute["MEDIA_REPEAT"] = "repeat"; MediaPlayerAttribute["MEDIA_SEARCH_QUERY"] = "search_query"; MediaPlayerAttribute["MEDIA_SEASON"] = "media_season"; MediaPlayerAttribute["MEDIA_SEEK_POSITION"] = "seek_position"; MediaPlayerAttribute["MEDIA_SERIES_TITLE"] = "media_series_title"; MediaPlayerAttribute["MEDIA_SHUFFLE"] = "shuffle"; MediaPlayerAttribute["MEDIA_TITLE"] = "media_title"; MediaPlayerAttribute["MEDIA_TRACK"] = "media_track"; MediaPlayerAttribute["MEDIA_VOLUME_LEVEL"] = "volume_level"; MediaPlayerAttribute["MEDIA_VOLUME_MUTED"] = "is_volume_muted"; MediaPlayerAttribute["SOUND_MODE"] = "sound_mode"; MediaPlayerAttribute["SOUND_MODE_LIST"] = "sound_mode_list"; })(MediaPlayerAttribute || (MediaPlayerAttribute = {})); export var MediaPlayerState; (function (MediaPlayerState) { MediaPlayerState["OFF"] = "off"; MediaPlayerState["ON"] = "on"; MediaPlayerState["IDLE"] = "idle"; MediaPlayerState["PLAYING"] = "playing"; MediaPlayerState["PAUSED"] = "paused"; MediaPlayerState["STANDBY"] = "standby"; MediaPlayerState["BUFFERING"] = "buffering"; })(MediaPlayerState || (MediaPlayerState = {})); export var MediaClass; (function (MediaClass) { MediaClass["ALBUM"] = "album"; MediaClass["APP"] = "app"; MediaClass["ARTIST"] = "artist"; MediaClass["CHANNEL"] = "channel"; MediaClass["COMPOSER"] = "composer"; MediaClass["CONTRIBUTING_ARTIST"] = "contributing_artist"; MediaClass["DIRECTORY"] = "directory"; MediaClass["EPISODE"] = "episode"; MediaClass["GAME"] = "game"; MediaClass["GENRE"] = "genre"; MediaClass["IMAGE"] = "image"; MediaClass["MOVIE"] = "movie"; MediaClass["MUSIC"] = "music"; MediaClass["PLAYLIST"] = "playlist"; MediaClass["PODCAST"] = "podcast"; MediaClass["SEASON"] = "season"; MediaClass["TRACK"] = "track"; MediaClass["TV_SHOW"] = "tv_show"; MediaClass["URL"] = "url"; MediaClass["VIDEO"] = "video"; })(MediaClass || (MediaClass = {})); export var MediaType; (function (MediaType) { MediaType["ALBUM"] = "album"; MediaType["APP"] = "app"; MediaType["APPS"] = "apps"; MediaType["ARTIST"] = "artist"; MediaType["CHANNEL"] = "channel"; MediaType["CHANNELS"] = "channels"; MediaType["COMPOSER"] = "composer"; MediaType["CONTRIBUTING_ARTIST"] = "contributing_artist"; MediaType["EPISODE"] = "episode"; MediaType["GAME"] = "game"; MediaType["GENRE"] = "genre"; MediaType["IMAGE"] = "image"; MediaType["MOVIE"] = "movie"; MediaType["MUSIC"] = "music"; MediaType["PLAYLIST"] = "playlist"; MediaType["PODCAST"] = "podcast"; MediaType["SEASON"] = "season"; MediaType["TRACK"] = "track"; MediaType["TVSHOW"] = "tvshow"; MediaType["URL"] = "url"; MediaType["VIDEO"] = "video"; })(MediaType || (MediaType = {})); export var RepeatMode; (function (RepeatMode) { RepeatMode["ALL"] = "all"; RepeatMode["OFF"] = "off"; RepeatMode["ONE"] = "one"; })(RepeatMode || (RepeatMode = {})); export var MediaPlayerDeviceClass; (function (MediaPlayerDeviceClass) { MediaPlayerDeviceClass["TV"] = "tv"; MediaPlayerDeviceClass["SPEAKER"] = "speaker"; MediaPlayerDeviceClass["RECEIVER"] = "receiver"; })(MediaPlayerDeviceClass || (MediaPlayerDeviceClass = {})); export var MediaPlayerEnqueue; (function (MediaPlayerEnqueue) { MediaPlayerEnqueue["ADD"] = "add"; MediaPlayerEnqueue["NEXT"] = "next"; MediaPlayerEnqueue["PLAY"] = "play"; MediaPlayerEnqueue["REPLACE"] = "replace"; })(MediaPlayerEnqueue || (MediaPlayerEnqueue = {})); export var MediaPlayerEntityFeature; (function (MediaPlayerEntityFeature) { MediaPlayerEntityFeature[MediaPlayerEntityFeature["PAUSE"] = 1] = "PAUSE"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["SEEK"] = 2] = "SEEK"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["VOLUME_SET"] = 4] = "VOLUME_SET"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["VOLUME_MUTE"] = 8] = "VOLUME_MUTE"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["PREVIOUS_TRACK"] = 16] = "PREVIOUS_TRACK"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["NEXT_TRACK"] = 32] = "NEXT_TRACK"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["TURN_ON"] = 128] = "TURN_ON"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["TURN_OFF"] = 256] = "TURN_OFF"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["PLAY_MEDIA"] = 512] = "PLAY_MEDIA"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["VOLUME_STEP"] = 1024] = "VOLUME_STEP"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["SELECT_SOURCE"] = 2048] = "SELECT_SOURCE"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["STOP"] = 4096] = "STOP"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["CLEAR_PLAYLIST"] = 8192] = "CLEAR_PLAYLIST"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["PLAY"] = 16384] = "PLAY"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["SHUFFLE_SET"] = 32768] = "SHUFFLE_SET"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["SELECT_SOUND_MODE"] = 65536] = "SELECT_SOUND_MODE"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["BROWSE_MEDIA"] = 131072] = "BROWSE_MEDIA"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["REPEAT_SET"] = 262144] = "REPEAT_SET"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["GROUPING"] = 524288] = "GROUPING"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["MEDIA_ANNOUNCE"] = 1048576] = "MEDIA_ANNOUNCE"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["MEDIA_ENQUEUE"] = 2097152] = "MEDIA_ENQUEUE"; MediaPlayerEntityFeature[MediaPlayerEntityFeature["SEARCH_MEDIA"] = 4194304] = "SEARCH_MEDIA"; })(MediaPlayerEntityFeature || (MediaPlayerEntityFeature = {})); export var LightEntityFeature; (function (LightEntityFeature) { LightEntityFeature[LightEntityFeature["EFFECT"] = 4] = "EFFECT"; LightEntityFeature[LightEntityFeature["FLASH"] = 8] = "FLASH"; LightEntityFeature[LightEntityFeature["TRANSITION"] = 32] = "TRANSITION"; })(LightEntityFeature || (LightEntityFeature = {})); export var ColorMode; (function (ColorMode) { ColorMode["UNKNOWN"] = "unknown"; ColorMode["ONOFF"] = "onoff"; ColorMode["BRIGHTNESS"] = "brightness"; ColorMode["COLOR_TEMP"] = "color_temp"; ColorMode["HS"] = "hs"; ColorMode["XY"] = "xy"; ColorMode["RGB"] = "rgb"; ColorMode["RGBW"] = "rgbw"; ColorMode["RGBWW"] = "rgbww"; ColorMode["WHITE"] = "white"; })(ColorMode || (ColorMode = {})); export const ATTR_COLOR_MODE = 'color_mode'; export const ATTR_SUPPORTED_COLOR_MODES = 'supported_color_modes'; export const ATTR_TRANSITION = 'transition'; export const ATTR_RGB_COLOR = 'rgb_color'; export const ATTR_RGBW_COLOR = 'rgbw_color'; export const ATTR_RGBWW_COLOR = 'rgbww_color'; export const ATTR_XY_COLOR = 'xy_color'; export const ATTR_HS_COLOR = 'hs_color'; export const ATTR_COLOR_TEMP_KELVIN = 'color_temp_kelvin'; export const ATTR_MIN_COLOR_TEMP_KELVIN = 'min_color_temp_kelvin'; export const ATTR_MAX_COLOR_TEMP_KELVIN = 'max_color_temp_kelvin'; export const ATTR_COLOR_NAME = 'color_name'; export const ATTR_WHITE = 'white'; export const DEFAULT_MIN_KELVIN = 2000; export const DEFAULT_MAX_KELVIN = 6535; export const DIRECTION_FORWARD = 'forward'; export const DIRECTION_REVERSE = 'reverse'; export var FanEntityFeature; (function (FanEntityFeature) { FanEntityFeature[FanEntityFeature["SET_SPEED"] = 1] = "SET_SPEED"; FanEntityFeature[FanEntityFeature["OSCILLATE"] = 2] = "OSCILLATE"; FanEntityFeature[FanEntityFeature["DIRECTION"] = 4] = "DIRECTION"; FanEntityFeature[FanEntityFeature["PRESET_MODE"] = 8] = "PRESET_MODE"; FanEntityFeature[FanEntityFeature["TURN_OFF"] = 16] = "TURN_OFF"; FanEntityFeature[FanEntityFeature["TURN_ON"] = 32] = "TURN_ON"; })(FanEntityFeature || (FanEntityFeature = {})); export var ValveEntityFeature; (function (ValveEntityFeature) { ValveEntityFeature[ValveEntityFeature["OPEN"] = 1] = "OPEN"; ValveEntityFeature[ValveEntityFeature["CLOSE"] = 2] = "CLOSE"; ValveEntityFeature[ValveEntityFeature["SET_POSITION"] = 4] = "SET_POSITION"; ValveEntityFeature[ValveEntityFeature["STOP"] = 8] = "STOP"; })(ValveEntityFeature || (ValveEntityFeature = {})); export var ValveDeviceClass; (function (ValveDeviceClass) { ValveDeviceClass["WATER"] = "water"; ValveDeviceClass["GAS"] = "gas"; })(ValveDeviceClass || (ValveDeviceClass = {})); export var ValveState; (function (ValveState) { ValveState["OPENING"] = "opening"; ValveState["CLOSING"] = "closing"; ValveState["CLOSED"] = "closed"; ValveState["OPEN"] = "open"; })(ValveState || (ValveState = {})); export var VacuumEntityFeature; (function (VacuumEntityFeature) { VacuumEntityFeature[VacuumEntityFeature["TURN_ON"] = 1] = "TURN_ON"; VacuumEntityFeature[VacuumEntityFeature["TURN_OFF"] = 2] = "TURN_OFF"; VacuumEntityFeature[VacuumEntityFeature["PAUSE"] = 4] = "PAUSE"; VacuumEntityFeature[VacuumEntityFeature["STOP"] = 8] = "STOP"; VacuumEntityFeature[VacuumEntityFeature["RETURN_HOME"] = 16] = "RETURN_HOME"; VacuumEntityFeature[VacuumEntityFeature["FAN_SPEED"] = 32] = "FAN_SPEED"; VacuumEntityFeature[VacuumEntityFeature["BATTERY"] = 64] = "BATTERY"; VacuumEntityFeature[VacuumEntityFeature["STATUS"] = 128] = "STATUS"; VacuumEntityFeature[VacuumEntityFeature["SEND_COMMAND"] = 256] = "SEND_COMMAND"; VacuumEntityFeature[VacuumEntityFeature["LOCATE"] = 512] = "LOCATE"; VacuumEntityFeature[VacuumEntityFeature["CLEAN_SPOT"] = 1024] = "CLEAN_SPOT"; VacuumEntityFeature[VacuumEntityFeature["MAP"] = 2048] = "MAP"; VacuumEntityFeature[VacuumEntityFeature["STATE"] = 4096] = "STATE"; VacuumEntityFeature[VacuumEntityFeature["START"] = 8192] = "START"; })(VacuumEntityFeature || (VacuumEntityFeature = {})); export var VacuumActivity; (function (VacuumActivity) { VacuumActivity["CLEANING"] = "cleaning"; VacuumActivity["DOCKED"] = "docked"; VacuumActivity["IDLE"] = "idle"; VacuumActivity["PAUSED"] = "paused"; VacuumActivity["RETURNING"] = "returning"; VacuumActivity["ERROR"] = "error"; })(VacuumActivity || (VacuumActivity = {})); export var ClimateEntityFeature; (function (ClimateEntityFeature) { ClimateEntityFeature[ClimateEntityFeature["TARGET_TEMPERATURE"] = 1] = "TARGET_TEMPERATURE"; ClimateEntityFeature[ClimateEntityFeature["TARGET_TEMPERATURE_RANGE"] = 2] = "TARGET_TEMPERATURE_RANGE"; ClimateEntityFeature[ClimateEntityFeature["TARGET_HUMIDITY"] = 4] = "TARGET_HUMIDITY"; ClimateEntityFeature[ClimateEntityFeature["FAN_MODE"] = 8] = "FAN_MODE"; ClimateEntityFeature[ClimateEntityFeature["PRESET_MODE"] = 16] = "PRESET_MODE"; ClimateEntityFeature[ClimateEntityFeature["SWING_MODE"] = 32] = "SWING_MODE"; ClimateEntityFeature[ClimateEntityFeature["TURN_OFF"] = 128] = "TURN_OFF"; ClimateEntityFeature[ClimateEntityFeature["TURN_ON"] = 256] = "TURN_ON"; ClimateEntityFeature[ClimateEntityFeature["SWING_HORIZONTAL_MODE"] = 512] = "SWING_HORIZONTAL_MODE"; })(ClimateEntityFeature || (ClimateEntityFeature = {})); export var HVACMode; (function (HVACMode) { HVACMode["OFF"] = "off"; HVACMode["HEAT"] = "heat"; HVACMode["COOL"] = "cool"; HVACMode["HEAT_COOL"] = "heat_cool"; HVACMode["AUTO"] = "auto"; HVACMode["DRY"] = "dry"; HVACMode["FAN_ONLY"] = "fan_only"; })(HVACMode || (HVACMode = {})); export var HVACAction; (function (HVACAction) { HVACAction["COOLING"] = "cooling"; HVACAction["DEFROSTING"] = "defrosting"; HVACAction["DRYING"] = "drying"; HVACAction["FAN"] = "fan"; HVACAction["HEATING"] = "heating"; HVACAction["IDLE"] = "idle"; HVACAction["OFF"] = "off"; HVACAction["PREHEATING"] = "preheating"; })(HVACAction || (HVACAction = {})); export const CLIMATE_FAN_ON = 'on'; export const CLIMATE_FAN_OFF = 'off'; export const CLIMATE_FAN_AUTO = 'auto'; export const CLIMATE_FAN_LOW = 'low'; export const CLIMATE_FAN_MEDIUM = 'medium'; export const CLIMATE_FAN_HIGH = 'high'; export const CLIMATE_FAN_TOP = 'top'; export const CLIMATE_FAN_MIDDLE = 'middle'; export const CLIMATE_FAN_FOCUS = 'focus'; export const CLIMATE_FAN_DIFFUSE = 'diffuse'; export const CLIMATE_SWING_ON = 'on'; export const CLIMATE_SWING_OFF = 'off'; export const CLIMATE_SWING_BOTH = 'both'; export const CLIMATE_SWING_VERTICAL = 'vertical'; export const CLIMATE_SWING_HORIZONTAL = 'horizontal'; export const CLIMATE_SWING_HORIZONTAL_ON = 'on'; export const CLIMATE_SWING_HORIZONTAL_OFF = 'off'; export const DEFAULT_MIN_TEMP = 7; export const DEFAULT_MAX_TEMP = 35; export const DEFAULT_MIN_HUMIDITY = 30; export const DEFAULT_MAX_HUMIDITY = 99; export var EventDeviceClass; (function (EventDeviceClass) { EventDeviceClass["DOORBELL"] = "doorbell"; EventDeviceClass["BUTTON"] = "button"; EventDeviceClass["MOTION"] = "motion"; })(EventDeviceClass || (EventDeviceClass = {})); export var UnitOfApparentPower; (function (UnitOfApparentPower) { UnitOfApparentPower["MILLIVOLT_AMPERE"] = "mVA"; UnitOfApparentPower["VOLT_AMPERE"] = "VA"; UnitOfApparentPower["KILO_VOLT_AMPERE"] = "kVA"; })(UnitOfApparentPower || (UnitOfApparentPower = {})); export var UnitOfPower; (function (UnitOfPower) { UnitOfPower["MILLIWATT"] = "mW"; UnitOfPower["WATT"] = "W"; UnitOfPower["KILO_WATT"] = "kW"; UnitOfPower["MEGA_WATT"] = "MW"; UnitOfPower["GIGA_WATT"] = "GW"; UnitOfPower["TERA_WATT"] = "TW"; UnitOfPower["BTU_PER_HOUR"] = "BTU/h"; })(UnitOfPower || (UnitOfPower = {})); export var UnitOfReactivePower; (function (UnitOfReactivePower) { UnitOfReactivePower["MILLIVOLT_AMPERE_REACTIVE"] = "mvar"; UnitOfReactivePower["VOLT_AMPERE_REACTIVE"] = "var"; UnitOfReactivePower["KILO_VOLT_AMPERE_REACTIVE"] = "kvar"; })(UnitOfReactivePower || (UnitOfReactivePower = {})); export var UnitOfEnergy; (function (UnitOfEnergy) { UnitOfEnergy["JOULE"] = "J"; UnitOfEnergy["KILO_JOULE"] = "kJ"; UnitOfEnergy["MEGA_JOULE"] = "MJ"; UnitOfEnergy["GIGA_JOULE"] = "GJ"; UnitOfEnergy["MILLIWATT_HOUR"] = "mWh"; UnitOfEnergy["WATT_HOUR"] = "Wh"; UnitOfEnergy["KILO_WATT_HOUR"] = "kWh"; UnitOfEnergy["MEGA_WATT_HOUR"] = "MWh"; UnitOfEnergy["GIGA_WATT_HOUR"] = "GWh"; UnitOfEnergy["TERA_WATT_HOUR"] = "TWh"; UnitOfEnergy["CALORIE"] = "cal"; UnitOfEnergy["KILO_CALORIE"] = "kcal"; UnitOfEnergy["MEGA_CALORIE"] = "Mcal"; UnitOfEnergy["GIGA_CALORIE"] = "Gcal"; })(UnitOfEnergy || (UnitOfEnergy = {})); export var UnitOfReactiveEnergy; (function (UnitOfReactiveEnergy) { UnitOfReactiveEnergy["VOLT_AMPERE_REACTIVE_HOUR"] = "varh"; UnitOfReactiveEnergy["KILO_VOLT_AMPERE_REACTIVE_HOUR"] = "kvarh"; })(UnitOfReactiveEnergy || (UnitOfReactiveEnergy = {})); export var UnitOfEnergyDistance; (function (UnitOfEnergyDistance) { UnitOfEnergyDistance["KILO_WATT_HOUR_PER_100_KM"] = "kWh/100km"; UnitOfEnergyDistance["WATT_HOUR_PER_KM"] = "Wh/km"; UnitOfEnergyDistance["MILES_PER_KILO_WATT_HOUR"] = "mi/kWh"; UnitOfEnergyDistance["KM_PER_KILO_WATT_HOUR"] = "km/kWh"; })(UnitOfEnergyDistance || (UnitOfEnergyDistance = {})); export var UnitOfElectricCurrent; (function (UnitOfElectricCurrent) { UnitOfElectricCurrent["MILLIAMPERE"] = "mA"; UnitOfElectricCurrent["AMPERE"] = "A"; })(UnitOfElectricCurrent || (UnitOfElectricCurrent = {})); export var UnitOfElectricPotential; (function (UnitOfElectricPotential) { UnitOfElectricPotential["MICROVOLT"] = "\u03BCV"; UnitOfElectricPotential["MILLIVOLT"] = "mV"; UnitOfElectricPotential["VOLT"] = "V"; UnitOfElectricPotential["KILOVOLT"] = "kV"; UnitOfElectricPotential["MEGAVOLT"] = "MV"; })(UnitOfElectricPotential || (UnitOfElectricPotential = {})); export const DEGREE = '°'; export var Currency; (function (Currency) { Currency["EURO"] = "\u20AC"; Currency["DOLLAR"] = "$"; Currency["CENT"] = "\u00A2"; })(Currency || (Currency = {})); export var UnitOfTemperature; (function (UnitOfTemperature) { UnitOfTemperature["CELSIUS"] = "\u00B0C"; UnitOfTemperature["FAHRENHEIT"] = "\u00B0F"; UnitOfTemperature["KELVIN"] = "K"; })(UnitOfTemperature || (UnitOfTemperature = {})); export var UnitOfTime; (function (UnitOfTime) { UnitOfTime["MICROSECONDS"] = "\u03BCs"; UnitOfTime["MILLISECONDS"] = "ms"; UnitOfTime["SECONDS"] = "s"; UnitOfTime["MINUTES"] = "min"; UnitOfTime["HOURS"] = "h"; UnitOfTime["DAYS"] = "d"; UnitOfTime["WEEKS"] = "w"; UnitOfTime["MONTHS"] = "m"; UnitOfTime["YEARS"] = "y"; })(UnitOfTime || (UnitOfTime = {})); export var UnitOfLength; (function (UnitOfLength) { UnitOfLength["MILLIMETERS"] = "mm"; UnitOfLength["CENTIMETERS"] = "cm"; UnitOfLength["METERS"] = "m"; UnitOfLength["KILOMETERS"] = "km"; UnitOfLength["INCHES"] = "in"; UnitOfLength["FEET"] = "ft"; UnitOfLength["YARDS"] = "yd"; UnitOfLength["MILES"] = "mi"; UnitOfLength["NAUTICAL_MILES"] = "nmi"; })(UnitOfLength || (UnitOfLength = {})); export var UnitOfFrequency; (function (UnitOfFrequency) { UnitOfFrequency["HERTZ"] = "Hz"; UnitOfFrequency["KILOHERTZ"] = "kHz"; UnitOfFrequency["MEGAHERTZ"] = "MHz"; UnitOfFrequency["GIGAHERTZ"] = "GHz"; })(UnitOfFrequency || (UnitOfFrequency = {})); export var UnitOfPressure; (function (UnitOfPressure) { UnitOfPressure["MILLIPASCAL"] = "mPa"; UnitOfPressure["PA"] = "Pa"; UnitOfPressure["HPA"] = "hPa"; UnitOfPressure["KPA"] = "kPa"; UnitOfPressure["BAR"] = "bar"; UnitOfPressure["CBAR"] = "cbar"; UnitOfPressure["MBAR"] = "mbar"; UnitOfPressure["MMHG"] = "mmHg"; UnitOfPressure["INHG"] = "inHg"; UnitOfPressure["INH2O"] = "inH\u2082O"; UnitOfPressure["PSI"] = "psi"; })(UnitOfPressure || (UnitOfPressure = {})); export var UnitOfSoundPressure; (function (UnitOfSoundPressure) { UnitOfSoundPressure["DECIBEL"] = "dB"; UnitOfSoundPressure["WEIGHTED_DECIBEL_A"] = "dBA"; })(UnitOfSoundPressure || (UnitOfSoundPressure = {})); export var UnitOfVolume; (function (UnitOfVolume) { UnitOfVolume["CUBIC_FEET"] = "ft\u00B3"; UnitOfVolume["CENTUM_CUBIC_FEET"] = "CCF"; UnitOfVolume["MILLE_CUBIC_FEET"] = "MCF"; UnitOfVolume["CUBIC_METERS"] = "m\u00B3"; UnitOfVolume["LITERS"] = "L"; UnitOfVolume["MILLILITERS"] = "mL"; UnitOfVolume["GALLONS"] = "gal"; UnitOfVolume["FLUID_OUNCES"] = "fl. oz."; })(UnitOfVolume || (UnitOfVolume = {})); export var UnitOfVolumeFlowRate; (function (UnitOfVolumeFlowRate) { UnitOfVolumeFlowRate["CUBIC_METERS_PER_HOUR"] = "m\u00B3/h"; UnitOfVolumeFlowRate["CUBIC_METERS_PER_MINUTE"] = "m\u00B3/min"; UnitOfVolumeFlowRate["CUBIC_METERS_PER_SECOND"] = "m\u00B3/s"; UnitOfVolumeFlowRate["CUBIC_FEET_PER_MINUTE"] = "ft\u00B3/min"; UnitOfVolumeFlowRate["LITERS_PER_HOUR"] = "L/h"; UnitOfVolumeFlowRate["LITERS_PER_MINUTE"] = "L/min"; UnitOfVolumeFlowRate["LITERS_PER_SECOND"] = "L/s"; UnitOfVolumeFlowRate["GALLONS_PER_HOUR"] = "gal/h"; UnitOfVolumeFlowRate["GALLONS_PER_MINUTE"] = "gal/min"; UnitOfVolumeFlowRate["GALLONS_PER_DAY"] = "gal/d"; UnitOfVolumeFlowRate["MILLILITERS_PER_SECOND"] = "mL/s"; })(UnitOfVolumeFlowRate || (UnitOfVolumeFlowRate = {})); export var UnitOfArea; (function (UnitOfArea) { UnitOfArea["SQUARE_METERS"] = "m\u00B2"; UnitOfArea["SQUARE_CENTIMETERS"] = "cm\u00B2"; UnitOfArea["SQUARE_KILOMETERS"] = "km\u00B2"; UnitOfArea["SQUARE_MILLIMETERS"] = "mm\u00B2"; UnitOfArea["SQUARE_INCHES"] = "in\u00B2"; UnitOfArea["SQUARE_FEET"] = "ft\u00B2"; UnitOfArea["SQUARE_YARDS"] = "yd\u00B2"; UnitOfArea["SQUARE_MILES"] = "mi\u00B2"; UnitOfArea["ACRES"] = "ac"; UnitOfArea["HECTARES"] = "ha"; })(UnitOfArea || (UnitOfArea = {})); export var UnitOfMass; (function (UnitOfMass) { UnitOfMass["GRAMS"] = "g"; UnitOfMass["KILOGRAMS"] = "kg"; UnitOfMass["MILLIGRAMS"] = "mg"; UnitOfMass["MICROGRAMS"] = "\u03BCg"; UnitOfMass["OUNCES"] = "oz"; UnitOfMass["POUNDS"] = "lb"; UnitOfMass["STONES"] = "st"; })(UnitOfMass || (UnitOfMass = {})); export var UnitOfConductivity; (function (UnitOfConductivity) { UnitOfConductivity["SIEMENS_PER_CM"] = "S/cm"; UnitOfConductivity["MICROSIEMENS_PER_CM"] = "\u03BCS/cm"; UnitOfConductivity["MILLISIEMENS_PER_CM"] = "mS/cm"; })(UnitOfConductivity || (UnitOfConductivity = {})); export class HomeAssistant extends EventEmitter { connected = false; ws = null; wsUrl; wsAccessToken; log; hassDevices = new Map(); hassEntities = new Map(); hassStates = new Map(); hassAreas = new Map(); hassLabels = new Map(); hassServices = null; hassConfig = null; static hassConfig = null; debug = hasParameter('debug') || hasParameter('verbose'); verbose = hasParameter('verbose'); pingInterval = undefined; pingTimeout = undefined; reconnectTimeout = undefined; pingIntervalTime = 30000; pingTimeoutTime = 35000; reconnectTimeoutTime = 60000; reconnectRetries = 10; _responseTimeout = 5000; certificatePath = undefined; rejectUnauthorized = undefined; reconnectRetry = 1; requestId = 1; fetchTimeout = undefined; fetchQueue = new Set(); emit(eventName, ...args) { return super.emit(eventName, ...args); } on(eventName, listener) { return super.on(eventName, listener); } get responseTimeout() { return this._responseTimeout; } set responseTimeout(value) { this._responseTimeout = value; } constructor(url, accessToken, reconnectTimeoutTime = 60, reconnectRetries = 10, certificatePath = undefined, rejectUnauthorized = undefined) { super(); this.wsUrl = url; this.wsAccessToken = accessToken; this.reconnectTimeoutTime = reconnectTimeoutTime * 1000; this.reconnectRetries = reconnectRetries; this.certificatePath = certificatePath; this.rejectUnauthorized = rejectUnauthorized; this.log = new AnsiLogger({ logName: 'HomeAssistant', logTimestampFormat: 4, logLevel: this.debug ? "debug" : "info", }); } onOpen = () => { this.log.debug('WebSocket connection established'); this.emit('socket_opened'); }; onPing(data) { this.log.debug('WebSocket ping received'); if (this.pingTimeout) { clearTimeout(this.pingTimeout); this.pingTimeout = undefined; this.log.debug('Stopped ping timeout'); } this.emit('ping', data); } onPong(data) { this.log.debug('WebSocket pong received'); if (this.pingTimeout) { clearTimeout(this.pingTimeout); this.pingTimeout = undefined; this.log.debug('Stopped ping timeout'); } this.emit('pong', data); } onError(error) { const errorMessage = `WebSocket error: ${error}`; this.log.debug(errorMessage); this.emit('error', errorMessage); } onMessage(data, isBinary) { let response; try { response = JSON.parse(isBinary ? data.toString() : data); } catch (error) { this.log.error(`Error parsing WebSocket message: ${error}`); return; } if (response.type === 'pong') { this.log.debug(`Home Assistant pong received with id ${response.id}`); if (this.pingTimeout) { clearTimeout(this.pingTimeout); this.pingTimeout = undefined; this.log.debug('Stopped ping timeout'); } this.emit('pong', Buffer.from('Home Assistant pong received')); } else if (response.type === 'event') { if (!response.event) { const errorMessage = `WebSocket event response missing event data for id ${response.id}`; this.log.error(errorMessage); this.emit('error', errorMessage); return; } if (this.verbose) this.log.debug(`Event ${CYAN}${response.event.event_type}${db} received id ${CYAN}${response.id}${db}:${rs}\n${debugStringify(response.event)}`); if (response.event.event_type === 'state_changed') { const entity = this.hassEntities.get(response.event.data.entity_id); if (!entity) { this.log.debug(`Entity id ${CYAN}${response.event.data.entity_id}${db} not found processing event`); return; } if (response.event.data.old_state && response.event.data.new_state) { this.hassStates.set(response.event.data.new_state.entity_id, response.event.data.new_state); this.emit('event', entity.device_id, entity.entity_id, response.event.data.old_state, response.event.data.new_state); } } else if (response.event.event_type === 'call_service') { this.log.debug(`Event ${CYAN}${response.event.event_type}${db} received id ${CYAN}${response.id}${db}`); this.emit('call_service'); } else if (response.event.event_type === 'core_config_updated') { this.log.debug(`Event ${CYAN}${response.event.event_type}${db} received id ${CYAN}${response.id}${db}`); clearTimeout(this.fetchTimeout); this.fetchTimeout = setTimeout(() => void this.onFetchTimeout(), 5000).unref(); this.fetchQueue.add('get_config'); } else if (response.event.event_type === 'device_registry_updated') { this.log.debug(`Event ${CYAN}${response.event.event_type}${db} received id ${CYAN}${response.id}${db}`); clearTimeout(this.fetchTimeout); this.fetchTimeout = setTimeout(() => void this.onFetchTimeout(), 5000).unref(); this.fetchQueue.add('config/device_registry/list'); } else if (response.event.event_type === 'entity_registry_updated') { this.log.debug(`Event ${CYAN}${response.event.event_type}${db} received id ${CYAN}${response.id}${db}`); clearTimeout(this.fetchTimeout); this.fetchTimeout = setTimeout(() => void this.onFetchTimeout(), 5000).unref(); this.fetchQueue.add('config/entity_registry/list'); } else if (response.event.event_type === 'area_registry_updated') { this.log.debug(`Event ${CYAN}${response.event.event_type}${db} received id ${CYAN}${response.id}${db}`); clearTimeout(this.fetchTimeout); this.fetchTimeout = setTimeout(() => void this.onFetchTimeout(), 5000).unref(); this.fetchQueue.add('config/area_registry/list'); } else if (response.event.event_type === 'label_registry_updated') { this.log.debug(`Event ${CYAN}${response.event.event_type}${db} received id ${CYAN}${response.id}${db}`); clearTimeout(this.fetchTimeout); this.fetchTimeout = setTimeout(() => void this.onFetchTimeout(), 5000).unref(); this.fetchQueue.add('config/label_registry/list'); } else { if (this.debug) this.log.debug(`Unknown event type ${CYAN}${response.event.event_type}${db} received id ${CYAN}${response.id}${db}`); } } } onClose(code, reason) { const closeMessage = `WebSocket connection closed. Code: ${code} Reason: ${reason.toString()}`; this.log.debug(closeMessage); this.connected = false; this.stopPing(); this.emit('socket_closed', code, reason); this.emit('disconnected', `Code: ${code} Reason: ${reason.toString()}`); this.startReconnect(); } async onFetchTimeout() { this.fetchTimeout = undefined; this.log.debug(`Fetch timeout reached, processing fetch queue of ${this.fetchQueue.size} fetch id(s)...`); for (const fetchId of this.fetchQueue) { this.log.debug(`Fetching ${CYAN}${fetchId}${db}...`); try { const data = await this.fetch(fetchId); this.log.debug(`Received data for ${CYAN}${fetchId}${db}`); if (fetchId === 'get_config') { const config = data; this.log.debug(`Received config.`); this.hassConfig = config; HomeAssistant.hassConfig = this.hassConfig; this.emit('config', config); } else if (fetchId === 'config/device_registry/list') { const devices = data; this.log.debug(`Received ${devices.length} devices.`); devices.forEach((device) => this.hassDevices.set(device.id, device)); this.emit('devices', devices); } else if (fetchId === 'config/entity_registry/list') { const entities = data; this.log.debug(`Received ${entities.length} entities.`); entities.forEach((entity) => this.hassEntities.set(entity.entity_id, entity)); this.emit('entities', entities); } else if (fetchId === 'config/area_registry/list') { const areas = data; this.log.debug(`Received ${areas.length} areas.`); areas.forEach((area) => this.hassAreas.set(area.area_id, area)); this.emit('areas', areas); } else if (fetchId === 'config/label_registry/list') { const labels = data; this.log.debug(`Received ${labels.length} labels.`); labels.forEach((label) => this.hassLabels.set(label.label_id, label)); this.emit('labels', labels); } } catch (error) { this.log.error(`Error fetching ${CYAN}${fetchId}${er}: ${error}`); } this.fetchQueue.delete(fetchId); } } connect() { return new Promise((resolve, reject) => { if (this.connected) { return reject(new Error('Already connected to Home Assistant')); } try { this.log.info(`Connecting to Home Assistant on ${this.wsUrl}...`); if (this.wsUrl.startsWith('ws://')) { this.ws = new WebSocket(this.wsUrl + '/api/websocket'); } else if (this.wsUrl.startsWith('wss://')) { let ca; if (this.certificatePath) { this.log.debug(`Loading CA certificate from ${this.certificatePath}...`); ca = readFileSync(this.certificatePath); this.log.debug(`CA certificate loaded successfully`); } this.ws = new WebSocket(this.wsUrl + '/api/websocket', { ca, rejectUnauthorized: this.rejectUnauthorized, }); } else { return reject(new Error(`Invalid WebSocket URL: ${this.wsUrl}. It must start with ws:// or wss://`)); } this.ws.on('open', this.onOpen.bind(this)); this.ws.on('ping', this.onPing.bind(this)); this.ws.on('pong', this.onPong.bind(this)); this.ws.on('close', this.onClose.bind(this)); this.ws.onerror = (event) => { this.log.error(`WebSocket error: ${event.message}`); this.emit('error', `WebSocket error: ${event.message}`); return reject(new Error(`WebSocket error: ${event.message}`)); }; this.ws.onmessage = (event) => { let response; try { response = JSON.parse(event.data.toString()); } catch (error) { return reject(new Error(`Error parsing WebSocket message: ${error}`)); } if (response.type === 'auth_required') { this.log.debug(`Authentication required: ${debugStringify(response)}`); this.log.debug('Authentication required. Sending auth message...'); this.ws?.send(JSON.stringify({ type: 'auth', access_token: this.wsAccessToken, })); } else if (response.type === 'auth_ok') { this.log.debug(`Authenticated successfully: ${debugStringify(response)}`); this.log.debug(`Authenticated successfully with Home Assistant v. ${response.ha_version}`); this.connected = true; this.reconnectRetry = 1; if (this.ws) this.ws.onmessage = null; this.ws?.on('message', this.onMessage.bind(this)); if (this.ws) this.ws.onerror = null; this.ws?.on('error', this.onError.bind(this)); this.startPing(); this.emit('connected', response.ha_version); return resolve(response.ha_version); } }; } catch (error) { const errorMessage = `WebSocket error connecting to Home Assistant: ${error}`; this.log.debug(errorMessage); return reject(new Error(errorMessage)); } }); } startPing() { if (this.pingInterval) { this.log.debug('Ping interval already started'); return; } this.log.debug('Starting ping interval...'); this.pingInterval = setInterval(() => { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.log.error('WebSocket not open sending ping. Closing connection...'); void this.close().catch(() => { }); return; } this.log.debug(`Sending WebSocket ping...`); this.ws.ping(); this.log.debug(`Sending Home Assistant ping id ${this.requestId}...`); this.ws.send(JSON.stringify({ id: this.requestId++, type: 'ping', })); this.log.debug('Starting ping timeout...'); this.pingTimeout = setTimeout(() => { this.log.error('Ping timeout. Closing connection...'); void this.close().catch(() => { }); this.startReconnect(); }, this.pingTimeoutTime).unref(); this.log.debug('Started ping timeout'); }, this.pingIntervalTime).unref(); this.log.debug('Started ping interval'); } stopPing() { this.log.debug('Stopping ping interval...'); if (this.pingInterval) { clearInterval(this.pingInterval); this.pingInterval = undefined; } this.log.debug('Stopped ping interval'); this.log.debug('Stopping ping timeout...'); if (this.pingTimeout) { clearTimeout(this.pingTimeout); this.pingTimeout = undefined; } this.log.debug('Stopped ping timeout'); } startReconnect() { if (this.reconnectTimeout) { this.log.debug(`Reconnecting already in progress.`); return; } if (this.reconnectTimeoutTime && this.reconnectRetry <= this.reconnectRetries) { this.log.notice(`Reconnecting in ${this.reconnectTimeoutTime / 1000} seconds...`); this.reconnectTimeout = setTimeout(() => { const attempt = this.reconnectRetry; this.log.notice(`Reconnecting attempt ${attempt} of ${this.reconnectRetries}...`); void this.connect().catch((error) => { const errorMessage = error instanceof Error ? error.message : String(error); this.log.debug(`Reconnection attempt ${attempt} failed: ${errorMessage}`); }); this.reconnectRetry++; this.reconnectTimeout = undefined; }, this.reconnectTimeoutTime).unref(); } else { this.log.error('Restart the plugin to reconnect.'); } } close(code = 1000, reason = 'Normal closure') { return new Promise((resolve, reject) => { this.log.info('Closing Home Assistant connection...'); this.stopPing(); if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = undefined; } const cleanup = () => { clearTimeout(timer); this.ws?.removeAllListeners(); this.ws = null; this.connected = false; this.emit('disconnected', 'WebSocket connection closed'); this.log.info('Home Assistant connection closed'); }; const timer = setTimeout(() => { const message = `Close did not complete before the timeout of ${this._responseTimeout} ms`; this.log.debug(message); cleanup(); return reject(new Error(message)); }, this._responseTimeout).unref(); const onClose = () => { this.log.debug('Close received closed event from Home Assistant'); this.emit('socket_closed', code, Buffer.from(reason)); cleanup(); return resolve(); }; const onError = () => { const message = 'Close received error event while closing connection to Home Assistant'; this.log.debug(message); cleanup(); return reject(new Error(message)); }; if (this.ws) { this.ws.removeAllListeners(); this.ws.onclose = onClose; this.ws.onerror = onError; } if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.log.debug('Close websocket is open, closing...'); this.ws.close(code, reason); } else { this.log.debug('Close websocket is not open, resolving...'); cleanup(); return resolve(); } }); } async fetchData() { try { this.log.debug('Fetching initial data from Home Assistant...'); this.hassConfig = (await this.fetch('get_config')); HomeAssistant.hassConfig = this.hassConfig; this.log.debug('Received config.'); this.emit('config', this.hassConfig); let retries = 1; while (this.hassConfig.state !== 'RUNNING' && retries <= 100) { this.log.debug(`State is ${CYAN}${this.hassConfig.state}${db}. Waiting (${retries}/100) for Home Assistant to be RUNNING...`); retries += 1; await new Promise((resolve) => setTimeout(resolve, 1000)); this.hassConfig = (await this.fetch('get_config')); HomeAssistant.hassConfig = this.hassConfig; this.emit('config', this.hassConfig); } this.hassServices = (await this.fetch('get_services')); this.log.debug('Received services.'); this.emit('services', this.hassServices); const devices = (await this.fetch('config/device_registry/list')); devices.forEach((device) => this.hassDevices.set(device.id, device)); this.log.debug(`Received ${devices.length} devices.`); this.emit('devices', devices); const entities = (await this.fetch('config/entity_registry/list')); entities.forEach((entity) => this.hassEntities.set(entity.entity_id, entity)); this.log.debug(`Received ${entities.length} entities.`); this.emit('entities', entities); const states = (await this.fetch('get_states')); states.forEach((state) => this.hassStates.set(state.entity_id, state)); this.log.debug(`Received ${states.length} states.`); this.emit('states', states); const areas = (await this.fetch('config/area_registry/list')); areas.forEach((area) => this.hassAreas.set(area.area_id, area)); this.log.debug(`Received ${areas.length} areas.`); this.emit('areas', areas); const labels = (await this.fetch('config/label_registry/list')); labels.forEach((label) => this.hassLabels.set(label.label_id, label)); this.log.debug(`Received ${labels.length} labels.`); this.emit('labels', labels); this.log.debug('Initial data fetched successfully.'); } catch (error) { this.log.error(`Error fetching initial data: ${error}`); } } fetch(type) { return new Promise((resolve, reject) => { if (!this.connected) { return reject(new Error('Fetch error: not connected to Home Assistant')); } if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { return reject(new Error('Fetch error: WebSocket not open')); } const requestId = this.requestId++; const timer = setTimeout(() => { this.ws?.removeEventListener('message', handleMessage); return reject(new Error(`Fetch type ${type} id ${requestId} did not complete before the timeout`)); }, this._responseTimeout).unref(); const handleMessage = (event) => { try { const response = JSON.parse(event.data.toString()); if (response.type === 'result' && response.id === requestId) { clearTimeout(timer); this.ws?.removeEventListener('message', handleMessage); if (response.success) { return resolve(response.result); } else { return reject(new Error(response.error?.message)); } } } catch (error) { clearTimeout(timer); this.ws?.removeEventListener('message', handleMessage); reject(error); } }; this.ws.addEventListener('message', handleMessage); this.log.debug(`Fetching ${CYAN}${type}${db} with id ${CYAN}${requestId}${db} and timeout ${CYAN}${this._responseTimeout}${db} ms ...`); this.ws.send(JSON.stringify({ id: requestId, type })); }); } subscribe(event) { return new Promise((resolve, reject) => { if (!this.connected) { return reject(new Error('Subscribe error: not connected to Home Assistant')); } if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { return reject(new Error('Subscribe error: WebSocket not open')); } const requestId = this.requestId++; const timer = setTimeout(() => { this.ws?.removeEventListener('message', handleMessage); return reject(new Error(`Subscribe event ${event} id ${requestId} did not complete before the timeout`)); }, this._responseTimeout).unref(); const handleMessage = (event) => { try { const response = JSON.parse(event.data.toString()); if (response.type === 'result' && response.id === requestId) { clearTimeout(timer); this.ws?.removeEventListener('message', handleMessage); if (response.success) { this.log.debug(`Subscribed successfully with id ${CYAN}${requestId}${db}`); return resolve(response.id); } else { return reject(new Error(response.error?.message)); } } } catch (error) { clearTimeout(timer); this.ws?.removeEventListener('message', handleMessage); reject(error); } }; this.ws.addEventListener('message', handleMessage); this.log.debug(`Subscribing to ${CYAN}${event ?? 'all events'}${db} with id ${CYAN}${requestId}${db} and timeout ${CYAN}${this._responseTimeout}${db} ms ...`); this.ws.send(JSON.stringify({ id: requestId, type: 'subscribe_events', event_type: event, })); }); } unsubscribe(subscriptionId) { return new Promise((resolve, reject) => { if (!this.connected) { return reject(new Error('Unsubscribe error: not connected to Home Assistant')); } if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { return reject(new Error('Unsubscribe error: WebSocket not open')); } const requestId = this.requestId++; const timer = setTimeout(() => { this.ws?.removeEventListener('message', handleMessage); return reject(new Error(`Unsubscri