ReactNative MQTT Listener - node.js

I have the following code excerpt:
client.on ('message', function (msg) {
// ....
});
Can someone tell me how I get the value of msg from this function and can access it from outside.
I tried but it doesn't work:
this.setState ({msg: msg})

You can make an MQTT manager and use it.
Example
module.exports = { // cached singleton instance
QOS: 1, // Only 0 and 1 supported by Rabbit
props: null,
create(userID, connectionProps = {}) {
if (userID && connectionProps) {
// http://www.hivemq.com/demos/websocket-client/
this.onConnectionOpened = this.onConnectionOpened.bind(this);
this.onConnectionClosed = this.onConnectionClosed.bind(this);
this.onError = this.onError.bind(this);
this.onMessageArrived = this.onMessageArrived.bind(this);
this.disconnect = this.disconnect.bind(this);
const deviceId = this.randIdCreator()
.replace(/[^a-zA-Z0-9]+/g, '');
this.conProps = _.extend({
clientId: `realtime.${userID}.${deviceId}`,
channelToUse: `mqtt-subscription-realtime.${userID}`,
auth: false,
clean: true, // clean session YES deletes the queue when all clients disconnect
}, connectionProps);
/* create mqtt client */
MQTT.createClient(this.conProps)
.then((client) => {
this.client = client;
client.on('closed', this.onConnectionClosed);
client.on('error', this.onError);
client.on('message', this.onMessageArrived);
client.on('connect', this.onConnectionOpened);
client.connect();
}).catch((err) => {
console.error(`MQTT.createtClient error: ${err}`);
});
}
},
...
onMessageArrived(message) {
if (message) {
console.log(`MQTT New message: ${JSON.stringify(message)}`)
}
}
...
Usage
import MqttNotificationsManager from './realtimeManager';
// init realtime
MqttNotificationsManager.create(
'bob',
{
uri: 'mqtt://test.mosquitto.org:1883',
},
);

Related

Jest Testing of Redis Service

I have a number of services in my Nodejs repo that uses Redis. I'm trying to improve the quality of my development and therefore am starting to implement Jest testing. However, I cannot get the Jest tests to connect to the (test) Redis database during testing.
I've tried using a continually running Redis server on the CI/CD (Jenkins) server and I have tried using this library "Redis-Memory-Server" which is meant to create a Redis server instance during testing. I've spent many hours this week trying to fix this issue and have no idea why it's happening.
Any help is greatly appreciated.
Redis Management File
// Redis database module for Unseen Games
const redis = require('redis');
const { promisifyAll } = require('bluebird');
const _ = require('lodash');
promisifyAll(redis);
//Contains all the redis clients currently made
var event_status = "unconnected";
var timeout_cleared = false;
const clients: any = {};
let connectionTimeout;
function throwTimeoutError() {
connectionTimeout = setTimeout(() => {
throw new Error('Redis connection failed');
}, 10000);
}
function instanceEventListenersRedis({ conn }) {
conn.on('connect', () => {
// console.log('CacheStore - Connection status: connected');
event_status = "connected";
timeout_cleared = false;
clearTimeout(connectionTimeout);
});
conn.on('ready', () => {
event_status = "ready";
// console.log('CacheStore - Connection status: ready');
})
conn.on('end', () => {
event_status = "disconnected";
// console.log('CacheStore - Connection status: disconnected');
//TODO: The below code should stop Jest from hanging when testing the code, but doesn't, fix?
// if(!timeout_cleared) {
// throwTimeoutError();
// }
});
conn.on('reconnecting', () => {
event_status = "reconnecting";
// console.log('CacheStore - Connection status: reconnecting');
clearTimeout(connectionTimeout);
});
conn.on('error', (err) => {
event_status = "error";
// console.log('CacheStore - Connection status: error ', { err });
throwTimeoutError();
});
}
export const redisInit = async () => {
if(process.env.BALENA==="1" || process.env.DEVICE === "local"){
const cacheInstance = redis.createClient(process.env.REDIS_URL);
clients.cacheInstance = cacheInstance;
instanceEventListenersRedis({ conn: cacheInstance });
} else if(process.env.DEVICE =="demo") {
event_status = "connecting"
const cacheInstance = redis.createClient({host: process.env.REDIS_HOST, port: process.env.REDIS_PORT});
clients.cacheInstance = cacheInstance;
instanceEventListenersRedis({ conn: cacheInstance });
} else {
throw Error;
}
};
export const redisCheckConnection = () => {
if(process.env.REDIS == "true") {
return event_status;
} else {
return "readyo";
}
}
export const redisGetClients = () => clients;
export const redisCloseConnections = () => {
timeout_cleared = true;
_.forOwn(clients, (conn) => conn.quit());
}
jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
setupFiles: ['dotenv/config'],
transform: {}
};
process.env = Object.assign(process.env, {
REDIS: 'true',
DEVICE: 'demo'
});

