-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Export device pictures #10808
Comments
You can create custom action and create your image file on the fly. For details see this SO thread |
Nice, thanks for the hint! For reference I got it to work with the following custom action let $injector = widgetContext.$scope.$injector;
let deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));
let attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));
// generates a download link and programmatically clicks it
function downloadBase64Picture(picture, fileName) {
// Extract the mime type from the base64 string and set extension
const mimeType = picture.match(/^data:(.*);base64,/)[1];
const fileExtension = mimeType.split('/')[1];
const link = document.createElement('a');
link.href = picture;
link.download = `${fileName}.${fileExtension}`;
// Append the link to the body (required for Firefox)
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function getDevicePicture() {
deviceService.getDevice(entityId.id).subscribe(
function (device) {
attributeService.getEntityAttributes(
device.id,
'SERVER_SCOPE',
['picture']
).subscribe(
function (attributes) {
for (let i = 0; i < attributes.length; i++) {
downloadBase64Picture(attributes[i].value, entityName)
}
}
);
}
);
}
getDevicePicture(); This works well for Widgets that select a single entity. Any idea how this could be adapted to also work for Widgets that select multiple entities? |
@dmohns Thanks for sharing the solution, will add it to awesome-thingsboard |
Nice! Thank you. Please note, that in my example |
What widget do you need? For example, if you use Entities Table widget, there are |
In fact, I have two use cases.
On this dashboard, I do show entities in an entity table widget (and want to add the download button there). Adding the code above only downloads the first image in the list. This is because My question is: How can I access the entire list of entityNames and Id's for all entities in the Widget programatically? |
|
Nice, thanks again. After fiddling a bit with the asynchronous nature of some of the involved functions: Here is an updated version of the action that downloads all Type: let $injector = widgetContext.$scope.$injector;
let deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));
let attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));
// generates a ZIP file of pictures, a download link and programmatically clicks it
async function downloadPictureZip(pictures, zipFilename) {
// JSZip magic
const zip = new JSZip();
// Loop through each picture in the array and add it to the zip file
pictures.forEach(picture => {
// Extract the file extension from the base64 string
const mimeType = picture.picture_base64.match(/^data:(.*);base64,/)[1];
const fileExtension = mimeType.split('/')[1];
// Add the base64 image to the zip file with the specified name and extension
zip.file(`${picture.name}.${fileExtension}`, picture.picture_base64.split(',')[1], { base64: true });
});
const zipBlob = await zip.generateAsync({ type: 'blob' });
// Create a URL for the blob and create a link element
const url = URL.createObjectURL(zipBlob);
const link = document.createElement('a');
link.href = url;
link.download = zipFilename;
// Append the link to the body (required for Firefox)
// programmatically click the link and finally clean up
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(url);
document.body.removeChild(link);
}
function getDevicePictures() {
const picturePromises = widgetContext.datasources.map(datasource => {
return new Promise((resolve, reject) => {
deviceService.getDevice(datasource.entityId).subscribe(
device => {
attributeService.getEntityAttributes(
device.id,
'SERVER_SCOPE',
['picture']
).subscribe(
attributes => {
const pictures = attributes.map(attribute => ({
name: datasource.entityName,
picture_base64: attribute.value
}));
resolve(pictures);
},
error => reject(error)
);
},
error => reject(error)
);
});
});
return Promise.all(picturePromises).then(results => results.flat());
}
// Asynchronous operation to fetch pictures and add them to the Zip file
getDevicePictures()
.then(pictures => {
return downloadPictureZip(pictures, 'all_pictures.zip');
})
.catch(error => {
console.error('Error fetching device pictures:', error);
});
|
Several notes:
|
Thanks for all the further advice!
Good catch. I changed the code around a little bit, such that I am now re-using the result of
👍
I don't fully understand this (and to an extend, not what even an injector is either 😇 ). I'm not sure how much of an improvement this is and if it's worth the time to further change the code. Too not have too many code blocks floating around in this thread I will update previously shared snippets with the suggestions. PS: This main goal of this issue was resolved, I think we can close it. WDYT? |
It is up to you, to close or not. In general, you made awesome contribution by sharing the solution.
Frankly speaking I just don't like TB's injector approach of getting access to one of available API services. In my opinion, the shorter form ( |
Component
Description
In my use-case my device a
picture
telemetry. The contains a base64 encoded image, which is used for example in thePhoto Camera Input
widget.I would like to give my users the possibility to export the image of a device via some kind of export. However, using the built-in export button I'm only able to export the base64 encoded string of the picture.
Is there a way to trigger a browser download of a device's image as PNG, JPEG, etc.. via some widget? For
?
Alternatives considered
I check the Thingsboard Widget collection https://github.com/devaskim/awesome-thingsboard if there already exists a custom widget for this. But couldn't find one. I'm not an export in Widget development, but I wasn't sure if it's even technically possible to implement one.
Environment
Disclaimer
We appreciate your contribution whether it is a bug report, feature request, or pull request with improvement (hopefully). Please comply with the Community ethics policy, and do not expect us to answer your requests immediately. Also, do not treat GitHub issues as a support channel.
The text was updated successfully, but these errors were encountered: