iobroker.netatmo-energy
Version:
Get and set data using Netatmo Energy API
1,397 lines (1,345 loc) • 202 kB
JavaScript
/* eslint-disable jsdoc/require-param-description */
'use strict';
// Load modules
const utils = require('@iobroker/adapter-core');
const abort = require('abort-controller');
const fs = require('fs');
const fetch = require('fetch');
const mytools = require('./lib/mytools');
const glob = require('./lib/globals');
//const { error } = require('console');
// Main Class
class NetatmoEnergy extends utils.Adapter {
/**
* @param [options]
*/
//Class Constructor
constructor(options) {
super(
Object.assign(options || {}, {
name: 'netatmo-energy',
}),
);
this.on('ready', this.onReady.bind(this));
this.on('stateChange', this.onStateChange.bind(this));
//this.on('objectChange', this.onObjectChange.bind(this));
this.on('message', this.onMessage.bind(this));
this.on('unload', this.onUnload.bind(this));
//Objects
this.globalDevice = null;
this.globalAPIChannel = '';
this.globalAPIChannelTrigger = null;
this.globalAPIChannelMessages = null;
this.globalAPIStatus = null;
this.globalNetatmo_AccessToken = null;
this.globalRefreshToken = null;
this.globalNetatmo_ExpiresIn = 0;
this.globalScheduleObjects = {};
this.globalScheduleList = {};
this.globalScheduleListArray = [];
this.globalRoomId = {};
this.globalRoomIdArray = [];
this.globalDeviceId = {};
this.globalDeviceIdArray = [];
this.globalWindowIndicators = [];
//Language
this.systemlang = 'de';
//Notifications
this.signal = {};
this.telegram = {};
this.whatsapp = {};
this.pushover = {};
this.email = {};
//Adapter status
this.FetchAbortController = new abort.AbortController();
this.RefreshTokenInterval = null;
this.adapterIntervals = [];
this.mySubscribedStates = [];
this.AdapterStarted = false;
this.SensorTimeouts = [];
//Authentication
this.scope = '';
this.storedOAuthData = {};
this.storedOAuthStates = {};
this.dataDir = '';
}
//Is called when databases are connected and adapter received configuration
async onReady() {
//Check adapter configuration
if (!this.config.HomeId || !this.config.ClientId || !this.config.ClientSecretID) {
this.log.error('*** Adapter deactivated, missing adaper configuration !!! ***');
this.setForeignState(`system.adapter.${this.namespace}.alive`, false);
return;
}
//Get settings
this.getForeignObject('system.config', (err, obj) => {
if (obj) {
this.systemLang = obj.common.language;
}
});
await this.initAdapter(this.systemLang);
await this._subscribeForeign(this.namespace, false);
await this.startAdapter();
}
//Subscribe foreign Sensors
async _subscribeForeign(own_namespace, only_unsubscribe) {
for (const sensor_attribs of this.config.sensors) {
if (
// @ts-expect-error Window_sensor is available
sensor_attribs.window_sensor &&
// @ts-expect-error Window_sensor is available
sensor_attribs.window_sensor != null &&
// @ts-expect-error Window_sensor is available
sensor_attribs.window_sensor != undefined
) {
// @ts-expect-error Window_sensor is available
if (sensor_attribs.window_sensor.search(own_namespace) >= 0) {
//nothing to do
} else {
// @ts-expect-error Window_sensor is available
await this.unsubscribeForeignStatesAsync(sensor_attribs.window_sensor);
if (!only_unsubscribe) {
// @ts-expect-error Window_sensor is available
await this.subscribeForeignStatesAsync(sensor_attribs.window_sensor);
}
}
}
}
}
//Get THAT
_getThat() {
return this;
}
//Tocken intervall to refresh
_setTokenIntervall(setTimer) {
const that = this._getThat();
const refreshTokenbyTimer = function () {
that._authenticate_refresh_token(that.storedOAuthData.redirect_uri, that.storedOAuthData.code);
};
if (this.RefreshTokenInterval != null) {
clearInterval(this.RefreshTokenInterval);
this.RefreshTokenInterval = null;
}
if (setTimer || this.globalNetatmo_ExpiresIn > 0) {
this.RefreshTokenInterval = setInterval(refreshTokenbyTimer, this.globalNetatmo_ExpiresIn);
}
}
//Get stored token from adapter data directory
async _getStoredToken() {
this.dataDir = utils.getAbsoluteInstanceDataDir(this);
try {
if (!fs.existsSync(this.dataDir)) {
fs.mkdirSync(this.dataDir);
}
if (fs.existsSync(`${this.dataDir}/tokens.json`)) {
const tokens = JSON.parse(fs.readFileSync(`${this.dataDir}/tokens.json`, 'utf8'));
if (tokens.client_id !== this.config.ClientId) {
this.log.error(
mytools.tl('Stored tokens belong to the different client ID', this.systemLang) +
tokens.client_id +
mytools.tl('and not to the configured ID ... deleting', this.systemLang),
);
await this.sendRequestNotification(
null,
glob.ErrorNotification,
'Get Token',
mytools.tl('Stored tokens belong to the different client ID', this.systemLang) +
tokens.client_id +
mytools.tl('and not to the configured ID ... deleting', this.systemLang),
);
fs.unlinkSync(`${this.dataDir}/tokens.json`);
} else {
if (!tokens.access_token || !tokens.refresh_token) {
this.globalNetatmo_AccessToken = null;
this.globalRefreshToken = null;
this.scope = '';
this.log.error(
mytools.tl(
'No tokens stored - Please use authentication in adapter config!',
this.systemLang,
),
);
await this.sendRequestNotification(
null,
glob.ErrorNotification,
'Get Token',
mytools.tl(
'No tokens stored - Please use authentication in adapter config!',
this.systemLang,
),
);
} else {
this.globalNetatmo_AccessToken = tokens.access_token;
this.globalRefreshToken = tokens.refresh_token;
this.scope = tokens.scope;
this.log.debug(
mytools.tl('Using stored tokens to initialize ... ', this.systemLang) +
JSON.stringify(tokens),
);
if (tokens.scope !== this.scope) {
this.log.warn(
mytools.tl('Stored tokens have different scope', this.systemLang) +
tokens.scope +
mytools.tl('and not the configured scope', this.systemLang) +
this.scope +
mytools.tl('... If you miss data please authenticate again!', this.systemLang),
);
await this.sendRequestNotification(
null,
glob.WarningNotification,
'Get Token',
mytools.tl('Stored tokens have different scope', this.systemLang) +
tokens.scope +
mytools.tl('and not the configured scope', this.systemLang) +
this.scope +
mytools.tl('... If you miss data please authenticate again!', this.systemLang),
);
}
}
}
} else {
this.globalNetatmo_AccessToken = null;
this.globalRefreshToken = null;
this.scope = '';
this.log.error(
mytools.tl('No tokens stored - Please use authentication in adapter config!', this.systemLang),
);
await this.sendRequestNotification(
null,
glob.ErrorNotification,
'Get Token',
mytools.tl('No tokens stored - Please use authentication in adapter config!', this.systemLang),
);
}
} catch (err) {
let _errormessage = 'Error';
if (err instanceof Error) {
_errormessage = err.message;
}
this.log.error(mytools.tl('Error reading stored tokens: ', this.systemLang) + _errormessage);
await this.sendRequestNotification(
null,
glob.ErrorNotification,
'Get Token',
mytools.tl('Error reading stored tokens: ', this.systemLang) + _errormessage,
);
}
}
//Save token to file system
_saveToken() {
const tokenData = {
access_token: this.globalNetatmo_AccessToken,
refresh_token: this.globalRefreshToken,
scope: this.scope,
client_id: this.config.ClientId,
};
try {
fs.writeFileSync(`${this.dataDir}/tokens.json`, JSON.stringify(tokenData), 'utf8');
this.log.debug(`${mytools.tl('Token saved', this.systemLang)}: ${this.dataDir}/tokens.json`);
} catch (err) {
this.log.error(mytools.tl('Cannot write token file: ', this.systemLang) + err);
}
}
//Get token from Netatmo
_getToken(HomeId, ClientId, ClientSecretID, redirect_uri, code) {
this.globalNetatmo_AccessToken = null;
let payload = '';
if (!this.globalRefreshToken) {
payload = `code=${code}&redirect_uri=${redirect_uri}&grant_type=authorization_code${
glob.payload_client_id
}${ClientId}${glob.payload_client_secret}${ClientSecretID}${glob.payload_scope}${this.scope}`;
} else {
payload = `grant_type=refresh_token${glob.payload_refresh_token}${this.globalRefreshToken}${
glob.payload_client_id
}${ClientId}${glob.payload_client_secret}${ClientSecretID}`;
}
return this._myFetch(glob.Netatmo_TokenRequest_URL, payload);
}
//Authenticate refresh token
async _authenticate_refresh_token(redirect_uri, code) {
this._setTokenIntervall(false);
this.log.info(mytools.tl('Start Token-Refresh', this.systemLang));
await this._getToken(this.config.HomeId, this.config.ClientId, this.config.ClientSecretID, redirect_uri, code)
.then(async tokenvalues => {
this.globalNetatmo_AccessToken = tokenvalues.access_token;
this.globalNetatmo_ExpiresIn = tokenvalues.expires_in + new Date().getTime() / 1000 - 20;
this.globalRefreshToken = tokenvalues.refresh_token;
await this._saveToken();
this._setTokenIntervall(true);
this.log.debug(mytools.tl('Token OK:', this.systemLang) + glob.blank + this.globalNetatmo_AccessToken);
await this.startAdapter();
})
.catch(async error => {
this.globalNetatmo_AccessToken = null;
this.globalRefreshToken = null;
this.globalNetatmo_ExpiresIn = 0;
await this._saveToken();
this.log.error(
mytools.tl('Did not get a tokencode:', this.systemLang) +
(error !== undefined && error !== null
? `${glob.blank + error.error}: ${error.error_description}`
: ''),
);
await this.sendRequestNotification(
null,
glob.ErrorNotification,
mytools.tl('API Token', this.systemLang),
mytools.tl('Did not get a tokencode:', this.systemLang) +
(error !== undefined && error !== null
? `${glob.blank + error.error}: ${error.error_description}`
: ''),
);
});
}
//Start initialization adapter
async initAdapter(systemLang) {
// define global constants
this.globalDevice = mytools.getDP([this.namespace, glob.Device_APIRequests]);
this.globalAPIChannel = mytools.getDP([this.namespace, glob.Device_APIRequests, glob.Channel_APIRequests]);
this.globalAPIChannelTrigger = mytools.getDP([this.namespace, glob.Device_APIRequests, glob.Channel_trigger]);
this.globalAPIChannelMessages = mytools.getDP([this.namespace, glob.Device_APIRequests, glob.Channel_messages]);
this.globalAPIChannelStatus = mytools.getDP([
this.namespace,
glob.Device_APIRequests,
glob.Channel_Status_API_running,
]);
try {
this.signal = {
type: 'message',
instance: this.config.signalInstance,
systemLang,
};
} catch (error) {
this.log.error(mytools.tl('Signal: ', this.systemLang) + error);
}
try {
this.telegram = {
type: 'message',
instance: this.config.telegramInstance,
SilentNotice: this.config.telegramSilentNotice,
User: this.config.telegramUser,
systemLang,
};
} catch (error) {
this.log.error(mytools.tl('Telegram: ', this.systemLang) + error);
}
try {
this.whatsapp = {
type: 'message',
instance: this.config.whatsappInstance,
systemLang,
};
} catch (error) {
this.log.error(mytools.tl('WhatsApp: ', this.systemLang) + error);
}
try {
this.pushover = {
type: 'message',
instance: this.config.pushoverInstance,
SilentNotice: this.config.pushoverSilentNotice,
deviceID: this.config.pushoverDeviceID,
systemLang,
};
} catch (error) {
this.log.error(mytools.tl('Pushover: ', this.systemLang) + error);
}
try {
this.email = {
type: 'message',
instance: this.config.emailInstance,
emailReceiver: this.config.emailReceiver,
emailSender: this.config.emailSender,
systemLang,
};
} catch (error) {
this.log.error(mytools.tl('E-Mail: ', this.systemLang) + error);
}
//Get stored token
await this._getStoredToken();
}
//Start initialization
async startAdapter() {
// Check if it is neccessary
if (this.AdapterStarted == true) {
return;
}
// Set polling intervall
const refreshtime = this.config.refreshstates;
const that = this._getThat();
if (this.globalNetatmo_AccessToken == null || this.globalRefreshToken == null) {
return;
}
const updateAPIStatus = async function () {
that.log.debug(
mytools.tl('API Request homestatus sent to API each', that.systemLang) +
glob.blank +
refreshtime +
mytools.tl('sec', that.systemLang),
);
await that.RefreshWholeStructure(true);
};
if (refreshtime && refreshtime > 0) {
that.log.info(mytools.tl('Refresh homestatus interval', that.systemLang) + glob.blank + refreshtime * 1000);
// Timer
that.adapterIntervals.push(setInterval(updateAPIStatus, refreshtime * 1000));
}
//Start initial requests for adapter
this.AdapterStarted = true;
await this.createEnergyAPP();
await this.RefreshWholeStructure(false);
}
//Create APP Requests device
async createEnergyAPP() {
// Device energyAPP
await this.createMyDevice(this.globalDevice, 'Netatmo Energy APP');
// Channel APIRequests
await this.createMyChannel(this.globalAPIChannel, 'Requests for Netatmo Energy API');
// Channel setthomesdata
await this.createMyChannel(mytools.getDP([this.globalAPIChannel, glob.Channel_homesdata]), 'API homesdata');
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_homesdata, glob.Channel_parameters]),
'parameters',
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_homesdata, glob.APIRequest_homesdata]),
'homesdata',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_homesdata,
glob.Channel_parameters,
glob.State_gateway_types,
]),
'gateway types',
'',
true,
'text',
true,
true,
glob.List_gateway_type,
false,
false,
);
await this.subscribeStates(
mytools.getDP([this.globalAPIChannel, glob.Channel_homesdata, glob.APIRequest_homesdata]),
);
// Channel setthomestatus
await this.createMyChannel(mytools.getDP([this.globalAPIChannel, glob.Channel_homestatus]), 'API homesstatus');
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_homestatus, glob.Channel_parameters]),
'parameters',
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_homestatus,
glob.Channel_parameters,
glob.State_device_types,
]),
'device types',
'',
true,
'text',
true,
true,
glob.List_device_types,
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_homestatus, glob.APIRequest_homestatus]),
'homesstatus',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.subscribeStates(
mytools.getDP([this.globalAPIChannel, glob.Channel_homestatus, glob.APIRequest_homestatus]),
);
// Channel setthermmode
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_setthermmode]),
'API setthermmode',
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_setthermmode,
`${glob.APIRequest_setthermmode}_${glob.APIRequest_setthermmode_schedule}`,
]),
'setthermmode_schedule',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_setthermmode,
`${glob.APIRequest_setthermmode}_${glob.APIRequest_setthermmode_hg}`,
]),
'setthermmode_hg',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_setthermmode,
`${glob.APIRequest_setthermmode}_${glob.APIRequest_setthermmode_away}`,
]),
'setthermmode_away',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.subscribeStates(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_setthermmode,
`${glob.APIRequest_setthermmode}_${glob.APIRequest_setthermmode_schedule}`,
]),
);
await this.subscribeStates(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_setthermmode,
`${glob.APIRequest_setthermmode}_${glob.APIRequest_setthermmode_hg}`,
]),
);
await this.subscribeStates(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_setthermmode,
`${glob.APIRequest_setthermmode}_${glob.APIRequest_setthermmode_away}`,
]),
);
// Channel synchomeschedule
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_synchomeschedule]),
'API synchomeschedule',
);
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_synchomeschedule, glob.Channel_parameters]),
'parameters',
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_synchomeschedule, glob.APIRequest_synchomeschedule]),
'synchomeschedule',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_synchomeschedule,
glob.Channel_parameters,
glob.State_schedule_id,
]),
'Id of the schedule',
'',
true,
'text',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_synchomeschedule,
glob.Channel_parameters,
glob.State_zones,
]),
'Array of data used to define time periods to build a schedule. More info on the Thermostat page. id of zone | type of zone | Name of zone | Temperature',
'',
true,
'list',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_synchomeschedule,
glob.Channel_parameters,
glob.State_timetable,
]),
'Array describing the timetable. More info on the Thermostat page. ID of the zone - offset in minutes since Monday 00:00:01',
'',
true,
'list',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_synchomeschedule,
glob.Channel_parameters,
glob.State_hg_temp,
]),
'Frost guard temperature value',
7,
true,
'number',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_synchomeschedule,
glob.Channel_parameters,
glob.State_away_temp,
]),
'Away temperature value',
12,
true,
'number',
true,
true,
'',
false,
false,
);
await this.subscribeStates(
mytools.getDP([this.globalAPIChannel, glob.Channel_synchomeschedule, glob.APIRequest_synchomeschedule]),
);
// Channel createnewhomeschedule
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_createnewhomeschedule]),
'API createnewhomeschedule',
);
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_createnewhomeschedule, glob.Channel_parameters]),
'parameters',
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_createnewhomeschedule,
glob.APIRequest_createnewhomeschedule,
]),
'createnewhomeschedule',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_createnewhomeschedule,
glob.Channel_parameters,
glob.State_name,
]),
'Name of the schedule',
'',
true,
'text',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_createnewhomeschedule,
glob.Channel_parameters,
glob.State_zones,
]),
'Array of data used to define time periods to build a schedule. More info on the Thermostat page. id of zone | type of zone | Name of zone | Temperature',
'',
true,
'list',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_createnewhomeschedule,
glob.Channel_parameters,
glob.State_timetable,
]),
'Array describing the timetable. More info on the Thermostat page. ID of the zone - offset in minutes since Monday 00:00:01',
'',
true,
'list',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_createnewhomeschedule,
glob.Channel_parameters,
glob.State_hg_temp,
]),
'Frost guard temperature value',
7,
true,
'number',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_createnewhomeschedule,
glob.Channel_parameters,
glob.State_away_temp,
]),
'Away temperature value',
12,
true,
'number',
true,
true,
'',
false,
false,
);
await this.subscribeStates(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_createnewhomeschedule,
glob.APIRequest_createnewhomeschedule,
]),
);
// Channel getroommeasure
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_getroommeasure]),
'API getroommeasure',
);
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_getroommeasure, glob.Channel_parameters]),
'parameters',
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_getroommeasure, glob.APIRequest_getroommeasure]),
'getroommeasure',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_getroommeasure, glob.State_response]),
'Request response',
'',
true,
'text',
false,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getroommeasure,
glob.Channel_parameters,
glob.State_room_id,
]),
'Id of room',
'',
true,
'text',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getroommeasure,
glob.Channel_parameters,
glob.State_scale,
]),
'Timelapse between two measurements',
'',
true,
'text',
true,
true,
glob.List_scale,
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getroommeasure,
glob.Channel_parameters,
glob.State_type,
]),
'Type of data to be returned. Setpoint temperature is only available for scales from 30 to 3hours and min/max temp and dates for scales from 1day to 1month.',
'',
true,
'text',
true,
true,
glob.List_type_rm,
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getroommeasure,
glob.Channel_parameters,
glob.State_date_begin,
]),
'Timestamp of the first measure to retrieve. Default is null',
'',
true,
'number',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getroommeasure,
glob.Channel_parameters,
glob.State_date_end,
]),
'Timestamp of the last measure to retrieve (default and max are 1024). Default is null',
'',
true,
'number',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getroommeasure,
glob.Channel_parameters,
glob.State_limit,
]),
'Maximum number of measurements (default and max Are 1024)',
'',
true,
'number',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getroommeasure,
glob.Channel_parameters,
glob.State_optimize,
]),
"Determines the format of the answer. Default is true. For mobile apps we recommend True and False if bandwidth isn't an issue as it is easier to parse",
false,
true,
'indicator',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getroommeasure,
glob.Channel_parameters,
glob.State_real_time,
]),
'real_time',
false,
true,
'indicator',
true,
true,
'',
false,
false,
);
await this.subscribeStates(
mytools.getDP([this.globalAPIChannel, glob.Channel_getroommeasure, glob.APIRequest_getroommeasure]),
);
// Channel getmeasure
await this.createMyChannel(mytools.getDP([this.globalAPIChannel, glob.Channel_getmeasure]), 'API getmeasure');
await this.createMyChannel(
mytools.getDP([this.globalAPIChannel, glob.Channel_getmeasure, glob.Channel_parameters]),
'parameters',
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_getmeasure, glob.APIRequest_getmeasure]),
'getmeasure',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_getmeasure, glob.State_response]),
'Request response',
'',
true,
'text',
false,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getmeasure,
glob.Channel_parameters,
glob.State_device_id,
]),
'Mac adress of the device',
'',
true,
'text',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_getmeasure, glob.Channel_parameters, glob.State_scale]),
'Timelapse between two measurements',
'',
true,
'text',
true,
true,
glob.List_scale,
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_getmeasure, glob.Channel_parameters, glob.State_type]),
'Type of data to be returned. Setpoint temperature is only available for scales from 30 to 3hours and min/max temp and dates for scales from 1day to 1month.',
'',
true,
'text',
true,
true,
glob.List_type_mm,
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getmeasure,
glob.Channel_parameters,
glob.State_date_begin,
]),
'Timestamp of the first measure to retrieve. Default is null',
'',
true,
'number',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getmeasure,
glob.Channel_parameters,
glob.State_date_end,
]),
'Timestamp of the last measure to retrieve (default and max are 1024). Default is null',
'',
true,
'number',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannel, glob.Channel_getmeasure, glob.Channel_parameters, glob.State_limit]),
'Maximum number of measurements (default and max Are 1024)',
'',
true,
'number',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getmeasure,
glob.Channel_parameters,
glob.State_optimize,
]),
"Determines the format of the answer. Default is true. For mobile apps we recommend True and False if bandwidth isn't an issue as it is easier to parse",
false,
true,
'indicator',
true,
true,
'',
false,
false,
);
await this.createNetatmoStructure(
mytools.getDP([
this.globalAPIChannel,
glob.Channel_getmeasure,
glob.Channel_parameters,
glob.State_real_time,
]),
'real_time',
false,
true,
'indicator',
true,
true,
'',
false,
false,
);
await this.subscribeStates(
mytools.getDP([this.globalAPIChannel, glob.Channel_getmeasure, glob.APIRequest_getmeasure]),
);
// Channel trigger
await this.createMyChannel(this.globalAPIChannelTrigger, 'API trigger');
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannelTrigger, glob.Trigger_applychanges]),
'trigger to send changes to Netatmo Cloud',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.subscribeStates(mytools.getDP([this.globalAPIChannelTrigger, glob.Trigger_applychanges]));
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannelTrigger, glob.Trigger_refresh_all]),
'trigger to refresh homestructure from Netatmo Cloud',
false,
true,
'button',
true,
true,
'',
false,
false,
);
await this.subscribeStates(mytools.getDP([this.globalAPIChannelTrigger, glob.Trigger_refresh_all]));
await this.createMyChannel(mytools.getDP([this.globalAPIChannelStatus]), 'API Request status');
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannelStatus, glob.State_Status_API_running]),
'API running status ',
false,
true,
'indicator',
false,
true,
'',
false,
true,
);
// Channel message
await this.createMyChannel(this.globalAPIChannelMessages, 'API messages');
await this.createNetatmoStructure(
mytools.getDP([this.globalAPIChannelMessages, glob.Messages_Text]),
'message from the Netatmo Energy APP',
'',
true,
'text',
true,
true,
'',
false,
false,
);
}
//Send notifications
async sendNotification(errortype, subject, messageText) {
let MesgText = '';
let Subject = '';
if (!this.config.notificationEnabled) {
return;
}
if (errortype != glob.SendNotification) {
if (
!(
(this.config.notifications.substring(0, 1) != '0' && errortype == glob.InfoNotification) ||
(this.config.notifications.substring(1, 2) != '0' && errortype == glob.WarningNotification) ||
(this.config.notifications.substring(2, 3) != '0' && errortype == glob.ErrorNotification)
)
) {
return;
}
}
switch (this.config.notificationsType) {
//email
case glob.NotificationEmail:
if (this.email.instance !== '' && this.email.instance !== null && this.email.instance !== undefined) {
MesgText = `Netatmo Energy:\n${messageText}`;
Subject =
subject !== undefined && subject !== null ? subject : mytools.tl('Message', this.systemLang);
this.sendTo(this.email.instance, 'send', {
text: MesgText,
to: this.email.emailReceiver,
subject: Subject,
from: this.email.emailSender,
});
return;
}
break;
//pushover
case glob.NotificationPushover:
if (
this.pushover.instance !== '' &&
this.pushover.instance !== null &&
this.pushover.instance !== undefined
) {
MesgText = `Netatmo Energy:\n${messageText}`;
Subject =
subject !== undefined && subject !== null ? subject : mytools.tl('Message', this.systemLang);
if (this.pushover.SilentNotice === 'true' || this.pushover.SilentNotice === true) {
this.sendTo(this.pushover.instance, 'send', {
message: MesgText,
sound: '',
priority: -1,
title: Subject,
device: this.pushover.deviceID,
});
} else {
this.sendTo(this.pushover.instance, 'send', {
message: MesgText,
sound: '',
title: Subject,
device: this.pushover.deviceID,
});
}
}
break;
//telegram
case glob.NotificationTelegram:
if (
this.telegram.instance !== '' &&
this.telegram.instance !== null &&
this.telegram.instance !== undefined
) {
MesgText = `Netatmo Energy:\n${
subject !== undefined && subject !== null ? `${subject} - ${messageText}` : messageText
}`;
if (
this.telegram.User &&
(this.telegram.User === 'allTelegramUsers' || this.telegram.User === '')
) {
this.sendTo(this.telegram.instance, 'send', {
text: MesgText,
disable_notification: this.telegram.SilentNotice,
});
} else {
this.sendTo(this.telegram.instance, 'send', {
user: this.telegram.User,
text: MesgText,
disable_notification: this.telegram.SilentNotice,
});
}
}
break;
//signal
case glob.NotificationSignal:
if (
this.signal.instance !== '' &&
this.signal.instance !== null &&
this.signal.instance !== undefined
) {
MesgText = `Netatmo Energy: ${
subject !== undefined && subject !== null ? `${subject} - ${messageText}` : messageText
}`;
this.sendTo(this.signal.instance, 'send', { text: MesgText });
}
break;
//whatsapp
case glob.NotificationWhatsapp:
if (
this.whatsapp.instance !== '' &&
this.whatsapp.instance !== null &&
this.whatsapp.instance !== undefined
) {
MesgText = `Netatmo Energy:\n${
subject !== undefined && subject !== null ? `${subject} - ${messageText}` : messageText
}`;
this.sendTo(this.whatsapp.instance, 'send', { text: MesgText });
}
break;
}
//Message to datapoint
if (MesgText && MesgText != '') {
await this.setState(mytools.getDP([this.globalAPIChannelMessages, glob.Messages_Text]), MesgText, true);
}
}
//Send notification after request
async sendRequestNotification(NetatmoRequest, NotificationType, addText, longText) {
switch (NetatmoRequest) {
//set requests
case glob.APIRequest_setroomthermpoint:
await this.sendNotification(
NotificationType,
NetatmoRequest,
mytools.tl('Target temperature changed', this.systemLang) +
(this.config.NoticeType == glob.NoticeTypeLong && longText != '' ? `\n${longText}` : ''),
);
break;
case glob.APIRequest_setthermmode:
await this.sendNotification(
NotificationType,
NetatmoRequest,
mytools.tl('Mode for your heating system was set to', this.systemLang) +
glob.blank +
addText +
(this.config.NoticeType == glob.NoticeTypeLong && longText != '' ? `\n${longText}` : ''),
);
break;
case glob.APIRequest_switchhomeschedule:
await this.sendNotification(
NotificationType,
NetatmoRequest,
mytools.tl('Changed schedule for your heating system to', this.systemLang) +
glob.blank +
addText +
(this.config.NoticeType == glob.NoticeTypeLong && longText != '' ? `\n${longText}` : ''),
);
break;
case glob.APIRequest_synchomeschedule:
await this.sendNotification(
NotificationType,
NetatmoRequest,
mytools.tl('Changed weekly schedule', this.systemLang) +
glob.blank +
addText +
glob.blank +
mytools.tl('for your heating system', this.systemLang) +
(this.config.NoticeType == glob.NoticeTypeLong && longText != '' ? `\n${longText}` : ''),
);
break;
case glob.APIRequest_createnewhomeschedule:
await this.sendNotification(
NotificationType,
NetatmoRequest,
mytools.tl('Create a thermostat weekly schedule', this.systemLang) +
glob.blank +
addText +
glob.blank +
mytools.tl('for your heating system', this.systemLang) +
(this.config.NoticeType == glob.NoticeTypeLong && longText != '' ? `\n${longText}` : ''),
);
break;
//get requests
case glob.APIRequest_getroommeasure:
await this.getNameofRoom(
addText.substring(
addText.lastIndexOf(glob.payload_room_id) + 9,
addText.lastIndexOf(glob.payload_scale),
),
);
break;
case glob.APIRequest_getmeasure:
await this.getNameofDevice(
addText.substring(
addText.lastIndexOf(glob.payload_device_id) + 11,
addText.lastIndexOf(glob.p