Subscription to MQTT broker and get the data being passed - node.js

I am generating data with a node.js simulator and passing this data to a http route /simulator/data
In the application I am listening broker with MQTT mqtthandler.js file which I share below.
//This is mqtthandler.js file
const mqtt = require("mqtt");
class MqttHandler {
constructor() {
this.mqttClient = null;
this.host = "mqtt://localhost:1883";
this.username = "YOUR_USER"; // mqtt credentials if these are needed to connect
this.password = "YOUR_PASSWORD";
}
connect() {
// Connect mqtt with credentials (in case of needed, otherwise we can omit 2nd param)
this.mqttClient = mqtt.connect(this.host, {
username: this.username,
password: this.password,
});
// Mqtt error calback
this.mqttClient.on("error", (err) => {
console.log(err);
this.mqttClient.end();
});
// Connection callback
this.mqttClient.on("connect", () => {
console.log(`mqtt client connected`);
});
// mqtt subscriptions
this.mqttClient.subscribe("value", { qos: 0 });
// When a message arrives, console.log it
this.mqttClient.on("message", function (topic, message) {
console.log(message.toString());
});
this.mqttClient.on("close", () => {
console.log(`mqtt client disconnected`);
});
}
// Sends a mqtt message to topic: mytopic
sendMessage(message) {
this.mqttClient.publish("value", message);
}
}
module.exports = MqttHandler;
When simulator sending the data to the /simulator/data route, I am getting the value and sending the broker with value topic. I share the post request code and output of simulator below.
var mqttHandler = require("../mqtthandler");
module.exports = function (app) {
app.get("/simulator", function (req, res) {
res.render("iot/simulator");
});
// route to display all the data that is generated
app.get("/simulator/data", require("./controllers/data").all);
var mqttClient = new mqttHandler();
mqttClient.connect();
// route to write data to the database
app.post(
"/simulator/data",
require("./controllers/data").write,
(req, res) => {
mqttClient.sendMessage(req.body.value);
res.status(200).send("Message sent to mqtt");
}
);
// delete the data when the stream is stopped or when the app is closed
app.get("/simulator/data/delete", require("./controllers/data").delete);
};
When I send get request to /simulator/data I am able to see generated data, however this data is not being sent to broker.
//This is output of simulator
[
{
"_id": "5ecfadc13cb66f10e4d9d39b",
"value": "1.886768240197795",
"__v": 0,
"categories": []
},
{
"_id": "5ecfadc23cb66f10e4d9d39c",
"value": "7.351404601932272",
"__v": 0,
"categories": []
}
]
PS: Broker is created via node-red
I would like to pass this data to broker and see the result with MQTT subscription. However I can not find where am I making mistake.

Your solution is to fix your development process. Rather than working from failure debugging 2 subsystems (your publisher / simulator and your subscriber), work from success:
1) use publishers that you KNOW work, eg. mosquitto_pub, any simulator that works, etc.
2) use subscribers that you KNOW work, eg. mosquitto_sub
This will solve your problem in minutes, rather than hours or days, and let you focus on the code that you REALLY want to develop.

So a couple of things to look at here.
Your this.mqttClient.subscribe() call is inline with your connect(), on.("error",...), on.("message",...) etc. So the subscribe() could fire before the connect() has finished...and thus you will never subscribe. Put the subscribe() inside the connect() block.
You are subscribing to "value", which is not a proper MQTT topic. If you must, use value/# for the subscribe(), and "value/.." for the publish(). Your class only allows for the single, hard-coded topic, so won't be very useful when you want to reuse that class for other projects. Take the time now to pass the topic string to the class as well.

Related

Two different websockets working together

