Skip to content

Commit

Permalink
restapi
Browse files Browse the repository at this point in the history
  • Loading branch information
arteck committed Oct 31, 2023
1 parent c8e6e10 commit ac0e8e2
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 165 deletions.
32 changes: 31 additions & 1 deletion admin/jsonConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,25 @@
"title": "",
"default": true
},
{
"type": "select",
"title": "ApiType",
"attr": "apiType",
"default": "mqtt",
"width": "6%",
"validator": "data.apiType.length > 3",
"validatorNoSaveOnError": true,
"options": [
{
"label": "MQTT",
"value": "mqtt"
},
{
"label": "RestApi",
"value": "restapi"
}
]
},
{
"type": "text",
"title": "Device Name",
Expand Down Expand Up @@ -243,13 +262,24 @@
"md": 6,
"lg": 3
},
"restIntervall": {
"type": "number",
"min": 10000,
"max": 99999999,
"default": 6000,
"label": "Request Intervall",
"help": "in milliseconds (10000-99999999, default: 60000 = 1 Min.)",
"sm": 12,
"md": 7,
"lg": 3
},
"restCommandLogAsDebug": {
"type": "checkbox",
"label": "Successful commands as debug in log",
"help": "Log '... Command xxx successfully set to x' as 'debug' and not 'info'",
"default": false,
"sm": 12,
"md": 7,
"md": 8,
"lg": 5
}
}
Expand Down
5 changes: 3 additions & 2 deletions io-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@
"cancel"
]
}
]
],
"installedFrom": "arteck/ioBroker.fullybrowser#newFully"
},
"native": {
"tableDevices": [],
Expand Down Expand Up @@ -209,4 +210,4 @@
"native": {}
}
]
}
}
104 changes: 67 additions & 37 deletions lib/mqtt-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,50 @@ class MqttServer {
start() {
try {
this.port = this.adapter.config.mqttPort;
if (this.adapter.adapterDir.includes("/.dev-server/default/node_modules")) {
this.port = 3012;
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.`);
}

this.server.listen(this.port, () => {
this.adapter.log.info(`\u{1F680} MQTT Server started and is listening on port ${this.port}.`);
});

this.aedes.authenticate = (client, username, password, callback) => {
try {
if (this.notAuthorizedClients.includes(client.id)) {
callback(null, false);
return;
}
if (!this.devices[client.id])
this.devices[client.id] = {};
let ip = void 0;

// Create device entry with id as key, if not yet existing
if (!this.devices[client.id]) this.devices[client.id] = {};

/**
* Get IP
* This rather complicated way is needed, see https://github.com/moscajs/aedes/issues/186
* 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
*/
let ip = undefined;

if (client.conn && "remoteAddress" in client.conn && typeof client.conn.remoteAddress === "string") {
const ipSource = client.conn.remoteAddress;
const ipSource = client.conn.remoteAddress; // like: ::ffff:192.168.1.213
this.adapter.log.debug(`[MQTT] client.conn.remoteAddress = "${ipSource}" - ${client.id}`);

ip = ipSource.substring(ipSource.lastIndexOf(":") + 1);

if (!this.adapter.isIpAddressValid(ip))
ip === void 0;
ip = undefined;
}
if (ip && !Object.keys(this.adapter.fullysEnbl).includes(ip)) {

if (ip && !Object.keys(this.adapter.fullysMQTT).includes(ip)) {
this.adapter.log.error(`[MQTT] Client ${client.id} not authorized: ${ip} is not an active Fully device IP per adapter settings.`);
this.notAuthorizedClients.push(client.id);
callback(null, false);
return;
}
const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${client.id} (IP unknown)`;

const ipMsg = ip ? `${this.adapter.fullysMQTT[ip].name} (${ip})` : `${client.id} (IP unknown)`;
this.adapter.log.debug(`[MQTT] Client ${ipMsg} trys to authenticate...`);
if (ip)
this.devices[client.id].ip = ip;

if (ip) this.devices[client.id].ip = ip;

if (!this.adapter.config.mqttDoNotVerifyUserPw) {
if (username !== this.adapter.config.mqttUser) {
this.adapter.log.warn(`MQTT Client ${ipMsg} Authorization rejected: received user name '${username}' does not match '${this.adapter.config.mqttUser}' in adapter settings.`);
Expand All @@ -61,21 +72,25 @@ class MqttServer {
return;
}
}

this.adapter.log.info(`\u{1F511} MQTT Client ${ipMsg} successfully authenticated.`);
callback(null, true);
} catch (e) {
this.adapter.log.error(this.adapter.err2Str(e));
callback(null, false);
}
};

this.aedes.on("client", (client) => {
try {
if (!client)
return;

if (!this.devices[client.id])
this.devices[client.id] = {};

const ip = this.devices[client.id].ip;
const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${client.id} (IP unknown)`;
const ipMsg = ip ? `${this.adapter.fullysMQTT[ip].name} (${ip})` : `${client.id} (IP unknown)`;
this.adapter.log.debug(`[MQTT] Client ${ipMsg} connected to broker ${this.aedes.id}`);
this.adapter.log.info(`\u{1F517} MQTT Client ${ipMsg} successfully connected.`);
this.setIsAlive(client.id, true, "client connected");
Expand All @@ -90,6 +105,7 @@ class MqttServer {
try {
if (!client || !packet)
return;

this.setIsAlive(client.id, true, "client published message");
if (!this.devices[client.id])
this.devices[client.id] = {};
Expand All @@ -102,37 +118,45 @@ class MqttServer {
return;
}
const ip = info.ip4;
const devMsg = `${this.adapter.fullysEnbl[ip].name} (${ip})`;
if (!Object.keys(this.adapter.fullysEnbl).includes(ip)) {
const devMsg = `${this.adapter.fullysMQTT[ip].name} (${ip})`;
if (!Object.keys(this.adapter.fullysMQTT).includes(ip)) {
this.adapter.log.error(`[MQTT] Client ${devMsg} Packet rejected: IP is not allowed per adapter settings. ${client.id}`);
return;
}
this.devices[client.id].ip = ip;

const prevTime = this.devices[client.id].previousInfoPublishTime;
const limit = this.adapter.config.mqttPublishedInfoDelay * 1e3;
const limit = this.adapter.config.mqttPublishedInfoDelay * 1000; // milliseconds
if (prevTime && prevTime !== 0) {
if (Date.now() - prevTime < limit) {
const diffMs = Date.now() - prevTime;
this.adapter.log.silly(`[MQTT] ${devMsg} Packet rejected: Last packet came in ${diffMs}ms (${Math.round(diffMs / 1e3)}s) ago...`);
this.adapter.log.silly(`[MQTT] ${devMsg} Packet rejected: Last packet came in ${diffMs}ms (${Math.round(diffMs / 1000)}s) ago...`);
return;
}
}
this.devices[client.id].previousInfoPublishTime = Date.now();
if (!this.devices[client.id].mqttFirstReceived) {
this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullysEnbl[ip].name} = ${ip}`);
this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullysMQTT[ip].name} = ${ip}`);
this.devices[client.id].mqttFirstReceived = true;
}
const result = {
clientId: client.id,
ip,
ip: ip,
topic: packet.topic,
infoObj: info
};

this.adapter.onMqttInfo(result);

} else if (packet.qos === 1 && !packet.retain) {
/**
* Event coming in...
* Per fully documentation: Events will be published as fully/event/[eventId]/[deviceId] topic (non-retaining, QOS=1).
*/
// {"deviceId":"xxxxxxxx-xxxxxxxx","event":"screenOn"}
// NOTE: Device ID is different to client id, we actually disregard deviceId
const msg = JSON.parse(packet.payload.toString());

if (!("event" in msg)) {
this.adapter.log.error(`[MQTT] Packet rejected: Event packet expected, but event is not defined in packet. ${client.id}`);
return;
Expand All @@ -159,7 +183,7 @@ class MqttServer {
cmd: msg.event
};
if (!this.devices[client.id].mqttFirstReceived) {
this.adapter.log.info(`[MQTT] \u{1F517} Client ${client.id} = ${this.adapter.fullysEnbl[ip].name} (${ip})`);
this.adapter.log.info(`[MQTT] \u{1F517} Client ${client.id} = ${this.adapter.fullysMQTT[ip].name} (${ip})`);
this.devices[client.id].mqttFirstReceived = true;
}
this.adapter.onMqttEvent(result);
Expand All @@ -173,7 +197,7 @@ class MqttServer {
});
this.aedes.on("clientDisconnect", (client) => {
const ip = this.devices[client.id].ip;
const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id;
const logMsgName = ip ? this.adapter.fullysMQTT[ip].name : client.id;
if (this.adapter.config.mqttConnErrorsAsInfo) {
this.adapter.log.info(`MQTT Client ${logMsgName} disconnected.`);
} else {
Expand All @@ -185,7 +209,7 @@ class MqttServer {
if (this.notAuthorizedClients.includes(client.id))
return;
const ip = this.devices[client.id].ip;
const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id;
const logMsgName = ip ? this.adapter.fullysMQTT[ip].name : client.id;
if (this.adapter.config.mqttConnErrorsAsInfo) {
this.adapter.log.info(`[MQTT] ${logMsgName}: Client error - ${e.message}`);
} else {
Expand All @@ -196,7 +220,7 @@ class MqttServer {
});
this.aedes.on("connectionError", (client, e) => {
const ip = this.devices[client.id].ip;
const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id;
const logMsgName = ip ? this.adapter.fullysMQTT[ip].name : client.id;
if (this.adapter.config.mqttConnErrorsAsInfo) {
this.adapter.log.info(`[MQTT] ${logMsgName}: Connection error - ${e.message}`);
} else {
Expand All @@ -220,18 +244,20 @@ class MqttServer {
}
}
setIsAlive(clientId, isAlive, msg) {
var _a;
if (isAlive)
this.devices[clientId].lastTimeActive = Date.now();

if (isAlive) this.devices[clientId].lastTimeActive = Date.now();
this.devices[clientId].isActive = isAlive;
const ip = (_a = this.devices[clientId]) == null ? void 0 : _a.ip;

const ip = this.devices[clientId]?.ip;
if (ip) {
// Call Adapter function onMqttAliveChange()
this.adapter.onMqttAlive(ip, isAlive, msg);
if (isAlive) {
this.scheduleCheckIfStillActive(clientId);
this.scheduleCheckIfStillActive(clientId); // restart timer
} else {
if (this.devices[clientId].timeoutNoUpdate)
this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);
// clear timer
// @ts-expect-error "Type 'null' is not assignable to type 'Timeout'.ts(2345)" - we check for not being null via "if"
if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);
}
} else {
this.adapter.log.debug(`[MQTT] isAlive changed to ${isAlive}, but IP of client ${clientId} is still unknown.`);
Expand All @@ -240,25 +266,29 @@ class MqttServer {
async scheduleCheckIfStillActive(clientId) {
try {
const ip = this.devices[clientId].ip;
const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${clientId} (IP unknown)`;
const ipMsg = ip ? `${this.adapter.fullysMQTT[ip].name} (${ip})` : `${clientId} (IP unknown)`;

if (this.devices[clientId].timeoutNoUpdate)
this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);

if (!this.devices[clientId])
this.devices[clientId] = {};
const interval = 70 * 1e3;

const interval = 70 * 1000;

this.devices[clientId].timeoutNoUpdate = this.adapter.setTimeout(async () => {
try {
const lastTimeActive = this.devices[clientId].lastTimeActive;
if (!lastTimeActive)
return;
const diff = Date.now() - lastTimeActive;
if (diff > 7e4) {
this.adapter.log.debug(`[MQTT] ${ipMsg} NOT ALIVE - last contact ${Math.round(diff / 1e3)}s (${diff}ms) ago`);
if (diff > 70000) {
this.adapter.log.debug(`[MQTT] ${ipMsg} NOT ALIVE - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);
this.setIsAlive(clientId, false, "client did not send message for more than 70 seconds");
} else {
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.`);
this.adapter.log.warn(`[MQTT] ${ipMsg} is alive - last contact ${Math.round(diff / 1e3)}s (${diff}ms) ago`);
this.setIsAlive(clientId, true, `alive check is successful (last contact: ${Math.round(diff / 1e3)}s ago)`);
this.adapter.log.warn(`[MQTT] ${ipMsg} is alive - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);
this.setIsAlive(clientId, true, `alive check is successful (last contact: ${Math.round(diff / 1000)}s ago)`);
}
this.scheduleCheckIfStillActive(clientId);
} catch (e) {
Expand Down
Loading

0 comments on commit ac0e8e2

Please sign in to comment.