UNPKG

homebridge-melcloud-control

Version:

Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.

501 lines (468 loc) • 26.5 kB
import { promises as fsPromises } from 'fs'; import { Agent } from 'https'; import axios from 'axios'; import EventEmitter from 'events'; import ImpulseGenerator from './impulsegenerator.js'; import { ApiUrls } from './constants.js'; class MelCloudAtw extends EventEmitter { constructor(config) { super(); this.devicesFile = config.devicesFile; this.deviceId = config.deviceId; this.enableDebugMode = config.enableDebugMode; //set default values this.deviceState = {}; this.axiosInstancePost = axios.create({ method: 'POST', baseURL: ApiUrls.BaseURL, timeout: 25000, headers: { 'X-MitsContextKey': config.contextKey, 'content-type': 'application/json' }, withCredentials: true, httpsAgent: new Agent({ keepAlive: false, rejectUnauthorized: false }) }); this.impulseGenerator = new ImpulseGenerator(); this.impulseGenerator.on('checkState', async () => { try { await this.checkState(); } catch (error) { this.emit('error', `Impulse generator error: ${error}`); }; }).on('state', (state) => { const emitState = state ? this.emit('success', `Impulse generator started`) : this.emit('warn', `Impulse generator stopped`); }); }; async checkState() { try { //read device info from file const devicesData = await this.readData(this.devicesFile); if (!Array.isArray(devicesData)) { this.emit('warn', `Device data not found`); return null; } const deviceData = devicesData.find(device => device.DeviceID === this.deviceId); const debug = this.enableDebugMode ? this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`) : false; //device info const deviceId = deviceData.DeviceID; const deviceName = deviceData.DeviceName; const buildingId = deviceData.BuildingID; const buildingName = deviceData.BuildingName; const floorId = deviceData.FloorID; const floorName = deviceData.FloorName; const areaId = deviceData.AreaID; const areaName = deviceData.AreaName; const imageId = deviceData.ImageID; const installationDate = deviceData.InstallationDate; const lastServiceDate = deviceData.LastServiceDate; //presets const presets = deviceData.Presets ?? []; //device info const ownerId = deviceData.OwnerID; const ownerName = deviceData.OwnerName; const ownerEmail = deviceData.OwnerEmail; const accessLevel = deviceData.AccessLevel; const directAccess = deviceData.DirectAccess; const endDate = deviceData.EndDate; const zone1Name = deviceData.Zone1Name; const zone2Name = deviceData.Zone2Name; const minTemperature = deviceData.MinTemperature; const maxTemperature = deviceData.MaxTemperature; const hideVaneControls = deviceData.HideVaneControls; const hideDryModeControl = deviceData.HideDryModeControl; const hideRoomTemperature = deviceData.HideRoomTemperature; const hideSupplyTemperature = deviceData.HideSupplyTemperature; const hideOutdoorTemperature = deviceData.HideOutdoorTemperature; const buildingCountry = deviceData.BuildingCountry; const ownerCountry = deviceData.OwnerCountry; const adaptorType = deviceData.AdaptorType; const linkedDevice = deviceData.LinkedDevice; const type = deviceData.Type; const macAddress = deviceData.MacAddress; const serialNumber = deviceData.SerialNumber ?? 'Undefined'; //device const device = deviceData.Device ?? {}; const listHistory24Formatters = Array.isArray(device.ListHistory24Formatters) ? device.ListHistory24Formatters : []; const listHistory24FormattersCount = listHistory24Formatters.length; const pCycleActual = device.PCycleActual; const errorMessages = device.ErrorMessages; const deviceType = device.DeviceType; const canHeat = device.CanHeat; const canCool = device.CanCool; const hasHotWaterTank = device.HasHotWaterTank ?? false; const fTCVersion = device.FTCVersion; const fTCRevision = device.FTCRevision; const lastFTCVersion = device.LastFTCVersion; const lastFTCRevision = device.LastFTCRevision; const fTCModel = device.FTCModel; const refridgerentAddress = device.RefridgerentAddress; // TODO: Typo in MELCloud service? const dipSwitch1 = device.DipSwitch1; const dipSwitch2 = device.DipSwitch2; const dipSwitch3 = device.DipSwitch3; const minTempHeat = device.DipSwitch4; const dipSwitch5 = device.DipSwitch5; const dipSwitch6 = device.DipSwitch6; const hasThermostatZone1 = device.HasThermostatZone1; const hasThermostatZone2 = device.HasThermostatZone2; const temperatureIncrement = device.TemperatureIncrement; const defrostMode = device.DefrostMode; const heatPumpFrequency = device.HeatPumpFrequency; const maxSetTemperature = device.MaxSetTemperature; const minSetTemperature = device.MinSetTemperature; const roomTemperatureZone1 = device.RoomTemperatureZone1; const roomTemperatureZone2 = device.RoomTemperatureZone2; const outdoorTemperature = device.OutdoorTemperature; const flowTemperature = device.FlowTemperature; const flowTemperatureZone1 = device.FlowTemperatureZone1; const flowTemperatureZone2 = device.FlowTemperatureZone2; const flowTemperatureBoiler = device.FlowTemperatureBoiler; const returnTemperature = device.ReturnTemperature; const returnTemperatureZone1 = device.ReturnTemperatureZone1; const returnTemperatureZone2 = device.ReturnTemperatureZone2; const returnTemperatureBoiler = device.ReturnTemperatureBoiler; const boilerStatus = device.BoilerStatus; const boosterHeater1Status = device.BoosterHeater1Status; const boosterHeater2Status = device.BoosterHeater2Status; const boosterHeater2PlusStatus = device.BoosterHeater2PlusStatus; const immersionHeaterStatus = device.ImmersionHeaterStatus; const waterPump1Status = device.WaterPump1Status; const waterPump2Status = device.WaterPump2Status; const waterPump3Status = device.WaterPump3Status; const valveStatus3Way = device.ValveStatus3Way; const valveStatus2Way = device.ValveStatus2Way; const waterPump4Status = device.WaterPump4Status; const valveStatus2Way2a = device.ValveStatus2Way2a; const valveStatus2Way2b = device.ValveStatus2Way2b; const tankWaterTemperature = device.TankWaterTemperature; const unitStatus = device.UnitStatus; const heatingFunctionEnabled = device.HeatingFunctionEnabled; const serverTimerEnabled = device.ServerTimerEnabled; const thermostatStatusZone1 = device.ThermostatStatusZone1; const thermostatStatusZone2 = device.ThermostatStatusZone2; const thermostatTypeZone1 = device.ThermostatTypeZone1; const thermostatTypeZone2 = device.ThermostatTypeZone2; const mixingTankWaterTemperature = device.MixingTankWaterTemperature; const condensingTemperature = device.CondensingTemperature; const effectiveFlags = device.EffectiveFlags; const lastEffectiveFlags = device.LastEffectiveFlags; const power = device.Power; const ecoHotWater = device.EcoHotWater; const operationMode = device.OperationMode; const operationModeZone1 = device.OperationModeZone1; const operationModeZone2 = device.OperationModeZone2; const setTemperatureZone1 = device.SetTemperatureZone1; const setTemperatureZone2 = device.SetTemperatureZone2; const setTankWaterTemperature = device.SetTankWaterTemperature; const targetHCTemperatureZone1 = device.TargetHCTemperatureZone1; const targetHCTemperatureZone2 = device.TargetHCTemperatureZone2; const forcedHotWaterMode = device.ForcedHotWaterMode; const holidayMode = device.HolidayMode; const prohibitHotWater = device.ProhibitHotWater; const prohibitHeatingZone1 = device.ProhibitHeatingZone1; const prohibitHeatingZone2 = device.ProhibitHeatingZone2; const prohibitCoolingZone1 = device.ProhibitCoolingZone1; const prohibitCoolingZone2 = device.ProhibitCoolingZone2; const serverTimerDesired = device.ServerTimerDesired; const secondaryZoneHeatCurve = device.SecondaryZoneHeatCurve; const setHeatFlowTemperatureZone1 = device.SetHeatFlowTemperatureZone1; const setHeatFlowTemperatureZone2 = device.SetHeatFlowTemperatureZone2; const setCoolFlowTemperatureZone1 = device.SetCoolFlowTemperatureZone1; const setCoolFlowTemperatureZone2 = device.SetCoolFlowTemperatureZone2; const thermostatTemperatureZone1 = device.ThermostatTemperatureZone1; const thermostatTemperatureZone2 = device.ThermostatTemperatureZone2; const dECCReport = device.DECCReport; const cSVReport1min = device.CSVReport1min; const zone2Master = device.Zone2Master; const dailyEnergyConsumedDate = device.DailyEnergyConsumedDate; const dailyEnergyProducedDate = device.DailyEnergyProducedDate; const currentEnergyConsumed = device.CurrentEnergyConsumed; const currentEnergyProduced = device.CurrentEnergyProduced; const currentEnergyMode = device.CurrentEnergyMode; const heatingEnergyConsumedRate1 = device.HeatingEnergyConsumedRate1; const heatingEnergyConsumedRate2 = device.HeatingEnergyConsumedRate2 const coolingEnergyConsumedRate1 = device.CoolingEnergyConsumedRate1; const coolingEnergyConsumedRate2 = device.CoolingEnergyConsumedRate2; const hotWaterEnergyConsumedRate1 = device.HotWaterEnergyConsumedRate1; const hotWaterEnergyConsumedRate2 = device.HotWaterEnergyConsumedRate2; const heatingEnergyProducedRate1 = device.HeatingEnergyProducedRate1; const heatingEnergyProducedRate2 = device.HeatingEnergyProducedRate2; const coolingEnergyProducedRate1 = device.CoolingEnergyProducedRate1; const coolingEnergyProducedRate2 = device.CoolingEnergyProducedRate2; const hotWaterEnergyProducedRate1 = device.HotWaterEnergyProducedRate1; const hotWaterEnergyProducedRate2 = device.HotWaterEnergyProducedRate2; const errorCode2Digit = device.ErrorCode2Digit; const sendSpecialFunctions = device.SendSpecialFunctions; const requestSpecialFunctions = device.RequestSpecialFunctions; const specialFunctionsState = device.SpecialFunctionsState; const pendingSendSpecialFunctions = device.PendingSendSpecialFunctions; const pendingRequestSpecialFunctions = device.PendingRequestSpecialFunctions; const hasSimplifiedZone2 = device.HasSimplifiedZone2; const canSetTankTemperature = device.CanSetTankTemperature; const canSetEcoHotWater = device.CanSetEcoHotWater; const hasEnergyConsumedMeter = device.HasEnergyConsumedMeter; const hasEnergyProducedMeter = device.HasEnergyProducedMeter; const canMeasureEnergyProduced = device.CanMeasureEnergyProduced; const canMeasureEnergyConsumed = device.CanMeasureEnergyConsumed; const zone1InRoomMode = device.Zone1InRoomMode; const zone2InRoomMode = device.Zone2InRoomMode; const zone1InHeatMode = device.Zone1InHeatMode; const zone2InHeatMode = device.Zone2InHeatMode; const zone1InCoolMode = device.Zone1InCoolMode; const zone2InCoolMode = device.Zone2InCoolMode; const allowDualRoomTemperature = device.AllowDualRoomTemperature; const isGeodan = device.IsGeodan; const hasEcoCuteSettings = device.HasEcoCuteSettings; const hasFTC45Settings = device.HasFTC45Settings; const hasFTC6Settings = device.HasFTC6Settings; const canEstimateEnergyUsage = device.CanEstimateEnergyUsage; const canUseRoomTemperatureCooling = device.CanUseRoomTemperatureCooling; const isFtcModelSupported = device.IsFtcModelSupported; const maxTankTemperature = device.MaxTankTemperature; const idleZone1 = device.IdleZone1 ?? false; const idleZone2 = device.IdleZone2 ?? false; const minPcycle = device.MinPcycle; const maxPcycle = device.MaxPcycle; const maxOutdoorUnits = device.MaxOutdoorUnits; const maxIndoorUnits = device.MaxIndoorUnits; const maxTemperatureControlUnits = device.MaxTemperatureControlUnits; //const deviceId = device.DeviceID; //const macAddress = device.MacAddress; //const serialNumber = device.SerialNumber; const timeZoneId = device.TimeZoneID; const diagnosticMode = device.DiagnosticMode; const diagnosticEndDate = device.DiagnosticEndDate; const expectedCommand = device.ExpectedCommand; const owner = device.Owner; const detectedCountry = device.DetectedCountry; //const adaptorType = device.AdaptorType; const firmwareDeployment = device.FirmwareDeployment; const firmwareUpdateAborted = device.FirmwareUpdateAborted; //const linkedDevice = device.LinkedDevice; const wifiSignalStrength = device.WifiSignalStrength; const wifiAdapterStatus = device.WifiAdapterStatus; const position = device.Position; const pCycle = device.PCycle; const recordNumMax = device.RecordNumMax; const lastTimeStamp = device.LastTimeStamp; const errorCode = device.ErrorCode; const hasError = device.HasError; const lastReset = device.LastReset; const flashWrites = device.FlashWrites; const scene = device.Scene; const sSLExpirationDate = device.SSLExpirationDate; const sPTimeout = device.SPTimeout; const passcode = device.Passcode; const serverCommunicationDisabled = device.ServerCommunicationDisabled; const consecutiveUploadErrors = device.ConsecutiveUploadErrors; const doNotRespondAfter = device.DoNotRespondAfter; const ownerRoleAccessLevel = device.OwnerRoleAccessLevel; //const ownerCountry = device.OwnerCountry; const hideEnergyReport = device.HideEnergyReport; const exceptionHash = device.ExceptionHash; const exceptionDate = device.ExceptionDate; const exceptionCount = device.ExceptionCount; const rate1StartTime = device.Rate1StartTime; const rate2StartTime = device.Rate2StartTime; const protocolVersion = device.ProtocolVersion; const unitVersion = device.UnitVersion; const firmwareAppVersion = device.FirmwareAppVersion?.toString() ?? 'Undefined'; const firmwareWebVersion = device.FirmwareWebVersion; const firmwareWlanVersion = device.FirmwareWlanVersion; const effectivePCycle = device.EffectivePCycle; const mqttFlags = device.MqttFlags; const hasErrorMessages = device.HasErrorMessages; const offline = device.Offline ?? false; const supportsHourlyEnergyReport = device.SupportsHourlyEnergyReport; const hasZone2 = device.HasZone2 ?? false; //units const units = Array.isArray(device.Units) ? device.Units : []; const unitsCount = units.length; const manufacturer = 'Mitsubishi'; //indoor let idIndoor = 0; let deviceIndoor = 0; let serialNumberIndoor = 'Undefined'; let modelNumberIndoor = 0; let modelIndoor = false; let typeIndoor = 0; //outdoor let idOutdoor = 0; let deviceOutdoor = 0; let serialNumberOutdoor = 'Undefined'; let modelNumberOutdoor = 0; let modelOutdoor = false; let typeOutdoor = 0; //units array for (const unit of units) { const unitId = unit.ID; const unitDevice = unit.Device; const unitSerialNumber = unit.SerialNumber ?? 'Undefined'; const unitModelNumber = unit.ModelNumber; const unitModel = unit.Model ?? false; const unitType = unit.UnitType; const unitIsIndoor = unit.IsIndoor ?? false; switch (unitIsIndoor) { case true: idIndoor = unitId; deviceIndoor = unitDevice; serialNumberIndoor = unitSerialNumber; modelNumberIndoor = unitModelNumber; modelIndoor = unitModel; typeIndoor = unitType; break; case false: idOutdoor = unitId; deviceOutdoor = unitDevice; serialNumberOutdoor = unitSerialNumber; modelNumberOutdoor = unitModelNumber; modelOutdoor = unitModel; typeOutdoor = unitType; break; } } //diagnostic //const diagnosticMode = deviceData.DiagnosticMode; //const diagnosticEndDate = deviceData.DiagnosticEndDate; const location = deviceData.Location; //const detectedCountry = deviceData.DetectedCountry; const registrations = deviceData.Registrations; const localIPAddress = deviceData.LocalIPAddress; const timeZone = deviceData.TimeZone; const registReason = deviceData.RegistReason; //const expectedCommand = deviceData.ExpectedCommand; const registRetry = deviceData.RegistRetry; const dateCreated = deviceData.DateCreated; //const firmwareDeployment = deviceData.FirmwareDeployment; //const firmwareUpdateAborted = deviceData.FirmwareUpdateAborted; //permissions const permissionCanSetForcedHotWater = deviceData.Permissions.CanSetForcedHotWater; const permissionCanSetOperationMode = deviceData.Permissions.CanSetOperationMode; const permissionCanSetPower = deviceData.Permissions.CanSetPower; const permissionCanSetTankWaterTemperature = deviceData.Permissions.CanSetTankWaterTemperature; const permissionCanSetEcoHotWater = deviceData.Permissions.CanSetEcoHotWater; const permissionCanSetFlowTemperature = deviceData.Permissions.CanSetFlowTemperature; const permissionCanSetTemperatureIncrementOverride = deviceData.Permissions.CanSetTemperatureIncrementOverride; //display info if units are not configured in MELCloud service if (unitsCount === 0) { this.emit('message', `Units are not configured in MELCloud service`); }; const deviceState = { Power: power, IdleZone1: idleZone1, IdleZone2: idleZone2, UnitStatus: unitStatus, SetTemperatureZone1: setTemperatureZone1, SetTemperatureZone2: setTemperatureZone2, RoomTemperatureZone1: roomTemperatureZone1, RoomTemperatureZone2: roomTemperatureZone2, OperationMode: operationMode, OperationModeZone1: operationModeZone1, OperationModeZone2: operationModeZone2, SetHeatFlowTemperatureZone1: setHeatFlowTemperatureZone1, SetHeatFlowTemperatureZone2: setHeatFlowTemperatureZone2, SetCoolFlowTemperatureZone1: setCoolFlowTemperatureZone1, SetCoolFlowTemperatureZone2: setCoolFlowTemperatureZone2, TankWaterTemperature: tankWaterTemperature, SetTankWaterTemperature: setTankWaterTemperature, ForcedHotWaterMode: forcedHotWaterMode, OutdoorTemperature: outdoorTemperature, TemperatureIncrement: temperatureIncrement, EcoHotWater: ecoHotWater, HolidayMode: holidayMode, ProhibitZone1: prohibitHeatingZone1, ProhibitZone2: prohibitHeatingZone2, ProhibitHotWater: prohibitHotWater } //check state changes const deviceDataHasNotChanged = JSON.stringify(deviceState) === JSON.stringify(this.deviceState); if (deviceDataHasNotChanged) { return; } this.deviceState = deviceState; //restFul this.emit('restFul', 'info', deviceData); this.emit('restFul', 'state', deviceData.Device); //mqtt this.emit('mqtt', 'Info', deviceData); this.emit('mqtt', 'State', deviceData.Device); //emit info this.emit('deviceInfo', manufacturer, modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion, hasHotWaterTank, hasZone2); //emit state this.emit('deviceState', deviceData); return deviceData; } catch (error) { throw new Error(`Check state error: ${error}`); }; }; async readData(path) { try { const savedData = await fsPromises.readFile(path) if (savedData.toString().trim().length === 0) { return null; } try { const data = JSON.parse(savedData); return data; } catch (error) { throw new Error(`Parse JSON error: ${error}`); } } catch (error) { throw new Error(`Read data error: ${error}`); } } async send(deviceData) { try { //prevent to set out of range temp const minTempZones = 0; const maxTempZones = 60; const minTempWaterTank = 16; const maxTempWaterTank = deviceData.Device.MaxTankTemperature ?? 70; deviceData.Device.SetTemperatureZone1 = deviceData.Device.SetTemperatureZone1 < minTempZones ? minTempZones : deviceData.Device.SetTemperatureZone1; deviceData.Device.SetTemperatureZone1 = deviceData.Device.SetTemperatureZone1 > maxTempZones ? maxTempZones : deviceData.Device.SetTemperatureZone1; deviceData.Device.SetTemperatureZone1 = deviceData.Device.SetTemperatureZone2 < minTempZones ? minTempZones : deviceData.Device.SetTemperatureZone2; deviceData.Device.SetTemperatureZone1 = deviceData.Device.SetTemperatureZone2 > maxTempZones ? maxTempZones : deviceData.Device.SetTemperatureZone2; deviceData.Device.SetTankWaterTemperature = deviceData.Device.SetTankWaterTemperature < minTempWaterTank ? minTempWaterTank : deviceData.Device.SetTankWaterTemperature; deviceData.Device.SetTankWaterTemperature = deviceData.Device.SetTankWaterTemperature > maxTempWaterTank ? maxTempWaterTank : deviceData.Device.SetTankWaterTemperature; const payload = { data: { DeviceID: deviceData.Device.DeviceID, EffectiveFlags: deviceData.Device.EffectiveFlags, Power: deviceData.Device.Power, SetTemperatureZone1: deviceData.Device.SetTemperatureZone1, SetTemperatureZone2: deviceData.Device.SetTemperatureZone2, OperationMode: deviceData.Device.OperationMode, OperationModeZone1: deviceData.Device.OperationModeZone1, OperationModeZone2: deviceData.Device.OperationModeZone2, SetHeatFlowTemperatureZone1: deviceData.Device.SetHeatFlowTemperatureZone1, SetHeatFlowTemperatureZone2: deviceData.Device.SetHeatFlowTemperatureZone2, SetCoolFlowTemperatureZone1: deviceData.Device.SetCoolFlowTemperatureZone1, SetCoolFlowTemperatureZone2: deviceData.Device.SetCoolFlowTemperatureZone2, SetTankWaterTemperature: deviceData.Device.SetTankWaterTemperature, ForcedHotWaterMode: deviceData.Device.ForcedHotWaterMode, EcoHotWater: deviceData.Device.EcoHotWater, HolidayMode: deviceData.Device.HolidayMode, ProhibitZone1: deviceData.Device.ProhibitHeatingZone1, ProhibitZone2: deviceData.Device.ProhibitHeatingZone2, ProhibitHotWater: deviceData.Device.ProhibitHotWater, HasPendingCommand: true } } await this.axiosInstancePost(ApiUrls.SetAtw, payload); this.updateData(deviceData); return true; } catch (error) { throw new Error(`Send data error: ${error}`); }; }; updateData(deviceData) { setTimeout(() => { this.emit('externalIntegrations', deviceData); this.emit('deviceState', deviceData); }, 500); } }; export default MelCloudAtw;