diff --git a/README.md b/README.md index 48a685e..ed3ec02 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This adapter controls your [Fully Kiosk Browser](https://www.fully-kiosk.com) (a Plus License is required). It provides you with a bunch of possibilites to control your tablet through ioBroker, like turning the display on/off, launch any tablet app, launch the screensaver etc. Also, it provides various information in states, like battery level of your tablet, etc. which you can use e.g. for Visualization. -Unlike [ioBroker.fullybrowser](https://github.com/arteck/ioBroker.fullybrowser), this adapter also supports **MQTT**. +Unlike [ioBroker.fullybrowser](https://github.com/arteck/ioBroker.fullybrowser), this adapter also supports und uses **MQTT**. ## Documentation diff --git a/build/lib/mqtt-server.js b/build/lib/mqtt-server.js index cd81f47..e513b95 100644 --- a/build/lib/mqtt-server.js +++ b/build/lib/mqtt-server.js @@ -70,7 +70,7 @@ class MqttServer { return; } const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`; - this.adapter.log.info(`[MQTT] Client ${ipMsg} trys to authenticate...`); + this.adapter.log.debug(`[MQTT] Client ${ipMsg} trys to authenticate...`); if (ip) this.devices[client.id].ip = ip; if (!this.adapter.config.mqttDoNotVerifyUserPw) { diff --git a/build/lib/mqtt-server.js.map b/build/lib/mqtt-server.js.map index 8048b4d..8827480 100644 --- a/build/lib/mqtt-server.js.map +++ b/build/lib/mqtt-server.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../src/lib/mqtt-server.ts"], - "sourcesContent": ["import Aedes from 'aedes';\nimport net from 'net';\nimport { FullyMqtt } from '../main';\nimport { IMqttDevice } from './interfaces';\n//import { inspect } from 'util';\n\nexport class MqttServer {\n private readonly adapter: FullyMqtt;\n private server: net.Server;\n private aedes: Aedes;\n public devices: { [mqttClientId: string]: IMqttDevice }; // {}\n private port = -1;\n private notAuthorizedClients: string[] = []; // to avoid multiple log lines\n\n /**\n * Constructor\n */\n public constructor(adapter: FullyMqtt) {\n this.adapter = adapter;\n //this.server = new net.Server();\n this.aedes = new Aedes();\n /** @ts-expect-error - https://github.com/moscajs/aedes/issues/801 */\n this.server = net.createServer(undefined, this.aedes.handle);\n this.devices = {}; // key = MQTT Client ID, property: IMqttDevice\n }\n\n /**\n * Listen\n */\n public start(): void {\n try {\n /**\n * Port\n */\n this.port = this.adapter.config.mqttPort;\n /**\n * #############################################################\n * For Developer only: change port if in dev environment\n * #############################################################\n */\n if (this.adapter.adapterDir.includes('/.dev-server/default/node_modules')) {\n this.port = 3012;\n this.adapter.log.warn(`DEVELOPER: Port changed to ${this.port} as we are in DEV Environment! If you see this log message, please open an issue on Github.`);\n }\n\n /**\n * Start Listening\n */\n this.server.listen(this.port, () => {\n this.adapter.log.info(`[MQTT]\uD83D\uDE80 Server started and listening on port ${this.port}`);\n });\n\n /**\n * Verify authorization\n * This fires first and before this.aedes.on('client', (client) ...\n * https://github.com/moscajs/aedes/blob/main/docs/Aedes.md#handler-authenticate-client-username-password-callback\n */\n this.aedes.authenticate = (client, username, password, callback) => {\n try {\n // If we saw client before and is not authorized\n if (this.notAuthorizedClients.includes(client.id)) {\n callback(null, false);\n return;\n }\n\n // Create device entry with id as key, if not yet existing\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n /**\n * Get IP\n * This rather complicated way is needed, see https://github.com/moscajs/aedes/issues/186\n * Not sure if this always works, but client.req was undefined in my test - which is suggested in https://github.com/moscajs/aedes/issues/527\n */\n let ip: string | undefined = undefined;\n if (client.conn && 'remoteAddress' in client.conn && typeof client.conn.remoteAddress === 'string') {\n const ipSource = client.conn.remoteAddress; // like: ::ffff:192.168.10.101\n this.adapter.log.debug(`[MQTT] client.conn.remoteAddress = \"${ipSource}\" - ${client.id}`);\n ip = ipSource.substring(ipSource.lastIndexOf(':') + 1); // get everything after last \":\"\n if (!this.adapter.isIpAddressValid(ip)) ip === undefined;\n }\n // Check if IP is an active device IP\n if (ip && !this.adapter.activeDeviceIPs.includes(ip)) {\n this.adapter.log.error(`[MQTT] Client ${client.id} not authorized: ${ip} is not an active Fully device IP per adapter settings.`);\n this.notAuthorizedClients.push(client.id);\n callback(null, false);\n return;\n }\n\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`;\n this.adapter.log.info(`[MQTT] Client ${ipMsg} trys to authenticate...`);\n if (ip) this.devices[client.id].ip = ip;\n\n /**\n * Verify User and Password\n */\n if (!this.adapter.config.mqttDoNotVerifyUserPw) {\n // Username\n if (username !== this.adapter.config.mqttUser) {\n this.adapter.log.warn(`[MQTT] Client ${ipMsg} Authorization rejected: received user name '${username}' does not match '${this.adapter.config.mqttUser}' in adapter settings.`);\n callback(null, false);\n return;\n }\n // Password\n if (password.toString() !== this.adapter.config.mqttPassword) {\n this.adapter.log.warn(`[MQTT] Client ${ipMsg} Authorization rejected: received password does not match with password in adapter settings.`);\n callback(null, false);\n return;\n }\n }\n this.adapter.log.info(`[MQTT]\uD83D\uDD11 Client ${ipMsg} successfully authenticated.`);\n callback(null, true);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n callback(null, false);\n }\n };\n\n /**\n * fired when a client connects\n */\n this.aedes.on('client', (client) => {\n try {\n if (!client) return;\n\n // Create device entry with id as key, if not yet existing (should have been set in this.aedes.authenticate already)\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n // IP\n const ip = this.devices[client.id].ip;\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`;\n\n this.adapter.log.debug(`[MQTT] Client ${ipMsg} connected to broker ${this.aedes.id}`);\n this.adapter.log.info(`[MQTT]\uD83D\uDD17 Client ${ipMsg} successfully connected.`);\n //this.adapter.log.debug(inspect(client)); //https://stackoverflow.com/a/31557814\n\n // set isAlive\n this.setIsAlive(client.id, true, 'client connected');\n\n // Schedule check if still alive\n this.scheduleCheckIfStillActive(client.id);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n });\n\n /**\n * fired when a client publishes a message packet on the topic\n */\n this.aedes.on('publish', (packet, client) => {\n try {\n if (!client || !packet) return;\n\n this.setIsAlive(client.id, true, 'client published message');\n\n // Create device entry with id as key, if not yet existing\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n // QOS is always 1 per Fully documentation\n if (packet.qos !== 1) return;\n\n if (packet.retain) {\n /**\n * Device Info coming in...\n * Per fully documentation: The complete device info will be published every 60 seconds as fully/deviceInfo/[deviceId] topic (retaining, QOS=1).\n */\n\n // Payload as object\n const info = JSON.parse(packet.payload.toString());\n\n // Verification of device info packet\n // We don't use topic to check since we do not want to rely on user's input in Fully Browser \"MQTT Device Info Topic\" settings.\n if (!('startUrl' in info) && !('ip4' in info)) {\n this.adapter.log.error(`[MQTT] Packet rejected: ${info.ip4} - Info packet expected, but ip4 and startUrl is not defined in packet. ${info.deviceId}`);\n return;\n }\n\n // IP\n const ip = info.ip4;\n const devMsg = `${this.adapter.fullys[ip].name} (${ip})`;\n // Check IP - already done in this.aedes.authenticate, but just in case we were unable to get ip there\n if (!this.adapter.activeDeviceIPs.includes(ip)) {\n this.adapter.log.error(`[MQTT] Client ${devMsg} Packet rejected: IP is not allowed per adapter settings. ${client.id}`);\n return;\n }\n this.devices[client.id].ip = ip;\n\n // Slow down: Don't accept info event more often than x seconds\n // Per Fully doc, should not come in more often than 60s anyway...\n const prevTime = this.devices[client.id].previousInfoPublishTime;\n const limit = this.adapter.config.mqttPublishedInfoDelay * 1000; // milliseconds\n if (prevTime && prevTime !== 0) {\n if (Date.now() - prevTime < limit) {\n const diffMs = Date.now() - prevTime;\n this.adapter.log.silly(`[MQTT] ${devMsg} Packet rejected: Last packet came in ${diffMs}ms (${Math.round(diffMs / 1000)}s) ago...`);\n return;\n }\n }\n this.devices[client.id].previousInfoPublishTime = Date.now(); // set for future events\n\n /**\n * First time received device info incl. IP address etc.\n */\n if (!this.devices[client.id].mqttFirstReceived) {\n // show only once\n this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullys[ip].name} = ${ip}`);\n // set to true\n this.devices[client.id].mqttFirstReceived = true;\n }\n /**\n * Call Adapter function onMqttInfo()\n */\n const result = {\n clientId: client.id,\n ip: ip,\n topic: packet.topic,\n infoObj: info,\n };\n this.adapter.onMqttInfo(result);\n } else if (packet.qos === 1 && !packet.retain) {\n /**\n * Event coming in...\n * Per fully documentation: Events will be published as fully/event/[eventId]/[deviceId] topic (non-retaining, QOS=1).\n */\n // {\"deviceId\":\"xxxxxxxx-xxxxxxxx\",\"event\":\"screenOn\"}\n // NOTE: Device ID is different to client id, we actually disregard deviceId\n const msg = JSON.parse(packet.payload.toString());\n\n // Verification of event packet\n // We don't use topic to check since we do not want to rely on user's input in Fully Browser \"MQTT Event Topic\" settings.\n if (!('event' in msg)) {\n this.adapter.log.error(`[MQTT] Packet rejected: Event packet expected, but event is not defined in packet. ${client.id}`);\n return;\n }\n\n // Disregard first event once connected: mqttConnected\n if (msg.event === 'mqttConnected') {\n this.adapter.log.silly(`[MQTT] Client Publish Event: Disregard mqttConnected event - ${msg.deviceId}`);\n return;\n }\n\n // Get IP\n if (!this.devices[client.id]) {\n this.adapter.log.info(`[MQTT] Client Publish Event: Device ID and according IP not yet seen thru \"Publish Info\"`);\n this.adapter.log.info(`[MQTT] We wait until first info is published. ${msg.deviceId}`);\n return;\n }\n const ip = this.devices[client.id].ip ? this.devices[client.id].ip : '';\n if (ip === '' || typeof ip !== 'string') {\n this.adapter.log.debug(`[MQTT] Client Publish Event: IP address could not be determined. - Client ID: ${client.id}`);\n this.adapter.log.debug(`[MQTT] Please be patient until first MQTT info packet coming in (takes up to 1 minute)`);\n return; // Disregard since IP is unknown!\n }\n\n // Call function\n const result = {\n clientId: client.id,\n ip: ip,\n topic: packet.topic,\n cmd: msg.event,\n };\n if (!this.devices[client.id].mqttFirstReceived) {\n // show only once\n this.adapter.log.info(`[MQTT] \uD83D\uDD17 Client ${client.id} = ${this.adapter.fullys[ip].name} (${ip})`);\n this.devices[client.id].mqttFirstReceived = true;\n }\n /**\n * Call Adapter function onMqttEvent()\n */\n this.adapter.onMqttEvent(result);\n } else {\n // Ignore\n return;\n }\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n });\n\n /**\n * fired when a client disconnects\n */\n this.aedes.on('clientDisconnect', (client) => {\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] Client ${logMsgName} disconnected.`);\n } else {\n this.adapter.log.error(`[MQTT] Client ${logMsgName} disconnected.`);\n }\n this.setIsAlive(client.id, false, 'client disconnected');\n });\n\n /**\n * fired on client error\n */\n this.aedes.on('clientError', (client, e) => {\n if (this.notAuthorizedClients.includes(client.id)) return; // Error msg was already thrown in aedes.authenticate() before\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] ${logMsgName}: Client error - ${e.message}`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 ${logMsgName}: Client error - ${e.message}`);\n }\n this.adapter.log.debug(`[MQTT]\uD83D\uDD25 ${logMsgName}: Client error - stack: ${e.stack}`);\n this.setIsAlive(client.id, false, 'client error');\n });\n\n this.aedes.on('connectionError', (client, e) => {\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] ${logMsgName}: Connection error - ${e.message}`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 ${logMsgName}: Connection error - ${e.message}`);\n }\n this.adapter.log.debug(`[MQTT]\uD83D\uDD25 ${logMsgName}: Connection error - stack: ${e.stack}`);\n this.setIsAlive(client.id, false, 'connection error');\n });\n\n /**\n * fired on server error\n */\n this.server.on('error', (e: any) => {\n if (e instanceof Error && e.message.startsWith('listen EADDRINUSE')) {\n this.adapter.log.debug(`[MQTT] Cannot start server - ${e.message}`);\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 Cannot start server - Port ${this.port} is already in use. Try a different port!`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 Cannot start server - ${e.message}`);\n }\n this.terminate();\n });\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }\n\n /**\n * If Client is alive or not\n */\n private setIsAlive(clientId: string, isAlive: true | false, msg: string): void {\n if (isAlive) this.devices[clientId].lastTimeActive = Date.now();\n this.devices[clientId].isActive = isAlive;\n\n const ip = this.devices[clientId]?.ip;\n if (ip) {\n this.adapter.onAliveChange('MQTT', ip, isAlive, msg);\n if (isAlive) {\n this.scheduleCheckIfStillActive(clientId); // restart timer\n } else {\n // clear timer\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n }\n } else {\n this.adapter.log.debug(`[MQTT] isAlive changed to ${isAlive}, but IP of client ${clientId} is still unknown.`);\n }\n }\n\n /**\n * Schedule: Check if MQTT topic was sent last x seconds ago\n * @param ip IP Address\n * @returns void\n */\n private async scheduleCheckIfStillActive(clientId: string): Promise {\n try {\n const ip = this.devices[clientId].ip;\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${clientId} (IP unknown)`;\n // this.adapter.log.debug(`[MQTT] ${ipMsg}: - Start scheduleCheckIfStillActive`);\n\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n\n if (!this.devices[clientId]) this.devices[clientId] = {};\n\n const interval = 70 * 1000; // every 60s + 10s buffer\n this.devices[clientId].timeoutNoUpdate = this.adapter.setTimeout(async () => {\n try {\n const lastTimeActive = this.devices[clientId].lastTimeActive;\n if (!lastTimeActive) return;\n const diff = Date.now() - lastTimeActive;\n if (diff > 70000) {\n this.adapter.log.debug(`[MQTT] ${ipMsg} NOT ALIVE - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, false, 'client did not send message for more than 70 seconds');\n } else {\n this.adapter.log.warn(`[MQTT] ${ipMsg} Please open a issue on Github, this should never happen: scheduleCheckIfStillActive() timeout, and last contact was less than 70s ago.`);\n this.adapter.log.warn(`[MQTT] ${ipMsg} is alive - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, true, `alive check is successful (last contact: ${Math.round(diff / 1000)}s ago)`);\n }\n // Call function again since we are in callback of timeout\n this.scheduleCheckIfStillActive(clientId);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }, interval);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }\n\n /**\n * Terminate MQTT Server and close all...\n */\n public terminate(): void {\n this.adapter.log.info(`[MQTT] Disconnect all clients and close server`);\n // isAlive\n for (const clientId in this.devices) {\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n this.setIsAlive(clientId, false, 'MQTT server was terminated');\n }\n\n if (this.aedes) {\n this.aedes.close(() => {\n this.adapter.log.debug('[MQTT] aedes.close() succeeded');\n if (this.server) {\n this.server.close(() => {\n this.adapter.log.debug('[MQTT] server.close() succeeded');\n });\n }\n });\n } else if (this.server) {\n this.server.close(() => {\n this.adapter.log.debug('[MQTT] server.close() succeeded');\n });\n }\n }\n}\n"], - "mappings": "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", + "sourcesContent": ["import Aedes from 'aedes';\nimport net from 'net';\nimport { FullyMqtt } from '../main';\nimport { IMqttDevice } from './interfaces';\n//import { inspect } from 'util';\n\nexport class MqttServer {\n private readonly adapter: FullyMqtt;\n private server: net.Server;\n private aedes: Aedes;\n public devices: { [mqttClientId: string]: IMqttDevice }; // {}\n private port = -1;\n private notAuthorizedClients: string[] = []; // to avoid multiple log lines\n\n /**\n * Constructor\n */\n public constructor(adapter: FullyMqtt) {\n this.adapter = adapter;\n //this.server = new net.Server();\n this.aedes = new Aedes();\n /** @ts-expect-error - https://github.com/moscajs/aedes/issues/801 */\n this.server = net.createServer(undefined, this.aedes.handle);\n this.devices = {}; // key = MQTT Client ID, property: IMqttDevice\n }\n\n /**\n * Listen\n */\n public start(): void {\n try {\n /**\n * Port\n */\n this.port = this.adapter.config.mqttPort;\n /**\n * #############################################################\n * For Developer only: change port if in dev environment\n * #############################################################\n */\n if (this.adapter.adapterDir.includes('/.dev-server/default/node_modules')) {\n this.port = 3012;\n this.adapter.log.warn(`DEVELOPER: Port changed to ${this.port} as we are in DEV Environment! If you see this log message, please open an issue on Github.`);\n }\n\n /**\n * Start Listening\n */\n this.server.listen(this.port, () => {\n this.adapter.log.info(`[MQTT]\uD83D\uDE80 Server started and listening on port ${this.port}`);\n });\n\n /**\n * Verify authorization\n * This fires first and before this.aedes.on('client', (client) ...\n * https://github.com/moscajs/aedes/blob/main/docs/Aedes.md#handler-authenticate-client-username-password-callback\n */\n this.aedes.authenticate = (client, username, password, callback) => {\n try {\n // If we saw client before and is not authorized\n if (this.notAuthorizedClients.includes(client.id)) {\n callback(null, false);\n return;\n }\n\n // Create device entry with id as key, if not yet existing\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n /**\n * Get IP\n * This rather complicated way is needed, see https://github.com/moscajs/aedes/issues/186\n * Not sure if this always works, but client.req was undefined in my test - which is suggested in https://github.com/moscajs/aedes/issues/527\n */\n let ip: string | undefined = undefined;\n if (client.conn && 'remoteAddress' in client.conn && typeof client.conn.remoteAddress === 'string') {\n const ipSource = client.conn.remoteAddress; // like: ::ffff:192.168.10.101\n this.adapter.log.debug(`[MQTT] client.conn.remoteAddress = \"${ipSource}\" - ${client.id}`);\n ip = ipSource.substring(ipSource.lastIndexOf(':') + 1); // get everything after last \":\"\n if (!this.adapter.isIpAddressValid(ip)) ip === undefined;\n }\n // Check if IP is an active device IP\n if (ip && !this.adapter.activeDeviceIPs.includes(ip)) {\n this.adapter.log.error(`[MQTT] Client ${client.id} not authorized: ${ip} is not an active Fully device IP per adapter settings.`);\n this.notAuthorizedClients.push(client.id);\n callback(null, false);\n return;\n }\n\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`;\n this.adapter.log.debug(`[MQTT] Client ${ipMsg} trys to authenticate...`);\n if (ip) this.devices[client.id].ip = ip;\n\n /**\n * Verify User and Password\n */\n if (!this.adapter.config.mqttDoNotVerifyUserPw) {\n // Username\n if (username !== this.adapter.config.mqttUser) {\n this.adapter.log.warn(`[MQTT] Client ${ipMsg} Authorization rejected: received user name '${username}' does not match '${this.adapter.config.mqttUser}' in adapter settings.`);\n callback(null, false);\n return;\n }\n // Password\n if (password.toString() !== this.adapter.config.mqttPassword) {\n this.adapter.log.warn(`[MQTT] Client ${ipMsg} Authorization rejected: received password does not match with password in adapter settings.`);\n callback(null, false);\n return;\n }\n }\n this.adapter.log.info(`[MQTT]\uD83D\uDD11 Client ${ipMsg} successfully authenticated.`);\n callback(null, true);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n callback(null, false);\n }\n };\n\n /**\n * fired when a client connects\n */\n this.aedes.on('client', (client) => {\n try {\n if (!client) return;\n\n // Create device entry with id as key, if not yet existing (should have been set in this.aedes.authenticate already)\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n // IP\n const ip = this.devices[client.id].ip;\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`;\n\n this.adapter.log.debug(`[MQTT] Client ${ipMsg} connected to broker ${this.aedes.id}`);\n this.adapter.log.info(`[MQTT]\uD83D\uDD17 Client ${ipMsg} successfully connected.`);\n //this.adapter.log.debug(inspect(client)); //https://stackoverflow.com/a/31557814\n\n // set isAlive\n this.setIsAlive(client.id, true, 'client connected');\n\n // Schedule check if still alive\n this.scheduleCheckIfStillActive(client.id);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n });\n\n /**\n * fired when a client publishes a message packet on the topic\n */\n this.aedes.on('publish', (packet, client) => {\n try {\n if (!client || !packet) return;\n\n this.setIsAlive(client.id, true, 'client published message');\n\n // Create device entry with id as key, if not yet existing\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n // QOS is always 1 per Fully documentation\n if (packet.qos !== 1) return;\n\n if (packet.retain) {\n /**\n * Device Info coming in...\n * Per fully documentation: The complete device info will be published every 60 seconds as fully/deviceInfo/[deviceId] topic (retaining, QOS=1).\n */\n\n // Payload as object\n const info = JSON.parse(packet.payload.toString());\n\n // Verification of device info packet\n // We don't use topic to check since we do not want to rely on user's input in Fully Browser \"MQTT Device Info Topic\" settings.\n if (!('startUrl' in info) && !('ip4' in info)) {\n this.adapter.log.error(`[MQTT] Packet rejected: ${info.ip4} - Info packet expected, but ip4 and startUrl is not defined in packet. ${info.deviceId}`);\n return;\n }\n\n // IP\n const ip = info.ip4;\n const devMsg = `${this.adapter.fullys[ip].name} (${ip})`;\n // Check IP - already done in this.aedes.authenticate, but just in case we were unable to get ip there\n if (!this.adapter.activeDeviceIPs.includes(ip)) {\n this.adapter.log.error(`[MQTT] Client ${devMsg} Packet rejected: IP is not allowed per adapter settings. ${client.id}`);\n return;\n }\n this.devices[client.id].ip = ip;\n\n // Slow down: Don't accept info event more often than x seconds\n // Per Fully doc, should not come in more often than 60s anyway...\n const prevTime = this.devices[client.id].previousInfoPublishTime;\n const limit = this.adapter.config.mqttPublishedInfoDelay * 1000; // milliseconds\n if (prevTime && prevTime !== 0) {\n if (Date.now() - prevTime < limit) {\n const diffMs = Date.now() - prevTime;\n this.adapter.log.silly(`[MQTT] ${devMsg} Packet rejected: Last packet came in ${diffMs}ms (${Math.round(diffMs / 1000)}s) ago...`);\n return;\n }\n }\n this.devices[client.id].previousInfoPublishTime = Date.now(); // set for future events\n\n /**\n * First time received device info incl. IP address etc.\n */\n if (!this.devices[client.id].mqttFirstReceived) {\n // show only once\n this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullys[ip].name} = ${ip}`);\n // set to true\n this.devices[client.id].mqttFirstReceived = true;\n }\n /**\n * Call Adapter function onMqttInfo()\n */\n const result = {\n clientId: client.id,\n ip: ip,\n topic: packet.topic,\n infoObj: info,\n };\n this.adapter.onMqttInfo(result);\n } else if (packet.qos === 1 && !packet.retain) {\n /**\n * Event coming in...\n * Per fully documentation: Events will be published as fully/event/[eventId]/[deviceId] topic (non-retaining, QOS=1).\n */\n // {\"deviceId\":\"xxxxxxxx-xxxxxxxx\",\"event\":\"screenOn\"}\n // NOTE: Device ID is different to client id, we actually disregard deviceId\n const msg = JSON.parse(packet.payload.toString());\n\n // Verification of event packet\n // We don't use topic to check since we do not want to rely on user's input in Fully Browser \"MQTT Event Topic\" settings.\n if (!('event' in msg)) {\n this.adapter.log.error(`[MQTT] Packet rejected: Event packet expected, but event is not defined in packet. ${client.id}`);\n return;\n }\n\n // Disregard first event once connected: mqttConnected\n if (msg.event === 'mqttConnected') {\n this.adapter.log.silly(`[MQTT] Client Publish Event: Disregard mqttConnected event - ${msg.deviceId}`);\n return;\n }\n\n // Get IP\n if (!this.devices[client.id]) {\n this.adapter.log.info(`[MQTT] Client Publish Event: Device ID and according IP not yet seen thru \"Publish Info\"`);\n this.adapter.log.info(`[MQTT] We wait until first info is published. ${msg.deviceId}`);\n return;\n }\n const ip = this.devices[client.id].ip ? this.devices[client.id].ip : '';\n if (ip === '' || typeof ip !== 'string') {\n this.adapter.log.debug(`[MQTT] Client Publish Event: IP address could not be determined. - Client ID: ${client.id}`);\n this.adapter.log.debug(`[MQTT] Please be patient until first MQTT info packet coming in (takes up to 1 minute)`);\n return; // Disregard since IP is unknown!\n }\n\n // Call function\n const result = {\n clientId: client.id,\n ip: ip,\n topic: packet.topic,\n cmd: msg.event,\n };\n if (!this.devices[client.id].mqttFirstReceived) {\n // show only once\n this.adapter.log.info(`[MQTT] \uD83D\uDD17 Client ${client.id} = ${this.adapter.fullys[ip].name} (${ip})`);\n this.devices[client.id].mqttFirstReceived = true;\n }\n /**\n * Call Adapter function onMqttEvent()\n */\n this.adapter.onMqttEvent(result);\n } else {\n // Ignore\n return;\n }\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n });\n\n /**\n * fired when a client disconnects\n */\n this.aedes.on('clientDisconnect', (client) => {\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] Client ${logMsgName} disconnected.`);\n } else {\n this.adapter.log.error(`[MQTT] Client ${logMsgName} disconnected.`);\n }\n this.setIsAlive(client.id, false, 'client disconnected');\n });\n\n /**\n * fired on client error\n */\n this.aedes.on('clientError', (client, e) => {\n if (this.notAuthorizedClients.includes(client.id)) return; // Error msg was already thrown in aedes.authenticate() before\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] ${logMsgName}: Client error - ${e.message}`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 ${logMsgName}: Client error - ${e.message}`);\n }\n this.adapter.log.debug(`[MQTT]\uD83D\uDD25 ${logMsgName}: Client error - stack: ${e.stack}`);\n this.setIsAlive(client.id, false, 'client error');\n });\n\n this.aedes.on('connectionError', (client, e) => {\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] ${logMsgName}: Connection error - ${e.message}`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 ${logMsgName}: Connection error - ${e.message}`);\n }\n this.adapter.log.debug(`[MQTT]\uD83D\uDD25 ${logMsgName}: Connection error - stack: ${e.stack}`);\n this.setIsAlive(client.id, false, 'connection error');\n });\n\n /**\n * fired on server error\n */\n this.server.on('error', (e: any) => {\n if (e instanceof Error && e.message.startsWith('listen EADDRINUSE')) {\n this.adapter.log.debug(`[MQTT] Cannot start server - ${e.message}`);\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 Cannot start server - Port ${this.port} is already in use. Try a different port!`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 Cannot start server - ${e.message}`);\n }\n this.terminate();\n });\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }\n\n /**\n * If Client is alive or not\n */\n private setIsAlive(clientId: string, isAlive: true | false, msg: string): void {\n if (isAlive) this.devices[clientId].lastTimeActive = Date.now();\n this.devices[clientId].isActive = isAlive;\n\n const ip = this.devices[clientId]?.ip;\n if (ip) {\n this.adapter.onAliveChange('MQTT', ip, isAlive, msg);\n if (isAlive) {\n this.scheduleCheckIfStillActive(clientId); // restart timer\n } else {\n // clear timer\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n }\n } else {\n this.adapter.log.debug(`[MQTT] isAlive changed to ${isAlive}, but IP of client ${clientId} is still unknown.`);\n }\n }\n\n /**\n * Schedule: Check if MQTT topic was sent last x seconds ago\n * @param ip IP Address\n * @returns void\n */\n private async scheduleCheckIfStillActive(clientId: string): Promise {\n try {\n const ip = this.devices[clientId].ip;\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${clientId} (IP unknown)`;\n // this.adapter.log.debug(`[MQTT] ${ipMsg}: - Start scheduleCheckIfStillActive`);\n\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n\n if (!this.devices[clientId]) this.devices[clientId] = {};\n\n const interval = 70 * 1000; // every 60s + 10s buffer\n this.devices[clientId].timeoutNoUpdate = this.adapter.setTimeout(async () => {\n try {\n const lastTimeActive = this.devices[clientId].lastTimeActive;\n if (!lastTimeActive) return;\n const diff = Date.now() - lastTimeActive;\n if (diff > 70000) {\n this.adapter.log.debug(`[MQTT] ${ipMsg} NOT ALIVE - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, false, 'client did not send message for more than 70 seconds');\n } else {\n this.adapter.log.warn(`[MQTT] ${ipMsg} Please open a issue on Github, this should never happen: scheduleCheckIfStillActive() timeout, and last contact was less than 70s ago.`);\n this.adapter.log.warn(`[MQTT] ${ipMsg} is alive - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, true, `alive check is successful (last contact: ${Math.round(diff / 1000)}s ago)`);\n }\n // Call function again since we are in callback of timeout\n this.scheduleCheckIfStillActive(clientId);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }, interval);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }\n\n /**\n * Terminate MQTT Server and close all...\n */\n public terminate(): void {\n this.adapter.log.info(`[MQTT] Disconnect all clients and close server`);\n // isAlive\n for (const clientId in this.devices) {\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n this.setIsAlive(clientId, false, 'MQTT server was terminated');\n }\n\n if (this.aedes) {\n this.aedes.close(() => {\n this.adapter.log.debug('[MQTT] aedes.close() succeeded');\n if (this.server) {\n this.server.close(() => {\n this.adapter.log.debug('[MQTT] server.close() succeeded');\n });\n }\n });\n } else if (this.server) {\n this.server.close(() => {\n this.adapter.log.debug('[MQTT] server.close() succeeded');\n });\n }\n }\n}\n"], + "mappings": "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", "names": ["Aedes", "net"] } diff --git a/docs/de/README.md b/docs/de/README.md index bbe707a..2371e5b 100644 --- a/docs/de/README.md +++ b/docs/de/README.md @@ -4,7 +4,7 @@ Mit diesem Adapter kann der [Fully Kiosk Browser](https://www.fully-kiosk.com) (mit Plus-Lizenz) gesteuert werden. Über die [REST API](https://www.fully-kiosk.com/en/#rest) können diverse Befehle wie "Bildschirm an/aus", "Bildschirmschoner an/aus", etc. an den Fully gesendet werden. -Zusätzlich – sofern dies in den Adapter-Optionen MQTT aktiviert ist – werden Events (wie z.B. "Bildschirm an") immer sofort an den Adapter mitgeteilt und in den entsprechenden Datenpunkten angezeigt. Außerdem sendet der Fully Browser bei aktivem [MQTT](https://www.fully-kiosk.com/en/#mqtt) sämtliche Geräteinformationen immer automatisch mindestens alle 60 Sekunden, welche entsprechend in die Info-Datenpunkte geschrieben werden. Ist MQTT deaktiviert, werden diese Infos manuell regelmäßig über die REST API abgeholt. Das Senden von Befehlen erfolgt übrigens bei aktiviertem MQTT über die REST API, da der Fully Browser das Senden über MQTT nicht unterstützt. +Außerdem werden Events (wie z.B. "Bildschirm an") immer sofort via [MQTT](https://www.fully-kiosk.com/en/#mqtt) dem Adapter mitgeteilt und in den entsprechenden Datenpunkten angezeigt/gesetzt. Desweiteren sendet der Fully Browser via MQTT sämtliche Geräteinformationen immer automatisch mindestens alle 60 Sekunden, welche entsprechend in die Info-Datenpunkte geschrieben werden. Das Senden von Befehlen erfolgt übrigens über die REST API, da der Fully Browser das Senden über MQTT nicht unterstützt. ## Fully-Browser vorbereiten @@ -42,20 +42,17 @@ Fully-Browser-Gerät(e), also die Tablets, auf denen der Fully-Browser läuft, e ### MQTT-Konfiguration * **Port**: Dieselbe Portnummer wie oben in den Fullybrowser MQTT-Einstellungen verwenden (z.B. `3000`). + * **Benutzername und Passwort nicht verifizieren**: Damit kann die Überprüfung von Benutzernamen und Passwort deaktiviert werden. * **Benutzername**: Optional * **Passwort**: Optional - * **Benutzername und Passwort nicht verifizieren**: Damit kann die Überprüfung von Benutzernamen und Passwort deaktiviert werden. - -### Experten-Einstellungen: Remote Admin (REST API) - * **Request Timeout**: Nach Anzahl dieser Millisekunden wird ein REST API Request abgebrochen, wenn nicht erfolgreich. - * **Request Interval**: Wie oft sollen die Geräte-Infos abgefragt werden (Batteriestand, u.v.m.)? Etwa 60 Sekunden sollten locker reichen, gerne auch ein höheres Intervall vergeben. *Hinweis*: Wenn MQTT aktiviert ist, wird dies ignoriert, da per MQTT automatisch mind. alle 60 Sekunden Infos empfangen werden. - * **Info-Datenpunkte immer aktualisieren**: Normalerweise werden alle Info-Datenpunkte nur dann neu gesetzt, wenn es eine Änderung gab. Ist dies aktiviert, werden diese immer aktualisiert (mit ack:true), auch wenn es keine Änderung zum vorherigen Wert gab. ### Experten-Einstellungen: MQTT * **Publizierte Infos nicht öfter als alle x Sekunden verarbeiten**: Lt. [Fully-Dokumentation](https://www.fully-kiosk.com/en/#mqtt) werden Infos nur alle 60 Sekunden publiziert, in meinen Tests erfolgte dies aber deutlich öfter, also kann hiermit ein Limit gesetzt werden. * **Info-Datenpunkte immer aktualisieren**: Normalerweise werden alle Info-Datenpunkte nur dann neu gesetzt, wenn es eine Änderung gab. Ist dies aktiviert, werden diese immer aktualisiert (mit ack:true), auch wenn es keine Änderung zum vorherigen Wert gab. * **Client- und Connection-Fehler als info im Log**: Wenn aktiviert, werden Client- und Verbindungsfehler immer als Info und nicht als Error im Log ausgegeben. Dies dient dazu, das Log sauber zu halten und nicht unnötig zu füllen, nur weil sich mal kurzzeitig ein Tablet abmeldet und nach wenigen Sekunden wieder anmeldet. Längerzeitige Fehler und Warnungen werden immer im Log entsprechend angezeigt. +### Experten-Einstellungen: Remote Admin (REST API) + * **Request Timeout**: Nach Anzahl dieser Millisekunden wird ein REST API Request (also das Senden von Kommandos) abgebrochen, wenn nicht erfolgreich. ## Links diff --git a/docs/en/README.md b/docs/en/README.md index 771dc07..8d55312 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -4,7 +4,7 @@ With this adapter the [Fully Kiosk Browser](https://www.fully-kiosk.com) (with Plus license) can be controlled. Via the [REST API](https://www.fully-kiosk.com/en/#rest) various commands like "screen on/off", "screen saver on/off", etc. can be sent to the Fully. -Additionally - if MQTT is enabled in the adapter options - events (like "screen on") are always immediately communicated to the adapter and set in the corresponding states. Furthermore, if [MQTT](https://www.fully-kiosk.com/en/#mqtt) is active, the Fully Browser always sends all device information automatically at least every 60 seconds, which are set to the accoring info states accordingly. If MQTT is disabled, this info is requested manually on a regular basis via the REST API. Please note that commands are sent via the REST API even if MQTT is enabled, since the Fully Browser does not support sending commands via MQTT. +Additionally, [MQTT](https://www.fully-kiosk.com/en/#mqtt) events (like "screen on") are always immediately communicated to the adapter and set in the corresponding states. Furthermore, the Fully Browser always sends all device information via MQTT automatically at least every 60 seconds, which are set to the info states accordingly. Please note that all commands are sent via the REST API and not MQTT, since the Fully Browser does not support sending commands via MQTT. ## Fully-Browser settings @@ -38,24 +38,20 @@ Add Fully Browser device(s), i.e. the tablets running Fully Browser, accordingly 1. **Device Name**: Any name, which is also used as part of the objects/states, e.g. `Tablet Flur` becomes `fully-mqtt.0.Tablet-Flur`. 1. **Protocol**: leave `http` as it is. If `https` should be used: see notes under [Remote Admin](https://www.fully-kiosk.com/en/#remoteadmin). 1. **Remote Admin Password**: enter the password as set above. -1. **MQTT**: should be enabled to use all features of the adapter. ### MQTT Configuration * **Port**: Use the same port number as above in the Fullybrowser MQTT settings (e.g. `3000`). + * **Do not verify user and password**: can be activated to disable username and password verification * **User name**: optional * **Password**: optional - * **Do not verify user and password**: can be activated to disable username and password verification - -### Expert Settings: Remote Admin (REST API) - * **Request Timeout**: After this number milliseconds, REST API requests are aborted if not successful. - * **Request Interval**: How often should the device info be requested (battery level, etc.)? About 60 seconds should be enough, you can also assign a higher interval. *Note*: If MQTT is activated, this will be ignored, because MQTT automatically receives info at least every 60 seconds. - * **Always update info objects**: Normally all info sets are set/updated only if there was a change. If this option is enabled, states will always be updated (with ack:true), even if there was no change from the previous value. ### Expert Settings: MQTT - * **Do not process published info more than every x seconds**: Lt. [Fully-Dokumentation](https://www.fully-kiosk.com/en/#mqtt) werden Infos nur alle 60 Sekunden publiziert, in meinen Tests erfolgte dies aber deutlich öfter, also kann hiermit ein Limit gesetzt werden. + * **Do not process published info more than every x seconds**: Lt. [Fully-Dokumentation](https://www.fully-kiosk.com/en/#mqtt) info is published only every 60 seconds, but in my tests this happened more often, so a limit can be set with this option. * **Always update info objects**: Normally all info sets are set/updated only if there was a change. If this option is enabled, states will always be updated (with ack:true), even if there was no change from the previous value. * **Client and Connection errors as info in log**: If activated, client and connection errors are always output as info and not as error in the log. This serves to keep the log clean and not to fill it unnecessarily just because a tablet logs off briefly and logs on again after a few seconds. "Longer-term" errors and warnings are always displayed in the log accordingly. +### Expert Settings: Remote Admin (REST API) + * **Request Timeout**: After this number milliseconds, REST API requests (i.e. sending commands) are aborted if not successful. ## Links diff --git a/src/lib/mqtt-server.ts b/src/lib/mqtt-server.ts index 3145a1c..94bf768 100644 --- a/src/lib/mqtt-server.ts +++ b/src/lib/mqtt-server.ts @@ -87,7 +87,7 @@ export class MqttServer { } const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`; - this.adapter.log.info(`[MQTT] Client ${ipMsg} trys to authenticate...`); + this.adapter.log.debug(`[MQTT] Client ${ipMsg} trys to authenticate...`); if (ip) this.devices[client.id].ip = ip; /**