I'm trying to figure out how to implement a two separate websockets together. Not sure if this possible or not, but I have a websocket that works on it's own in from node.js file to angular another node.js file that uses Kraken (crypto exchange) websocket that also works in it's own file. I'm trying to consolidate them both together so that whenever a event onChange comes from Kraken websocket, I can relay that data to angular with angular socket.io websocket. Trying to do something like this
const webSocketClient = new WebSocket(connectionURL);
webSocketClient.on("open", function open() {
webSocketClient.send(webSocketSubscription);
});
webSocketClient.on("message", function incoming(wsMsg) {
const data = JSON.parse(wsMsg);
let io = require("socket.io")(server, {
cors: {
origin: "http://localhost:4200",
methods: ["GET", "POST"],
allowedHeaders: ["*"],
credentials: true,
},
});
io.on("connection", (socket) => {
const changes = parseTrades(data);
socketIo.sockets.emit(connection.change, changes);
Log whenever a user connects
console.log("user connected");
socket.emit("test event", JSON.stringify(changes));
});
console.log("DATA HERE", data[0]);
});
webSocketClient.on("close", function close() {
console.log("kraken websocket closed");
});
Although doing this doesnt relay the data to frontend and gives me a memory leak. Is there some way I can accomplish this?
I would probably split up the task a little bit. So have a service for the kraken websocket and maybe a service for your own socket, then have them communicate via observables, that you can also tap into from the front end to display data you want.
#Injectable()
export class KrakenService{
private webSocketClient : WebSocket | null;
private messages$ = new BehaviorSubject<any>(null); // give it a type
openConnection(){
// is connectionUrl from environment ??
this.webSocketClient = new WebSocket(connectionURL);
webSocketClient.on("open", function open() {
/// is webSocketSubscription from environment ??
webSocketClient.send(webSocketSubscription);
});
webSocketClient.on("message", function incoming(wsMsg) {
const data = JSON.parse(wsMsg);
this.messages$.next(data);
console.log("DATA HERE", data[0]);
});
webSocketClient.on("close", function close() {
console.log("kraken websocket closed");
this.webSocketClient = null;
});
}
getKrakenMessages(){
if(webSocketClient == null) this.openConnection();
return messages$.asObserbable();
}
}
So now when you want to either read the web socket messages or use them with the other socket you just subscribe to the krakenService.getKrakenMessages();
Then you can do something similar with you local service as well. Have something that opens connections, and one that emits messages. The example you showed it would open up a connection every time you got a message, so keep those logics separated. And reuse existing connection.

How does a Node server subscribe correctly to a Redis channel with Laravel 8?

