A while back I picked up a very cute circuit python board with procedurally generated graphics called the PicoPlanet. (I got design number 12.) A few months into the Pandemic, a bunch of projects around making video calls easier to use appeared. Adafruit even has one for circuit python; a video call panic button, for PyRuler. I found their setup, which mutes audio and turns off video at the same time, didn't work for me. Mostly because the controls in these apps are mostly toggles. So if I had my video on, but microphone off, and I hit that button, I'd be turning off my camera but turning on my microphone. Instead I broke the controls down to;
  • button one un/mutes my microphone, in all my most common applications,

  • button two turns on/off my camera, in all my most common applications,

  • button three un/mutes the speakers on my computer.

Code )

Which works great (for Zoom and Discord, Skype is experimental), but the board dangling at the end of a USB cable isn't the easiest to grab. So when I got a 3D printer the first thing I printed was a case.

The PicoPlanet github contains a two-piece frame case, and a link to a bumper case. I went with the bumper case because I didn't want to obscure the design. I printed the closed back version, and it was a snug fit, with some gentle hammering to get it in flat. But it definitely makes it easier to grab.

Photo )



Closed bumper case on PicoPlanet (design #12)
Photo by [personal profile] chebe

Deleting twitter DMs

2020-Dec-07, Monday 08:15 pm
I wanted to delete my DMs on twitter. (Just because seeing things makes me remember things about them, and it's plenty crowded in this head already.) I saw you can delete (your copy) of DMs, but only message-by-message. I tried to find 3rd party tools to do it for me, and they have all stopped existing and/or working. So, I hit up the API.

It turns out versions are crucially important. I'm using python3, tweepy 3.9.0, and twitter API 1.1.

Following on from my previous adventures I had to add 'Direct Messages' permission to my app and regenerate the keys.

Actually deleting the DM is the easy part;
api.destroy_direct_message(direct_message.id)
(Just remember you are only deleting your copy of the messages.)

Finding the message/id to delete is the hard bit. The best available is list_direct_messages which lists both sent and received direct messages, but only in the last 30 days. It uses cursors for paging through potentially long list of results, and can be rate limited.

Best go ahead and make sure you have tweepy 3.9.0, because the previous version has a bug that means the cursors aren't handled correctly in this call;
pip3 install tweepy --upgrade

Putting it all together you get something like;
code )

As I was manually deleting the DMs older than 30 days I noticed that twitter itself is using version 2 of their API, and doing a circuitous lookup via DM conversations. Something to look into when the new API is supported.

But really, all this could have been avoided if twitter only provided a 'Delete Conversation' button in DMs.
Hello Internet. It's been a while, huh? Like many people, I got caught up in completely reconfiguring my workspace to accommodate a work-from-home station. Just yesterday I got to close all my IKEA tabs. It felt good. But unfortunately I haven't had the opportunity to get much work done on projects.

One of the short things I did manage was an update to my tweet-my-blog script, because my photos got too big so I needed to add handling to downsize before posting.

First, pull in a new library;
pip3 install pillow


Then update the script (changes in strong);

code )

Pi Life, part two

2020-Jul-11, Saturday 01:47 pm
In Pi Life, part one I only got as far as making my raspberry pi functional again through upgrade. Now on to the script I want to put on it.

After doing a project, and writing it up, I am usually too lazy to tweet my own blog posts, so I wrote a little python script to scrape my RSS feed and post a link to the most recent post to twitter. (For people who don't use RSS.)


Step Two; create twitter app entry and get access tokens

This part seems to change constantly, so here are some posts I used to help me along the way;
https://www.digitalocean.com/community/tutorials/how-to-create-a-twitter-app
https://iag.me/socialmedia/how-to-create-a-twitter-app-in-8-easy-steps/
https://code.tutsplus.com/articles/creating-a-twitter-oauth-application--net-16614


Step Three; create python script

Assuming you have python3 already;
pip3 install tweepy
pip3 install feedparser
pip3 install bs4
pip3 install lxml


And because BeautifulSoup can't seem to find lxml this way;
sudo apt-get install python3-lxml

Create a python script something like this (the exact code will depend on the structure of your RSS);
code )


Step Four; deal with errors

My main error happened when I wanted to get an image used in a post. My images are self-hosted, Let's Encrypt signed. And the python3 libraries were not happy;
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)

Now you can just turn off verification;
response = get(img_link, verify=False)
But you still get an annoying warning message.

I tried so many different ways to try and fix this. This is the only thing I found that worked.

First, go to Let's Encrypt Certs and save the Intermediate, Active, cross-signed cert. Remove the .txt suffix. Verify this cert fixes the error for you with;
curl https://your_website --cacert /path/to/lets-encrypt-x3-cross-signed.pem
If no errors, all good.

Now, in python;
import certifi
print(certifi.where())


