UNPKG

homebridge-plugin-samsung-air-conditioner2878

Version:

Homebridge plugin for controlling Samsung Air Conditioners working on port 2878

321 lines (257 loc) 11.4 kB
const API = require('./air-conditioner-api'); const State = require('./state'); const OpMode = require('./op-mode'); const Direction = require('./direction'); const WindLevel = require('./wind-level'); const mapper = require('./mapper'); var Service, Characteristic; module.exports = function (homebridge) { Service = homebridge.hap.Service; Characteristic = homebridge.hap.Characteristic; mapper.setCharacteristic(Characteristic); homebridge.registerAccessory("homebridge-plugin-samsung-air-conditioner2878", "Samsung Air Conditioner", AirConditioner); }; function AirConditioner(log, config) { this.log = log; this.name = config["name"]; this.duid = config["mac"].replace(/:/g, '').replace(/\-/g, ''); this.api = new API( config["ip_address"], config["skip_certificate"] === true, this.duid, config["token"], log, config["log_socket_activity"] === true, config["keep_alive"] ); this.debug = config.debug || false; // Enable debug logging if set in config.json this.currentDeviceState = {}; // Set initial state. Done only to not deal with nulls if getters are called before first connection. this.currentDeviceState[State.Active] = 'Off'; this.currentDeviceState[State.TempNow] = 20; this.currentDeviceState[State.TempSet] = 16; this.currentDeviceState[State.OpMode] = OpMode.Cool; this.currentDeviceState[State.Direction] = Direction.Fixed; this.currentDeviceState[State.WindLevel] = WindLevel.Auto; }; AirConditioner.prototype = { getServices: function () { this.api.connect(); this.api .on('stateUpdate', this.updateState.bind(this)); this.acService = new Service.HeaterCooler(this.name); // ACTIVE STATE this.acService .getCharacteristic(Characteristic.Active) .on('get', this.getActive.bind(this)) .on('set', this.setActive.bind(this)); // CURRENT TEMPERATURE this.acService .getCharacteristic(Characteristic.CurrentTemperature) .setProps({ minValue: 0, maxValue: 100, minStep: 1 }) .on('get', this.getCurrentTemperature.bind(this)); // TARGET TEMPERATURE this.acService .getCharacteristic(Characteristic.CoolingThresholdTemperature) .setProps({ minValue: 16, maxValue: 30, minStep: 1 }) .on('get', this.getTargetTemperature.bind(this)) .on('set', this.setTargetTemperature.bind(this)); this.acService .getCharacteristic(Characteristic.HeatingThresholdTemperature) .setProps({ minValue: 16, maxValue: 30, minStep: 1 }) .on('get', this.getTargetTemperature.bind(this)) .on('set', this.setTargetTemperature.bind(this)); // TARGET STATE this.acService .getCharacteristic(Characteristic.TargetHeaterCoolerState) .on('get', this.getTargetState.bind(this)) .on('set', this.setTargetState.bind(this)); // CURRENT STATE this.acService .getCharacteristic(Characteristic.CurrentHeaterCoolerState) .on('get', this.getCurrentState.bind(this)); // SWING MODE this.acService .getCharacteristic(Characteristic.SwingMode) .on('get', this.getSwingMode.bind(this)) .on('set', this.setSwingMode.bind(this)); // ROTATION SPEED this.acService .getCharacteristic(Characteristic.RotationSpeed) .on('get', this.getRotationSpeed.bind(this)) .on('set', this.setRotationSpeed.bind(this)); const package = require('../package.json'); const informationService = new Service.AccessoryInformation(); informationService .setCharacteristic(Characteristic.SerialNumber, this.duid) .setCharacteristic(Characteristic.Manufacturer, package.author) .setCharacteristic(Characteristic.Model, package.name) .setCharacteristic(Characteristic.FirmwareRevision, package.version); return [this.acService, informationService]; }, // GETTERS getActive: function (callback) { if (this.debug) { this.log('Getting active...'); } const power = this.currentDeviceState[State.Power]; const isActive = power === 'On'; callback(null, isActive); }, getCurrentTemperature: function (callback) { if (this.debug) { this.log('Getting current temperature...'); } const currentTemperature = this.currentDeviceState[State.TempNow]; callback(null, currentTemperature); }, getTargetTemperature: function (callback) { if (this.debug) { this.log('Getting target temperature...'); } const targetTemperature = this.currentDeviceState[State.TempSet]; callback(null, targetTemperature); }, getTargetState: function (callback) { if (this.debug) { this.log('Getting target state...'); } const opMode = this.currentDeviceState[State.OpMode]; const targetState = mapper.targetStateFromOpMode(opMode); callback(null, targetState); }, getCurrentState: function (callback) { callback(null, this.currentHeaterCoolerState()); }, getSwingMode: function (callback) { if (this.debug) { this.log('Getting swing mode...'); } const direction = this.currentDeviceState[State.Direction]; const isOscillating = direction === Direction.SwingUpDown callback(null, isOscillating); }, getRotationSpeed: function(callback) { if (this.debug) { this.log('Getting rotation speed...'); } const windLevel = this.currentDeviceState[State.WindLevel]; const rotationSpeed = mapper.rotationSpeedFromWindLevel(windLevel); callback(null, rotationSpeed); }, // SETTERS setActive: function (isActive, callback) { this.log('Setting active:', isActive); this.api.deviceControl(State.Power, isActive ? "On" : "Off", function (err) { if (!!err) this.log('Active set'); callback(err); }.bind(this)); }, setTargetTemperature: function (temperature, callback) { this.log('Setting target temperature:', temperature); this.api.deviceControl(State.TempSet, temperature, function (err) { if (!!err) this.log('Target temperature set'); callback(err); }.bind(this)); }, setTargetState: function (state, callback) { this.log('Setting target state:', state); this.api.deviceControl(State.OpMode, mapper.opModeFromTargetState(state), function (err) { if (!!err) this.log('Target state set'); callback(err); }.bind(this)); }, setSwingMode: function (enabled, callback) { this.log('Setting swing mode:', enabled); this.api.deviceControl(State.Direction, enabled ? Direction.SwingUpDown : Direction.Fixed, function (err) { if (!!err) this.log('Swing mode set'); callback(err); }.bind(this)); }, setRotationSpeed: function(speed, callback) { this.log('Setting rotation speed:', speed); this.api.deviceControl(State.WindLevel, mapper.windLevelFromRotationSpeed(speed), function(err) { if (!!err) this.log('Rotation speed set'); callback(err); }.bind(this)); }, currentHeaterCoolerState: function() { const currentTemperature = this.currentDeviceState[State.TempNow]; const targetTemperature = this.currentDeviceState[State.TempSet]; const opMode = this.currentDeviceState[State.OpMode]; var state; if (opMode === OpMode.Cool) { if(currentTemperature > targetTemperature) { state = Characteristic.CurrentHeaterCoolerState.COOLING; } else { state = Characteristic.CurrentHeaterCoolerState.IDLE; } } else if (opMode === OpMode.Heat) { if(currentTemperature < targetTemperature) { state = Characteristic.CurrentHeaterCoolerState.HEATING; } else { state = Characteristic.CurrentHeaterCoolerState.IDLE; } } else if (opMode == OpMode.Auto) { if(currentTemperature > targetTemperature) { state = Characteristic.CurrentHeaterCoolerState.COOLING; } else if(currentTemperature < targetTemperature) { state = Characteristic.CurrentHeaterCoolerState.HEATING; } else { state = Characteristic.CurrentHeaterCoolerState.IDLE; } } else { // Dry, Wind // Not the best state for Dry/Wind setting, but not sure what // else to use here. state = Characteristic.CurrentHeaterCoolerState.IDLE; } return state; }, updateState: function (stateUpdate) { if (stateUpdate && Object.keys(stateUpdate).length > 0) { this.log("State updated:", JSON.stringify(stateUpdate, Object.values(State))); } // Merge state update into current device state this.currentDeviceState = Object.assign({}, this.currentDeviceState, stateUpdate); // Update characteristics which correspond to updated states Object.keys(stateUpdate || {}).forEach(function(key) { this.updateCharacteristic(key, stateUpdate[key]); }.bind(this)); this.updateDerivedCharacteristics(); }, updateCharacteristic: function(name, value) { var characteristic; var mappedValue; switch(name) { case State.Power: characteristic = Characteristic.Active; mappedValue = value === "On"; break; case State.TempNow: characteristic = Characteristic.CurrentTemperature; mappedValue = value; break; case State.OpMode: characteristic = Characteristic.TargetHeaterCoolerState; mappedValue = mapper.targetStateFromOpMode(value); break; case State.Direction: characteristic = Characteristic.SwingMode; mappedValue = value === Direction.SwingUpDown; break; case State.WindLevel: characteristic = Characteristic.RotationSpeed; mappedValue = mapper.rotationSpeedFromWindLevel(value); break; } if(!!characteristic) { this.acService.getCharacteristic(characteristic).updateValue(mappedValue); } }, updateDerivedCharacteristics: function() { const targetTemperature = this.currentDeviceState[State.TempSet]; this.acService.getCharacteristic(Characteristic.CurrentHeaterCoolerState).updateValue(this.currentHeaterCoolerState()); this.acService.getCharacteristic(Characteristic.HeatingThresholdTemperature).updateValue(targetTemperature); this.acService.getCharacteristic(Characteristic.CoolingThresholdTemperature).updateValue(targetTemperature); }, };