In Laravel 8, I generate a broadcast event that writes data to Redis.
The broadcastOn method of the "NewEvent implements ShouldBroadcast" class specifies the name of the event that writes data to Redis.
public function broadcastOn()
{
return ['news-action'];
}
With the help of the redis-cli monitor, I can see the format of the data that goes to the Redis server.
1616158237.374187 [0 127.0.0.1:60746] "SELECT" "0"
1616158237.374770 [0 127.0.0.1:60746] "EVAL" "for i = 2, #ARGV do\n redis.call('publish', ARGV[i], ARGV[1])\nend" "0" "{\"event\":\"App\\\\Events\\\\NewEvent\",\"data\":{\"result\":{\"labels\":[\"\\u043c\\u0430\\u0440\\u0442\",\"\\u0430\\u043f\\u0440\\u0435\\u043b\\u044c\",\"\\u043c\\u0430\\u0439\",\"\\u0438\\u044e\\u043d\\u044c\",\"33333\"],\"datasets\":[{\"label\":\"\\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438\",\"backgroundColor\":[\"#D01919\",\"#F26202\",\"#EAAE00\",\"#B5CC18\"],\"data\":[15000,50000,10000,8000,500]}]},\"socket\":null},\"socket\":null}" "laravel_database_news-action"
1616158237.374850 [0 lua] "publish" "laravel_database_news-action" "{\"event\":\"App\\\\Events\\\\NewEvent\",\"data\":{\"result\":{\"labels\":[\"\\u043c\\u0430\\u0440\\u0442\",\"\\u0430\\u043f\\u0440\\u0435\\u043b\\u044c\",\"\\u043c\\u0430\\u0439\",\"\\u0438\\u044e\\u043d\\u044c\",\"33333\"],\"datasets\":[{\"label\":\"\\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438\",\"backgroundColor\":[\"#D01919\",\"#F26202\",\"#EAAE00\",\"#B5CC18\"],\"data\":[15000,50000,10000,8000,500]}]},\"socket\":null},\"socket\":null}"
Since this data is incomprehensible to a person in this form, I tried to parse it manually, and that's what I got.
1616158237.374187 [0 127.0.0.1:60746] "SELECT" "0"
1616158237.374770 [0 127.0.0.1:60746] "EVAL" "for i = 2, #ARGV do\n redis.call('publish', ARGV[i], ARGV[1])\nend" "0"
"{
\"event\":\"App\\\\Events\\\\NewEvent\",
\"data\":{
\"result\":{
\"labels\":[
\"\\u043c\\u0430\\u0440\\u0442\",
\"\\u0430\\u043f\\u0440\\u0435\\u043b\\u044c\",
\"\\u043c\\u0430\\u0439\",
\"\\u0438\\u044e\\u043d\\u044c\",
\"33333\"
],
\"datasets\":[
{
\"label\":\"\\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438\",
\"backgroundColor\":[
\"#D01919\",
\"#F26202\",
\"#EAAE00\",
\"#B5CC18\"
],
\"data\":[
15000,50000,10000,8000,500
]
}
]
},
\"socket\":null
},
\"socket\":null
}"
"laravel_database_news-action"
1616158237.374850 [
0 lua
]
"publish"
"laravel_database_news-action"
"{
\"event\":\"App\\\\Events\\\\NewEvent\",
\"data\":{
\"result\":{
\"labels\":[
\"\\u043c\\u0430\\u0440\\u0442\",
\"\\u0430\\u043f\\u0440\\u0435\\u043b\\u044c\",
\"\\u043c\\u0430\\u0439\",
\"\\u0438\\u044e\\u043d\\u044c\",
\"33333\"
],
\"datasets\":[
{
\"label\": \"\\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438\",
\"backgroundColor\":[\"#D01919\",\"#F26202\",\"#EAAE00\",\"#B5CC18\"],
\"data\":[15000,50000,10000,8000,500]
}
]
},
\"socket\":null
},
\"socket\":null
}"
My Node server correctly subscribes to the Redis channel "*"
redis.psubscribe("*", (err, count) => {
if (err) {
console.error("Failed to subscribe: %s", err.message);
} else {
console.log(
`Subscribed successfully! This client is currently subscribed to ${count} channels.`
);
}
});
and issues a message about it to the console.
$ node node-server.js
Listening on Port: 3000
Subscribed successfully! This client is currently subscribed to 1 channels.
The Node server then connects to the socket.
redis.on("pmessage", (channel, message) => {
console.log(channel);
console.log(message);
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
But for some reason, not the message itself gets into the variable "message", but this text "laravel_database_news-action" which I see in the console
$ node node-server.js
Listening on Port: 3000
Subscribed successfully! This client is currently subscribed to 1 channels.
*
laravel_database_news-action
Because of this, during an attempt to pass this string in the JSON.parse (message) function, the Node-server immediately crashes.
From this I conclude that at the time of subscribing to the channel, I must indicate the specific name of the channel, but not "*"
But if I change "*" to "news-action" then the server stops responding and does not output any messages to the console.
Therefore, I want to know how to specify the channel name correctly so that the subscription is performed correctly and that the correct data, which is written in Redis server, gets into the variable "message"
Okay, everybody, I figured it out.
First, I added another method in the "Broadcast" class of the event.
public function broadcastAs(){
return 'new-event';
}
Secondly, fixed some functions in my Node server.
If suddenly someone needs it, then I represent him completely.
const http = require('http').Server();
const io = require('socket.io')(http, {
cors: {
// origin: "http://127.0.0.1:8000",
// methods: ["GET", "POST"],
// withCredentials: false
}
});
const Redis = require('ioredis');
const redis = new Redis(6379, "127.0.0.1");
redis.psubscribe("*", (err, count) => {
//
});
redis.on("pmessage", (pattern, channel, message) => {
message = JSON.parse(message);
// console.log(pattern);
// console.log(channel);
// console.log(message);
io.emit("news-action:"+message.event, message.data);
});
http.listen(3000, function (){
console.log('Listening on Port: 3000')
});
And thirdly, the client must subscribe to the channel as follows
socket.on("news-action:new-event", function (data){
// console.log(data);
this.data = data.result;
}.bind(this));

How to pass on an updated variable outside of MQTT event to use it otherwise as condition? (Hyperledger Fabric & MQTT)

First, we are quite new in the area Hyperledger Fabric and MQTT. We are building a prototype with a Blockchain platform (Hyperledger Fabric) written in nodeJS and a raspberry pi for transmitting the IoT data (working with mqtt). In our nodeJS file we create a MQTT client that subscribes to a topic and gets as a message a JSON object. The parameters of that object have to be passed on to the smart contract (submitContract). We realized that the gateway for Hyperledger Fabric disconnects before it runs the MQTT event functions (client.on) and therefore we thought we let the gateway disconnect on the condition of a submitted contract (see below code). In order to do so we want to get the success variable out of the client.on("message") function so that we can use it for the if statement at the end. However, the success variable doesn't seem to get updated to success=true by simply using return within client.on() so that eventually the program never exits (aka gateway is not disconnected). Below, you see parts of the code (the relevant parts). Does anyone know how to pass on properly an updated variable out of the MQTT event function?
'use strict';
const { FileSystemWallet, Gateway } = require('fabric-network');
const mqtt = require("mqtt");
async function main() {
try {
// Create a new gateway for connecting to our peer node.
const gateway = new Gateway();
await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
// Get the network (channel) our contract is deployed to.
const network = await gateway.getNetwork('mychannel');
// Get the contract from the network.
const contract = network.getContract('fabcar');
//MQTT client connection & contract submission
console.log("connecting to broker");
const client = mqtt.connect("mqtt://192.168.43.217");
var success = false;
client.on("connect", () =>{
console.log("Subscribing");
client.subscribe("rfidData");
console.log("Please hold your tag on the RFID reader. Wait...");
});
client.on("message", (topic, message) =>{
var rfidPayload = JSON.parse(message.toString());
var carKeyIn = rfidPayload.carKey;
var renterIDIn = rfidPayload.renterID;
var timestampIn = rfidPayload.timestamp;
console.log(rfidPayload);
contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
success = true;
console.log("Success? " + success);
return success;
});
client.stream.on('error', (err) => {
console.log('errorMessage', err);
client.end()
});
client.on("offline",() =>{
console.log("offline");
});
client.on("reconnect", ()=>{
console.log("reconnect");
});
// Disconnect from the gateway.
if (success === true){
client.end();
gateway.disconnect();
};
} catch (error) {
console.error(`Failed to submit transaction: ${error}`);
process.exit(1);
}
}
main();
You need to take on the concept of asynchronous execution and event driven systems. The application you have written does not execute in the order it is written.
The on('message',....) call back will only be executed when a message is delivered to client. This function also does not return any values (it is not actually called by any of your code, it is called by the underlying MQTT client code) so it will not "return" the value of success.
If you want the application to disconnect after it has received the first message then the easiest thing to do is to move the code that disconnects the MQTT client and the Fabric gateway to inside the callback. e.g.
client.on("message", (topic, message) =>{
var rfidPayload = JSON.parse(message.toString());
var carKeyIn = rfidPayload.carKey;
var renterIDIn = rfidPayload.renterID;
var timestampIn = rfidPayload.timestamp;
console.log(rfidPayload);
contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
gateway.disconnect();
client.end();
});
EDIT:
Looking at the doc it appears that submitTransaction() is flagged as being an async function, so you should be able to use await to block until it completes
...
console.log(rfidPayload);
await contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
gateway.disconnect();
client.end();
});

