I'm following this http://siglerdev.us/blog/2021/02/26/google-home-message-broadcast-system-node-js/31 which uses this library castv2-client to send messages to my google home. It works. I get the messages no problem, but the code throws
C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72
callback(null, response.status.volume);
^
TypeError: callback is not a function
at C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72:5 ver.js:72
at fn.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\request-response.js:27:7)
at fn.emit (events.js:203:15)
at Channel.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\controller.js:16:10) s\receiver.js:72:5
at Channel.emit (events.js:198:13) lib\controllers\request-response.js:27:7)
at Client.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2\lib\channel.js:23:10) ient\lib\controllers\controller.js:16:10)
at Client.emit (events.js:203:15)
at PacketStreamWrapper.onpacket (C:\Users\Phil\Documents\google home\node_module\channel.js:23:10)s\castv2\lib\client.js:81:10)
at PacketStreamWrapper.emit (events.js:198:13) s\castv2\lib\client.js:81:10)
at TLSSocket.<anonymous> (C:\Users\Phil\Documents\google home\node_modules\castv2\lib\packet-stream-wrapper.js:28:16)
What's wrong with the code that is throwing this AND/OR how can I fix it so it's either more graceful in catching error and doesn't throw since the message still delivers to google home or fix it to not throw this at all?
I appreciate any help!
I believe it's here in the castv2-client library that it's referencing, but I haven't been able to make it happy.
ReceiverController.prototype.launch = function(appId, callback) {
this.request({ type: 'LAUNCH', appId: appId }, function(err, response) {
if(err) return callback(err);
if(response.type === 'LAUNCH_ERROR') {
return callback(new Error('Launch failed. Reason: ' + response.reason));
}
callback(null, response.status.applications || []);
});
};
my code
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 });
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED'
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on('status', function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on('error', function (err) {
console.log('Error: %s', err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: 'en-US',
slow: false,
host: 'https://translate.google.com',
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
})
},
broadcast: function(text){
const ips = '192.168.0.15'.split(","); //From config, 192.168.68.105,192.168.68.107,192.168.68.124
for (var s of ips){
App.run(s, text);
}
}
}
App.broadcast("Broadcasted to all of the devices"); //Only works if you did step 4.5
The error you reported:
C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72
callback(null, response.status.volume);
^
TypeError: callback is not a function
at C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72:5
seems to be related to the invocation of the method setVolume in your client:
client.setVolume({ level: 1 });
Please, consider review the source code of receiver.js in the castv2-client library:
ReceiverController.prototype.setVolume = function(options, callback) {
var data = {
type: 'SET_VOLUME',
volume: options // either `{ level: 0.5 }` or `{ muted: true }`
};
this.request(data, function(err, response) {
if(err) return callback(err);
callback(null, response.status.volume);
});
};
The library is claiming because you aren't providing a proper callback when invoking that function.
I have never used the library but probably providing something similar to the following callback could be of help:
client.setVolume({ level: 1 }, function(err, volume) {
if (err) {
// Handle error as appropriate
console.log('Error on setVolume:', err);
} else {
console.log('Volume:', volume)
}
});
Your final code would look like this:
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 }, function(err, volume) {
if (err) {
// Handle error as appropriate
console.log('Error on setVolume:', err);
} else {
console.log('Volume:', volume)
}
});
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED'
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on('status', function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on('error', function (err) {
console.log('Error: %s', err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: 'en-US',
slow: false,
host: 'https://translate.google.com',
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
})
},
broadcast: function(text){
const ips = '192.168.0.15'.split(","); //From config, 192.168.68.105,192.168.68.107,192.168.68.124
for (var s of ips){
App.run(s, text);
}
}
}
App.broadcast("Broadcasted to all of the devices"); //Only works if you did step 4.5
Related
I have castv2-client and google-tts-api used to output audio messages to my google home using ip address. It works fine, but the volume is kind of loud and I'm having issues figuring out how to lower the volume. If I lower the volume manually on google home then the app overrides it and it's loud still. How can I set the volume lower?
Also the console.log("VOLUME", volume) outputs
{
controlType: 'master',
level: 1,
muted: false,
stepInterval: 0.019999999552965164
}
var Client = require("castv2-client").Client;
var DefaultMediaReceiver = require("castv2-client").DefaultMediaReceiver;
const googleTTS = require("google-tts-api");
var cron = require("node-cron");
function broadcastMessage(message) {
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 }, function (err, volume) {
console.log("VOLUME", volume);
if (err) {
// Handle error as appropriate
console.log("Error on setVolume:", err);
} else {
console.log("Sending new alert");
}
});
var media = {
contentId: url,
contentType: "audio/mp3",
streamType: "BUFFERED",
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on("status", function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on("error", function (err) {
console.log("Error: %s", err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: "en-US",
slow: false,
host: "https://translate.google.com",
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
});
},
broadcast: function (text) {
const ip = "192.168.0.17";
App.run(ip, text);
},
};
App.broadcast(message);
setTimeout(() => {
App.broadcast(message);
}, 4000);
}
module.exports = {
broadcastMessage,
};
broadcastMessage("Incoming message");
As far as I can tell, you set the volume in your code to 1, which is full volume. So, your audio will play at full volume. This is where you set the volume:
client.setVolume({ level: 1 }, function (err, volume) {
console.log("VOLUME", volume);
if (err) {
// Handle error as appropriate
console.log("Error on setVolume:", err);
} else {
console.log("Sending new alert");
}
});
Simply remove this code and your message will play at the volume you set manually on the device.
Also: if you intend to set the volume of the device, you should wait for the callback to return, and only then send the message.
client.setVolume({ level: 1 }, function (err, volume) {
console.log("VOLUME", volume);
if (err) {
// Handle error as appropriate
console.log("Error on setVolume:", err);
} else {
// Here you should send the message, since only now you know for sure that the volume was set.
}
});
The solution was I had to define the value before client.setVolume({ level: 1 } call
so basically
const volume = 0.6;
client.setVolume({ level: volume }
I currently have a script that checks for an incoming email (in a mailbox) every 30 seconds, using a recursion.
The package I'm using for this testing is imap-simple.
The below script currently does this as required;
var imaps = require('imap-simple');
const { connect } = require('net');
var config = {
imap: {
user: 'qatestspecialist#outlook.com',
password: 'specialistQa',
host: 'imap-mail.outlook.com',
port: 993,
tls: true,
authTimeout: 30000
}
};
module.exports = {
'delete any existing emails...': function () {
imaps.connect(config).then(function (connection) {
connection.openBox('INBOX').then(function () {
var searchCriteria = ['ALL'];
var fetchOptions = { bodies: ['TEXT'], struct: true };
return connection.search(searchCriteria, fetchOptions);
//Loop over each message
}).then(function (messages) {
let taskList = messages.map(function (message) {
return new Promise((res, rej) => {
var parts = imaps.getParts(message.attributes.struct);
parts.map(function (part) {
return connection.getPartData(message, part)
.then(function (partData) {
//Display e-mail body
if (part.disposition == null && part.encoding != "base64"){
console.log(partData);
}
//Mark message for deletion
connection.addFlags(message.attributes.uid, "\Deleted", (err) => {
if (err){
console.log('Problem marking message for deletion');
rej(err);
}
res(); //Final resolve
});
});
});
});
});
return Promise.all(taskList).then(() => {
connection.imap.closeBox(true, (err) => { //Pass in false to avoid delete-flagged messages being removed
if (err){
console.log(err);
}
});
connection.end();
});
});
});
},
'send email to seller and wait for mailbox notification': function (browser) {
// script to send an email to the mailbox...
},
'get new email info': function(browser) {
const createPromise = ms => new Promise((resolve, reject) => {
setTimeout(() => resolve(ms), ms)
});
function findUnseenEmails(connection) {
return connection.openBox('INBOX').then(function () {
var searchCriteria = ['UNSEEN'];
var fetchOptions = {
bodies: ['HEADER', 'TEXT'],
markSeen: false
};
return connection.search(searchCriteria, fetchOptions).then(function (results) {
var subjects = results.map(function (res) {
return res.parts.filter(function (part) {
return part.which === 'HEADER';
})
[0].body.subject[0];
});
return subjects.length > 0 ? subjects : createPromise(30000).then(function() { return findUnseenEmails(connection);
});
});
});
}
imaps.connect(config).then(function (connection) {
return findUnseenEmails(connection)
})
.then((subjects) => console.log(JSON.stringify(subjects)));
},
'Closing the browser': function (browser) {
browser.browserEnd();
}
};
This waits for an email and then displays the email 'header'.
However, the imap connection does not close, and stays open which is stopping my test suite from completing as the associated test never actually finishes.
I've tried adding the imap-simple command connection.end() in several places after the
imaps.connect(config).then(function (connection) {
return findUnseenEmails(connection)
})
part of the script, but it doesn't work.
So I'm just wondering if anyone knows where I should be adding this connection.end() command in order for the connection to be closed once an email has been received?
Any help would be greatly appreciated.
This has now been resolved in another post, using the following;
if (subjects.length > 0) {
connection.end();
return subjects;
} else {
return createPromise(5000).then(function() { return findUnseenEmails(connection)});
}
this is my test file for upload and i explain it step by step:
I wrote a test to upload the file. the uploader method written with busboy module and it working true
but i have problem in test.
when result of uploader is error, this error never returned in .catch and go in .then.
more explain in code:
const http = require('http');
// const request = require('request');
const rp = require('request-promise');
const fs = require('fs');
const assert = require('chai').assert;
const port = process.env.PORT || 80;
const Q = require('q');
let server;
const options = {
method: 'POST',
uri: 'http://127.0.0.1/upload',
formData: {
name: 'test',
file: {
value: fs.createReadStream('./test/test.jpg'),
options: {
filename: 'test.jpg',
contentType: 'image/jpg'
}
}
},
headers: {
'Connection': 'Keep-Alive',
'content-type': 'multipart/form-data' // Is set automatically
},
json: true,
};
function startServer(port, cb) {
server = http.createServer(function (req, res) {
if (req.method === 'POST') {
if (req.url === '/upload') {
serveRequest(req, res);
}
}
});
server.listen(port, () => {
cb(function stopServer(done) {
setTimeout(function () {
server.close();
done();
}, 20);
});
console.log(`listening on port ${port} ...`);
});
}
function serveRequest(request, response) {
if (request.headers.hasOwnProperty('content-type')
&& request.headers['content-type'].split(';')[0] === 'multipart/form-data') {
serveUpload(request, response);
}
}
function serveUpload(request, response) {
uploader.upload(request, function (error, res) {
if (error) {
response.end();
}
else {
response.write(JSON.stringify(res));
response.end();
}
});
}
// -----------------------
describe('upload', function () {
let stopServer = null;
before('start server', function (done) {
startServer(port, function (stop) {
stopServer = stop;
done();
});
});
it('upload a file - options is true', function (done) {
rp(options)
.then(function (r) {
console.log(r);
})
.catch(function (error) {
console.log(error);
});
});
after('stop server', function (done) {
stopServer(done);
});
});
I make a request to the uploader and the result of my request is returned in the serveUpload() method. The result of serveUpload() is error and error is object like this :
error =
meta: {
code: '',
sourceType: 'module',
sourceName: '',
version: '2.0.4'
},
data: {
message: {
en: 'uploaded data size is out of limit'
}
}
}
this error must returned .catch(e) in the rp(options), but in fact it must go to .then(r) in rp(options)
log r in .then is undefined.
rp(options)
.then(function (r) {
console.log(r); // error always come here But in fact it must go to catch and r is undefined
})
.catch(function (error) {
console.log(error);
});
I don't understand why this is happening, I would be very grateful if anyone could help me.
I am trying to implement a simple Node Js example on AWS Lambda,
The code has a sample example of Async library.
The code works fine, but from some reason the Lambda Function return null response.
I am new to Node as well, please help.
Following is the code -
var async = require('async');
exports.handler = async (event, context, callback) => {
async.waterfall([
function(callback) {
console.log("ONE");
callback(null, 1);
},
function(resp, callback) {
console.log("TWO : ", resp);
callback(null, 2);
},
function(resp, callback){
console.log("THREE : ", resp);
callback(null, "Done");
}
],
function(err, resp) {
let response = {};
if (err) {
console.log("Error",err);
response = {
statusCode: 500,
body: JSON.stringify('Error!'),
};
return response;
} else {
console.log("Success",resp);
response = {
statusCode: 200,
body: JSON.stringify('Ok!'),
};
return response;
}
});
};
Following are the CloudWatch logs -
START RequestId: ab9aa426-dfc9-44ac-8d96-a4f102e30861 Version: $LATEST
2019-03-21T15:15:26.597Z ab9aa426-dfc9-44ac-8d96-a4f102e30861 ONE
2019-03-21T15:15:26.597Z ab9aa426-dfc9-44ac-8d96-a4f102e30861 TWO : 1
2019-03-21T15:15:26.597Z ab9aa426-dfc9-44ac-8d96-a4f102e30861 THREE : 2
2019-03-21T15:15:26.597Z ab9aa426-dfc9-44ac-8d96-a4f102e30861 Success Done
END RequestId: ab9aa426-dfc9-44ac-8d96-a4f102e30861
REPORT RequestId: ab9aa426-dfc9-44ac-8d96-a4f102e30861 Duration: 37.28 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 67 MB
I used the sample Node blueprint, which works fine -
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
Since you're already using Node 8, you don't need to use the outdated, confusing callback approach anymore. Use await instead
exports.handler = async (event) => {
try {
await somePromise1
await somePromise2
await somePromise3
console.log("Success", resp);
response = {
statusCode: 200,
body: JSON.stringify('Ok!'),
};
return response;
} catch (err) {
console.log("Error", err);
response = {
statusCode: 500,
body: JSON.stringify(err),
};
return response;
}
};
where somePromise1, somePromise2 and somePromise3 are your promisified callbacks.
More on async/await here.
Try to remove the async from the handler and use callback instead of return:
var async = require('async');
exports.handler = (event, context, callback) => {
async.waterfall([
function(callback) {
console.log("ONE");
callback(null, 1);
},
function(resp, callback) {
console.log("TWO : ", resp);
callback(null, 2);
},
function(resp, callback){
console.log("THREE : ", resp);
callback(null, "Done");
}
],
function(err, resp) {
let response = {};
if (err) {
console.log("Error",err);
callback(null, {
statusCode: 500,
body: 'Error!',
});
} else {
console.log("Success",resp);
callback(null, {
statusCode: 200,
body: 'Ok!',
});
}
});
};
Let's say i need to constantly collecting some data from a lot of clients and in parallel running some complex loop that solving some stuff with this data. How can i do it? Should i just write this in my piece of code:
app.get('/', function(req, res) {
res.sendFile(__dirname + '/public/views/index0.html');
});
io.sockets.on('connection', function(socket) {
// SOME STUFF WITH THE SOCKET
socket.on('disconnect', function(data) {
//SOME OTHER STUFF
});
});
while(...) {
//THE LOOP STUFF
}
Or i need to use the setTimeout() and setInterval() functions? How can i do the loop on the server that runs in parallel with the callbacks' stuff?
Don’t use while for make it, this block a thread. setTimeout() will run only once. You need to use setInterval() function.
You can use the async module to handle the async operation with the callback or use promise to avoid callback.
Here how I handle a complex async for each operation, that might helpfull to you get idea handeling ayncs forach
var cond = { _schedule: schedule_id }; // find curse by schedule id
Course.find(cond, function (err, courses) {
if (err) {
callback({ "success": false, "message": "Not able to update" });
} else {
async.forEachLimit(courses, 1, function (course, coursesCallback) {
async.waterfall([
function (callback) {
var schedule_date = moment(change_data.date).format('YYYY-MM-DD') + "T00:00:00.000Z"
var Assignmentcond = {
assignment_schedule_order: {
$gte: schedule_date
},
_course: course._id,
_schedule: schedule_id,
_user: userid
};
Assignment.find(Assignmentcond)
.populate({
path: '_course',
})
.lean()
.sort({ assignment_schedule_order: 1 })
.exec(function (err, AssignmentList) {
if (err) {
callback(null, '');
} else {
//console.log("------------------AssignmentList---------------------------");
//console.log(AssignmentList);
async.forEachLimit(AssignmentList, 1, function (ThisAssignmentCell, ThisAssignmentCellCallback) {
async.waterfall([
function (callback) {
var SearchObj = items;
var lebelObject = {};
for (var i = 0, flag = 0, insert = 0; i < SearchObj.length; i++) {
if (SearchObj[i].date == ThisAssignmentCell.assignment_schedule_date) {
flag = 1;
}
if (flag == 1 && SearchObj[i].label != "") {
if (ThisAssignmentCell.day == SearchObj[i].day_index) {
insert = 1;
var lebelObject = SearchObj[i];
break;
}
}
}
callback(null, ThisAssignmentCell, lebelObject, insert);
},
function (ThisAssignmentCell, SearchObj, insert, callback) {
console.log('----------------------');
console.log('ThisAssignmentCell', ThisAssignmentCell);
console.log('SearchObj', SearchObj);
console.log('----------------------');
if (insert > 0) {
var query = { _id: ThisAssignmentCell._id },
fields = {
assignment_date: moment(SearchObj.date).format('MM/DD/YYYY'),
assignment_schedule_date: moment(SearchObj.date).format('YYYY-MM-DD'),
assignment_schedule_order: new Date(SearchObj.date),
day: SearchObj.day_index,
dayNum: SearchObj.weekday_num
},
options = { upsert: false };
Assignment.update(query, fields, options, function (err, affected) {
callback(null, '');
});
} else {
// var cond = { _id: ThisAssignmentCell._id};
// Assignment.remove(cond)
// .exec(function (err, cnt) {
// callback(null, '');
// });
}
}
], function (err, result) {
// result now equals 'done'
console.log('done')
ThisAssignmentCellCallback();
});
}, function (err) {
console.log("Assignment For Loop Completed");
callback(null, AssignmentList);
});
}
});
}
], function (err, result) {
// result now equals 'done'
console.log('done')
coursesCallback();
});
}, function (err) {
console.log("courses For Loop Completed");
});
}
});