I am building chrome application for POS Terminal printers. The problem that I have is that chrome application for some reason can't find USB device and I am not sure why it is not working. I tried few methods (converted hexadecimal vendor/product ID to decimal, used hexadecimal product/vendor id to connect to USB printer) and all of the reports that device can't be found...
EDIT:
I got this error from device log:
Failed to open device: Operation not supported or unimplemented on this platform
Does someone know where is the problem?
Here is details about printer (DATECS DPP 255):
Manifest:
{
"manifest_version": 2,
"name": "Thermal Printer Extension",
"description": "Thermal Printer Extension for ESC/POS Commands",
"version": "1.0",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": ["usb", "notifications"],
"optional_permissions": [
{
"usbDevices": [
{
"vendorId": 65520, "productId": 98
}
]
}
],
"externally_connectable": {
"matches": ["http://localhost:3000/*"]
}
}
main.js
chrome.runtime.onMessageExternal.addListener(function (stuff) {
if(stuff){
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
var port = 0;
var endpoint = 0x01;
// needs to be set in optional permissions for each VID/PID pair
var device = {vendorId: 0xfff0, productId: 0x0062};
// var device = {vendorId: 65520, productId: 98};
String.prototype.toBytes = function() {
var arr = [];
for (var i=0; i < this.length; i++) {
arr.push(this[i].charCodeAt(0));
}
return arr;
}
// setting variables for printing, array for data, and calculations
var alignLeft = [0x01B, 0x061, 0];
var alignCenter = [0x01B, 0x061, 1];
var alignRight = [0x01B, 0x061, 2];
var endPrint = [0x01B, 0x064, 2];
// enter new line
var lineFeed = [0x00A];
var buzz = [07];
var drawLineMiddle = "________________________________________________".toBytes();
var drawLineSmall = "________________________________".toBytes();
var horizontalTab = [0x009];
var cutpaperFull = [0x01D, 0x056, 1];
var cutpaperPartial = [0x01D, 0x056, 0];
function createAnything() {
var anything = alignCenter;
anything = anything.concat("Awesome Chrome Extension".toBytes());
anything = anything.concat(lineFeed);
anything = anything.concat(drawLineMiddle);
anything = anything.concat(stuff.stuffToPrint.toBytes());
anything = anything.concat(lineFeed);
return anything;
}
function createReceipt(devices) {
var data = [];
data = data.concat(createAnything());
data = data.concat(endPrint);
data = data.concat(cutpaperPartial);
var buffer = new Uint8Array(data).buffer;
var transferinfo = {direction: "out", endpoint: endpoint, data: buffer };
chrome.usb.bulkTransfer({handle: devices[0].handle, vendorId: devices[0].vendorId, productId: devices[0].productId}, transferinfo, function(response) {
if (response.resultCode == 0) {
chrome.usb.closeDevice({handle: devices[0].handle, vendorId: devices[0].vendorId, productId: devices[0].productId}, chrome.notifications.create("4", {type: "basic", iconUrl: "icon.png", title: "Success!", message: "Printing receipt..."}, function() {console.log("Success!")}));
} else {
console.log("Error ", response);
}
});
}
var connect = function(callback) {
chrome.permissions.getAll(function(p) {
if (p.permissions.indexOf("usb") >= 0) {
//construct permission object for our device
var obj = { usbDevices: [device] };
//now request the permissions
chrome.permissions.request({ permissions: [obj] }, function(granted) {
if (granted) {
chrome.usb.findDevices(device, function(devices) {
if (devices && devices.length > 0) {
//use the first found device
var foundDevice = devices[0];
//now lets reset the device
chrome.usb.resetDevice(foundDevice, function() {
//perform some error checking to make sure we reset the device
if ( ! chrome.runtime.lastError) {
//now claim the interface using the port we specified
chrome.usb.claimInterface(foundDevice, port, function() {
if ( ! chrome.runtime.lastError) {
callback(devices);
} else {
throw chrome.runtime.lastError.message;
}
});
} else {
throw chrome.runtime.lastError.message;
}
});
} else {
chrome.notifications.create("1", {type: "basic", iconUrl: "icon.png", title: "Error", message: "Device not found!"}, function() {console.warn("Device not found!")});
console.warn("Device not found!");
}
});
} else {
chrome.notifications.create("2", {type: "basic", iconUrl: "icon.png", title: "Error", message: "USB Permission not granted."}, function() {console.warn("USB Permission not granted.")});
console.warn("USB Permission not granted.");
}
});
} else {
chrome.notifications.create("3", {type: "basic", iconUrl: "icon.png", title: "Error", message: "No USB permissions granted."}, function() {console.warn("No USB permissions granted.")});
console.warn("No USB permissions granted.");
}
});
}
connect(createReceipt);
}
});
Related
I'm getting the error:
Invalid JSON payload received. Unable to parse number. INVALID_ARGUMENT
I got the key and everything and I'm still having the same error return. A few suggestions are resizing the image first to less fit the 4MB image limit.
I also tried emulating the same JSON request coming from their docs. But still have the same issue.
https://cloud.google.com/vision/docs/drag-and-drop
Here's the code I'm using: (I got the sample from Akshay Kadam)
me.detection_types = {
LABEL_DETECTION: 'label',
DOCUMENT_TEXT_DETECTION: 'text',
LOGO_DETECTION: 'logo',
LANDMARK_DETECTION: 'landmark'
};
var api_key = '';
$scope.takePicture = function(){
var options = {
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
targetWidth: 150,
targetHeight: 150,
correctOrientation: true,
cameraDirection: 0,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
quality: 25
};
$cordovaCamera.getPicture(options).then(function(imagedata){
me.current_image = "data:image/jpeg;base64," + imagedata;
me.image_description = '';
me.locale = '';
var vision_api_json = {
"requests":[
{
"image":{
"content": imagedata
},
"imageContext": {
"cropHintsParams": {
"aspectRatios": [
0.8,
1,
1.2
]
}
},
"features":[
{
"type": me.detection_type,
"maxResults": 1
}
]
}
]
}
var file_contents = JSON.stringify(vision_api_json);
$cordovaFile.writeFile(
cordova.file.applicationStorageDirectory,
'file.json',
file_contents,
true
).then(function(result){
var headers = {
'Content-Type': 'application/json'
};
options.headers = headers;
var server = 'https://vision.googleapis.com/v1/images:annotate?key=' + api_key;
var filePath = cordova.file.applicationStorageDirectory + 'file.json';
$cordovaFileTransfer.upload(server, filePath, options, true)
.then(function(result){
alert(JSON.stringify(result))
var res = JSON.parse(result.response);
var key = me.detection_types[me.detection_type] + 'Annotations';
me.image_description = res.responses[0][key][0].description;
}
Need help. Thank you!
UPDATE:
I tried adding Content-Length and was still having the same problem.
I'm just trying to get started with the sample addon Google describes here for extending the compose UI:
https://developers.google.com/gsuite/add-ons/gmail/extending-compose-ui
However when I run it I'm getting this error:
Error with the add-on. Run time error. Cannot return a card markup
from the callback function of a universal action.
I have not setup any universal actions in my manifest file:
{
"timeZone": "America/New_York",
"oauthScopes":[
"https://www.googleapis.com/auth/script.send_mail",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.metadata",
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/gmail.addons.current.action.compose"
],
"gmail":{
"name": "My Mail Merge",
"logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/label_googblue_24dp.png",
"composeTrigger": {
"draftAccess": "METADATA",
"selectActions": [
{
"text": "show UI",
"runFunction": "buildImageComposeCard"
}
]
},
"openLinkUrlPrefixes": [
"https://mail.google.com/"
],
"primaryColor": "#42585F4",
"secondaryColor": "#42585F4"
},
"exceptionLogging": "STACKDRIVER"
}
Below is Code.gs:
function getInsertImageComposeUI(e) {
return [buildImageComposeCard()];
}
function buildImageComposeCard() {
// Get a list of image URLs to display in the UI.
// This function is not shown in this example.
var imageUrls = [
"https://mail.google.com/1",
"https://mail.google.com/2",
"https://mail.google.com/3"
];
var card = CardService.newCardBuilder();
var cardSection = CardService.newCardSection().setHeader('My Images');
for (var i = 0; i < imageUrls.length; i++) {
var imageUrl = imageUrls[i];
cardSection.addWidget(
CardService.newImage()
.setImageUrl(imageUrl)
.setOnClickAction(CardService.newAction()
.setFunctionName('applyInsertImageAction')
.setParameters({'url' : imageUrl})));
}
return card.addSection(cardSection).build();
}
function applyInsertImageAction(e) {
var imageUrl = e.parameters.url;
var imageHtmlContent = '<img style=\"display: block\" src=\"'
+ imageUrl + '\"/>';
var response = CardService.newUpdateDraftActionResponseBuilder()
.setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()
.addUpdateContent(
imageHtmlContent,
CardService.ContentType.HTML)
.setUpdateType(
CardService.UpdateDraftBodyType.IN_PLACE_INSERT))
.build();
return response;
}
In the manifest file, runFunction has to be set to "getInsertImageComposeUI",
Also, in the code file, CardService.ContentType.HTML has to be one of
CardService.ContentType.MUTABLE_HTML
or
CardService.ContentType.IMMUTABLE_HTML
I have the same error message and couldn't get that example to work. However, I created a simpler Hello-World example that actually works. I think it is more useful for newbies to start with. Don't forget to set the runFunction in the manifest to getComposeCard.
function getComposeCard(e) { return [buildComposeCard()]; }
function buildComposeCard() {
var card = CardService.newCardBuilder();
var cardSection = CardService.newCardSection().setHeader('Test Header');
testVar = ['item1', 'item2'];
for (var i = 0; i < testVar.length; i++) {
var item = testVar[i];
cardSection.addWidget(
CardService.newTextParagraph().setText(item));
}
return card.addSection(cardSection).build();
}
Probably it's way too late, but the problem can be fixed by simply wrap the result of buildImageComposeCard as array
return [card.addSection(cardSection).build()]
We are trying to download a 7 GB from CDN using JSZip.js. The chrome browser suddenly seems to close the connection when the download reaches 3.5gb every time. The approximate time is around 15 mins. Is there a way we increase the tolerant time to 1 hr say?
$("#downloadJSZip").on('click', function () {
var result = [{ "cdn": "url....", "filename": "7.84 gb.zip", "size": 4194304, "path": "7.84 gb" }];
var Promise = window.Promise;
if (!Promise) {
Promise = JSZip.external.Promise;
}
function urlToPromise(url) {
return new Promise(function(resolve, reject) {
JSZipUtils.getBinaryContent(url, function (err, data) {
if(err) {
reject(err);
} else {
resolve(data);
}
});
});
}
var fileNameArray = [];
function changeFileName(fileName,j){
var i = fileName.lastIndexOf('.');
var newfilename = fileName.slice(0,i)+"--"+j+fileName.slice(i);
if(fileNameArray.indexOf(newfilename) != -1){
j = j+1;
changeFileName(fileName,j);
}
return newfilename;
}
var zip = new JSZip();
// find every checked item
result.forEach(function(file){
var filename = file.filename;
if(fileNameArray.indexOf(filename) != -1){
var newfilename = changeFileName(filename,1);
filename = newfilename;
}
fileNameArray.push(filename);
var url = file.cdn;
var folder = (file.path);
zip.folder(folder).file(filename, urlToPromise(url), {binary:true});
// zip.file(filename, urlToPromise(url), {binary:true});
});
// when everything has been downloaded, we can trigger the dl
zip.generateAsync({type:"blob",
}, function updateCallback(metadata) {
var msg = "progression : " + metadata.percent.toFixed(2) + " %";
if(metadata.currentFile) {
msg += ", current file = " + metadata.currentFile;
}
console.log(msg);
console.log(metadata.percent|0);
})
.then(function callback(blob) {
// see FileSaver.js
//console.log("blob=====>");
//console.dir(blob);
//saveAs(blob, "example.zip") ;
saveAs(blob, $scope.folderName+".zip") ;
//console.log("done !");
}, function (e) {
});
});
Is this chrome browser configuration?
in my SAPUI5 app I am using a Multi Input Field with tokens which are bound to a JSON Model. Newly added entries are saved in the JSON Model. However, when deleting a token by pressing the "x" next to the token text, the token disappears from the multi input field. But when adding a new token the deleted one reappears.
How can I ensure that the deleted entry is also deleted from the JSON Model?
This is my current code for adding the token to the model:
multiInputField.addValidator(function(args){
MessageBox.confirm("Do you really want to add Token\"" + args.text + "\"?", {
onClose: function(oAction) {
if (oAction === MessageBox.Action.OK){
var oToken = new Token({key: args.text, text: args.text});
args.asyncCallback(oToken);
var aFields = sap.ui.getCore().getView().getModel("myModel").getProperty("/Tokens");
var oNewFields= {
Tokens: args.text
};
aFields .push(oNewFields);
sap.ui.getCore().getView().getModel("myModel").setProperty("/Tokens", aFields );
sap.ui.getCore().getView().getModel("myModel").refresh();
} else {
args.asyncCallback(null);
}
},
title: "Add Token"
});
return sap.m.MultiInput.WaitForAsyncValidation;
});
I guess we can use "tokenUpdate" event for this.
For example, given that I have this MultiInput in my view:
<MultiInput width="500px" id="multiInput" suggestionItems="{ path: 'dataModel>/data'}" showValueHelp="true" tokenUpdate="onTokenUpdate">
<core:Item key="{dataModel>key}" text="{dataModel>value}"/>
</MultiInput>
then in my controller I can handle this like :
onTokenUpdate: function(oEvent) {
var sType = oEvent.getParameter("type");
if (sType === "removed") {
var sKey = oEvent.getParameter("removedTokens")[0].getProperty("key");
var oModel = this.getView().getModel("dataModel");
var aData = this.getView().getModel("dataModel").getProperty("/data");
for (var i = 0, len = aData.length; i < len; i++) {
var idx;
console.log(sKey + "-" + aData[i].key);
if (aData[i].key === sKey) {
idx = i;
}
}
aData.splice(idx, 1);
oModel.setProperty("/data", aData);
console.log(oModel);
}
}
And this is my json:
{
"data": [
{
"key": "token1",
"value": "token1"
},
{
"key": "token2",
"value": "token2"
}
]
}
I want to meet new clients in telegram bot with a keyboard buttons
So far I have this code written in Google Apps Script, but in result there is nothing.
function doPost(e) {
var API_TOKEN = 'bla-bla-bla';
var update = JSON.parse(e.postData.contents);
if (update.hasOwnProperty('message')) {
var msg = update.message;
var chatId = msg.chat.id;
if (msg.text == '/start') {
function sendText(chatId, text, keyboard) {
var payload = {
'method': 'sendMessage',
'chat_id': String(chatId),
'text': "Hello",
'parse_mode': 'HTML',
reply_markup: JSON.stringify({
keyboard: [
[
"A",
"B"
],
[
"C",
"D"
]
],
resize_keyboard: true,
one_time_keyboard:true
})
}
var data = {
"method": "post",
"payload": payload
}
UrlFetchApp.fetch('https://api.telegram.org/bot' + API_TOKEN + '/', data);
}
}
}
}
Is this a missing feature or it's me, doing something wrong?
You probably want something like this?
PS: replace it with your chat id
function testKeyboard() {
const chatId = YOURCHATID; // REPLACE THIS
const choices = ['Mozzarella','Gouda cheese','Camembert','Gorgonzola','Cheddar Cheese'];
const text = 'Make your choice';
sendKeyboard(chatId, choices, text);
}
You can do it with this code.
PS: replace token with your bot's token
const token = ''; // REPLACE IT WITH YOUR BOTS TOKEN
const telegramApi = 'https://api.telegram.org/bot' + token;
function sendKeyboard(chatId, choices, text = 'Menu') {
const buttons = transformArrayToKeyboard(choices);
const replyKeyboardMarkup = {keyboard: buttons,
one_time_keyboard: true,
resize_keyboard: true};
const replyMarkup = JSON.stringify(replyKeyboardMarkup);
const url = telegramApi + '/sendMessage?chat_id=' + encodeURIComponent(chatId) + '&text=' + encodeURIComponent(text) + '&disable_web_page_preview=true&reply_markup=' + encodeURIComponent(replyMarkup);
const response = UrlFetchApp.fetch(url);
Logger.log(response.getContentText());
}
function transformArrayToKeyboard(choices) {
const maxLengthRowKeyboardMenu = 3;
const arr = choices.map(item => ({text: item }));
const result = [];
let index = 0;
while(arr.slice(index).length > 0) {
const newRowValuesForButtons = arr.slice(index,index+maxLengthRowKeyboardMenu);
result.push(newRowValuesForButtons);
index += maxLengthRowKeyboardMenu;
}
return result;
}
// function transformArrayToKeyboard transforms [4,5,6,7,8] to:
// [
// [
// {text: "4" },{text: "5" },{text: "6" }
// ]
// [
// {text: "7" },{text: "8" }
// ]
// ];