iobroker.heizoel24-mex
Version:
Read in data from MEX
780 lines (696 loc) • 30.8 kB
JavaScript
;
/*
* Created with @iobroker/create-adapter v2.6.2
*/
const utils = require('@iobroker/adapter-core');
const axios = require('axios');
const mqtt = require('mqtt');
const fs = require('node:fs');
axios.defaults.timeout = 2000;
class Heizoel24Mex extends utils.Adapter {
constructor(options) {
super({
...options,
name: 'heizoel24-mex',
});
this.on('ready', this.onReady.bind(this));
this.on('unload', this.onUnload.bind(this));
this.oil_usage = null;
this.Items = [
{ id: 'DataReceived', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'SensorId', role: 'value', unit: '', type: 'number' },
{ id: 'IsMain', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'CurrentVolumePercentage', role: 'level', unit: '%', type: 'number' },
{ id: 'CurrentVolume', role: 'level', unit: 'L', type: 'number' },
{ id: 'NotifyAtLowLevel', role: 'level.color.red', unit: '%', type: 'number' },
{ id: 'NotifyAtAlmostEmptyLevel', role: 'level.color.red', unit: '%', type: 'number' },
{ id: 'NotificationsEnabled', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'Usage', role: 'value', unit: 'L/Day', type: 'number' },
{ id: 'RemainsUntil', role: 'date', unit: '', type: 'string' },
{ id: 'MaxVolume', role: 'level.max', unit: 'L', type: 'number' },
{ id: 'ZipCode', role: 'value', unit: '', type: 'string' },
{ id: 'MexName', role: 'value', unit: '', type: 'string' },
{ id: 'LastMeasurementTimeStamp', role: 'date', unit: '', type: 'string' },
{ id: 'LastMeasurementWithDifferentValue', role: 'date', unit: '', type: 'string' },
{ id: 'BatteryPercentage', role: 'value.battery', unit: '%', type: 'number' },
{ id: 'Battery', role: 'value.battery', unit: 'V', type: 'number' },
{ id: 'LitresPerCentimeter', role: 'value', unit: 'L/cm', type: 'number' },
{ id: 'LastMeasurementWasSuccessfully', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'SensorTypeId', role: 'value', unit: '', type: 'number' },
{ id: 'HasMeasurements', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'MeasuredDaysCount', role: 'value', unit: 'Days', type: 'number' },
{ id: 'LastMeasurementWasTooHigh', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'YearlyOilUsage', role: 'value', unit: 'L', type: 'number' },
{ id: 'RemainingDays', role: 'value', unit: 'Days', type: 'number' },
{ id: 'LastOrderPrice', role: 'value', unit: '€|CHF', type: 'number' },
{ id: 'ResultCode', role: 'value', unit: '', type: 'boolean' },
{ id: 'ResultMessage', role: 'value', unit: '', type: 'boolean' },
{ id: 'LowBattery', role: 'indicator', unit: '', type: 'boolean' },
];
this.PricingForecast = [
{ id: 'LastOrderPrice', role: 'value', unit: '€|CHF/100L', type: 'number' },
{ id: 'PriceComparedToYesterdayPercentage', role: 'value', unit: '%', type: 'number' },
{ id: 'PriceForecastPercentage', role: 'value', unit: '%', type: 'number' },
{ id: 'HasMultipleMexDevices', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'DashboardViewMode', role: 'value', unit: '', type: 'number' },
{ id: 'ShowComparedToYesterday', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'ShowForecast', role: 'indicator', unit: '', type: 'boolean' },
{ id: 'ResultCode', role: 'value', unit: '', type: 'boolean' },
{ id: 'ResultMessage', role: 'value', unit: '', type: 'boolean' },
];
this.RemainsUntilCombined = [
{ id: 'MonthAndYear', role: 'value', unit: '', type: 'string' },
{ id: 'RemainsValue', role: 'value', unit: '', type: 'string' },
{ id: 'RemainsUnit', role: 'value', unit: '', type: 'string' },
];
this.contentItems = [];
this.contentPricingForecast = [];
this.contentRemainsUntilCombined = [];
}
async onReady() {
const username = this.config.username;
const passwort = this.config.passwort;
const broker_address = this.config.broker_address;
const mqtt_active = this.config.mqtt_active;
const mqtt_user = this.config.mqtt_user;
const mqtt_pass = this.config.mqtt_pass;
const mqtt_port = this.config.mqtt_port;
const sensor_in = this.config.sensor_id;
let sensor_id = 1;
const reference_month_in = this.config.reference_month;
let reference_month = 6;
const storeJson = this.config.storeJson;
const storeDir = this.config.storeDir;
if (Number(sensor_in)) {
sensor_id = parseInt(sensor_in);
if (sensor_id < 1 || sensor_id > 20) {
this.log.error('Sensor ID has no value between 1 and 20');
this.terminate(2);
}
} else {
this.log.error('Sensor ID has no valid value');
this.terminate(2);
}
this.log.debug(`Sensor ID is ${sensor_id}`);
if (Number(reference_month_in)) {
reference_month = parseInt(reference_month_in);
if (reference_month < 1 || reference_month > 12) {
this.log.warn('Reference month has no value between 1 and 12. Set it to 6');
reference_month = 6;
}
} else {
this.log.warn('Reference month has no value between 1 and 12. Set it to 6');
reference_month = 6;
}
this.log.debug(`Reference month is ${reference_month}`);
if (username.trim().length === 0 || passwort.trim().length === 0) {
this.log.error('User email and/or user password empty - please check instance configuration');
this.terminate(0);
}
let client = null;
if (mqtt_active) {
if (broker_address.trim().length === 0 || broker_address == '0.0.0.0') {
this.log.error('MQTT IP address is empty - please check instance configuration');
this.terminate(2);
}
client = mqtt.connect(`mqtt://${broker_address}:${mqtt_port}`, {
connectTimeout: 4000,
username: mqtt_user,
password: mqtt_pass,
});
}
try {
const instObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
if (instObj && instObj.common && instObj.common.schedule && instObj.common.schedule === '0 */3 * * *') {
instObj.common.schedule = `${Math.floor(Math.random() * 60)} */3 * * *`;
this.log.info(`Default schedule found and adjusted to spread calls better over the full hour!`);
await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, instObj);
this.terminate(0);
return;
}
} catch (err) {
this.log.error(`Could not check or adjust the schedule: ${err.message}`);
}
this.log.debug(`MQTT active: ${mqtt_active}`);
this.log.debug(`MQTT port: ${mqtt_port}`);
await this.mainRoutine(
client,
username,
passwort,
mqtt_active,
sensor_id,
storeJson,
storeDir,
reference_month,
);
// Finished - stopping instance
this.log.info('Everything done. Going to terminate till next schedule');
this.terminate(0);
}
async calcAnnualFor(entries, year, reference_month) {
// 1. Stichtag bestimmen (1. des Folgemonats)
let endMonth = reference_month + 1;
let endYear = year;
if (endMonth === 13) {
endMonth = 1;
endYear = year + 1;
}
// 2. Start- und Enddatum setzen
const end = new Date(endYear, endMonth - 1, 1);
const start = new Date(endYear - 1, endMonth - 1, 1);
// 3. Werte im Zeitraum sammeln
const window = entries.filter(e => e.date >= start && e.date < end);
// 4. Summe bilden (App-Logik)
return window.reduce((sum, e) => sum + e.value, 0);
}
async sendMqtt(sensor_id, mqtt_active, client, topic, wert) {
if (mqtt_active) {
client.publish(`MEX/${sensor_id.toString()}/${topic}`, wert);
}
}
async login(username, passwort) {
this.log.debug('Login in...');
this.username = username;
this.passwort = passwort;
this.url = 'https://api.heizoel24.de/app/api/app/Login';
this.newHeaders = { 'Content-type': 'application/json' };
try {
const reply = await axios.post(
this.url,
{ Password: this.passwort, Username: this.username },
{ headers: this.newHeaders },
);
if (reply.status === 200) {
this.log.debug('Login OK');
const reply_json = reply.data;
if (reply_json['ResultCode'] === 0) {
const session_id = reply_json['SessionId'];
this.log.debug(`Session ID: ${session_id}`);
this.log.debug('Logged in');
return [true, session_id];
}
this.log.error('ResultCode not 0. No session ID received!');
}
} catch (error) {
const errorMsg = error.response?.status
? `Login failed! Error: ${error.response.status}`
: `Login failed! Error: ${error.message || error}`;
this.log.error(errorMsg);
this.terminate(2);
}
return [false, ''];
}
async getCalculateRemaining(sensor_id, session_id) {
this.log.debug('Get future residual oil levels...');
const url = `https://api.heizoel24.de/app/api/app/measurement/CalculateRemaining/${session_id}/${sensor_id}/False`;
try {
const reply = await axios.get(url);
if (reply.status === 200) {
this.log.debug('Future residual oil levels received');
return reply.data;
}
this.log.debug(`Heizoel24 residual oil levels > Status Code: ${reply.status}`);
return 'error';
} catch (error) {
const errorMsg = error.response?.status
? `Error fetching data: ${error.response.status}`
: `Error fetching data: ${error.message || error}`;
this.log.error(errorMsg);
return 'error';
}
}
async getMexData(username, passwort, sensor_id) {
const [login_status, session_id] = await this.login(username, passwort);
if (!login_status) {
return [false, false];
}
this.log.debug('Refresh sensor data cache...');
const url1 = `https://api.heizoel24.de/app/api/app/GetDashboardData/${session_id}/2/${sensor_id}/False`;
const url2 = `https://api.heizoel24.de/app/api/app/GetOilUsage/${session_id}/False`;
try {
const reply1 = await axios.get(url1);
if (reply1.status !== 200) {
this.log.error('Dashboard data unsuccessful.');
return [false, false];
}
try {
const r2 = await axios.get(url2);
if (r2.status === 200) {
this.oil_usage = r2.data;
} else {
this.log.warn('Oil consumption could not be loaded (status ≠ 200).');
}
} catch (e) {
this.log.warn(`Error retrieving oil consumption: ${e?.response?.status || e.message}`);
}
this.log.debug('Dashboard data successfully received.');
return [reply1.data, session_id];
} catch (error) {
const status = error?.response?.status || 'unknown';
this.log.error(`Error retrieving dashboard data. Status: ${status}`);
this.terminate(1);
}
return [false, false];
}
async mainRoutine(client, username, passwort, mqtt_active, sensor_id, storeJson, storeDir, reference_month) {
const [daten, session_id] = await this.getMexData(username, passwort, sensor_id);
if (daten === false) {
this.log.error('No data received');
if (mqtt_active) {
await this.sendMqtt(sensor_id, mqtt_active, client, 'Items/DataReceived', 'false');
client.end();
}
await this.noDataReceived(sensor_id);
return false;
}
const datenJson = daten;
for (let n = 0; n < this.PricingForecast.length; n++) {
const result = datenJson[this.PricingForecast[n].id] || false;
this.contentPricingForecast[n] = result;
if (mqtt_active) {
if (
this.contentPricingForecast[n] == false &&
(this.PricingForecast[n].id == 'PriceComparedToYesterdayPercentage' ||
this.PricingForecast[n].id == 'PriceForecastPercentage')
) {
this.log.debug(`${this.PricingForecast[n].id} omitted, because it's false`);
} else {
await this.sendMqtt(
sensor_id,
mqtt_active,
client,
`PricingForecast/${this.PricingForecast[n].id}`,
result.toString(),
);
}
}
this.log.debug(
`PricingForecast: ${this.PricingForecast[n].id}: ${result.toString()}, unit: ${
this.PricingForecast[n].unit
}, Typ: ${typeof datenJson[this.PricingForecast[n].id]}`,
);
}
const items = datenJson['Items'][0];
// Rohwert von Batteriespannung holen
const rawBattery = datenJson?.Items?.[0]?.Battery;
// Nur akzeptieren, wenn eine gültige Zahl ist
const batteryVoltage = typeof rawBattery === 'number' && Number.isFinite(rawBattery) ? rawBattery : null;
// Alles Unerwartete = true
const lowBattery = batteryVoltage === null || batteryVoltage < 2.5;
for (let n = 1; n < this.Items.length; n++) {
const result = items[this.Items[n].id] || false;
this.contentItems[n] = result;
if (mqtt_active) {
await this.sendMqtt(sensor_id, mqtt_active, client, `Items/${this.Items[n].id}`, result.toString());
}
this.log.debug(
`Items: ${this.Items[n].id}: ${result.toString()}, unit: ${this.Items[n].unit}, Typ: ${typeof result}`,
);
}
// LowBattery an der richtigen Position speichern
const lowBatteryIndex = this.Items.findIndex(i => i.id === 'LowBattery');
this.contentItems[lowBatteryIndex] = lowBattery;
if (mqtt_active) {
await this.sendMqtt(sensor_id, mqtt_active, client, 'Items/DataReceived', 'true');
await this.sendMqtt(sensor_id, mqtt_active, client, 'Items/LowBattery', String(lowBattery));
}
const dataReceivedIndex = this.Items.findIndex(i => i.id === 'DataReceived');
this.contentItems[dataReceivedIndex] = true;
const daten3 = items['RemainsUntilCombined'];
for (let n = 0; n < this.RemainsUntilCombined.length; n++) {
let result = 'empty';
try {
result = daten3[this.RemainsUntilCombined[n].id] || false;
} catch {
this.log.debug('RemainsUntilCombined no data found');
}
this.contentRemainsUntilCombined[n] = result;
try {
if (mqtt_active) {
await this.sendMqtt(
sensor_id,
mqtt_active,
client,
`RemainsUntilCombined/${this.RemainsUntilCombined[n].id}`,
result.toString(),
);
}
this.log.debug(
`RemainsUntilCombined: ${this.RemainsUntilCombined[n].id}: ${result.toString()}, unit: ${
this.RemainsUntilCombined[n].unit
}, Typ: ${typeof daten3[this.RemainsUntilCombined[n].id]}`,
);
} catch {
this.log.debug('RemainsUntilCombined no data found');
}
}
const sensorId = this.contentItems[1]; // get SensorId
let zukunftsDaten = await this.getCalculateRemaining(sensorId, session_id);
if (zukunftsDaten === 'error') {
this.log.debug('Error. No data received.');
await this.noDataReceived(sensor_id);
return false;
}
if (storeJson) {
try {
const json = JSON.stringify(zukunftsDaten, null, 4);
fs.writeFileSync(`${storeDir}/calculatedRemaining.json`, json, 'utf8');
} catch {
this.log.warn(
'calculatedRemaining.json file not saved. Have ioBroker write permissions in the specified folder?',
);
}
if (this.oil_usage) {
try {
const oilJson = JSON.stringify(this.oil_usage, null, 4);
fs.writeFileSync(`${storeDir}/oilUsage.json`, oilJson, 'utf8');
} catch {
this.log.warn(
'oilUsage.json file not saved. Have ioBroker write permissions in the specified folder?',
);
}
} else {
this.log.warn('OilUsage data not available! oilUsage.json file was not saved.');
}
try {
const data1 = JSON.stringify(daten, null, 4);
fs.writeFileSync(`${storeDir}/jsonData.json`, data1, 'utf8');
} catch {
this.log.warn('jsonData.json file not saved. Have ioBroker write permissions in the specified folder?');
}
}
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.CalculatedRemaining`, {
type: 'channel',
common: {
name: 'CalculatedRemaining',
},
native: {},
});
zukunftsDaten = zukunftsDaten['ConsumptionCurveResult'];
let jsonData = '[\n';
let unixTimestamp = 0;
let key = '';
let datum = '';
let n = 0;
for (key in zukunftsDaten) {
datum = key.split('T')[0];
if (n % 14 == 0) {
// Only every 14 days
if (mqtt_active) {
await this.sendMqtt(
sensor_id,
mqtt_active,
client,
`CalculatedRemaining/Today+${String(n).padStart(4, '0')} Days.Date`,
datum,
);
await this.sendMqtt(
sensor_id,
mqtt_active,
client,
`CalculatedRemaining/Today+${String(n).padStart(4, '0')} Days.Liter`,
zukunftsDaten[key].toString(),
);
}
unixTimestamp = new Date(datum).getTime() / 1000;
jsonData = `${jsonData} {"ts": ${unixTimestamp}, "val": ${zukunftsDaten[key].toString()}},\n`;
}
n++;
}
if (mqtt_active) {
await this.sendMqtt(
sensor_id,
mqtt_active,
client,
`CalculatedRemaining/Today+${String(n).padStart(4, '0')} Days.Date`,
datum,
);
try {
await this.sendMqtt(
sensor_id,
mqtt_active,
client,
`CalculatedRemaining/Today+${String(n).padStart(4, '0')} Days.Liter`,
zukunftsDaten[key].toString(),
);
} catch {
this.log.debug('CalculatedRemaining is empty');
}
}
this.log.debug(`${n.toString()} future days saved`);
unixTimestamp = new Date(datum).getTime() / 1000;
try {
jsonData = `${jsonData} {"ts": ${unixTimestamp}, "val": ${zukunftsDaten[key].toString()}}\n]`;
} catch {
this.log.debug('CalculatedRemaining is empty');
}
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.CalculatedRemaining.JsonForEcharts`, {
type: 'state',
common: {
name: 'OilLevelsInTheFuture',
type: 'string',
role: 'value',
unit: '',
read: true,
write: false,
},
native: {},
});
await this.setStateAsync(`${sensor_id.toString()}.CalculatedRemaining.JsonForEcharts`, {
val: jsonData,
ack: true,
});
// OilUsage verarbeiten und senden
if (this.oil_usage && this.oil_usage['Values']) {
const oilUsage = this.oil_usage['Values'];
let oilJsonData = '[\n';
let count = 0;
for (const key in oilUsage) {
const datum = key.split('T')[0];
const liter = oilUsage[key];
const unixTimestamp = new Date(datum).getTime() / 1000;
if (mqtt_active) {
await this.sendMqtt(sensor_id, mqtt_active, client, `OilUsage/${datum}`, `${liter} Ltr.`);
}
oilJsonData += ` {"ts": ${unixTimestamp}, "val": ${liter}},\n`;
count++;
}
this.log.debug(`${count.toString()} OilUsage-Einträge verarbeitet`);
// Letztes Komma ersetzen
if (count > 0) {
oilJsonData = `${oilJsonData.trimEnd().replace(/,$/, '')}\n]`;
} else {
oilJsonData = '[]';
}
// ioBroker-Datenpunkt erstellen
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.OilUsage.JsonForEcharts`, {
type: 'state',
common: {
name: 'OilUsage over time',
type: 'string',
role: 'value',
unit: '',
read: true,
write: false,
},
native: {},
});
// Wert setzen
await this.setStateAsync(`${sensor_id.toString()}.OilUsage.JsonForEcharts`, {
val: oilJsonData,
ack: true,
});
}
await this.setObjectNotExistsAsync(sensor_id.toString(), {
type: 'device',
common: {
name: '',
},
native: {},
});
// Items
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.Items`, {
type: 'channel',
common: {
name: 'Items',
},
native: {},
});
for (let n = 0; n < this.Items.length; n++) {
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.Items.${this.Items[n].id}`, {
type: 'state',
common: {
name: this.Items[n].id,
type: this.Items[n].type,
role: this.Items[n].role,
unit: this.Items[n].unit,
read: true,
write: false,
},
native: {},
});
// If received boolean instead number, set it to -999999
if (this.contentItems[n] === false && this.Items[n].type === 'number') {
this.contentItems[n] = -999999;
this.log.warn(`${this.Items[n].id} == false. Set it to -999999`);
}
// If received boolean instead string, set it to ---
if (this.contentItems[n] === false && this.Items[n].type === 'string') {
this.contentItems[n] = '---';
this.log.warn(`${this.Items[n].id} == false. Set it to ---`);
}
await this.setStateAsync(`${sensor_id.toString()}.Items.${this.Items[n].id}`, {
val: this.contentItems[n],
ack: true,
});
}
// PricingForecast
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.PricingForecast`, {
type: 'channel',
common: {
name: 'PricingForecast',
},
native: {},
});
for (let n = 0; n < this.PricingForecast.length; n++) {
await this.setObjectNotExistsAsync(
`${sensor_id.toString()}.PricingForecast.${this.PricingForecast[n].id}`,
{
type: 'state',
common: {
name: this.PricingForecast[n].name,
type: this.PricingForecast[n].type,
role: this.PricingForecast[n].role,
unit: this.PricingForecast[n].unit,
read: true,
write: false,
},
native: {},
},
);
if (
this.contentPricingForecast[n] == false &&
(this.PricingForecast[n].id == 'PriceComparedToYesterdayPercentage' ||
this.PricingForecast[n].id == 'PriceForecastPercentage')
) {
this.log.debug(`${this.PricingForecast[n].id} omitted, because it's false`);
} else {
await this.setStateAsync(`${sensor_id.toString()}.PricingForecast.${this.PricingForecast[n].id}`, {
val: this.contentPricingForecast[n],
ack: true,
});
}
}
// RemainsUntilCombined
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.RemainsUntilCombined`, {
type: 'channel',
common: {
name: 'RemainsUntilCombined',
},
native: {},
});
for (let n = 0; n < this.RemainsUntilCombined.length; n++) {
await this.setObjectNotExistsAsync(
`${sensor_id.toString()}.RemainsUntilCombined.${this.RemainsUntilCombined[n].id}`,
{
type: 'state',
common: {
name: this.RemainsUntilCombined[n].id,
type: this.RemainsUntilCombined[n].type,
role: this.RemainsUntilCombined[n].role,
unit: this.RemainsUntilCombined[n].unit,
read: true,
write: false,
},
native: {},
},
);
await this.setStateAsync(
`${sensor_id.toString()}.RemainsUntilCombined.${this.RemainsUntilCombined[n].id}`,
{ val: this.contentRemainsUntilCombined[n], ack: true },
);
}
// Yearly oil usage
const entries = Object.entries(this.oil_usage.Values).map(([dateStr, value]) => {
const [y, m] = dateStr.split('T')[0].split('-');
return {
date: new Date(Number(y), Number(m) - 1, 1), // IMMER Monatsanfang
value,
};
});
// Sortieren (aufsteigend)
entries.sort((a, b) => a.date - b.date);
// Alle Jahre extrahieren
const minYear = Math.min(...entries.map(e => e.date.getFullYear()));
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.AnnualConsumption`, {
type: 'channel',
common: {
name: 'AnnualConsumption',
},
native: {},
});
const now = new Date();
const currentYear = now.getFullYear();
for (let year = minYear + 1; year <= currentYear; year++) {
const monthStr = reference_month.toString().padStart(2, '0');
const dpId = `${sensor_id}.AnnualConsumption.${year}-${monthStr}`;
// DP anlegen
await this.setObjectNotExistsAsync(dpId, {
type: 'state',
common: {
name: `Annual consumption for ${year}-${monthStr}`,
type: 'number',
role: 'value',
unit: 'L',
read: true,
write: false,
},
native: {},
});
const value = await this.calcAnnualFor(entries, year, reference_month);
await this.setStateAsync(dpId, value, true);
// MQTT senden
if (mqtt_active) {
const topic = `AnnualConsumption/${year}-${monthStr}`;
if (value === null) {
this.log.debug(`MQTT: ${topic} omitted, because value is null (not enough months)`);
} else {
await this.sendMqtt(sensor_id, mqtt_active, client, topic, value.toString());
this.log.debug(`MQTT sent: ${topic} = ${value}`);
}
}
}
if (mqtt_active) {
client.end();
}
return true;
}
async noDataReceived(sensor_id) {
await this.setObjectNotExistsAsync(`${sensor_id.toString()}.Items.${this.Items[0].id}`, {
type: 'state',
common: {
name: this.Items[0].id,
type: 'boolean',
role: 'indicator',
read: true,
write: false,
},
native: {},
});
await this.setStateAsync(`${sensor_id.toString()}.Items.${this.Items[0].id}`, { val: false, ack: true });
this.log.error('No data received');
this.terminate(1);
}
onUnload(callback) {
try {
callback();
} catch {
callback();
}
}
}
if (require.main !== module) {
module.exports = options => new Heizoel24Mex(options);
} else {
new Heizoel24Mex();
}