UNPKG

iobroker.hydrawise

Version:

Adapter to control the Hydrawise irrigation control system

552 lines (551 loc) 22.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var utils = __toESM(require("@iobroker/adapter-core")); var import_axios = __toESM(require("axios")); const hydrawise_url = "https://api.hydrawise.com"; let nextpollSchedule = null; let nextpollCustomer = null; let resetSwitch = null; const RELAYS = Object; class Hydrawise extends utils.Adapter { constructor(options = {}) { super({ ...options, name: "hydrawise" }); this.on("ready", this.onReady.bind(this)); this.on("stateChange", this.onStateChange.bind(this)); this.on("unload", this.onUnload.bind(this)); } async onReady() { if (!this.config.apiKey) { this.log.error("No API-Key defined!"); } else { this.setStateChangedAsync("info.connection", false, true); try { await this.GetStatusSchedule(); nextpollSchedule = this.setInterval(async () => { await this.GetStatusSchedule(); }, this.config.apiInterval * 1e3); await this.GetCustomerDetails(); nextpollCustomer = this.setInterval( async () => { await this.GetCustomerDetails(); }, 5 * 60 * 1e3 ); } catch (error) { this.log.error(error.toString()); } await this.subscribeStatesAsync("*"); } } async GetStatusSchedule() { return new Promise((resolve, reject) => { this.buildRequest("statusschedule.php", { api_key: this.config.apiKey }).then(async (response) => { if ((response == null ? void 0 : response.status) === 200) { const content = response.data; this.setStateChangedAsync("info.connection", true, true); await this.setObjectNotExistsAsync("schedule.stopall", { type: "state", common: { name: { en: "stop all zones", de: "alle Zonen stoppen", ru: "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0432\u0441\u0435 \u0437\u043E\u043D\u044B", pt: "parar todas as zonas", nl: "stop alle zones", fr: "arr\xEAter toutes les zones", it: "fermare tutte le zone", es: "detener todas las zonas", pl: "zatrzymuj\u0105 wszystkie strefy", uk: "\u0437\u0443\u043F\u0438\u043D\u0438\u0442\u0438 \u0432\u0441\u0456 \u0437\u043E\u043D\u0438", "zh-cn": "\u505C\u6B62\u6240\u6709\u5730\u533A" }, type: "boolean", role: "button.stop", read: false, write: true }, native: {} }); await this.setObjectNotExistsAsync("schedule.runall", { type: "state", common: { name: { en: "run all zones for x seconds", de: "alle Zonen f\xFCr x Sekunden ausf\xFChren", ru: "\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0432\u0441\u0435 \u0437\u043E\u043D\u044B \u0437\u0430 x \u0441\u0435\u043A\u0443\u043D\u0434\u044B", pt: "executar todas as zonas por x segundos", nl: "ren alle zones voor x seconden", fr: "ex\xE9cuter toutes les zones pendant x secondes", it: "eseguire tutte le zone per x secondi", es: "ejecutar todas las zonas durante x segundos", pl: "wszystkie strefy startuj\u0105 dla x sekundy", uk: "\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0438 \u0432\u0441\u0456 \u0437\u043E\u043D\u0438 \u0434\u043B\u044F x \u0441\u0435\u043A\u0443\u043D\u0434", "zh-cn": "\u8DD1\u9053\u533A" }, type: "number", role: "level", unit: "seconds", read: true, write: true }, native: {} }); await this.setObjectNotExistsAsync("schedule.suspendall", { type: "state", common: { name: { en: "suspend all zones for x seconds", de: "alle Zonen f\xFCr x Sekunden aussetzen", ru: "\u043F\u0440\u0438\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0432\u0441\u0435 \u0437\u043E\u043D\u044B \u0437\u0430 x \u0441\u0435\u043A\u0443\u043D\u0434\u044B", pt: "suspender todas as zonas por x segundos", nl: "vertaling:", fr: "suspendre toutes les zones pendant x secondes", it: "sospendere tutte le zone per x secondi", es: "suspender todas las zonas durante x segundos", pl: "wszystkie strefy zawieszenia dla x sekundy", uk: "\u043F\u0440\u0438\u0437\u0443\u043F\u0438\u043D\u0438\u0442\u0438 \u0432\u0441\u0456 \u0437\u043E\u043D\u0438 \u043D\u0430 x \u0441\u0435\u043A\u0443\u043D\u0434", "zh-cn": "\u505C\u6B62\u6240\u6709\xD7\u4E8C\u533A" }, type: "number", role: "level", read: true, write: true }, native: {} }); for (let key in content) { key = this.name2id(key); if (key !== "relays" && key !== "sensors" && key !== "expanders" && !Number.isNaN(key)) { await this.setObjectNotExistsAsync(`schedule.${key}`, { type: "state", common: { name: key, type: key === "message" ? "string" : "number", role: key === "message" ? "text" : "value", read: true, write: false }, native: {} }); this.setStateChangedAsync(`schedule.${key}`, content[key], true); if (key === "time") { await this.setObjectNotExistsAsync("schedule.timestr", { type: "state", common: { name: "last api call", type: "string", role: "text", read: true, write: false }, native: {} }); const t = new Date(content[key] * 1e3); this.setStateChangedAsync("schedule.timestr", t.toString(), true); } } } for (const relay of content.relays) { const name = relay.relay; await this.setObjectNotExistsAsync(`schedule.${relay.relay}`, { type: "channel", common: { name: name.toString() }, native: {} }); RELAYS[relay.relay] = relay.relay_id; for (let key in relay) { key = this.name2id(key); await this.setObjectNotExistsAsync(`schedule.${relay.relay}.${key}`, { type: "state", common: { name: key, type: key === "name" || key === "timestr" ? "string" : "number", role: key === "name" || key === "timestr" ? "text" : "value", read: true, write: false }, native: {} }); if (key === "timestr") { const t = /* @__PURE__ */ new Date(); t.setSeconds(t.getSeconds() + relay.time); relay[key] = t.toString(); } this.setStateChangedAsync(`schedule.${relay.relay}.${key}`, relay[key], true); } await this.setObjectNotExistsAsync(`schedule.${relay.relay}.stopZone`, { type: "state", common: { name: { en: "stop zone", de: "Zone stoppen", ru: "\u0437\u043E\u043D\u0430 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438", pt: "zona de paragem", nl: "stop zone", fr: "zone d ' arr\xEAt", it: "zona di sosta", es: "zona de parada", pl: "strefa stopu", uk: "\u0437\u043E\u043D\u0430 \u0437\u0443\u043F\u0438\u043D\u043A\u0438", "zh-cn": "\u505C\u6B62\u5730\u533A" }, type: "boolean", role: "button.stop", read: false, write: true }, native: {} }); await this.setObjectNotExistsAsync(`schedule.${relay.relay}.runZone`, { type: "state", common: { name: { en: "run zone for x seconds", de: "Zone f\xFCr x Sekunden starten", ru: "\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0437\u043E\u043D\u0443 \u0437\u0430 x \u0441\u0435\u043A\u0443\u043D\u0434\u044B", pt: "zona de execu\xE7\xE3o por x segundos", nl: "ren zone voor x seconden", fr: "zone de course pour x secondes", it: "zona di corsa per x secondi", es: "zona de ejecuci\xF3n por x segundos", pl: "strefa x sekundy", uk: "\u0437\u043E\u043D\u0430 \u0437\u0430\u043F\u0443\u0441\u043A\u0443 \u0434\u043B\u044F x \u0441\u0435\u043A\u0443\u043D\u0434", "zh-cn": "\xD7\u4E8C\u533A" }, type: "number", role: "level", read: true, write: true }, native: {} }); await this.setObjectNotExistsAsync(`schedule.${relay.relay}.suspendZone`, { type: "state", common: { name: { en: "suspend zone for x seconds", de: "Zone f\xFCr x Sekunden aussetzen", ru: "\u043F\u0440\u0438\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0437\u043E\u043D\u0443 \u043D\u0430 x \u0441\u0435\u043A\u0443\u043D\u0434\u044B", pt: "zona de suspens\xE3o por x segundos", nl: "quality over quantity (qoq) releases vertaling:", fr: "zone de suspension pour x secondes", it: "zona di sospensione per x secondi", es: "zona de suspensi\xF3n por x segundos", pl: "strefa zawies\u0142a na x sekundy", uk: "\u0437\u043E\u043D\u0430 \u043F\u0456\u0434\u0432\u0456\u0441\u043A\u0438 \u0434\u043B\u044F x \u0441\u0435\u043A\u0443\u043D\u0434", "zh-cn": "\u505C\u6B62x\u4E8C\u533A" }, type: "number", role: "level", read: true, write: true }, native: {} }); await this.setObjectNotExistsAsync(`schedule.${relay.relay}.runDefault`, { type: "state", common: { name: { en: "run zone for default time", de: "Zone mit Standardlaufzeit starten", ru: "\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0437\u043E\u043D\u0443 \u0434\u043B\u044F \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E", pt: "fuso de execu\xE7\xE3o para o tempo padr\xE3o", nl: "run zone for default time", fr: "run zone for default time", it: "run zone per il tempo predefinito", es: "zona de ejecuci\xF3n por tiempo predeterminado", pl: "strefa czasu domy\u015Blnego", uk: "\u0437\u043E\u043D\u0430 \u0437\u0430\u043F\u0443\u0441\u043A\u0443 \u0437\u0430 \u0437\u0430\u043C\u043E\u0432\u0447\u0443\u0432\u0430\u043D\u043D\u044F\u043C", "zh-cn": "a. \u6682\u505C\u65F6\u95F4\u533A" }, type: "boolean", role: "button.start", read: true, write: true }, native: {} }); } for (const sensor of content.sensors) { await this.setObjectNotExistsAsync(`schedule.sensors.${sensor.input}`, { type: "channel", common: { name: "sensors" }, native: {} }); for (let key in sensor) { if (key !== "relays") { key = this.name2id(key); await this.setObjectNotExistsAsync(`schedule.sensors.${sensor.input}.${key}`, { type: "state", common: { name: key, type: "number", role: "value", read: true, write: false }, native: {} }); this.setStateChangedAsync(`schedule.sensors.${sensor.input}.${key}`, sensor[key], true); } } } } resolve(response.status); }).catch((error) => { var _a; this.clearInterval(nextpollSchedule); if (error.code === "EAI_AGAIN" || error.code === "ECONNABORTED" || error.code === "ENOTFOUND" || ((_a = error.response) == null ? void 0 : _a.status) === 429) { nextpollSchedule = this.setInterval(async () => { await this.GetStatusSchedule(); }, this.config.apiInterval * 1e3); } else { this.log.debug(`(stats) received error - API is now offline: ${JSON.stringify(error)}`); this.setStateChangedAsync("info.connection", false, true); reject(error); } }); }); } async GetCustomerDetails() { return new Promise((resolve, reject) => { this.buildRequest("customerdetails.php", { api_key: this.config.apiKey }).then(async (response) => { if ((response == null ? void 0 : response.status) === 200) { const content = response.data; this.setStateChangedAsync("info.connection", true, true); for (let key in content) { if (key !== "controllers" && !Number.isNaN(key)) { key = this.name2id(key); await this.setObjectNotExistsAsync(`customer.${key}`, { type: "state", common: { name: key, type: key === "message" || key === "current_controller" ? "string" : "number", role: key === "message" || key === "current_controller" ? "text" : "value", read: true, write: false }, native: {} }); this.setStateChangedAsync(`customer.${key}`, content[key], true); } } for (const controller of content.controllers) { await this.setObjectNotExistsAsync(`customer.controllers.${controller.name}`, { type: "channel", common: { name: controller.name }, native: {} }); for (let key in controller) { key = this.name2id(key); await this.setObjectNotExistsAsync(`customer.controllers.${controller.name}.${key}`, { type: "state", common: { name: key, type: key !== "controller_id" ? "string" : "number", role: key !== "controller_id" ? "text" : "value", read: true, write: false }, native: {} }); if (key === "last_contact") { const t = new Date(controller[key] * 1e3); controller[key] = t.toString(); } this.setStateChangedAsync(`customer.controllers.${controller.name}.${key}`, controller[key], true); } } } resolve(response.status); }).catch((error) => { var _a; this.clearInterval(nextpollCustomer); if (error.code === "EAI_AGAIN" || error.code === "ECONNABORTED" || error.code === "ENOTFOUND" || ((_a = error.response) == null ? void 0 : _a.status) === 429) { nextpollCustomer = this.setInterval( async () => { await this.GetCustomerDetails(); }, 5 * 60 * 1e3 ); } else { this.log.debug(`(stats) received error - API is now offline: ${JSON.stringify(error)}`); this.setStateChangedAsync("info.connection", false, true); reject(error); } }); }); } buildRequest(service, params) { return new Promise((resolve, reject) => { const url = `/api/v1/${service}`; let lastErrorCode = 0; if (params.api_key) { try { (0, import_axios.default)({ method: "GET", baseURL: hydrawise_url, url, timeout: 3e4, responseType: "json", params }).then((response) => { lastErrorCode = 0; resolve(response); }).catch((error) => { if (error.response) { this.log.warn(`received ${error.response.status} response from ${url} with content: ${JSON.stringify(error.response.data)}`); } else if (error.request) { if (error.code === lastErrorCode) { this.log.debug(error.message); } else { this.log.info(`error ${error.code} from ${url}: ${error.message}`); lastErrorCode = error.code; } } else { this.log.error(error.message); } reject(error); }); } catch (error) { reject(error); } } else { reject("API key is not configured"); } }); } /** * Is called when adapter shuts down - callback has to be called under any circumstances! */ onUnload(callback) { try { this.clearInterval(nextpollSchedule); this.clearInterval(nextpollCustomer); callback(); } catch (e) { callback(); } } /** * Is called if a subscribed state changes */ onStateChange(id, state) { if (state && !state.ack) { if (id.indexOf("stopall") !== -1) { this.buildRequest("setzone.php", { api_key: this.config.apiKey, action: "stopall" }); } else if (id.indexOf("stop") !== -1) { const relay = id.match(/.*schedule\.(.*)\.stopZone/); if (relay && (relay == null ? void 0 : relay.length) > 1) { this.buildRequest("setzone.php", { api_key: this.config.apiKey, action: "stop", relay_id: RELAYS[relay[1]] }); } } if (id.indexOf("runall") !== -1 && (state.val || state.val === 0)) { this.buildRequest("setzone.php", { api_key: this.config.apiKey, action: "runall", period_id: 999, custom: state.val }); } else if (id.indexOf("runZone") !== -1 && (state.val || state.val === 0)) { const relay = id.match(/.*schedule\.(.*)\.runZone/); if (relay && (relay == null ? void 0 : relay.length) > 1) { this.buildRequest("setzone.php", { api_key: this.config.apiKey, action: "run", period_id: 999, custom: state.val, relay_id: RELAYS[relay[1]] }); } } if (id.indexOf("runDefault") !== -1 && state.val !== null) { this.initRunDefault(id, state.val); } if (id.indexOf("suspendall") !== -1 && (state.val || state.val === 0)) { const num = state.val; this.buildRequest("setzone.php", { api_key: this.config.apiKey, action: "suspendall", period_id: 999, custom: Math.trunc((state.ts + num) / 1e3) }); } else if (id.indexOf("suspend") !== -1 && (state.val || state.val === 0)) { const num = state.val; const relay = id.match(/.*schedule\.(.*)\.suspendZone/); if (relay && (relay == null ? void 0 : relay.length) > 1) { this.buildRequest("setzone.php", { api_key: this.config.apiKey, action: "suspend", period_id: 999, custom: Math.trunc((state.ts + num) / 1e3), relay_id: RELAYS[relay[1]] }); } } } } async initRunDefault(id, run) { const relay = id.match(/(.*schedule.*\.)runDefault/); this.clearTimeout(resetSwitch); if (relay) { if (run) { const defaultRunTime = await this.getStateAsync(relay[1] + "run"); if (defaultRunTime && defaultRunTime.val) { this.setStateAsync(relay[1] + "runZone", defaultRunTime.val, false); resetSwitch = this.setTimeout( () => { this.setStateAsync(id, false, false); }, defaultRunTime.val * 1e3 ); } } else { this.setStateAsync(relay[1] + "stopZone", true, false); } } } name2id(pName) { return (pName || "").replace(this.FORBIDDEN_CHARS, "_"); } } if (require.main !== module) { module.exports = (options) => new Hydrawise(options); } else { (() => new Hydrawise())(); } //# sourceMappingURL=main.js.map