Send a typing indicator on conversation update event Node.js

I am trying to send a typing indicator on conversationUpdate but seems for me session.sendTyping() is only working within a dialog. At least I tried in Webchat and BotEmulator channels with no success. Any ideas about how to accomplish this?
Here my try:
bot.on('conversationUpdate', async (message) => {
if (message.membersAdded) {
for (let identity of message.membersAdded) {
if (identity.id === message.address.bot.id) {
const replies = getGreetingReplies(message);
cosmosDB.writeOnConvUpdate({ userMessage: message, botMessage: replies[0] }, message.address.conversation.id);
bot.loadSession(message.address, async (err, session) => {
for (let reply of replies) {
//var msg = new builder.Message().address(message.address).text('');
//msg.type = 'Typing';
//bot.send(msg);
await utilities.SendTyping(session, 3000);
bot.send(reply);
}
});
}
}
}});
For directline/webchat make sure to set up your bot connection on the frontend like so:
botConnection = new BotChat.DirectLine({
secret: "<secret>",
webSocket:true,
sendTyping: true
});
Then to actually send a sendTyping event you just have to call session.sendTyping() when a new message is received, or if you want to be more specific, you can call the method before something you know is going to take a while, like an API call.
The sendTyping event only works with an open webSocket, and is not supported in all channels. However it will work in webchat when using the above configuration.

In Node js - Emit event for particular user in Socket

Currently I have facing issue that I got response to all users.
I want to just notify for Selected user in single chat module.
like below i want to sent notification to this "userid" only
Client side file code has below
var forwardString =
{
"userid": actual_receiverID,
"id": msgId
};
socket.emit('insertReceiverIdInMessageForwardReq',forwardString);
socket.on('insertReceiverIdInMessageForwardRes', function (messages) {
var updatedMsg = messages.Messages;
});
------------------Server Side code------------------
io.on('connection', function (socket) {
socket.on('insertReceiverIdInMessageForwardReq', function (status) {
io.emit('insertReceiverIdInMessageForwardRes', res);
});
});
// sending to individual socketid (private message)
socket.to(<socketid>).emit('hey', 'I just met you');
// or you can do this as well
io.sockets.connected[<socketid>].emit('hey', 'I just met you');
Please refer the documentation for more details: https://socket.io/docs/emit-cheatsheet/

Resources