RabbitMQ HeartBeat Timeout issue

I'm currently using RabbitMQ as a message broker. Recently, I see many error HeartBeat Timeout in my error log.
Also in RabbitMQ log, I see this log:
I don't know why there is too many connection from vary ranges of port. I use default setup without any further configuration.
Here is my code used to publish and consume:
import { connect } from 'amqplib/callback_api';
import hanlder from '../calculator/middleware';
import { logger } from '../config/logger';
async function consumeRabbitMQServer(serverURL, exchange, queue) {
connect('amqp://localhost', async (error0, connection) => {
if (error0) throw error0;
const channel = connection.createChannel((error1) => {
if (error1) throw error1;
});
channel.assertExchange(exchange, 'direct', {
durable: true
});
channel.assertQueue(
queue,
{
durable: true
},
(error2) => {
if (error2) throw error2;
logger.info(`Connect to ${serverURL} using queue ${queue}`);
}
);
channel.prefetch(1);
channel.bindQueue(queue, exchange, 'info');
channel.noAck = true;
channel.consume(queue, (msg) => {
hanlder(JSON.parse(msg.content.toString()))
.then(() => {
channel.ack(msg);
})
.catch((err) => {
channel.reject(msg);
});
});
});
}
export default consumeRabbitMQServer;
Code used to publish message:
import createConnection from './connection';
import { logger } from '../config/logger';
async function publishToRabbitMQServer(serverURL, exchange, queue) {
const connection = createConnection(serverURL);
const c = await connection.then(async (conn) => {
const channel = await conn.createChannel((error1) => {
if (error1) throw error1;
});
channel.assertExchange(exchange, 'direct', {
durable: true
});
channel.assertQueue(
queue,
{
durable: true
},
(error2) => {
if (error2) throw error2;
logger.info(`Publish to ${serverURL} using queue ${queue}`);
}
);
channel.bindQueue(queue, exchange, 'info');
return channel;
});
return c;
}
export default publishToRabbitMQServer;
Whenever I start my server, I run this piece of code to create a client consume to RabbitMQ:
const { RABBITMQ_SERVER } = process.env;
consumeRabbitMQServer(RABBITMQ_SERVER, 'abc', 'abc');
And this piece of code is used when ever a message in need published to RabbitMQ
const payloads = call.request.payloads;
const { RABBITMQ_SERVER } = process.env;
const channel = await publishToRabbitMQServer(RABBITMQ_SERVER, 'abc', 'abc');
for (let i = 0; i < payloads.length; i++) {
channel.publish('abc', 'info', Buffer.from(JSON.stringify(payloads[i])));
}
I'm reusing code from RabbitMQ document, and it seem that this problem happen whenever there are too many user publish message. Thanks for helping.
Update: I think the root cause is when I need to publish a message, I create a new connection. I'm working to improve it, any help is appreciate. Many thanks.

Nodejs - IBM Watson Speech To Text Websocket connection error

