iobroker.fb-checkpresence
Version:
The adapter checks the presence of family members over the fritzbox. You must fill in the name of the family member and the mac-address of the used device. The comment is optional and you can enable or disable the family member. The datapoint based on the
1,092 lines (1,031 loc) • 135 kB
JavaScript
'use strict';
/*
* Created with @iobroker/create-adapter vunknown
*/
// The adapter-core module gives you access to the core ioBroker functions
const utils = require('@iobroker/adapter-core');
// load your modules here, e.g.:
//const dateFormat = require('dateformat');
// own libraries
const fb = require('./lib/fb');
const obj = require('./lib/objects');
const dateFormat = require('./lib/dateformat/dateformat');
class Warn extends Error {
constructor(message) {
super(message);
this.name = 'Warning';
this.toString = function () {
return `${this.name}: ${this.message}`;
};
}
}
class FbCheckpresence extends utils.Adapter {
/**
* @param {Partial<utils.AdapterOptions>} options - adapter options
*/
constructor(options) {
super({
...options,
name: 'fb-checkpresence',
});
//events
this.on('ready', this.onReady.bind(this));
//this.on('objectChange', this.onObjectChange);
this.on('stateChange', this.onStateChange.bind(this));
this.on('message', this.onMessage.bind(this));
this.on('unload', this.onUnload.bind(this));
//constants
this.urn = 'urn:dslforum-org:service:';
this.HTML =
'<table class="mdui-table"><thead><tr><th>Name</th><th>Status</th><th>Kommt</th><th>Geht</th></tr></thead><tbody>';
this.HTML_HISTORY = '<table class="mdui-table"><thead><tr><th>Status</th><th>Date</th></tr></thead><tbody>';
this.HTML_END = '</body></table>';
this.HTML_GUEST =
'<table class="mdui-table"><thead><tr><th>Hostname</th><th>IPAddress</th><th>MACAddress</th></tr></thead><tbody>';
this.HTML_FB =
'<table class="mdui-table"><thead><tr><th>Hostname</th><th>IPAddress</th><th>MACAddress</th><th>Active</th><th>Type</th></tr></thead><tbody>';
this.FORBIDDEN_CHARS = /[\][.*,;'"`<>\\?\s]+/g;
this.errorCntMax = 10;
this.allDevices = [];
this.adapterStates = [];
this.memberStates = [];
this.historyAlive = { val: 'false' }; //false;
this.hosts = null;
this.jsonTab;
this.htmlTab;
this.enabled = true;
this.errorCnt = 0;
this.Fb = null;
this.tout = null;
this.suppressMesg = false;
this.suppressMesgEmptyHostname = false;
this.suppressArr = [];
this.triggerActive = false;
}
/**
* @param {any | string} error - error object
* @param {string} title - error name
*/
errorHandler(error, title) {
//if(adapter === null) adapter = this;
if (typeof error === 'string') {
if (error === undefined) {
this.log.warn(`${title} error unknown`);
} else {
this.log.warn(`${title}: ${error}`);
}
} else if (typeof error === 'object') {
if (error instanceof TypeError) {
if (error.name === undefined || error.message === undefined) {
this.log.warn(title); // + ' ' + JSON.stringify(error));
} else {
this.log.warn(`${title} ${error.name}: ${error.message}`);
}
} else if (error instanceof Error) {
if (error.message == 'NoSuchEntryInArray' && this.suppressMesg == false) {
this.log.warn(
`${title} ${error.name}: ${
error.message
} -> please check entry (mac, ip, hostname) in configuration! It is not listed in the Fritzbox device list`,
);
this.suppressMesg = true;
}
if (error.message.includes('EHOSTUNREACH')) {
this.log.warn(
`${title} ${error.name}: ${
error.message
} -> please check fritzbox connection! Ip-address in configuration correct?`,
);
}
if (error.message != 'NoSuchEntryInArray' && !error.message.includes('EHOSTUNREACH')) {
if (error.name === undefined || error.message === undefined) {
this.log.warn(title); // + ' ' + JSON.stringify(error));
} else {
this.log.warn(`${title} ${error.name}: ${error.message}`);
}
}
} else {
this.log.warn(`${title} ${error.name}: ${error.message}`);
}
}
}
/**
* @param {number | undefined} milliseconds - delay in milli seconds
*/
_sleep(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
createHTMLTableRow(dataArray) {
return `<tr>${dataArray.map(data => `<td>${data}</td>`).join('')}</tr>`;
}
getAdapterState(id) {
try {
const ind = this.adapterStates.findIndex(x => x.id == id);
if (ind != -1) {
return true;
}
return false;
} catch (error) {
this.errorHandler(error, 'getAdapterState: ');
}
}
/**
* @param {string | any[]} items - fb-device objects of the adapter
*/
async resyncFbObjects(items) {
try {
// Get all fb-device objects of this adapter
if (items && this.config.syncfbdevices == true) {
const devices = await this.getDevicesAsync();
for (const id in devices) {
if (devices[id] != undefined && devices[id].common != undefined) {
const dName = devices[id].common.name;
const shortName = dName.replace('fb-devices.', '');
let found = false;
if (dName.includes('fb-devices.')) {
for (let i = 0; i < items.length; i++) {
let hostName = items[i]['HostName'];
hostName = hostName.replace(this.FORBIDDEN_CHARS, '-');
if (shortName == hostName) {
found = true;
break;
}
}
if (found == false && !dName.includes('whitelist')) {
const states = await this.getStatesAsync(`${dName}.*`);
for (const idS in states) {
await this.delObjectAsync(idS);
await this.delStateAsync(idS);
}
const ch = await this.getChannelsOfAsync();
for (const c in ch) {
if (ch[c]._id.includes(`${dName}.`)) {
await this.delObjectAsync(ch[c]._id);
}
}
await this.delObjectAsync(devices[id]._id);
this.log.info(`device <${devices[id]._id}> successfully deleted`);
}
}
}
}
const adapterObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
if (adapterObj) {
adapterObj.native.syncfbdevices = false;
this.config.syncfbdevices = false;
await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, adapterObj);
this.log.info('fb-devices synchronized successfully');
} else {
this.log.info('resyncFbObjects: could not clear the resync checkbox! ');
}
}
} catch (error) {
this.errorHandler(error, 'resyncFbObjects: ');
}
}
async checkDevices() {
try {
if (this && this.Fb && (this.config.fbdevices === true || this.config.guestinfo === true)) {
await this.Fb.getDeviceList();
if (this.Fb && this.Fb.deviceList) {
if (this.config.fbdevices === true) {
this.setDeviceStates();
}
await this.getAllFbObjects();
if (this.hosts) {
//this.log.warn('devicelist2: ' + JSON.stringify(this.hosts));
if (this.config.enableWl == true) {
await this.getWlBlInfo();
}
await this.getDeviceInfo();
if (
this.Fb.GETMESHPATH != null &&
this.Fb.GETMESHPATH == true &&
this.config.meshinfo == true
) {
await this.Fb.getMeshList();
}
if (this.Fb.meshList && this.config.meshinfo == true) {
await this.getMeshInfo();
}
}
}
}
} catch (error) {
this.errorHandler(error, 'checkDevices: ');
}
}
async connCheck() {
try {
if (this && this.Fb && (await this.Fb.connectionCheck())) {
await this.setStateChangedAsync('info.connection', { val: true, ack: true });
} else {
await this.setStateChangedAsync('info.connection', { val: false, ack: true });
}
await this.setStateChangedAsync('info.lastUpdate', { val: new Date().toString(), ack: true });
} catch (error) {
this.errorHandler(error, 'connCheck: ');
}
}
/**
* @param {number} cnt1 - loop counter family members
* @param {number} cnt2 - loop counter fb-devices
* @param {number | undefined} int1 - max count family members
* @param {number | undefined} int2 - max count fb-devices
*/
async loop(cnt1, cnt2, int1, int2) {
while (this.enabled === true) {
this.tout = setTimeout(() => {
this.log.error('cycle error! Adapter restarted');
this.startAdapter();
}, 300000);
let time = null;
let work = null;
try {
await this._sleep(1000);
cnt1++;
cnt2++;
work = process.hrtime();
cnt1 < cnt2 && cnt1 >= int1 ? await this.connCheck() : cnt2 >= int2 ? await this.connCheck() : null;
if (cnt1 >= int1) {
cnt1 = 0;
await this.checkPresence(false);
time = process.hrtime(work);
this.log.debug(`loop family ends after ${time} s`);
}
if (cnt2 >= int2) {
cnt2 = 0;
if (this.config.extip === true) {
await this.setStateChangedAsync('info.extIp', { val: await this.Fb.getExtIp(), ack: true });
await this.setStateChangedAsync('info.extIpv6', { val: await this.Fb.getExtIpv6(), ack: true });
await this.setStateChangedAsync('info.extIpv6Prf', {
val: await this.Fb.getExtIpv6Prefix(),
ack: true,
});
}
if (this.config.guestinfo === true) {
await this.setStateChangedAsync('guest.wlan', { val: await this.Fb.getGuestWlan(), ack: true });
}
if (this.config.qrcode === true) {
await this.setStateChangedAsync('guest.wlanQR', { val: await this.Fb.getGuestQR(), ack: true });
}
if (this.Fb.GETPATH != null && this.Fb.GETPATH == true) {
this.checkDevices();
}
time = process.hrtime(work);
this.log.debug(`loop main ends after ${time} s`);
}
} catch (error) {
this.errorHandler(error, 'loop: ');
} finally {
clearTimeout(this.tout);
this.tout = null;
}
}
}
async setDeviceStates() {
try {
const deviceList = this.Fb.deviceList;
if (this.config.compatibility == true) {
await this.setStateChangedAsync('devices', { val: deviceList.length, ack: true });
}
await this.setStateChangedAsync('fb-devices.count', { val: deviceList.length, ack: true });
let inActiveHosts = deviceList.filter(host => host.Active == '0');
await this.setStateChangedAsync('fb-devices.inactive', { val: inActiveHosts.length, ack: true });
inActiveHosts = null;
let activeHosts = deviceList.filter(host => host.Active == '1');
await this.setStateChangedAsync('fb-devices.active', { val: activeHosts.length, ack: true });
if (this.config.compatibility == true) {
await this.setStateChangedAsync('activeDevices', { val: activeHosts.length, ack: true });
}
activeHosts = null;
} catch (error) {
this.errorHandler(error, 'setDeviceStates: ');
}
}
async getAllFbObjects() {
try {
const items = this.Fb.deviceList;
if (items === null || items === false) {
return null;
}
// Get all fb-device objects of this adapter
let hosts = [];
let devices = await this.getDevicesAsync();
let fbDevices = devices.filter(x => x._id.includes(`${this.namespace}` + '.fb-devices.'));
for (let i = 0; i < items.length; i++) {
let hostName = items[i]['HostName'];
hostName = hostName.replace(this.FORBIDDEN_CHARS, '-');
if (hostName === null || hostName == '') {
if (this.suppressMesgEmptyHostname === false) {
this.log.warn(
`getAllFbObjects: Hostname of fritzbox device ${items[i]['MACAddress']} is empty!`,
);
this.log.warn(
'getAllFbObjects: To delete this device in the fritzbox please add the device manually in the fritzbox with a hostname and the shown mac-address. After a reboot you can hopefully delete the device.',
);
this.suppressMesgEmptyHostname = true;
}
continue;
}
const host = fbDevices.filter(
x => x._id.replace(`${this.namespace}` + '.fb-devices.', '') === hostName,
);
let item = items.filter(x => x.HostName == items[i].HostName);
let itemActive = items.filter(x => x.HostName == items[i].HostName && x.Active == '1');
if (!host || host.length == 0) {
//new devices
if (!item || item.length == 1) {
const device = {
status: 'new',
dp: `fb-devices.${hostName}`,
hn: hostName,
hnOrg: items[i]['HostName'],
mac: items[i]['MACAddress'],
ip: items[i]['IPAddress'],
active: items[i]['Active'] == 1 ? true : false,
data: items[i],
interfaceType: items[i]['InterfaceType'],
speed: parseInt(items[i]['X_AVM-DE_Speed']),
guest: items[i]['X_AVM-DE_Guest'] == 0 ? false : true,
};
hosts.push(device);
}
if (!item || item.length > 1) {
let mac = '';
let ip = '';
let active = false;
for (let it = 0; it < item.length; it++) {
mac += mac == '' ? item[it].MACAddress : `, ${item[it].MACAddress}`;
ip += ip == '' ? item[it].IPAddress : `, ${item[it].IPAddress}`;
}
if (itemActive && itemActive.length > 0) {
active = true;
}
const device = {
status: 'unchanged',
dp: `fb-devices.${hostName}`,
hn: hostName,
hnOrg: items[i]['HostName'],
mac: mac,
ip: ip,
active: active,
data: active == true ? itemActive[0] : items[i],
interfaceType: active == true ? itemActive[0]['InterfaceType'] : items[i]['InterfaceType'],
speed:
active == true
? parseInt(itemActive[0]['X_AVM-DE_Speed'])
: parseInt(items[i]['X_AVM-DE_Speed']),
guest:
active == true
? itemActive[0]['X_AVM-DE_Guest'] == 0
? false
: true
: items[i]['X_AVM-DE_Guest'] == 0
? false
: true,
};
const temp = hosts.filter(x => x.hn == hostName);
if (temp.length == 0) {
hosts.push(device);
}
}
} else {
if (!item || item.length == 1) {
const device = {
status: 'unchanged',
dp: `fb-devices.${hostName}`,
hn: hostName,
hnOrg: items[i]['HostName'],
mac: items[i]['MACAddress'],
ip: items[i]['IPAddress'],
active: items[i]['Active'] == 1 ? true : false,
data: items[i],
interfaceType: items[i]['InterfaceType'],
speed: parseInt(items[i]['X_AVM-DE_Speed']),
guest: items[i]['X_AVM-DE_Guest'] == 0 ? false : true,
};
hosts.push(device);
}
if (!item || item.length > 1) {
let mac = '';
let ip = '';
let active = false;
for (let it = 0; it < item.length; it++) {
mac += mac == '' ? item[it].MACAddress : `, ${item[it].MACAddress}`;
ip += ip == '' ? item[it].IPAddress : `, ${item[it].IPAddress}`;
}
if (itemActive && itemActive.length > 0) {
active = true;
}
const device = {
status: 'unchanged',
dp: `fb-devices.${hostName}`,
hn: hostName,
hnOrg: items[i]['HostName'],
mac: mac,
ip: ip,
active: active,
data: active == true ? itemActive[0] : items[i],
interfaceType: active == true ? itemActive[0]['InterfaceType'] : items[i]['InterfaceType'],
speed:
active == true
? parseInt(itemActive[0]['X_AVM-DE_Speed'])
: parseInt(items[i]['X_AVM-DE_Speed']),
guest:
active == true
? itemActive[0]['X_AVM-DE_Guest'] == 0
? false
: true
: items[i]['X_AVM-DE_Guest'] == 0
? false
: true,
};
const temp = hosts.filter(x => x.hn == hostName);
if (temp.length == 0) {
hosts.push(device);
}
}
}
item = null;
itemActive = null;
}
for (const id in fbDevices) {
if (fbDevices[id] != undefined && fbDevices[id].common != undefined) {
const dName = fbDevices[id].common.name;
const shortName = dName.replace('fb-devices.', '');
const shortNameOrg = fbDevices[id].common.desc;
//shortNameOrg = shortNameOrg.replace('-', '.');
let host = items.filter(x => x.HostName === shortName);
if (host && host.length == 0) {
let host = items.filter(x => x.HostName === shortNameOrg);
if (host && host.length == 0) {
const device = {
status: 'old',
dp: `fb-devices.${shortName}`,
hn: shortName,
hnOrg: shortNameOrg,
mac: '', //await this.getStateAsync('fb-devices.' + shortName + '.macaddress').val,
ip: '', //await this.getStateAsync('fb-devices.' + shortName + '.ipaddress').val,
active: false,
data: null,
interfaceType: '',
speed: 0,
guest: false,
};
hosts.push(device);
}
host = null;
}
host = null;
}
}
let newObjs = hosts.filter(host => host.status == 'new');
if (newObjs) {
await obj.createFbDeviceObjects(this, this.adapterStates, newObjs, this.enabled);
}
devices = null;
newObjs = null;
fbDevices = null;
this.hosts = hosts;
hosts = null;
return true;
} catch (error) {
this.errorHandler(error, 'getAllFbObjects: ');
return null;
}
}
async stopAdapter() {
try {
const adapterObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
adapterObj.common.enabled = false; // Adapter ausschalten
await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, adapterObj);
} catch (error) {
this.errorHandler(error, 'stopAdapter: ');
}
}
async startAdapter() {
try {
const adapterObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
adapterObj.common.enabled = true; // Adapter ausschalten
await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, adapterObj);
} catch (error) {
this.errorHandler(error, 'startAdapter: ');
}
}
/**
* Is called when databases are connected and adapter received configuration.
*/
async onReady() {
try {
// Initialize your adapter here
const membersFiltered = this.config.familymembers.filter(x => x.enabled == true);
const familyGroups = this.removeDuplicates(membersFiltered);
this.Fb = await fb.Fb.init(
{
host: this.config.ipaddress,
uid: this.config.username,
pwd: this.config.password,
},
this.config.ssl,
this,
);
if (this.Fb.services === null) {
this.log.error('Can not get services! Adapter stops');
this.stopAdapter();
}
//Logging of adapter start
this.log.info(
`start ` +
`${this.namespace}` +
`: ${this.Fb.modelName} version: ${this.Fb.version} ip-address: "${
this.config.ipaddress
}" - interval devices: ${this.config.interval} s` +
` - interval members: ${this.config.intervalFamily} s`,
);
this.config.username === '' || this.config.username === null
? this.log.warn('please insert a user for full functionality')
: this.log.debug(`configuration user: <${this.config.username}>`);
this.log.debug(`configuration history: <${this.config.history}>`);
this.log.debug(`configuration dateformat: <${this.config.dateformat}>`);
this.log.debug(`configuration familymembers count: ${membersFiltered.length}`);
this.log.debug(`configuration familymembers: ${JSON.stringify(this.config.familymembers)}`);
this.log.debug(`configuration fb-devices ${this.config.fbdevices}`);
this.log.debug(`configuration mesh info: ${this.config.meshinfo}`);
this.log.debug(`configuration whitelist: ${this.config.enableWl}`);
this.log.debug(`configuration compatibility: ${this.config.compatibility}`);
this.log.debug(`configuration ssl: ${this.config.ssl}`);
this.log.debug(`configuration qr code: ${this.config.qrcode}`);
this.log.debug(`configuration guest info: ${this.config.guestinfo}`);
this.log.debug(`configuration external ip address: ${this.config.extip}`);
this.log.debug(`configuration filter delay: ${this.config.delay}`);
const mesg = this.Fb.connection == null ? '-' : this.Fb.connection;
this.log.info(`configuration default connection: ${mesg}`);
this.Fb.suportedServices.forEach(element => {
element.enabled
? this.log.info(`${element.name} is supported`)
: this.log.warn(`${element.name} is not supported! Feature is deactivated!`);
});
//Configuration changes if needed
let adapterObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
let adapterObjChanged = false; //for changes
if (this.config.interval_seconds === false) {
//Workaround: Switch interval to seconds
this.log.warn('Interval changed to seconds!');
adapterObj.native.interval_seconds = true;
adapterObj.native.interval = adapterObj.native.interval * 60;
adapterObjChanged = true;
}
//if interval <= 0 than set to 1
if (this.config.interval <= 0) {
adapterObj.native.interval = 60;
adapterObjChanged = true;
this.config.interval = 60;
this.log.warn('interval is less than 1. Set to 60 s');
}
//if interval <= 9 than set to 10
if (this.config.intervalFamily <= 9) {
adapterObj.native.intervalFamily = 10;
adapterObjChanged = true;
this.config.intervalFamily = 10;
this.log.warn('interval is less than 10. Set to 10s.');
}
//create new configuration items -> workaround for older versions
for (let i = 0; i < this.config.familymembers.length; i++) {
if (this.config.familymembers[i].usefilter == undefined) {
this.log.warn(
`${this.config.familymembers[i].familymember} usefilter is undefined! Changed to false`,
);
adapterObj.native.familymembers[i].usefilter = false;
adapterObjChanged = true;
}
if (this.config.familymembers[i].group == undefined) {
this.log.warn(`${this.config.familymembers[i].familymember} group is undefined! Changed to ""`);
adapterObj.native.familymembers[i].group = '';
adapterObjChanged = true;
}
if (this.config.familymembers[i].devicename == undefined) {
this.log.warn(
`${this.config.familymembers[i].familymember} devicename is undefined! Changed to ""`,
);
adapterObj.native.familymembers[i].devicename = '';
adapterObjChanged = true;
}
if (this.config.familymembers[i].usage == undefined) {
if (this.config.familymembers[i].useip == true) {
adapterObj.native.familymembers[i].usage = 'IP';
}
if (this.config.familymembers[i].usename == true) {
adapterObj.native.familymembers[i].usage = 'Hostname';
}
if (this.config.familymembers[i].usename == false && this.config.familymembers[i].useip == false) {
adapterObj.native.familymembers[i].usage = 'MAC';
}
if (adapterObj.native.familymembers[i].usage == undefined) {
adapterObj.native.familymembers[i].usage = 'MAC';
}
this.log.warn(
`${this.config.familymembers[i].familymember} usage is undefined! Changed to ${
adapterObj.native.familymembers[i].usage
}`,
);
adapterObjChanged = true;
}
}
//suppress messages from family members after one occurence
for (let i = 0; i < this.config.familymembers.length; i++) {
if (this.config.familymembers[i].enabled === true) {
this.suppressArr[i] = {
name: this.config.familymembers[i].familymember,
suppress: false,
hostname: this.config.familymembers[i].devicename,
mac: this.config.familymembers[i].macaddress,
ip: this.config.familymembers[i].ipaddress,
};
}
}
if (adapterObjChanged === true) {
//Save changes
this.log.info('some familymember attributes were changed! Please check the configuration');
this.log.info('Adapter restarts');
await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, adapterObj);
}
const intDev = this.config.interval;
const intFamily = this.config.intervalFamily;
if (this.config.compatibility === true) {
this.log.warn(
'In a future version some states are not more existent. Please disable the compatibility mode in the adapter settings by unchecking the box.',
);
this.log.warn('With this change in the adapter settings the new folder familyMembers will be created');
this.log.warn('Then you should manually delete the old family member data points!');
}
//Create global objects
await obj.createGlobalObjects(
this,
this.adapterStates,
this.HTML + this.HTML_END,
this.HTML_GUEST + this.HTML_END,
this.enabled,
);
this.Fb.suportedServices.forEach(async element => {
await this.setStateAsync(`info.${element.id}`, { val: element.enabled, ack: true });
});
await this.setStateAsync('reboot', { val: false, ack: true });
await this.setStateAsync('reconnect', { val: false, ack: true });
if (this.config.extip === false) {
//await this.setStateChangedAsync('info.extIp', { val: '', ack: true });
}
//If history is enbled, check if history adapter is running.
if (this.config.history != '') {
this.historyAlive = await this.getForeignStateAsync(`system.adapter.${this.config.history}.alive`);
if (this.historyAlive.val === false) {
for (let to = 0; to < 6; to++) {
this.log.warn('history adapter is not alive! Waiting 10s');
await this._sleep(10000);
this.historyAlive = await this.getForeignStateAsync(
`system.adapter.${this.config.history}.alive`,
);
if (this.historyAlive.val === true) {
break;
}
}
if (this.historyAlive.val === false) {
this.stopAdapter();
}
}
}
await obj.createMemberObjects(
this,
membersFiltered,
familyGroups,
this.adapterStates,
this.memberStates,
this.config,
this.HTML_HISTORY + this.HTML_END,
this.enabled,
this.historyAlive.val,
);
//create Fb devices
if (this.Fb.GETPATH != null && this.Fb.GETPATH == true && this.config.fbdevices == true) {
await this.Fb.getDeviceList();
if (this.Fb.deviceList != null) {
await this.getAllFbObjects();
//this.log.warn('devicelist2: ' + JSON.stringify(this.hosts));
const res = await obj.createFbDeviceObjects(this, this.adapterStates, this.hosts, this.enabled);
if (res === true) {
this.log.info('createFbDeviceObjects finished successfully');
}
} else {
this.log.error('createFbDeviceObjects -> ' + "can't read devices from fritzbox! Adapter stops");
adapterObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
adapterObj.common.enabled = false; // Adapter ausschalten
await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, adapterObj);
}
await this.resyncFbObjects(this.Fb.deviceList);
}
// states changes inside the adapters namespace are subscribed
if (this.Fb.SETENABLE === true && this.Fb.WLAN3INFO === true && this.config.guestinfo === true) {
this.subscribeStates(`${this.namespace}` + '.guest.wlan');
}
if (this.Fb.DISALLOWWANACCESSBYIP === true && this.Fb.GETWANACCESSBYIP === true) {
this.subscribeStates(`${this.namespace}` + '.fb-devices.*.disabled');
}
if (this.Fb.REBOOT === true) {
this.subscribeStates(`${this.namespace}` + '.reboot');
}
if (this.Fb.RECONNECT === true) {
this.subscribeStates(`${this.namespace}` + '.reconnect');
}
this.log.info('states successfully subscribed');
this.loop(intFamily - 1, intDev - 3, intFamily, intDev); //values must be less than intFamily or intDev
this.log.info('loop successfully started');
} catch (error) {
this.errorHandler(error, 'onReady: ');
this.stopAdapter();
}
}
/**
* Is called when adapter shuts down - callback has to be called under any circumstances!
*
* @param callback - callback
*/
async onUnload(callback) {
try {
this.enabled = false;
if (this.Fb != null) {
this.Fb.exitRequest();
}
this.tout && clearTimeout(this.tout);
this.setState('info.connection', { val: false, ack: true });
this.log.info('cleaned everything up ...');
callback && callback();
//setTimeout(callback, 1000);
} catch (e) {
this.log.error(`onUnload: ${e.name} ${e.message}`);
callback && callback();
}
}
/**
* Is called if a subscribed object changes
*
* @param {string} id
* @param {ioBroker.Object | null | undefined} obj
*/
/*onObjectChange(id, obj) {
if (obj) {
// The object was changed
this.log.debug(`object ${id} changed: ${JSON.stringify(obj)}`);
} else {
// The object was deleted
this.log.debug(`object ${id} deleted`);
}
}*/
/**
* Is called if a subscribed state changes
*
* @param id - id of the changed state
* @param state - name of the changed state
*/
async onStateChange(id, state) {
try {
if (state) {
//Enable/disable Guest WLAN
if (
id == `${this.namespace}` + '.guest.wlan' &&
this.Fb.SETENABLE == true &&
state.ack === false &&
this.Fb.WLAN3INFO === true &&
this.config.guestinfo === true
) {
this.log.info(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
const val = state.val ? '1' : '0';
const soapResult = await this.Fb.soapAction(
'/upnp/control/wlanconfig3',
'urn:dslforum-org:service:' + 'WLANConfiguration:3',
'SetEnable',
[[1, 'NewEnable', val]],
);
//if (guestwlan['status'] == 200 || guestwlan['result'] == true) {
if (soapResult) {
await this.Fb.getGuestWlan(id, this.config.qrcode);
//if(state.val == wlanStatus) this.setState('guest.wlan', { val: wlanStatus, ack: true });
} else {
throw {
name: `onStateChange ${id}`,
message: `Can not change state${JSON.stringify(soapResult)}`,
};
}
}
//Enable/disable Internet
if (
id.includes('disabled') &&
!state.ack &&
this.Fb.DISALLOWWANACCESSBYIP &&
this.Fb.GETWANACCESSBYIP
) {
this.log.info(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
// IP-Adresse des Geräts holen
const ipId = `${id.replace('.disabled', '')}.ipaddress`;
const ipaddress = await this.getStateAsync(ipId);
const val = state.val ? '1' : '0';
if (!ipaddress || ipaddress.val === '') {
throw { name: `onStateChange ${id}`, message: 'Cannot change state. IP address is empty!' };
}
const soapResult = await this.Fb.soapAction(
'/upnp/control/x_hostfilter',
'urn:dslforum-org:service:' + 'X_AVM-DE_HostFilter:1',
'DisallowWANAccessByIP',
[
[1, 'NewIPv4Address', ipaddress.val],
[2, 'NewDisallow', val],
],
);
if (!soapResult) {
throw {
name: `onStateChange ${id}`,
message: `Cannot change state: ${JSON.stringify(soapResult)}`,
};
}
// Kurz warten und Zugriffsstatus überprüfen
await this._sleep(1000);
const newDisallow = (await this.Fb.getWanAccess(ipaddress)) !== '0';
if (newDisallow === state.val) {
this.setState(id, { val: newDisallow, ack: true });
this.log.info(`state ${id} changed: ${state.val} (ack = true)`);
} else {
this.log.warn(`state ${id} changed: ${state.val} (ack = false)`);
}
}
//Reboot Fritzbox
if (id == `${this.namespace}` + '.reboot' && this.Fb.REBOOT === true) {
this.log.info(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
if (state.val === true) {
const soapResult = await this.Fb.soapAction(
'/upnp/control/deviceconfig',
'urn:dslforum-org:service:' + 'DeviceConfig:1',
'Reboot',
null,
);
//if (reboot['status'] == 200 || reboot['result'] == true) {
if (soapResult) {
this.setState(`${this.namespace}` + '.reboot', { val: false, ack: true });
} else {
this.setState(`${this.namespace}` + '.reboot', { val: false, ack: true });
throw Error(`reboot failure! ${JSON.stringify(soapResult)}`);
}
}
}
//Reconnect
if (id == `${this.namespace}` + '.reconnect' && this.Fb.RECONNECT === true) {
this.log.info(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
if (state.val === true) {
let soapResult = null;
if (
this.Fb &&
this.Fb.RECONNECT &&
this.Fb.RECONNECT == true &&
this.Fb.connection == '1.WANPPPConnection.1'
) {
soapResult = await this.Fb.soapAction(
'/upnp/control/wanpppconn1',
'urn:dslforum-org:service:' + 'WANPPPConnection:1',
'ForceTermination',
null,
);
} else {
soapResult = await this.Fb.soapAction(
'/upnp/control/wanipconnection1',
'urn:dslforum-org:service:' + 'WANIPConnection:1',
'ForceTermination',
null,
);
}
if (soapResult) {
this.setState(`${this.namespace}` + '.reconnect', { val: false, ack: true });
} else {
this.setState(`${this.namespace}` + '.reconnect', { val: false, ack: true });
throw `reconnect failure! ${JSON.stringify(soapResult)}`;
}
}
}
} else {
// The state was deleted
this.log.debug(`state ${id} deleted`);
}
} catch (error) {
if (error.message == 'DisconnectInProgress') {
this.setState(`${this.namespace}` + '.reconnect', { val: false, ack: true });
this.log.info('Fritzbox reconnect in progress');
await this._sleep(5000);
} else {
this.log.error(`onStateChange: ${error.name} ${error.message}`);
}
}
}
/**
* Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...
* Using this method requires "common.message" property to be set to true in io-package.json
*
* @param obj - message object
*/
async onMessage(obj) {
try {
//this.log.debug(`[MSSG] Received: ${JSON.stringify(obj)}`);
if (!obj) {
return;
}
if (typeof obj === 'object' && obj.message) {
const reply = result => {
this.sendTo(obj.from, obj.command, JSON.stringify(result), obj.callback);
};
switch (obj.command) {
case 'triggerPresence': {
this.log.info('triggerPresence');
if (this.triggerActive === false) {
this.triggerActive = true;
await this.checkPresence(true);
await this._sleep(10000);
this.triggerActive = false;
reply(true);
} else {
reply(false);
}
return true;
}
case 'getDevices': {
this.allDevices = [];
let onlyActive, reread;
if (typeof obj.message === 'object') {
onlyActive = obj.message.onlyActive;
reread = obj.message.reread;
}
if (!obj.callback) {
return false;
}
if (!reread && this.allDevices.length > 0 && this.allDevices.onlyActive === onlyActive) {
reply(this.allDevices);
return true;
}
this.allDevices.onlyActive = onlyActive;
if (this.Fb && this.Fb.GETPATH && this.Fb.GETPATH == true) {
await this.Fb.getDeviceList();
if (this.Fb.deviceList == null) {
reply(this.allDevices);
return;
}
for (let i = 0; i < this.Fb.deviceList.length; i++) {
const active = this.Fb.deviceList[i]['Active'];
if (!onlyActive || active) {
this.allDevices.push({
name: this.Fb.deviceList[i]['HostName'],
ip: this.Fb.deviceList[i]['IPAddress'],
mac: this.Fb.deviceList[i]['MACAddress'],
active: active,
});
}
}
}
reply(this.allDevices);
return true;
}
default:
//this.log.warn('Unknown command: ' + obj.command);
break;
}
if (obj.callback) {
this.sendTo(obj.from, obj.command, obj.message, obj.callback);
}
return true;
}
} catch (error) {
this.log.error(`onMessage: ${error.message}`);
}
}
async getMeshInfo() {
try {
const hosts = this.hosts;
const mesh = this.Fb.meshList;
if (!hosts) {
return false;
}
if (!mesh) {
return false;
}
const enabledMeshInfo = this.config.meshinfo;
const ch = await this.getChannelsOfAsync(); //get all channels
if (mesh) {
await this.setStateChangedAsync('fb-devices.mesh', { val: JSON.stringify(mesh), ack: true });
}
for (let i = 0; i < hosts.length; i++) {
const hostName = hosts[i]['hn'];
//hostName = hostName.replace(this.FORBIDDEN_CHARS, '-');
const hostCh = ch.filter(ch => ch._id.includes(`.${hostName}.`));
//Get mesh info for device
if (this.Fb.GETMESHPATH != null && this.Fb.GETMESHPATH == true && enabledMeshInfo == true) {
if (mesh != null) {
let meshdevice = mesh.find(el => el.device_mac_address === hosts[i]['mac']);
if (meshdevice == null) {
meshdevice = mesh.find(el => el.device_name === hosts[i]['hnOrg']);
}
if (meshdevice != null) {
/