Mine returns /home/pi/.local/lib/python3.7/site-packages/certifi/cacert.pem
Edit this file, append everything in lets-encrypt-x3-cross-signed.pem to the end. (Leave a comment telling yourself what you did for future.) Save and exit. Rerun the tweeting python script.


Step Five;

Glory in the lack of errors/warnings. And automatically created tweets, I guess.
You know what's super annoying? Not knowing the ip-address of the server you're trying to access. I could scan the network and find the right machine. But, I'm lazy. And, the server knows its own ip, it should simply tell me.

This is something I believed my Pirate Radio could manage. When the pHAT BEAT bonnet gets installed it sets itself up with a daemon in /usr/bin. Technically I installed it twice (once for spotipy as well). I don't know if the code differs, but as I installed vlc-radio last, that's the code I will play with.

Think it through. I've just booted up the server. The volume and power buttons do very necessary jobs, so we'll leave them alone. The fast-forward and rewind buttons start playing a station/cycle through stations. Very useful if you can't be bothered finding the ip-address. That leaves Play/Pause, which doesn't do anything until a station has started streaming. And if I have access to the web interface, well I can do that very job there. That's it, I'm sacrificing Play/Pause!

In the end I simply replaced line#204 with a call out to my script;
myip.read_ip()
(making sure to import it at the top of the file. Let's hear it for the wonders of Open Source!)

What script you ask? A simple one, to parse the wlan0 ip from ifconfig, and then a call to text-to-speech to actually read it out loud to me.

You'll probably need to install the text-to-speech stuff;
sudo pip3 install pyttsx3
sudo apt-get install espeak


Then save this as a file named myip.py (or whatever) into /usr/bin
#!/usr/bin/env python3

import os, pyttsx3, time

def read_ip():
    f = os.popen('ifconfig wlan0 | grep "inet\ addr" | cut -d: -f2 | cut -d" " -f1')
    f = os.popen("ifconfig wlan0 | grep 'inet ' | awk '/inet/ {print $2}'")
    your_ip = f.read()

    if not your_ip:
        your_ip = "not online"
    
    engine = pyttsx3.init()
    engine.say(your_ip)
    engine.runAndWait()

    time.sleep(3)
    print your_ip

if __name__ == '__main__':
    read_ip()


And sure, give it a reboot to make sure the changes are picked up.

Now, when I press play the Pirate Radio reads out its ip-address to me! So handy.
(Note; if you press play with monitor and OTG cable attached, it'll probably crash and reboot. So don't do that.)

*edit* 2020-07-10
*edit* 2021-08-12 python3 version
*edit* 2021-11-21 proper python3 version
This weekend involved getting to see Placebo in concert, so not much else happened. But that's the great thing about software, you can be productive without getting out of bed.

I recently attended a Quantified Self meetup on sleep, that gave me the kick up the arse I've been needing. (Which was only compounded when I heard about Pebble closing, and that Fitbit would take over their servers, and staff.) One of the presenters talked about how he got his Fitbit data and how he analysed it. (If you're interested, slides are here, and blog post here.) He switched to using the official APIs when they became available, but I went with his original approach, a python program to log in and get your intraday data points instead of the aggregated stuff you get on the dashboard.

I haven't been the most steady wearable user, but there were a few months when I wore it reliably. Simply because I was sleeping terribly and wanted to understand just how badly. Although I never got around to actually analaysing the data, until now. (That's one thing about all these QS talks, typically everyone showing their data is the picture of good health. I'm more interested in seeing what it looks like when it goes wrong.)

I used the website dashboard to find the months that actually contained data. Then I exported csv files, month-by-month, using the official export option. This is the aggregated information, but it will be nice to see if it correlates with the intraday info.

Then armed with dates that contain valid data, I put them into the python program, and grabbed all my data. In short, download and extract the source code. In the same folder as setup.py create your own python file, where you will create your own client. (This information is apparently in the example files, but I poked around before I noticed that.) To create your own client you just pass in your email address and password;
client = fitbit.Client.login(email="bob@example.com", password="password")
You grab the specific info you're interested in;
client.intraday_sleep(date)
And then dump it to screen/file/whatever you want. Although it's a good idea to format it a little.

Sleep data is recorded as one value per minute.

# The different values for sleep are:
# 0: no sleep data
# 1: asleep
# 2: awake
# 3: very awake

# data will be a similar list of tuples, but spaced one minute apart
# [
# (datetime.datetime(2010, 2, 20, 23, 59), 2),
# (datetime.datetime(2010, 2, 21, 0, 0), 1),
# (datetime.datetime(2010, 2, 21, 0, 1), 1),
# ....
# (datetime.datetime(2010, 2, 21, 8, 34), 1),
# ]


And then, we are left with the task of analysing the data. Which is a much bigger one.

Profile

chebe: (Default)
chebe

Syndicate

RSS Atom

June 2025

M T W T F S S
      1
23 45678
9101112131415
1617 1819202122
23242526272829
30      

Expand Cut Tags

No cut tags

Style Credit