I have been trying to follow this official guide from IBM (https://developer.ibm.com/tutorials/add-a-trigger-word-to-your-watson-assistant/) to build a Nodejs voice assistant that answer after it recognizes a "wake up word". That guide seems a little bit outdated so I decided to use it with Assistant V2 and ibm-watson 5.2.0 package from npm.
I am getting a WebSocket connection error with not much information on it, the issue seems to be on line 33 and with the params I am sending to the 'recognizeUsingWebsocket' method. Am I missing something along this parameters?
const AssistantV2 = require('ibm-watson/assistant/v2');
const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
const SpeechToTextV1 = require('ibm-watson/speech-to-text/v1');
const { IamAuthenticator } = require('ibm-watson/auth');
const mic = require('mic');
const conversation = new AssistantV2({
authenticator: new IamAuthenticator({ apikey: '<api_key>' }),
url: 'https://gateway-lon.watsonplatform.net/assistant/api/',
version: '2018-09-19'
});
const speechToText = new SpeechToTextV1({
authenticator: new IamAuthenticator({ apikey: '<api_key>' }),
serviceUrl: 'https://gateway-lon.watsonplatform.net/speech-to-text/api'
});
const textToSpeech = new TextToSpeechV1({
authenticator: new IamAuthenticator({ apikey: '<api_key>' })
});
const micParams = {
rate: 44100,
channels: 2,
debug: true,
exitOnSilence: 6
};
const microphone = mic(micParams);
const micInputStream = microphone.getAudioStream();
const textStream = micInputStream
.pipe(
speechToText.recognizeUsingWebSocket({
accessToken:'<access_token>',
contentType: 'audio/l16; rate=44100; channels=2',
interimResults: true,
inactivityTimeout: -1
})
)
.setEncoding('utf8');
const speakResponse = (text) => {
var params = {
text: text,
accept: 'audio/wav',
voice: 'en-US_AllisonVoice'
};
var writeStream = fs.createWriteStream('output.wav');
textToSpeech
.synthesize(params)
.then((audio) => {
audio.pipe(writeStream);
})
.catch((err) => {
console.log('error:', err);
});
writeStream.on('finish', function() {
ffprobe('output.wav', function(err, probeData) {
if (probeData) {
pauseDuration = probeData.format.duration;
microphone.pause();
speaker.play('output.wav');
startTime = new Date();
}
});
});
writeStream.on('error', function(err) {
console.log('Text-to-speech streaming error: ' + err);
});
};
function printContext(header) {
if (debug) {
console.log(header);
if (context.system) {
if (context.system.dialog_stack) {
const util = require('util');
console.log(" dialog_stack: ['" + util.inspect(context.system.dialog_stack, false, null) + "']");
}
}
}
}
function watsonSays(response) {
if (typeof response !== 'undefined') {
console.log('Watson says:', response);
}
}
function isActive(text) {
var elapsedTime = new Date() - startTime;
if (elapsedTime > SLEEP_TIME) {
// go to sleep
startTime = new Date();
botIsActive = false;
}
if (botIsActive) {
// in active conversation, so stay awake
startTime = new Date();
return true;
} else {
// we are asleep - did we get a wake up call?
if (text.toLowerCase().indexOf(wakeWord) > -1) {
// time to wake up
console.log('App just woke up');
botIsActive = true;
} else {
// false alarm, go back to sleep
console.log('App needs the wake up command');
}
return botIsActive;
}
}
function performConversation() {
console.log('App is listening, you may speak now.');
textStream.on('data', (user_speech_text) => {
userSpeechText = user_speech_text.toLowerCase();
console.log('\n\nApp hears: ', user_speech_text);
if (isActive(user_speech_text)) {
conversation.message(
{
assistantId: process.env.ASSISTANT_ID,
sessionId: process.env.SESSION_ID,
input: { text: user_speech_text }
},
(err, response) => {
console.log(err);
context = response.context;
watson_response = response.output.text[0];
if (watson_response) {
speakResponse(watson_response);
}
watsonSays(watson_response);
}
);
}
});
}
microphone.start();
performConversation();
It seems that you do not use the right endpoint for websockets :
note : api endpoints have had a new version in dec 2019
you use :
https://gateway-lon.watsonplatform.net/speech-to-text/api
and it should be something like : (i think the prefix wss is key)
wss://api.{location}.speech-to-text.watson.cloud.ibm.com/instances/{instance_id}/v1/recognize
cf Api reference : https://cloud.ibm.com/apidocs/speech-to-text/speech-to-text#websocket_methods

Migrating users from OneSignal to our own database that we push notifications from

I am trying to migrate our subscribers from OneSignal. I exported the endpoint, the keys (auth and P256DH) and I configured the VAPID keys of my OS account on my server.
When I try to send a notification from OS then remove the service worker of OS and use my own, it's sending the same notification that I previously sent through OS (quite odd), and when I programmatically remove the service worker of OS (through the console) and register my own service worker, it's responding with 410 error from chrome ("NotRegistered") and 401 from Firefox ("Request did not validate missing authorization header").
app.js file:
let isSubscribed = false;
let swRegistration = null;
let applicationKey = "PUBLIC_VAPID_KEY_HERE";
function urlB64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
if ('serviceWorker' in navigator && 'PushManager' in window) {
console.log('Service Worker and Push is supported');
navigator.serviceWorker.register('sw.js')
.then(function (swReg) {
console.log('service worker registered');
swRegistration = swReg;
swRegistration.pushManager.getSubscription()
.then(function (subscription) {
isSubscribed = !(subscription === null);
if (isSubscribed) {
console.log('User is subscribed');
} else {
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array(applicationKey)
})
.then(function (subscription) {
console.log(subscription);
console.log('User is subscribed');
saveSubscription(subscription);
isSubscribed = true;
})
.catch(function (err) {
console.log('Failed to subscribe user: ', err);
})
}
})
})
.catch(function (error) {
console.error('Service Worker Error', error);
});
} else {
console.warn('Push messaging is not supported');
}
function saveSubscription(subscription) {
let xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", "/subscribe");
xmlHttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState != 4) return;
if (xmlHttp.status != 200 && xmlHttp.status != 304) {
console.log('HTTP error ' + xmlHttp.status, null);
} else {
console.log("User subscribed to server");
}
};
xmlHttp.send(JSON.stringify(subscription));
}
sw.js file:
let notificationUrl = '';
self.addEventListener('push', function (event) {
console.log('Push received: ', event);
let _data = event.data ? JSON.parse(event.data.text()) : {};
notificationUrl = _data.url;
event.waitUntil(
self.registration.showNotification(_data.title, {
body: _data.message,
icon: _data.icon,
tag: _data.tag
})
);
});
self.addEventListener('notificationclick', function (event) {
event.notification.close();
event.waitUntil(
clients.matchAll({
type: "window"
})
.then(function (clientList) {
if (clients.openWindow) {
return clients.openWindow(notificationUrl);
}
})
);
});
push.js file which pushes notifications:
const express = require('express');
const router = express.Router();
const q = require('q');
const webPush = require('web-push');
const keys = require('./../config/keys');
const mysql = require("mysql");
const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: 'root',
database: 'webpush',
multipleStatements: true,
dateStrings: true
});
router.post('/push', (req, res) => {
const payload = {
title: req.body.title,
message: req.body.message,
url: req.body.url,
ttl: req.body.ttl,
icon: req.body.icon,
image: req.body.image,
badge: req.body.badge,
tag: req.body.tag
};
pool.query('SELECT * FROM subscriber', (err, subscriptions) => {
if (err) {
return console.log(err);
console.error(`Error occurred while getting subscriptions`);
return res.status(500).json({
error: 'Technical error occurred'
});
}
if (!subscriptions.length) {
console.error(`No subscribers found`);
return res.status(500).json({
error: 'Subscribers not found'
});
}
let parallelSubscriptionCalls = subscriptions.map(subscription => {
return new Promise((resolve, reject) => {
const pushSubscription = {
endpoint: subscription.endpoint,
keys: {
p256dh: subscription.p256dh,
auth: subscription.auth
}
};
const pushPayload = JSON.stringify(payload);
const pushOptions = {
vapidDetails: {
subject: 'https://www.mydomainhere.com',
privateKey: keys.privateKey,
publicKey: keys.publicKey
},
TTL: payload.ttl,
headers: {}
};
webPush.sendNotification(pushSubscription, pushPayload, pushOptions)
.then((value) => {
resolve({
status: true,
endpoint: subscription.endpoint,
data: value
});
}).catch((err) => {
reject({
status: false,
endpoint: subscription.endpoint,
data: err
});
});
});
});
q.allSettled(parallelSubscriptionCalls).then((pushResults) => {
console.info(pushResults);
});
res.json({
data: 'Push triggered'
});
})
});
module.exports = router;
subscribe.js file which does the subscription:
const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: 'root',
database: 'webpush',
multipleStatements: true,
dateStrings: true
});
router.post('/subscribe', (req, res) => {
const endpoint = req.body.endpoint;
const auth = req.body.keys.auth;
const p256dh = req.body.keys.p256dh;
const subscriptionSet = { endpoint, auth, p256dh }
pool.getConnection((err, connection) => {
if (err) {
console.error(`Error occurred while saving subscription. Err: ${err}`);
return res.status(500).json({
error: 'Technical error occurred'
});
};
connection.query('INSERT INTO subscriber SET ?', subscriptionSet, (err, subscription) => {
if (err) {
console.error(`Error occurred while saving subscription. Err: ${err}`);
return res.status(500).json({
error: 'Technical error occurred'
});
}
res.json({
data: 'Subscription saved.'
})
})
});
});
module.exports = router;
Im trying to do the same, how and where did you export the subscribers auth and P256DH from onesignal? because in the csv export, onesignal provides the push_token (endpoint) and not the auth and P256DH. Where would i get the all the users auth and P256DH?
From my understanding you cannot re register the service worker unless the subscriber comes back and visits your domain. However, the service worker updates every day, so what you can do is edit the existing One signal service worker files - delete their content and add your own code or import scripts. Make sure not to change file location or rename the one signal service worker file, it needs to be the same filename. This will count as an 'update' and browsers should automatically replace the contents without any action from users. If there's two OneSignal service worker files then change both.

