Storing hand-written notes on reMarkable as Google Documents
I'm about 48 hours in on owning a reMarkable 2, and let me tell you... I. Love. This. Thing.
I've been longing for a tablet that allowed me the convenience of writing like paper (I love a good bullet journal) but would actually allow me to convert those hand-written notes into editable documents.
Now, the reMarkable 2 software comes pretty close, as it allows you to store PDFs of your hand-written pages OR convert your notes to text and email them to yourself. I figured, surely someone's figured out how to get it from converted text to digital document, right?!?!
That's when I found this post on Nomad Bento's site, detailing how they can email themselves attachments from the reMarkable, and use Google Apps Script to parse those attachments and save them to Google drive.
So... the mind got to thinking... I can definitely add the rest of the logic to at least store text in a google document... and that's what I did.
For those unfamiliar with Google Apps Script, it's a way Google allows you to write logic using a subset of JavaScript that runs on their servers, and can access many of their services. Since my email address is hosted on Google Workspace, I can definitely make use of this. This will also work if you have a standard gmail account.
The rest of this article is going to assume you know JavaScript, as well as the "+" hack for aliasing your gmail account (that is, if your email is email@gmail.com, you can add a plus sign and any other characters to "email" and it will act as an alias that will show up in your main account).
I use an alias just to reduce the amount of messages I have to parse - all I have to search for is [address]+remarkable@[domain].com, and I can find all the documents I email myself.
So let's start similarly to the way Nomad Bento does, and declare 3 constants.
var EMAIL_ALIAS = 'email+alias@gmail.com';
var DRIVE_FOLDER_ID = 'Google-Drive-Folder-ID';
var REMARKABLE_LABEL = GmailApp.getUserLabelByName('remarkable-stored')
The first constant, EMAIL_ALIAS, is the email alias I use solely for sending myself stuff from my reMarkable tablet. The second constant is the id of the folder I use on drive. If you go to Google Drive, and create a folder, then open the folder, the ID will be in the URL and look something like https://drive.google.com/drive/folders/random-characters-here.
You'll use that last part and store it as your ID. The third constant will be a reference to your gmail label object in gmail - I created a label in gmail called "remarkable-stored", and we can reference that in the google apps script code.
For the main logic of the script, I'll break it into 3 parts:
- fetching all new threads that have unread messages, and were sent to EMAIL_ALIAS
- Loop over all threads, and only grab the unread messages
- For each message, get the content of the email, save it in a Google Doc, store it in the proper Google Drive folder, label it with the REMARKABLE_LABEL, mark the message as read and archive it (to reduce the introduction of duplicate files).
So... here's part 1 - fetching all new threads.
functionStoreOnDrive() {
var BASE_QUERY = 'in:inbox to:'+ EMAIL_ALIAS +'';
var results = GmailApp.search(BASE_QUERY);
We open a new function block, and I've named it "StoreOnDrive" since that's the ultimate goal of this function. First, I declare a base query, which just looks for all new threads in the inbox that was sent to our EMAIL_ALIAS. This also allows me to make some more complex queries over time. Next, we use the GmailApp
object to execute the query and get all the threads that match.
Part 2 - iterating over threads and messages
for(var index in results) {
var messages = results[index].getMessages()
if(messages.length > 0) {
for(var mIndex in messages) {
Using a for
loop, we loop over each thread that matched and get all the messages for a given thread. If the thread has at least 1 message, we loop over the results to check each individual message. And finally... the logic:
if(messages[mIndex].isUnread()) {
var messageContent = messages[mIndex].getPlainBody()
var messageSubject = messages[mIndex].getSubject()
DriveApp.getFolderById(DRIVE_FOLDER_ID)
.createFile(messageSubject, messageContent, MimeType.RTF)
messages[mIndex].markRead()
}
}
results[index].addLabel(REMARKABLE_LABEL)
results[index].moveToArchive()
}
}
}
For each message that is still unread, we get the content of the email body as plain text (this is the text that was converted from your handwriting).
Next, we grab the subject line of the email so we know what to call the file
After that, we use the DriveApp object to grab the folder we store our reMarkable documents by id, and create a new file, with the name, the content, and the MimeType is RTF (rich text format).
Then, we mark this message as read.
Once we're done looping over all the messages in a thread, add the Remarkable label we created earlier, and move the thread out of the inbox and into the archive. This will ensure that the email won't pop back up into the Inbox.
Lastly, we'll go to the Trigger action, and just like Nomad Bento, trigger this function to run every 15 minutes (somewhat like long polling to see if there's new messages).
Now... for some caveats:
1) Using the same subject may trigger a new email to hit an existing thread. I have yet to take any of that into account as this was a quick first pass at getting this functionality working.
2) My longer term plan is to introduce more granular ways to store files in different places, or even append to an exist document, based on the subject line.
The full code is pasted below. If you have any suggestions, let me know!
var EMAIL_ALIAS = 'email+alias@gmail.com';
var DRIVE_FOLDER_ID = 'your-drive-document-folder-id';
var REMARKABLE_LABEL = GmailApp.getUserLabelByName('your-label-name')
function StoreOnDrive() {
var BASE_QUERY = 'in:inbox to:'+ EMAIL_ALIAS +'';
var results = GmailApp.search(BASE_QUERY);
for(var index in results) {
var messages = results[index].getMessages()
if(messages.length > 0) {
for(var mIndex in messages) {
if(messages[mIndex].isUnread()) {
var messageContent = messages[mIndex].getPlainBody()
var messageSubject = messages[mIndex].getSubject()
DriveApp.getFolderById(DRIVE_FOLDER_ID).createFile(messageSubject, messageContent, MimeType.RTF)
messages[mIndex].markRead()
}
}
results[index].addLabel(REMARKABLE_LABEL)
results[index].moveToArchive()
}
}
}