TL;DR
- if you are writing an article and plan to add a TL;DR section, then put it at the very top, right after the title.
- that's it, no excuses, end of discussion.
A blog about things I do: Debian, Linux, Python, whatever
TL;DR
If you use dput-ng, you may be familiar with the Twitter hook that tweets a message when uploading a package.
A similar hook is now available for Mastodon too; if interested, give it a try and comment on the MR
Most (if not all) QNAP models come with an LCD display, in my model i got a 2 lines by 16 characters each. The tool to control the panel is:
/sbin/lcd_tool
but sometimes it's a bit stubborn in executing the commands, so as mentioned here you may want to killall -9 lcdmond and then issue the command you want; this is the snippet i'm using:
# print a msg on the panel killall -9 lcdmond /sbin/lcd_tool -1 "$LINE1" -2 "$LINE2" sleep 5 # turn off screen light killall -9 lcdmond /sbin/lcd_tool -f
it ain't pretty, but it gets the job done.
If you look for how to make your system beep, you may find these commands:
/sbin/pic_raw 80 Beep short /sbin/pic_raw 81 Beep long
but they no longer work. So keep looking, i stumbled upon this post, mentioning hal_app, and that indeed works, but with a different syntax than the one presented at that link.
In order to use hal_app to make a beep sound, the syntax that worked for me is:
/sbin/hal_app --se_buzzer enc_id=0,mode=XXXIf you remove entirely mode=XXX, it produces a short beep, but here is the list of all the modes available and their results (found out via exhaustive search):
code | output |
---|---|
0 | short beep (0.5 secs) |
1 | long beep (1 sec) |
2 | 3x short beeps (0.5 secs), long pause (1 sec) |
3-7, 10, 15, 16, 17, 18, 19, 20 |
2x long beeps (1 sec), very long pause (2 secs) |
8, 9 | 3x long beeps (1 sec), very long pauses (2 secs) |
12, 13 | long beep, 2x very short pause (0.25) very short beep (0.25) |
14 | 3x very short beep (0.25) very short pause (0.25) |
(you can prepend as many 0 to the mode strings as you wish, so 000 is equivalent to 0, 000018 is equivalent to 18, and so on).
PS: this article is based on my system, TVS-473 running firmware 4.5.3.1652
PPS: friendly reminder that this is how you edit the crontab on a QNAP machine.
I'm very lucky to be able to see the Empire State Building from my apartment windows, and at night the lights are fantastic! But i'm also curious to know what's going to be today's lights, and tomorrow, etc.
I thought I'd easily find a calendar to add to gCal to show that, but i wasn't able to find any, so I made it myself: https://sandrotosi.github.io/esb-lights-calendar/
If you enjoyed watching Devils, like me, and if you tend to obsess over tiny details that have absolutely nothing to do with the story being told, like me, and you lived in that general area, like me, then you may be curious of the location of Massimo's apartment.
In several shots we can see views of both The Gherkin and the north side of Canary Wharf, so that means the building is in and around E1/E2/E3 zip codes. But London is huge, and while yes i tried to just browse Google Maps for clues, that didn't produce any result.
The final clue is revealed in the 9th episode, at minute 20:10, and precisely with this shot:
at the bottom left we can see the entrance sign for Tobacco Dock, located at:
So right there, just off to the right from the entrance, it's the apartment building: Park Vista Tower.
The exact apartment number, i don't know, it's probably somewhere on the door or on the door frame, which can probably be seen one of the times when Sofia or others come to visit Massimo, but since that happened mid 9th episode, i'm not gonna go back to find that clue 😀
Recently the Python Team started the migration from Alioth mailing list to a tracker.debian.org team.
Alioth mailing list, being a real ml, included a List-Id header, which is handled by Gmail natively and which you can use to create a filter based on it.
Tracker.d.o instead uses a custom header, X-Distro-Tracker-Team, and Gmail doesn't allow to create filters on custom headers. But there's a solution: Google Apps Scripts.
To create a new apps script, click "New project" from the scripts dashboard: this will open a code editor, so now cut and paste this code (change the label name to match the one you want to use, and the header too if you're going to use this for another team):
function tagPythonTeamEmails() {
// search for all recent threads (hours is the smallest interval searchable)
var threads = GmailApp.search("newer_than:1h");
// this is the label i want to apply to the messages
var label = GmailApp.getUserLabelByName("Debian_Python_Pkgs"); // <-- EDIT THIS
for (var i = 0; i < threads.length; i++) {
// get all messages in a given thread
var messages = threads[i].getMessages();
// for each message in the thread
for (var j = 0; j < messages.length; j++) {
var message = messages[j];
// needed to have access to the full email: headers + body
var email = message.getRawContent();
// this is the header i want to check for: if present, add label and archive
if (email.indexOf("X-Distro-Tracker-Team: python") > -1) { // <-- EDIT THIS
threads[i].addLabel(label);
threads[i].moveToArchive();
}
}
}
}
(This code is largely inspired by this StackOverflow answer.)
Given we're going to perform changes to the emails, we need to add extra permissions to the script; first we need to toggle the option "View > Show manifest file" which will show the appsscript.json file. Once done, edit it and update the scope of the script to include:
"oauthScopes": [
...
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.modify"
],
These new permission will be granted when we deploy the script, which we can actually do right now: from the editor window click on "Publish > Deploy from manifest..." and then "Install add-on"; it will make the app script available to operate on Gmail by opening the usual Google grants authorization popup.
Unlike filters defined on the web UI, apps scripts are not triggered every time a mail is received (there's a pub-sub mechanism, but it doesn't look like it would work for this), so if we want to execute periodically the new script, we need to create a time-driven trigger.
The simplest way is probably from the script editor (but you can also follow this doc): "View > Current project's triggers" and then "Add trigger" from there. For my specific use-case i set:
Be aware of quotas and limitations for Apps scripts; in particular the "Triggers total runtime" set to "90 min / day" is what led me to set the trigger every 510 mins.
Use the scripts dashboard (doc) to monitor the script execution logs, in the "My Executions" section, and specifically the "Duration" column: the script execution time depends exclusively on the number of emails to process at every execution, so fine-tune the trigger time to stay within your quota.
The newer_than search operator doesn't allow a granularity smaller than hours (although that's not even documented), so that means we're going to scan multiple times the same emails.
UPDATE (2020-12-26): I've already had to bump from every 5 to every 10 minutes, as otherwise i'd get Exception: Service invoked too many times for one day: gmail. (remember the quota!)
to send emails with images you need to use MIMEMultipart, but the basic approach:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
msg = MIMEMultipart('alternative')
msg['Subject'] = "subject"
msg['From'] = from_addr
msg['To'] = to_addr
part = MIMEImage(open('/path/to/image', 'rb').read())
s = smtplib.SMTP('localhost')
s.sendmail(from_addr, to_addr, msg.as_string())
s.quit()
will produce an email with empty body and the image as an attachment.
The better way, ie to have the image as part of the body of the email, requires to write an HTML body that refers to that image:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
msg = MIMEMultipart('alternative')
msg['Subject'] = "subject
msg['From'] = from_addr
msg['To'] = to_addr
text = MIMEText('<img src="cid:image1">', 'html')
msg.attach(text)
image = MIMEImage(open('/path/to/image', 'rb').read())
# Define the image's ID as referenced in the HTML body above
image.add_header('Content-ID', '<image1>')
msg.attach(image)
s = smtplib.SMTP('localhost')
s.sendmail(from_addr, to_addr, msg.as_string())
s.quit()
The trick is to define an image with a specific Content-ID and make that the only item in an HTML body: now you have an email with contains that specific image as the only content of the body, embedded in it.