homebridge-daikin-local-platform
Version:
Homebridge platform plugin providing HomeKit support for Daikin air conditioners with local control
453 lines • 19.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DaikinLocalAPI = exports.DaikinDevice = exports.CLIMATE_FAN_SPEED_5 = exports.CLIMATE_FAN_SPEED_4 = exports.CLIMATE_FAN_SPEED_3 = exports.CLIMATE_FAN_SPEED_2 = exports.CLIMATE_FAN_SPEED_1 = exports.CLIMATE_FAN_SPEED_SLIENT = exports.CLIMATE_FAN_SPEED_AUTO = exports.CLIMATE_MODE_HUMIDIFY = exports.CLIMATE_MODE_DEHUMIDIFY = exports.CLIMATE_MODE_AUTO = exports.CLIMATE_MODE_COOLING = exports.CLIMATE_MODE_HEATING = exports.CLIMATE_MODE_FAN = void 0;
const axios_1 = __importDefault(require("axios"));
const axios_rate_limit_1 = __importDefault(require("axios-rate-limit"));
const const_1 = require("./const");
exports.CLIMATE_MODE_FAN = '0000';
exports.CLIMATE_MODE_HEATING = '0100';
exports.CLIMATE_MODE_COOLING = '0200';
exports.CLIMATE_MODE_AUTO = '0300';
exports.CLIMATE_MODE_DEHUMIDIFY = '0500';
exports.CLIMATE_MODE_HUMIDIFY = '0800';
exports.CLIMATE_FAN_SPEED_AUTO = '0A00';
exports.CLIMATE_FAN_SPEED_SLIENT = '0B00';
exports.CLIMATE_FAN_SPEED_1 = '0300';
exports.CLIMATE_FAN_SPEED_2 = '0400';
exports.CLIMATE_FAN_SPEED_3 = '0500';
exports.CLIMATE_FAN_SPEED_4 = '0600';
exports.CLIMATE_FAN_SPEED_5 = '0700';
const CLIMATE_OPERATE_ON = '00';
const CLIMATE_OPERATE_OFF = '01';
const CLIMATE_OPERATE_SETTING = '02';
const COMMAND_QUERY = '{"requests":[{"op":2,"to":"/dsiot/edge.adp_i?filter=pv"},{"op":2,"to":"/dsiot/edge.adp_d?filter=pv"},{"op":2,"to":"/dsiot/edge.adp_f?filter=pv"},{"op":2,"to":"/dsiot/edge.dev_i?filter=pv"},{"op":2,"to":"/dsiot/edge/adr_0100.dgc_status?filter=pv"}]}';
const COMMAND_QUERY_WITH_MD = '{"requests":[{"op":2,"to":"/dsiot/edge.adp_i?filter=pv"},{"op":2,"to":"/dsiot/edge.adp_d?filter=pv"},{"op":2,"to":"/dsiot/edge.adp_f?filter=pv"},{"op":2,"to":"/dsiot/edge.dev_i?filter=pv"},{"op":2,"to":"/dsiot/edge/adr_0100.dgc_status"},{"op":2,"to":"/dsiot/edge/adr_0200.dgc_status"}]}';
const http = (0, axios_rate_limit_1.default)(axios_1.default.create(), { maxRequests: 1, perMilliseconds: 500 });
class DaikinDevice {
constructor(IP, log) {
this.IP = IP;
this.log = log;
this._lastUpdateTimestamp = 0;
this._callback = null;
this._IP = IP;
this._Response = {};
this._log = log;
}
setCallback(callback) {
this._callback = callback;
}
async queryDevice(bForce = false) {
if (!bForce && (Date.now() - this._lastUpdateTimestamp) < const_1.SECONDS_BETWEEN_REQUEST) {
this.log.debug(`Daikin - queryDevice('${this._IP}'): Skipping query as last update was less than ${const_1.SECONDS_BETWEEN_REQUEST} micro seconds ago`);
return this._Response;
}
const response = await http.request({
method: 'post',
url: `http://${this._IP}${const_1.ENDPOINT}`,
headers: {
'Accept': 'application/json; charset=UTF-8',
'Content-Type': 'application/json',
'User-Agent': const_1.USER_AGENT,
},
data: COMMAND_QUERY_WITH_MD,
});
this._lastUpdateTimestamp = Date.now();
if (response.status === 200) {
//this.log.debug(`Daikin - queryDevice('${this._IP}'): Response: '${JSON.stringify(response.data)}'`);
return response.data;
}
this.log.debug(`Daikin - queryDevice('${this._IP}'): Error: Invalid response status code: '${response.status}'`);
return undefined;
}
async fetchDeviceStatus(bForce = false) {
const response = await this.queryDevice(bForce);
this._Response = response;
this.log.debug(`Daikin - fetchDeviceStatus(${bForce}): Name: ${this.getDeviceName()} MAC:${this.getMacAddress()} Power:${this.getPowerStatus()} Temp:${this.getIndoorTemperature()} Humidity:${this.getIndoorHumidity()} Target Temp:${this.getTargetTemperature()}' Mode:${this.getOperationModeName()} FanSpeed:${this.getFanSpeedName()} `);
if (this._callback) {
this._callback(this);
}
return true;
}
async setShowSSID(bShow) {
this.log.debug(`Daikin - setShowSSID(${bShow}): Name: ${this.getDeviceName()}`);
const command = { "requests": [{ "op": 3, "to": "/dsiot/edge.adp_d", "pc": { "pn": "adp_d", "pch": [{ "pn": "disp_ssid", "pv": bShow ? 0 : 1 }] } }] };
const response = await http.request({
method: 'post',
url: `http://${this._IP}${const_1.ENDPOINT}`,
headers: {
'Accept': 'application/json; charset=UTF-8',
'Content-Type': 'application/json',
'User-Agent': const_1.USER_AGENT,
},
data: JSON.stringify(command),
});
return response.status === 200;
}
getMacAddress() {
return this.extractValue(this._Response, '/dsiot/edge.adp_i', 'mac');
}
getSSID() {
return this.extractValue(this._Response, '/dsiot/edge.adp_i', 'ssid');
}
getDeviceName() {
return this.extractValue(this._Response, '/dsiot/edge.adp_d', 'name');
}
getDeviceReg() {
return this.extractValue(this._Response, '/dsiot/edge.adp_i', 'reg');
}
getDeviceType() {
return this.extractValue(this._Response, '/dsiot/edge.dev_i', 'type') + this.extractValue(this._Response, '/dsiot/edge.adp_i', 'enlv');
}
getFirmwareVersion() {
return this.extractValue(this._Response, '/dsiot/edge.adp_i', 'ver');
}
getDeviceIP() {
return this._IP;
}
getPowerStatus() {
return this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_A002/p_01') === '01';
}
getIndoorTemperature() {
return parseInt(this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_A00B/p_01'), 16);
}
getIndoorHumidity() {
return parseInt(this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_A00B/p_02'), 16);
}
//0000:fan 0100:heating 0200:cooling 0300:auto 0500:dehumidify
getOperationMode() {
return this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3001/p_01');
}
getOperationModeName() {
const mode = this.getOperationMode();
if (mode === exports.CLIMATE_MODE_FAN) {
return 'Fan';
}
else if (mode === exports.CLIMATE_MODE_DEHUMIDIFY) {
return 'Dehumidify';
}
else if (mode === exports.CLIMATE_MODE_AUTO) {
return 'Auto';
}
else if (mode === exports.CLIMATE_MODE_HEATING) {
return 'Heating';
}
else if (mode === exports.CLIMATE_MODE_COOLING) {
return 'Cooling';
}
return 'Unknown';
}
getTargetTemperatureWithMode(mode) {
if (mode === exports.CLIMATE_MODE_HEATING) {
return parseInt(this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3001/p_03'), 16) / 2.0;
}
else if (mode === exports.CLIMATE_MODE_COOLING) {
return parseInt(this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3001/p_02'), 16) / 2.0;
}
else if (mode === exports.CLIMATE_MODE_AUTO) {
return parseInt(this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3001/p_1D'), 16) / 2.0;
}
this.log.debug(`Daikin - getTargetTemperature(): Error: Invalid mode: '${mode}'`);
return 0;
}
getTargetTemperature() {
const mode = this.getOperationMode();
return this.getTargetTemperatureWithMode(mode);
}
getTargetTemperatureRange() {
const mode = this.getOperationMode();
let pn = 'p_02';
if (mode === exports.CLIMATE_MODE_HEATING) {
pn = 'p_03';
}
else if (mode === exports.CLIMATE_MODE_COOLING) {
pn = 'p_02';
}
else if (mode === exports.CLIMATE_MODE_AUTO) {
pn = 'p_1D';
}
else {
this.log.debug(`Daikin - getTargetTemperatureRange(): Error: Invalid mode: '${mode}'`);
return [0, 0];
}
const md = this.extractObject(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3001/' + pn);
const min = md ? parseInt(md['md']['mi'], 16) / 2.0 : 0;
const max = md ? parseInt(md['md']['mx'], 16) / 2.0 : 0;
return [min, max];
}
getCoolingThresholdTemperatureRange() {
const md = this.extractObject(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3001/p_02');
const min = md ? parseInt(md['md']['mi'], 16) / 2.0 : 0;
const max = md ? parseInt(md['md']['mx'], 16) / 2.0 : 0;
return [min, max];
}
getHeatingThresholdTemperatureRange() {
const md = this.extractObject(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3001/p_03');
const min = md ? parseInt(md['md']['mi'], 16) / 2.0 : 0;
const max = md ? parseInt(md['md']['mx'], 16) / 2.0 : 0;
return [min, max];
}
getFanSpeed() {
const mode = this.getOperationMode();
let pn = 'p_09';
if (mode === exports.CLIMATE_MODE_FAN) {
pn = 'p_28';
}
else if (mode === exports.CLIMATE_MODE_DEHUMIDIFY) {
pn = 'p_27';
}
else if (mode === exports.CLIMATE_MODE_AUTO) {
pn = 'p_26';
}
else if (mode === exports.CLIMATE_MODE_HEATING) {
pn = 'p_0A';
}
else if (mode === exports.CLIMATE_MODE_COOLING) {
pn = 'p_09';
}
return this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3001/' + pn);
}
getFanSpeedName() {
const speed = this.getFanSpeed();
if (speed === exports.CLIMATE_FAN_SPEED_AUTO) {
return 'Auto';
}
else if (speed === exports.CLIMATE_FAN_SPEED_SLIENT) {
return 'Silent';
}
else if (speed === exports.CLIMATE_FAN_SPEED_1) {
return '1';
}
else if (speed === exports.CLIMATE_FAN_SPEED_2) {
return '2';
}
else if (speed === exports.CLIMATE_FAN_SPEED_3) {
return '3';
}
else if (speed === exports.CLIMATE_FAN_SPEED_4) {
return '4';
}
else if (speed === exports.CLIMATE_FAN_SPEED_5) {
return '5';
}
return 'Unknown';
}
getFanSpeedNumber() {
const speed = this.getFanSpeed();
if (speed === exports.CLIMATE_FAN_SPEED_AUTO) {
return 0;
}
else if (speed === exports.CLIMATE_FAN_SPEED_SLIENT) {
return 1;
}
else if (speed === exports.CLIMATE_FAN_SPEED_1) {
return 2;
}
else if (speed === exports.CLIMATE_FAN_SPEED_2) {
return 3;
}
else if (speed === exports.CLIMATE_FAN_SPEED_3) {
return 4;
}
else if (speed === exports.CLIMATE_FAN_SPEED_4) {
return 5;
}
else if (speed === exports.CLIMATE_FAN_SPEED_5) {
return 6;
}
return 0;
}
getMotionDetection() {
return this.extractValue(this._Response, '/dsiot/edge/adr_0100.dgc_status', 'e_1002/e_3003/p_27') === '01';
}
async setMotionDetection(bEnable) {
const command = [{ "pn": "e_3003", "pch": [{ "pn": "p_27", "pv": bEnable ? '01' : '00' }] }];
return await this.sendCommand(command);
}
async setPowerStatus(power) {
const command = [{ "pn": "e_3003", "pch": [{ "pn": "p_2D", "pv": CLIMATE_OPERATE_SETTING }] }, { "pn": "e_A002", "pch": [{ "pn": "p_01", "pv": power ? '01' : '00' }] }];
return await this.sendCommand(command);
}
async setOperationMode(mode) {
const command = [{ "pn": "e_3003", "pch": [{ "pn": "p_2D", "pv": CLIMATE_OPERATE_SETTING }] }, { "pn": "e_3001", "pch": [{ "pn": "p_01", "pv": mode }] }];
return await this.sendCommand(command);
}
async setTargetTemperature(temperature) {
const mode = this.getOperationMode();
let pn = 'p_02';
const pv = (temperature * 2).toString(16);
if (mode === exports.CLIMATE_MODE_HEATING) {
pn = 'p_03';
}
else if (mode === exports.CLIMATE_MODE_COOLING) {
pn = 'p_02';
}
else if (mode === exports.CLIMATE_MODE_AUTO) {
pn = 'p_1D';
}
else {
return false;
}
let command = [{ "pn": "e_3003", "pch": [{ "pn": "p_2D", "pv": CLIMATE_OPERATE_SETTING }] }, { "pn": "e_3001", "pch": [{ "pn": pn, "pv": pv }] }];
if (mode === exports.CLIMATE_MODE_COOLING) {
this.pushObject(command, 'e_3001', { "pn": "p_0B", "pv": '0A' });
this.pushObject(command, 'e_3001', { "pn": "p_0C", "pv": '01' });
}
return await this.sendCommand(command);
}
async setFanSpeed(speed) {
const mode = this.getOperationMode();
let pn = 'p_09';
if (mode === exports.CLIMATE_MODE_FAN) {
pn = 'p_28';
}
else if (mode === exports.CLIMATE_MODE_DEHUMIDIFY) {
pn = 'p_27';
}
else if (mode === exports.CLIMATE_MODE_AUTO) {
pn = 'p_26';
}
else if (mode === exports.CLIMATE_MODE_HEATING) {
pn = 'p_0A';
}
else if (mode === exports.CLIMATE_MODE_COOLING) {
pn = 'p_09';
}
const command = [{ "pn": "e_3003", "pch": [{ "pn": "p_2D", "pv": CLIMATE_OPERATE_SETTING }] }, { "pn": "e_3001", "pch": [{ "pn": pn, "pv": speed }] }];
return await this.sendCommand(command);
}
extractValue(responsesData, fr, path) {
try {
if (responsesData === undefined || responsesData.hasOwnProperty('responses') === false) {
this.log.debug('Daikin - extractValue(): Error: No responses object found');
return undefined;
}
let currentObject = responsesData['responses'];
for (const response of currentObject) {
if (response['fr'] === fr) {
currentObject = response['pc']['pch'];
}
}
const pathKeys = path.split('/');
for (let i = 0; i < pathKeys.length; i++) {
try {
const key = pathKeys[i];
for (const currentObjectElement of currentObject) {
if ((currentObjectElement['pn'] === key && currentObjectElement.hasOwnProperty('pch'))) {
currentObject = currentObjectElement['pch'];
break;
}
else if (currentObjectElement['pn'] === key && currentObjectElement.hasOwnProperty('pv')) {
if (i === pathKeys.length - 1) {
return currentObjectElement['pv'];
}
}
}
}
catch (e) {
this.log.debug('Daikin - extractValue(): Error:' + e);
}
}
}
catch (e) {
this.log.debug('Daikin - extractValue(): Error:' + e);
}
this.log.debug('Daikin - extractValue(): Error: No value found for path:' + path);
return undefined;
}
extractObject(responsesData, fr, path) {
try {
if (responsesData === undefined || responsesData.hasOwnProperty('responses') === false) {
this.log.debug('Daikin - extractObject(): Error: No responses object found');
return undefined;
}
let currentObject = responsesData['responses'];
for (const response of currentObject) {
if (response['fr'] === fr) {
currentObject = response['pc']['pch'];
}
}
const pathKeys = path.split('/');
for (const key of pathKeys) {
try {
for (const currentObjectElement of currentObject) {
if (currentObjectElement['pn'] === key && currentObjectElement.hasOwnProperty('pch')) {
currentObject = currentObjectElement['pch'];
break;
}
else if (currentObjectElement['pn'] === key && currentObjectElement.hasOwnProperty('pv')) {
return currentObjectElement;
}
}
}
catch (e) {
this.log.debug('Daikin - extractValue(): Error:' + e);
}
}
}
catch (e) {
this.log.debug('Daikin - extractValue(): Error:' + e);
}
this.log.debug('Daikin - extractValue(): Error: No value found for path:' + path);
return undefined;
}
// const command = [{"pn": "e_3003", "pch": [{"pn": "p_2D", "pv": CLIMATE_OPERATE_SETTING}]}, {"pn": "e_3001","pch": [{"pn": pn, "pv": speed}]}];
pushObject(jsonData, pn, obj) {
try {
for (const i in jsonData) {
const currentObject = jsonData[i];
if (currentObject['pn'] === pn && currentObject.hasOwnProperty('pch')) {
currentObject['pch'].push(obj);
return jsonData;
}
}
}
catch (e) {
this.log.debug('Daikin - pushObject(): Error:' + e);
}
return undefined;
}
async sendCommand(command) {
const param = { "requests": [{ "op": 3, "to": "/dsiot/edge/adr_0100.dgc_status", "pc": { "pn": "dgc_status", "pch": [{ "pn": "e_1002", "pch": command }] } }] };
const response = await http.request({
method: 'post',
url: `http://${this._IP}${const_1.ENDPOINT}`,
headers: {
'Accept': 'application/json; charset=UTF-8',
'Content-Type': 'application/json',
'User-Agent': const_1.USER_AGENT,
},
data: JSON.stringify(param),
});
if (response.status === 200) {
this.log.debug(`Daikin - sendCommand('${this._IP}'): '${JSON.stringify(param)} ' : Response: '${JSON.stringify(response.data)}'`);
}
else {
this.log.debug(`Daikin - sendCommand('${this._IP}'): '${JSON.stringify(param)} ' : Error: Invalid response status code: '${response.status}'`);
}
await this.fetchDeviceStatus(true);
return response.status === 200;
}
}
exports.DaikinDevice = DaikinDevice;
class DaikinLocalAPI {
constructor(log) {
this.log = log;
this._devices = [];
}
async fetchDevices(climateIPs, bForce = false) {
this.log.debug('Daikin: fetchDevices()');
this._devices = [];
for (const ip of climateIPs) {
const daikinDevice = new DaikinDevice(ip, this.log);
if (await daikinDevice.fetchDeviceStatus(bForce)) {
this._devices.push(daikinDevice);
}
}
return this._devices;
}
}
exports.DaikinLocalAPI = DaikinLocalAPI;
//# sourceMappingURL=daikin-local.js.map