share mqtt client object between files

I connect to MQTT this way:
//mqtt.js
const mqtt = require('mqtt');
var options = {
//needed options
};
var client = mqtt.connect('mqtt://someURL', options);
client.on('connect', () => {
console.log('Connected to MQTT server');
});
I want to export the client object this way:
//mqtt.js
module.exports = client;
So that I can import it in other files and make use of it this way:
//anotherFile.js
const client = require('./mqtt');
client.publish(...)
However, we all know that this will not work! How can I achieve this ?
Update
I tried promise and get a very strange behavior. When I use the promise in the same file (mqtt.js) like the code below, everything is OK:
//mqtt.js
const mqtt = require('mqtt');
var mqttPromise = new Promise(function (resolve, reject) {
var options = {
//needed options
};
var client = mqtt.connect('mqtt://someURL', options);
client.on('connect', () => {
client.subscribe('#', (err) => {
if (!err) {
console.log('Connected to MQTT server');
resolve(client);
} else {
console.log('Error: ' + err);
reject(err);
}
});
});
});
mqttPromise.then(function (client) {
//do sth with client
}, function (err) {
console.log('Error: ' + err);
});
But when I export the promise and use it in another file, like this:
//mqtt.js
//same code to create the promise
module.exports = mqttPromise;
//anotherFile.js
const mqttPromise = require('./mqtt');
mqttPromise.then(function (client) {
//do sth with client
}, function (err) {
console.log('Error: ' + err);
});
I get this error:
TypeError: mqttPromise.then is not a function
You can probably achieve your goal creating 2 files, one for handling mqtt methods and another to manage the connection object.
Here's the file for the mqtt handler:
//mqttHandler.js
const mqtt = require('mqtt');
class MqttHandler {
constructor() {
this.mqttClient = null;
this.host = 'YOUR_HOST';
this.username = 'YOUR_USER';
this.password = 'YOUR_PASSWORD';
}
connect() {
this.mqttClient = mqtt.connect(this.host, {port: 1883});
// 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`);
});
this.mqttClient.on('close', () => {
console.log(`mqtt client disconnected`);
});
}
// // Sends a mqtt message to topic: mytopic
sendMessage(message, topic) {
this.mqttClient.publish(topic, JSON.stringify(message));
}
}
module.exports = MqttHandler;
Now lets use the exported module to create a mqtt client connection on another file:
//mqttClient.js
var mqttHandler = require('./mqttHandler');
var mqttClient = new mqttHandler();
mqttClient.connect();
module.exports = mqttClient;
With this exported module you can now call your client connection object and use the methods created in the mqttHandler.js file in another file :
//main.js
var mqttClient = require('./mqttClient');
mqttClient.sendMessage('<your_topic>','<message>');
Although there may be a better method to perform your task, this one worked pretty well for me...
Hope it helps!
cusMqtt.js
const mqtt = require("mqtt");
function prgMqtt() {
const options = {
port: 1883,
host: "mqtt://xxxxxxx.com",
clientId: "mqttjs_" + Math.random().toString(16).substr(2, 8),
username: "xxxxxx",
password: "xxxxxx",
keepalive: 60,
reconnectPeriod: 1000,
protocolId: "MQIsdp",
protocolVersion: 3,
clean: true,
encoding: "utf8",
};
prgMqtt.client = mqtt.connect("mqtt://xxxxxxxx.com", options);
prgMqtt.client.on("connect", () => {
prgMqtt.client.subscribe("Room/Fan");
console.log("connected MQTT");
});
prgMqtt.client.on("message", (topic, message) => {
console.log("message is " + message);
console.log("topic is " + topic);
// client.end();
});
}
exports.prgMqtt = prgMqtt;
index.js/main program call
const { prgMqtt } = require("./startup/cusMqtt");
prgMqtt();
another .js
const { prgMqtt } = require("../startup/cusMqtt");
router.get("/:id", async (req, res) => {
prgMqtt.client.publish("Room/Reply", "Replied Message");
});

Resources