matterbridge-hass
Version:
Matterbridge hass plugin
385 lines (384 loc) • 38.9 kB
JavaScript
import { airQualitySensor, basicVideoPlayer, colorTemperatureLight, contactSensor, coverDevice, dimmableLight, doorLockDevice, electricalSensor, extendedColorLight, fanDevice, humiditySensor, lightSensor, modeSelect, occupancySensor, onOffLight, onOffOutlet, powerSource, pressureSensor, roboticVacuumCleaner, smokeCoAlarm, temperatureSensor, thermostatDevice, waterFreezeDetector, waterLeakDetector, waterValve, } from 'matterbridge';
import { AirQuality, BooleanState, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, ColorControl, DoorLock, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, FanControl, FormaldehydeConcentrationMeasurement, IlluminanceMeasurement, LevelControl, MediaPlayback, ModeSelect, NitrogenDioxideConcentrationMeasurement, OccupancySensing, OnOff, OzoneConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm25ConcentrationMeasurement, PowerSource, PressureMeasurement, RadonConcentrationMeasurement, RelativeHumidityMeasurement, RvcCleanMode, RvcOperationalState, RvcRunMode, SmokeCoAlarm, TemperatureMeasurement, Thermostat, TotalVolatileOrganicCompoundsConcentrationMeasurement, ValveConfigurationAndControl, WindowCovering, } from 'matterbridge/matter/clusters';
import { isValidArray, isValidBoolean, isValidNumber, isValidString } from 'matterbridge/utils';
import { ColorMode, HomeAssistant, HVACMode, UnitOfTemperature } from './homeAssistant.js';
export function getFeatureNames(featureEnum, supported_features) {
if (supported_features === undefined)
return [];
return Object.entries(featureEnum)
.filter(([_, value]) => typeof value === 'number' && (supported_features & value) !== 0)
.map(([key]) => key);
}
export function miredsToKelvin(mired, round) {
if (round === 'floor')
return Math.floor(1000000 / mired);
else if (round === 'ceil')
return Math.ceil(1000000 / mired);
else
return Math.round(1000000 / mired);
}
export function kelvinToMireds(kelvin, round) {
if (round === 'floor')
return clamp(Math.floor(1000000 / kelvin), 1, 65279);
else if (round === 'ceil')
return clamp(Math.ceil(1000000 / kelvin), 1, 65279);
else
return clamp(Math.round(1000000 / kelvin), 1, 65279);
}
export function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
export function roundTo(value, digits) {
const factor = Math.pow(10, digits);
return Math.round(value * factor) / factor;
}
export function temp(value, unit) {
if (unit === UnitOfTemperature.FAHRENHEIT)
return ((value - 32) * 5) / 9;
return value;
}
export function tempToFahrenheit(value) {
if (HomeAssistant.hassConfig?.unit_system.temperature === UnitOfTemperature.FAHRENHEIT)
return (value * 9) / 5 + 32;
return value;
}
export function pressure(value, unit) {
if (unit === 'hPa') {
return value;
}
if (unit === 'kPa') {
return value * 10;
}
if (unit === 'inHg') {
return Math.round(value * 33.8639);
}
if (unit === 'psi') {
return Math.round(value * 68.9475729318);
}
return null;
}
export function aqi(value, _unit) {
if (typeof value === 'string') {
value = value.toLowerCase();
if (value === 'excellent')
return AirQuality.AirQualityEnum.Good;
if (value === 'healthy')
return AirQuality.AirQualityEnum.Good;
if (value === 'fine')
return AirQuality.AirQualityEnum.Good;
if (value === 'good')
return AirQuality.AirQualityEnum.Good;
if (value === 'fair')
return AirQuality.AirQualityEnum.Fair;
if (value === 'moderate')
return AirQuality.AirQualityEnum.Moderate;
if (value === 'poor')
return AirQuality.AirQualityEnum.Poor;
if (value === 'unhealthy_for_sensitive_groups')
return AirQuality.AirQualityEnum.Poor;
if (value === 'very_poor')
return AirQuality.AirQualityEnum.VeryPoor;
if (value === 'unhealthy')
return AirQuality.AirQualityEnum.VeryPoor;
if (value === 'extremely_poor')
return AirQuality.AirQualityEnum.ExtremelyPoor;
if (value === 'very_unhealthy')
return AirQuality.AirQualityEnum.ExtremelyPoor;
if (value === 'hazardous')
return AirQuality.AirQualityEnum.ExtremelyPoor;
return null;
}
if (isValidNumber(value, 0, 500)) {
return Math.round((value / 500) * 5 + 1);
}
return null;
}
export function convertMatterXYToHA(currentX, currentY) {
const SCALE = 65536;
const MAX = 65279;
const MIN = 0;
const safeX = Math.max(MIN, Math.min(currentX, MAX));
const safeY = Math.max(MIN, Math.min(currentY, MAX));
const x = parseFloat((safeX / SCALE).toFixed(4));
const y = parseFloat((safeY / SCALE).toFixed(4));
return [x, y];
}
export function convertHAXYToMatter(xyColor) {
const SCALE = 65536;
const MAX_MATTER_VALUE = 65279;
const safeX = Math.max(0, Math.min(xyColor[0], 1));
const safeY = Math.max(0, Math.min(xyColor[1], 1));
const rawX = Math.round(safeX * SCALE);
const rawY = Math.round(safeY * SCALE);
const currentX = Math.min(rawX, MAX_MATTER_VALUE);
const currentY = Math.min(rawY, MAX_MATTER_VALUE);
return { currentX, currentY };
}
function getSelectOptionFromMode(request, state) {
const options = state?.attributes?.options;
const newMode = request['newMode'];
if (!isValidNumber(newMode, 1)) {
return undefined;
}
const optionIndex = newMode - 1;
if (!isValidArray(options, 1) || !isValidNumber(optionIndex, 0, options.length - 1) || !isValidString(options[optionIndex], 1)) {
return undefined;
}
return { option: options[optionIndex] };
}
export const hassUpdateStateConverter = [
{ domain: 'switch', state: 'on', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: true },
{ domain: 'switch', state: 'off', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: false },
{ domain: 'light', state: 'on', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: true },
{ domain: 'light', state: 'off', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: false },
{ domain: 'lock', state: 'locked', clusterId: DoorLock.Cluster.id, attribute: 'lockState', value: DoorLock.LockState.Locked },
{ domain: 'lock', state: 'locking', clusterId: DoorLock.Cluster.id, attribute: 'lockState', value: DoorLock.LockState.NotFullyLocked },
{ domain: 'lock', state: 'unlocking', clusterId: DoorLock.Cluster.id, attribute: 'lockState', value: DoorLock.LockState.NotFullyLocked },
{ domain: 'lock', state: 'unlocked', clusterId: DoorLock.Cluster.id, attribute: 'lockState', value: DoorLock.LockState.Unlocked },
{ domain: 'fan', state: 'on', clusterId: FanControl.Cluster.id, attribute: 'fanMode', value: FanControl.FanMode.Auto },
{ domain: 'fan', state: 'off', clusterId: FanControl.Cluster.id, attribute: 'fanMode', value: FanControl.FanMode.Off },
{ domain: 'cover', state: 'opening', clusterId: WindowCovering.Cluster.id, attribute: 'operationalStatus', value: { global: WindowCovering.MovementStatus.Opening, lift: WindowCovering.MovementStatus.Opening, tilt: 0 } },
{ domain: 'cover', state: 'open', clusterId: WindowCovering.Cluster.id, attribute: 'operationalStatus', value: { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: 0 } },
{ domain: 'cover', state: 'closed', clusterId: WindowCovering.Cluster.id, attribute: 'operationalStatus', value: { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: 0 } },
{ domain: 'cover', state: 'closing', clusterId: WindowCovering.Cluster.id, attribute: 'operationalStatus', value: { global: WindowCovering.MovementStatus.Closing, lift: WindowCovering.MovementStatus.Closing, tilt: 0 } },
{ domain: 'climate', state: 'off', clusterId: Thermostat.Cluster.id, attribute: 'systemMode', value: Thermostat.SystemMode.Off },
{ domain: 'climate', state: 'heat', clusterId: Thermostat.Cluster.id, attribute: 'systemMode', value: Thermostat.SystemMode.Heat },
{ domain: 'climate', state: 'cool', clusterId: Thermostat.Cluster.id, attribute: 'systemMode', value: Thermostat.SystemMode.Cool },
{ domain: 'climate', state: 'heat_cool', clusterId: Thermostat.Cluster.id, attribute: 'systemMode', value: Thermostat.SystemMode.Auto },
{ domain: 'climate', state: 'auto', clusterId: undefined, attribute: '', value: null },
{ domain: 'valve', state: 'opening', clusterId: ValveConfigurationAndControl.Cluster.id, attribute: 'currentState', value: ValveConfigurationAndControl.ValveState.Transitioning },
{ domain: 'valve', state: 'open', clusterId: ValveConfigurationAndControl.Cluster.id, attribute: 'currentState', value: ValveConfigurationAndControl.ValveState.Open },
{ domain: 'valve', state: 'closing', clusterId: ValveConfigurationAndControl.Cluster.id, attribute: 'currentState', value: ValveConfigurationAndControl.ValveState.Transitioning },
{ domain: 'valve', state: 'closed', clusterId: ValveConfigurationAndControl.Cluster.id, attribute: 'currentState', value: ValveConfigurationAndControl.ValveState.Closed },
{ domain: 'vacuum', state: 'idle', clusterId: RvcRunMode.Cluster.id, attribute: 'currentMode', value: 1 },
{ domain: 'vacuum', state: 'idle', clusterId: RvcOperationalState.Cluster.id, attribute: 'operationalState', value: RvcOperationalState.OperationalState.Stopped },
{ domain: 'vacuum', state: 'paused', clusterId: RvcRunMode.Cluster.id, attribute: 'currentMode', value: 1 },
{ domain: 'vacuum', state: 'paused', clusterId: RvcOperationalState.Cluster.id, attribute: 'operationalState', value: RvcOperationalState.OperationalState.Paused },
{ domain: 'vacuum', state: 'docked', clusterId: RvcRunMode.Cluster.id, attribute: 'currentMode', value: 1 },
{ domain: 'vacuum', state: 'docked', clusterId: RvcOperationalState.Cluster.id, attribute: 'operationalState', value: RvcOperationalState.OperationalState.Docked },
{ domain: 'vacuum', state: 'returning', clusterId: RvcRunMode.Cluster.id, attribute: 'currentMode', value: 1 },
{ domain: 'vacuum', state: 'returning', clusterId: RvcOperationalState.Cluster.id, attribute: 'operationalState', value: RvcOperationalState.OperationalState.SeekingCharger },
{ domain: 'vacuum', state: 'cleaning', clusterId: RvcRunMode.Cluster.id, attribute: 'currentMode', value: 2 },
{ domain: 'vacuum', state: 'cleaning', clusterId: RvcOperationalState.Cluster.id, attribute: 'operationalState', value: RvcOperationalState.OperationalState.Running },
{ domain: 'input_boolean', state: 'on', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: true },
{ domain: 'input_boolean', state: 'off', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: false },
{ domain: 'binary_sensor', state: 'on', clusterId: BooleanState.Cluster.id, attribute: 'stateValue', value: true },
{ domain: 'binary_sensor', state: 'off', clusterId: BooleanState.Cluster.id, attribute: 'stateValue', value: false },
{ domain: 'remote', state: 'on', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: true },
{ domain: 'remote', state: 'off', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: false },
{ domain: 'media_player', state: 'on', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: true },
{ domain: 'media_player', state: 'off', clusterId: OnOff.Cluster.id, attribute: 'onOff', value: false },
{ domain: 'media_player', state: 'playing', clusterId: MediaPlayback.Cluster.id, attribute: 'currentState', value: MediaPlayback.PlaybackState.Playing },
{ domain: 'media_player', state: 'paused', clusterId: MediaPlayback.Cluster.id, attribute: 'currentState', value: MediaPlayback.PlaybackState.Paused },
{ domain: 'media_player', state: 'idle', clusterId: MediaPlayback.Cluster.id, attribute: 'currentState', value: MediaPlayback.PlaybackState.NotPlaying },
{ domain: 'media_player', state: 'standby', clusterId: MediaPlayback.Cluster.id, attribute: 'currentState', value: MediaPlayback.PlaybackState.NotPlaying },
{ domain: 'media_player', state: 'buffering', clusterId: MediaPlayback.Cluster.id, attribute: 'currentState', value: MediaPlayback.PlaybackState.Buffering },
{ domain: 'media_player', state: 'on', clusterId: MediaPlayback.Cluster.id, attribute: 'currentState', value: MediaPlayback.PlaybackState.Playing },
{ domain: 'media_player', state: 'off', clusterId: MediaPlayback.Cluster.id, attribute: 'currentState', value: MediaPlayback.PlaybackState.NotPlaying },
];
export const hassUpdateAttributeConverter = [
{ domain: 'light', with: 'brightness', clusterId: LevelControl.Cluster.id, attribute: 'currentLevel', converter: (value) => (isValidNumber(value, 1, 255) ? Math.round(value / 255 * 254) : null) },
{ domain: 'light', with: 'color_mode', clusterId: ColorControl.Cluster.id, attribute: 'colorMode', converter: (value) => {
if (isValidString(value, 2, 10)) {
if (value === 'hs' || value === 'rgb')
return ColorControl.ColorMode.CurrentHueAndCurrentSaturation;
else if (value === 'xy')
return ColorControl.ColorMode.CurrentXAndCurrentY;
else if (value === 'color_temp')
return ColorControl.ColorMode.ColorTemperatureMireds;
else
return null;
}
else {
return null;
}
}
},
{ domain: 'light', with: 'color_temp_kelvin', clusterId: ColorControl.Cluster.id, attribute: 'colorTemperatureMireds', converter: (value, state) => (isValidNumber(value, 0, 65279) && state.attributes['color_mode'] === ColorMode.COLOR_TEMP ? kelvinToMireds(value, 'floor') : null) },
{ domain: 'light', with: 'hs_color', clusterId: ColorControl.Cluster.id, attribute: 'currentHue', converter: (value, state) => (isValidArray(value, 2, 2) && isValidNumber(value[0], 0, 360) && (state.attributes['color_mode'] === ColorMode.HS || state.attributes['color_mode'] === ColorMode.RGB) ? Math.round(value[0] / 360 * 254) : null) },
{ domain: 'light', with: 'hs_color', clusterId: ColorControl.Cluster.id, attribute: 'currentSaturation', converter: (value, state) => (isValidArray(value, 2, 2) && isValidNumber(value[1], 0, 100) && (state.attributes['color_mode'] === ColorMode.HS || state.attributes['color_mode'] === ColorMode.RGB) ? Math.round(value[1] / 100 * 254) : null) },
{ domain: 'light', with: 'xy_color', clusterId: ColorControl.Cluster.id, attribute: 'currentX', converter: (value, state) => (isValidArray(value, 2, 2) && isValidNumber(value[0], 0, 1) && state.attributes['color_mode'] === ColorMode.XY ? convertHAXYToMatter([value[0], value[1]]).currentX : null) },
{ domain: 'light', with: 'xy_color', clusterId: ColorControl.Cluster.id, attribute: 'currentY', converter: (value, state) => (isValidArray(value, 2, 2) && isValidNumber(value[1], 0, 1) && state.attributes['color_mode'] === ColorMode.XY ? convertHAXYToMatter([value[0], value[1]]).currentY : null) },
{ domain: 'fan', with: 'percentage', clusterId: FanControl.Cluster.id, attribute: 'percentCurrent', converter: (value) => (isValidNumber(value, 1, 100) ? Math.round(value) : null) },
{ domain: 'fan', with: 'preset_mode', clusterId: FanControl.Cluster.id, attribute: 'fanMode', converter: (value) => {
if (isValidString(value, 3, 6)) {
if (value === 'low')
return FanControl.FanMode.Low;
else if (value === 'medium')
return FanControl.FanMode.Medium;
else if (value === 'high')
return FanControl.FanMode.High;
else if (value === 'auto')
return FanControl.FanMode.Auto;
else
return null;
}
else {
return null;
}
} },
{ domain: 'fan', with: 'direction', clusterId: FanControl.Cluster.id, attribute: 'airflowDirection', converter: (value) => (isValidString(value, 7, 7) ? value === 'forward' ? FanControl.AirflowDirection.Forward : FanControl.AirflowDirection.Reverse : null) },
{ domain: 'fan', with: 'oscillating', clusterId: FanControl.Cluster.id, attribute: 'rockSetting', converter: (value) => (isValidBoolean(value) ? value === true ? { rockLeftRight: false, rockUpDown: false, rockRound: true } : { rockLeftRight: false, rockUpDown: false, rockRound: false } : null) },
{ domain: 'cover', with: 'current_position', clusterId: WindowCovering.Cluster.id, attribute: 'currentPositionLiftPercent100ths', converter: (value) => (isValidNumber(value, 0, 100) ? Math.round(10000 - value * 100) : null) },
{ domain: 'cover', with: 'current_position', clusterId: WindowCovering.Cluster.id, attribute: 'targetPositionLiftPercent100ths', converter: (value) => (isValidNumber(value, 0, 100) ? Math.round(10000 - value * 100) : null) },
{ domain: 'climate', with: 'temperature', clusterId: Thermostat.Cluster.id, attribute: 'occupiedHeatingSetpoint', converter: (value, state) => (isValidNumber(value) && (state.state === HVACMode.HEAT || (state.state === HVACMode.AUTO && state.attributes?.hvac_modes?.includes(HVACMode.HEAT))) ? Math.round(temp(value, HomeAssistant.hassConfig?.unit_system?.temperature) * 100) : null) },
{ domain: 'climate', with: 'temperature', clusterId: Thermostat.Cluster.id, attribute: 'occupiedCoolingSetpoint', converter: (value, state) => (isValidNumber(value) && (state.state === HVACMode.COOL || (state.state === HVACMode.AUTO && state.attributes?.hvac_modes?.includes(HVACMode.COOL))) ? Math.round(temp(value, HomeAssistant.hassConfig?.unit_system?.temperature) * 100) : null) },
{ domain: 'climate', with: 'target_temp_high', clusterId: Thermostat.Cluster.id, attribute: 'occupiedCoolingSetpoint', converter: (value, state) => (isValidNumber(value) && (state.attributes?.hvac_modes?.includes(HVACMode.HEAT_COOL) || state.attributes?.hvac_modes?.includes(HVACMode.COOL)) ? Math.round(temp(value, HomeAssistant.hassConfig?.unit_system?.temperature) * 100) : null) },
{ domain: 'climate', with: 'target_temp_low', clusterId: Thermostat.Cluster.id, attribute: 'occupiedHeatingSetpoint', converter: (value, state) => (isValidNumber(value) && (state.attributes?.hvac_modes?.includes(HVACMode.HEAT_COOL) || state.attributes?.hvac_modes?.includes(HVACMode.HEAT)) ? Math.round(temp(value, HomeAssistant.hassConfig?.unit_system?.temperature) * 100) : null) },
{ domain: 'climate', with: 'current_temperature', clusterId: Thermostat.Cluster.id, attribute: 'localTemperature', converter: (value) => (isValidNumber(value) ? Math.round(temp(value, HomeAssistant.hassConfig?.unit_system?.temperature) * 100) : null) },
{ domain: 'valve', with: 'current_position', clusterId: ValveConfigurationAndControl.Cluster.id, attribute: 'currentLevel', converter: (value) => (isValidNumber(value, 0, 100) ? Math.round(value) : null) },
];
export const hassDomainConverter = [
{ domain: 'switch', deviceType: onOffOutlet, clusterId: OnOff.Cluster.id },
{ domain: 'light', deviceType: onOffLight, clusterId: OnOff.Cluster.id },
{ domain: 'light', withAttribute: 'brightness', deviceType: dimmableLight, clusterId: LevelControl.Cluster.id },
{ domain: 'light', withAttribute: 'color_temp_kelvin', deviceType: colorTemperatureLight, clusterId: ColorControl.Cluster.id },
{ domain: 'light', withAttribute: 'hs_color', deviceType: extendedColorLight, clusterId: ColorControl.Cluster.id },
{ domain: 'light', withAttribute: 'rgb_color', deviceType: extendedColorLight, clusterId: ColorControl.Cluster.id },
{ domain: 'light', withAttribute: 'xy_color', deviceType: extendedColorLight, clusterId: ColorControl.Cluster.id },
{ domain: 'lock', deviceType: doorLockDevice, clusterId: DoorLock.Cluster.id },
{ domain: 'fan', deviceType: fanDevice, clusterId: FanControl.Cluster.id },
{ domain: 'cover', deviceType: coverDevice, clusterId: WindowCovering.Cluster.id },
{ domain: 'climate', deviceType: thermostatDevice, clusterId: Thermostat.Cluster.id },
{ domain: 'valve', deviceType: waterValve, clusterId: ValveConfigurationAndControl.Cluster.id },
{ domain: 'vacuum', deviceType: roboticVacuumCleaner, clusterId: RvcRunMode.Cluster.id },
{ domain: 'vacuum', deviceType: roboticVacuumCleaner, clusterId: RvcCleanMode.Cluster.id },
{ domain: 'vacuum', deviceType: roboticVacuumCleaner, clusterId: RvcOperationalState.Cluster.id },
{ domain: 'remote', deviceType: onOffOutlet, clusterId: OnOff.Cluster.id },
{ domain: 'input_select', deviceType: modeSelect, clusterId: ModeSelect.Cluster.id },
{ domain: 'select', deviceType: modeSelect, clusterId: ModeSelect.Cluster.id },
{ domain: 'media_player', deviceType: basicVideoPlayer, clusterId: MediaPlayback.Cluster.id },
{ domain: 'sensor', deviceType: null, clusterId: null },
{ domain: 'binary_sensor', deviceType: null, clusterId: null },
];
export const hassDomainSensorsConverter = [
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'battery', endpoint: '', deviceType: powerSource, clusterId: PowerSource.Cluster.id, attribute: 'batPercentRemaining', converter: (value) => (isValidNumber(value, 0, 100) ? Math.round((value) * 2) : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'voltage', endpoint: '', deviceType: powerSource, clusterId: PowerSource.Cluster.id, attribute: 'batVoltage', converter: (value, unit) => (isValidNumber(value, 0, 100000) ? Math.round(value * (unit === 'V' ? 1000 : 1)) : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'temperature', deviceType: temperatureSensor, clusterId: TemperatureMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, unit) => (isValidNumber(value, unit === '°F' ? -148 : -100, unit === '°F' ? 212 : 100) ? Math.round(temp(value, unit) * 100) : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'humidity', deviceType: humiditySensor, clusterId: RelativeHumidityMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value) => (isValidNumber(value, 0, 100) ? Math.round((value) * 100) : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'pressure', deviceType: pressureSensor, clusterId: PressureMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, unit) => (isValidNumber(value, 1) ? pressure(value, unit) : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'atmospheric_pressure', deviceType: pressureSensor, clusterId: PressureMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, unit) => (isValidNumber(value, 1) ? pressure(value, unit) : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'illuminance', deviceType: lightSensor, clusterId: IlluminanceMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value) => (isValidNumber(value) ? Math.round(Math.max(Math.min(10000 * Math.log10(value), 0xfffe), 0)) : null) },
{ domain: 'sensor', withStateClass: 'total_increasing', withDeviceClass: 'energy', endpoint: 'PowerEnergy', deviceType: electricalSensor, clusterId: ElectricalEnergyMeasurement.Cluster.id, attribute: 'cumulativeEnergyImported', converter: (value, unit) => (isValidNumber(value) && unit === 'kWh' ? { energy: value * 1000000 } : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'power', endpoint: 'PowerEnergy', deviceType: electricalSensor, clusterId: ElectricalPowerMeasurement.Cluster.id, attribute: 'activePower', converter: (value, unit) => (isValidNumber(value) && unit === 'W' ? (value) * 1000 : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'current', endpoint: 'PowerEnergy', deviceType: electricalSensor, clusterId: ElectricalPowerMeasurement.Cluster.id, attribute: 'activeCurrent', converter: (value, unit) => (isValidNumber(value) && unit === 'A' ? (value) * 1000 : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'voltage', endpoint: 'PowerEnergy', deviceType: electricalSensor, clusterId: ElectricalPowerMeasurement.Cluster.id, attribute: 'voltage', converter: (value, unit) => (isValidNumber(value) && unit === 'V' ? (value) * 1000 : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'aqi', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: AirQuality.Cluster.id, attribute: 'airQuality', converter: (value, unit) => aqi(value, unit) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'volatile_organic_compounds', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: TotalVolatileOrganicCompoundsConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'volatile_organic_compounds_parts', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: TotalVolatileOrganicCompoundsConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'carbon_dioxide', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: CarbonDioxideConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'carbon_monoxide', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: CarbonMonoxideConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'nitrogen_dioxide', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: NitrogenDioxideConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'ozone', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: OzoneConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'formaldehyde', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: FormaldehydeConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'radon', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: RadonConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'pm1', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: Pm1ConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'pm25', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: Pm25ConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'pm2_5', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: Pm25ConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'pm2.5', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: Pm25ConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
{ domain: 'sensor', withStateClass: 'measurement', withDeviceClass: 'pm10', endpoint: 'AirQuality', deviceType: airQualitySensor, clusterId: Pm10ConcentrationMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value, _unit) => (isValidNumber(value, 0) ? value : null) },
];
export const hassDomainBinarySensorsConverter = [
{ domain: 'binary_sensor', withDeviceClass: 'battery', endpoint: '', deviceType: powerSource, clusterId: PowerSource.Cluster.id, attribute: 'batChargeLevel', converter: (value) => (value === 'off' ? 0 : 2) },
{ domain: 'binary_sensor', withDeviceClass: 'window', deviceType: contactSensor, clusterId: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => (value === 'on' ? false : true) },
{ domain: 'binary_sensor', withDeviceClass: 'door', deviceType: contactSensor, clusterId: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => (value === 'on' ? false : true) },
{ domain: 'binary_sensor', withDeviceClass: 'garage_door', deviceType: contactSensor, clusterId: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => (value === 'on' ? false : true) },
{ domain: 'binary_sensor', withDeviceClass: 'vibration', deviceType: contactSensor, clusterId: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => (value === 'on' ? false : true) },
{ domain: 'binary_sensor', withDeviceClass: 'cold', deviceType: waterFreezeDetector, clusterId: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => (value === 'on' ? true : false) },
{ domain: 'binary_sensor', withDeviceClass: 'moisture', deviceType: waterLeakDetector, clusterId: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => (value === 'on' ? true : false) },
{ domain: 'binary_sensor', withDeviceClass: 'occupancy', deviceType: occupancySensor, clusterId: OccupancySensing.Cluster.id, attribute: 'occupancy', converter: (value) => ({ occupied: value === 'on' ? true : false }) },
{ domain: 'binary_sensor', withDeviceClass: 'motion', deviceType: occupancySensor, clusterId: OccupancySensing.Cluster.id, attribute: 'occupancy', converter: (value) => ({ occupied: value === 'on' ? true : false }) },
{ domain: 'binary_sensor', withDeviceClass: 'presence', deviceType: occupancySensor, clusterId: OccupancySensing.Cluster.id, attribute: 'occupancy', converter: (value) => ({ occupied: value === 'on' ? true : false }) },
{ domain: 'binary_sensor', withDeviceClass: 'smoke', deviceType: smokeCoAlarm, clusterId: SmokeCoAlarm.Cluster.id, attribute: 'smokeState', converter: (value) => (value === 'on' ? SmokeCoAlarm.AlarmState.Critical : SmokeCoAlarm.AlarmState.Normal) },
{ domain: 'binary_sensor', withDeviceClass: 'carbon_monoxide', deviceType: smokeCoAlarm, clusterId: SmokeCoAlarm.Cluster.id, attribute: 'coState', converter: (value) => (value === 'on' ? SmokeCoAlarm.AlarmState.Critical : SmokeCoAlarm.AlarmState.Normal) },
];
export const hassDomainEventConverter = [
{ hassEventType: 'single', matterbridgeEventType: 'Single' },
{ hassEventType: 'single_push', matterbridgeEventType: 'Single' },
{ hassEventType: 'press', matterbridgeEventType: 'Single' },
{ hassEventType: 'initial_press', matterbridgeEventType: 'Single' },
{ hassEventType: 'multi_press_1', matterbridgeEventType: 'Single' },
{ hassEventType: 'double', matterbridgeEventType: 'Double' },
{ hassEventType: 'double_press', matterbridgeEventType: 'Double' },
{ hassEventType: 'double_push', matterbridgeEventType: 'Double' },
{ hassEventType: 'multi_press_2', matterbridgeEventType: 'Double' },
{ hassEventType: 'long', matterbridgeEventType: 'Long' },
{ hassEventType: 'long_push', matterbridgeEventType: 'Long' },
{ hassEventType: 'long_press', matterbridgeEventType: 'Long' },
{ hassEventType: 'hold_press', matterbridgeEventType: 'Long' },
];
export const hassCommandConverter = [
{ command: 'on', domain: 'switch', service: 'turn_on' },
{ command: 'off', domain: 'switch', service: 'turn_off' },
{ command: 'toggle', domain: 'switch', service: 'toggle' },
{ command: 'on', domain: 'light', service: 'turn_on' },
{ command: 'off', domain: 'light', service: 'turn_off' },
{ command: 'toggle', domain: 'light', service: 'toggle' },
{ command: 'moveToLevel', domain: 'light', service: 'turn_on', converter: (request) => { return { brightness: Math.round(request.level / 254 * 255) }; } },
{ command: 'moveToLevelWithOnOff', domain: 'light', service: 'turn_on', converter: (request) => { return { brightness: Math.round(request.level / 254 * 255) }; } },
{ command: 'moveToColorTemperature', domain: 'light', service: 'turn_on', converter: (request, attributes, state) => { return { color_temp_kelvin: state && state.attributes.min_color_temp_kelvin && state.attributes.max_color_temp_kelvin ? clamp(miredsToKelvin(request.colorTemperatureMireds, 'floor'), state.attributes.min_color_temp_kelvin, state.attributes.max_color_temp_kelvin) : miredsToKelvin(request.colorTemperatureMireds, 'floor') }; } },
{ command: 'moveToColor', domain: 'light', service: 'turn_on', converter: (request) => { return { xy_color: convertMatterXYToHA(request.colorX, request.colorY) }; } },
{ command: 'moveToHue', domain: 'light', service: 'turn_on', converter: (request, attributes) => { return { hs_color: [Math.round(request.hue / 254 * 360), Math.round(attributes.currentSaturation.value / 254 * 100)] }; } },
{ command: 'moveToSaturation', domain: 'light', service: 'turn_on', converter: (request, attributes) => { return { hs_color: [Math.round(attributes.currentHue.value / 254 * 360), Math.round(request.saturation / 254 * 100)] }; } },
{ command: 'moveToHueAndSaturation', domain: 'light', service: 'turn_on', converter: (request) => { return { hs_color: [Math.round(request.hue / 254 * 360), Math.round(request.saturation / 254 * 100)] }; } },
{ command: 'lockDoor', domain: 'lock', service: 'lock' },
{ command: 'unlockDoor', domain: 'lock', service: 'unlock' },
{ command: 'upOrOpen', domain: 'cover', service: 'open_cover' },
{ command: 'downOrClose', domain: 'cover', service: 'close_cover' },
{ command: 'stopMotion', domain: 'cover', service: 'stop_cover' },
{ command: 'goToLiftPercentage', domain: 'cover', service: 'set_cover_position', converter: (request) => { return { position: Math.round(100 - request.liftPercent100thsValue / 100) }; } },
{ command: 'open', domain: 'valve', service: 'set_valve_position', converter: (request) => { return { position: Math.round(request.targetLevel) }; } },
{ command: 'close', domain: 'valve', service: 'close_valve' },
{ command: 'pause', domain: 'vacuum', service: 'pause' },
{ command: 'resume', domain: 'vacuum', service: 'start' },
{ command: 'goHome', domain: 'vacuum', service: 'return_to_base' },
{ command: 'changeToMode', domain: 'vacuum', service: 'start' },
{ command: 'on', domain: 'remote', service: 'turn_on' },
{ command: 'off', domain: 'remote', service: 'turn_off' },
{ command: 'changeToMode', domain: 'input_select', service: 'select_option', converter: (request, attributes, state) => { return getSelectOptionFromMode(request, state); } },
{ command: 'changeToMode', domain: 'select', service: 'select_option', converter: (request, attributes, state) => { return getSelectOptionFromMode(request, state); } },
{ command: 'on', domain: 'media_player', service: 'turn_on' },
{ command: 'off', domain: 'media_player', service: 'turn_off' },
{ command: 'play', domain: 'media_player', service: 'media_play' },
{ command: 'pause', domain: 'media_player', service: 'media_pause' },
{ command: 'stop', domain: 'media_player', service: 'media_stop' },
{ command: 'previous', domain: 'media_player', service: 'media_previous_track' },
{ command: 'next', domain: 'media_player', service: 'media_next_track' },
];
export const hassSubscribeConverter = [
{ domain: 'fan', service: 'turn_on', with: 'preset_mode', clusterId: FanControl.Cluster.id, attribute: 'fanMode', converter: (value) => {
if (isValidNumber(value, FanControl.FanMode.Low, FanControl.FanMode.Smart)) {
if (value === FanControl.FanMode.Low)
return 'low';
else if (value === FanControl.FanMode.Medium)
return 'medium';
else if (value === FanControl.FanMode.High)
return 'high';
else
return 'auto';
}
else {
return null;
}
} },
{ domain: 'fan', service: 'turn_on', with: 'percentage', clusterId: FanControl.Cluster.id, attribute: 'percentSetting', converter: (value) => { return value === 0 ? null : value; } },
{ domain: 'fan', service: 'set_direction', with: 'direction', clusterId: FanControl.Cluster.id, attribute: 'airflowDirection', converter: (value) => { return value === FanControl.AirflowDirection.Forward ? 'forward' : 'reverse'; } },
{ domain: 'fan', service: 'oscillate', with: 'oscillating', clusterId: FanControl.Cluster.id, attribute: 'rockSetting', converter: (value) => { return value.rockRound === true; } },
{ domain: 'climate', service: 'set_hvac_mode', with: 'hvac_mode', clusterId: Thermostat.Cluster.id, attribute: 'systemMode', converter: (value) => {
if (isValidNumber(value, Thermostat.SystemMode.Off, Thermostat.SystemMode.Heat)) {
if (value === Thermostat.SystemMode.Auto)
return 'heat_cool';
else if (value === Thermostat.SystemMode.Cool)
return 'cool';
else if (value === Thermostat.SystemMode.Heat)
return 'heat';
else
return null;
}
else {
return null;
}
} },
{ domain: 'climate', service: 'set_temperature', with: 'temperature', clusterId: Thermostat.Cluster.id, attribute: 'occupiedHeatingSetpoint', converter: (value) => { return tempToFahrenheit(value / 100); } },
{ domain: 'climate', service: 'set_temperature', with: 'temperature', clusterId: Thermostat.Cluster.id, attribute: 'occupiedCoolingSetpoint', converter: (value) => { return tempToFahrenheit(value / 100); } },
];