Automatically Avoiding Sporting Event Spoilers on iPhone

I don’t watch a lot of sports, but when I do, I normally watch events shortly after they’ve aired. This makes it very likely to stumble upon spoilers on social media or in the news. To try to solve this I have a Focus set up on my iPhone called “Avoid Spoilers”, which disables notifications from apps that may spoil me, e.g. Facebook’s Messenger app, where my friends routinely discuss the events live. This Focus also prevents me from from opening social media apps, like reddit, accidentally, since it’s connected to the ScreenTime+ app, in which I’ve set up a rule disallowing the opening of social media apps:

I then enable this Focus when a sport event starts that I know I’ll want to watch later, and then keep it enabled until I’ve watched the event. However, I’ve been wanting to automate this completely, so that this automatically gets enabled when the sporting event actually starts, and I finally found a neat solution for this.

Automation using Shortcuts app and IFTTT.com

The Shortcuts app on iOS supports a number of automated events that can trigger any of your Shortcuts. One of those triggers can happen when receiving an email, and this got me thinking that since I have a calendar with the sporting events I follow, e.g. Formula 1 races, maybe I could automate sending an email every time an event starts in that calendars, which in turn could trigger a Shortcut which enables to “Avoid Spoilers” Focus. So this is what I did:

  1. Ensure you have a sporting events calendar in, say, a Google Calendar account
  2. Create an IFTTT.com account using an iCloud email address (this becomes important later)
  3. Create a new applet with the following:
    1. Tap “If This”, open the Google Calendar service and pick the “Any event starts” trigger
    2. Connect and pick your Google Calendar account, pick the correct calendar and choose time before the event starts, e.g. 0 minutes. Then create the trigger
    3. Tap “Then That”, open the Email service, and pick the “Send me an email” action
    4. Choose the subject you want, e.g. I chose “F1 Event Starts Now”, do what you want with the body, and create the action

Now we’ll need to go into the Shortcuts app’s Automation tab and do the following:

  1. Create a new automation by tapping the top-right plus icon
  2. Choose the “Email” trigger and configure as follows:
    1. Sender: action@ifttt.com
    2. Subject Contains: The subject you used in your IFTTT applet, e.g. “F1 Event Starts Now”
    3. Account: Any Account
    4. Recipient: Any Recipient
    5. Choose “Run Immediately” at the bottom
    6. Tap “Next” in the top right
  3. Choose “New Blank Automation”
  4. Tap “Add Action” and find the “Set Focus” action
  5. Configure the action to it says: “Turn {Focus Name} On until Turned Off”, or however you would prefer it to be
  6. Tap “Done” in the top right

You should now see the automation in your Automations tab.

Lastly, we need to make sure your Mail app is set up to receive emails immediately when they are sent. On iOS, this is only possible using iCloud email accounts, any other account can only have new emails fetched, at most, every 15 minutes. This is why it was important to use an iCloud account for IFTTT, since the action there only sends emails to the account email address.

To ensure this is properly enabled, do the following:

  1. Open your Settings app, then “Mail”, and then “Accounts”
  2. If you do not see the iCloud account you used for your IFTTT account, then add it by tapping “Add Account”
  3. Once added, go back to the “Accounts” screen, open “Fetch New Data” and make sure “Push” is enabled at the top of the screen and for the iCloud account itself

That’s it, your Focus should now enable automatically whenever you receive a trigger email.

Cykling

Herude på landet, der cykler folk meget. Som i, det er normalt at ende bag 10 midaldrende mænd med topmave, cykelgrej til titusindvis af kroner og stramme, matchende holdtrøjer. Og det er jo så dejligt nemt at gå og more sig lidt over den moderne form for midtlivskrise.

Da Corona så ramte, lukkede mit lokale Crossgym-hold og jeg skulle ikke længere cykle til arbejde dagligt. Så jeg købte en billig racercykel. Og det var da hyggeligt nok at køre lidt rundt i området på den. Og så fik jeg ondt i hænderne og købte cykelhandsker. Og en bedre cykelhjelm. Og en iPhone-holder til styret. Og en bedre sadelpind. Og i dag kom min nye sadel. Og i weekenden brugte jeg to timer på at finjustere gearet, fordi jeg synes det var hyggeligt. Hyggeligt. Jeg ved, hvad en bagskifter og en forskifter er, jeg ved, hvordan man justerer H- og L-grænseværdi-skruerne og hvordan man indekserer gearet. Min YouTube-forside er kun cykelvideoer, og når jeg støder på cyklistflokkene, på min egen cykel, med mine 34 år på bagen og 10 kilo for meget på dunken, så nikker de forstående. Og jeg nikker igen.

How to set up your own CS:GO server using Amazon Web Services for free

This guide is based on this great reddit post, with a bunch of important additions and changes. It will go through all steps of setting up a Counter-Strike: Global Offensive server using Amazon Web Services’ free tier. There’s several reasons why you might want to do this, maybe you want to play matches with your team or have a retakes server, it’s up to you. Also, in the majority of use cases, the server is free (more info on pricing tiers below).

Prerequisites to get started:

  • Your Steam account must not be community banned or locked.
  • Your Steam account must not be limited.
  • Your Steam account must have a qualifying registered phone.
  • An Amazon Web Services account, which can be created for free.
  • A debit or credit card. Don’t worry. You will be charged around 2 rupees or around 3 cents. It will just be done to verify your card.

Assuming you have the fulfill the prerequisites, let’s start with the guide:

Creating Server Instance

  1. Log in to your AWS account as Root user.
  2. Select the region closest to your in the top-right corner.
  3. Choose EC2 from Services and select Launch Instance from the screen. You may get some error if you just created your account, wait a few hours and try again.
  4. On the next screen select Ubuntu server 20.04 from the list of machines you see. Remember to verify that you have selected the one with a Free tier eligible label.
  5. You now have to pick the instance type. To stay in the free tier, pick t2.micro. Tap Next: Configure Instance Details at the bottom.
  6. Don’t change anything on the Configure Instance Details screen, but continue on to Next: Add Storage.
  7. On the Add Storage screen, increase the storage size to 30 GiB in the Size column. The server will require at least 20 GiB, and 30 GiB is the limit for the free tier. Tap Next: Add Tags.
  8. Skip on to Next: Configure Security Group.
  9. Create the following rules:
    • SSH (used to access the server)
      • Type: SSH
      • Protocol: TCP
      • Port Range: 22
      • Source: Anywhere
    • TCP Ports (opens player connections to server)
      • Type: Custom TCP Rule
      • Protocol: TCP
      • Port Range: 27000-27020
      • Source: Anywhere
    • UDP Ports (opens player connections to server)
      • Type: Custom UDP Rule
      • Protocol: UDP
      • Port Range: 27000-27020
        Source: Anywhere
  10. Complete your server instance by tapping Review and Launch and then tapping Launch on the next screen.
  11. A pop-up will appear called Select an existing key pair or create a new key pair. This create a so-called key pair, consisting of a public and a private key. Whoever is in possession of the private key will be able to access the server securely. Select Create a New Key Pair and name it anything you want, e.g. “AWS CS GO Server Key Pair”. Tap Download Key Pair and then Launch Instances.
  12. Put the key pair .pem file somewhere safe on your computer.
  13. Tap View Instances and note down the instance’s IP address found in the IPv4 Public IP column.

Connecting to Server and Installing CS:GO

To securely connect to the server we will use the SSH protocol. If you are on Linux then install OpenSSH. If you are Windows then install Putty. If you are on macOS, you already have SSH installed.

  1. Open your Command Prompt and type ssh -i <path-to-my-keypair.pem> ubuntu@<instance-ip>.
    • If you get an “UNPROTECTED PRIVATE KEY FILE!” error, then just run sudo chmod 600 <path-to-my-keypair.pem>. If on Windows then go to properties of your key pair .pem file and go to Security and then Advanced. Then tap Disable Inheritance. Delete all the users in the list and just add yourself as the owner of the file.
  2. You are now connected to your AWS server instance and you’ll see a Ubuntu terminal in your terminal where we can set up the CS:GO server.
  3. First, add and update repositories by typing sudo -- sh -c 'dpkg --add-architecture i386; add-apt-repository multiverse; apt-get update; apt-get -y dist-upgrade'.
  4. Install Steam CMD using sudo apt-get install -y steamcmd. Accept the license when prompted.
  5. Create a directory for CS:GO using mkdir ~/csgosv and change to the directory by typing cd csgosv.
  6. Start SteamCMD using steamcmd.
  7. Inside SteamCMD, execute the following code:
    login anonymous
    force_install_dir /home/ubuntu/csgosv
    app_update 740 validate

    This will install Steam and CS:GO, and it’ll take around 10-15 minutes.

Configuring CS:GO Server

  1. While SteamCMD is updating, open Steam Game Server Account Management in your browser and log in with your steam account.
  2. Create a new game server account and use 730 for the App ID (Counter-Strike: Global Offensive) and type something in the Memo field, so you can remember where you are using this game token, e.g. “AWS CS GO Server”.
  3. Now we’ll configure the launch command that will start the CS:GO server. Copy the following into a text editor: @reboot /home/ubuntu/csgosv/srcds_run -game csgo -console -usercon -autoupdate -steam_dir "/home/ubuntu/.steam/steam/steamcmd" -steamcmd_script "/home/ubuntu/update.txt" +game_type 0 +game_mode 0 +mapgroup mg_active +map de_inferno +sv_setsteamaccount <your Steam game login token> -tickrate 128 -net_port_try 1 -nobots
  4. Replace <your Steam game login token> with the token you generated in step 2. You are free to change the other options, too:
    • +game_type and +game_mode can be changed depending on the type of server you want. Setting both to zero create a casual server, setting +game_type to 0 and +game_mode to 1 would create a server for competitive or scrimmage play, for instance. More info here.
    • -tickrate 128 sets the tick rate to 128 as opposed to 64, which is the standard for CS:GO competitive matchmaking.
    • -nobots can be removed if you want bots on your server
    • We’ll come back to the -autoupdate, -steam_dir and -steamcmd_script parameters, which have to do with keeping the server up to date.
  5. Once you’re happy with your launch command, come back to your terminal and when the update is done, exit SteamCMD by pressing Ctrl + C.
    • If you accidentally exited your AWS instance too, then reconnect using the same line as above: ssh -i <path-to-my-keypair.pem> ubuntu@<instance-ip>.
  6. Now type crontab -e to open crontab, a time-based job scheduler we’ll use to make sure the CS:GO server launches whenever the AWS instance is rebooted. Select the first editor and continue. It will open a file with several instructions all prefixed with #. Ignore them and type the full launch command on a new line.
  7. Save the file, most likely by tapping Ctrl + O, then Enter and then Ctrl + X.
  8. To make sure the server stays up to date, we need to add another file. Type cd ~ to go back to your home directory.
  9. Type touch update.txt
  10. Edit the file by typing nano update.txt
  11. Input the following:
    login anonymous
    force_install_dir "/home/ubuntu/csgosv"
    app_update 740
    quit
  12. Save the file by using Ctrl + O, then Enter and then Ctrl + X.
  13. Put this file in /home/ubuntu/.steam/steam/steamcmd.
  14. Go back to the web interface for the AWS instance and reboot it by selecting the server and then tapping Actions, Instance State and then Reboot.
  15. We are now done, close your terminal, open Counter-Strike: Global Offensive and type connect <instance IP address> into the console and you should connect to your server.

Getting Up-To-Date Daily Disposable Amount from Bank Account

I use You Need A Budget (YNAB) for my personal budget. It’s an amazing service built up around the idea of “giving every dollar a job”, meaning that you put all the money you have for a given month into a category in your budget. So, after categorizing all your money into “mortgage”, “groceries”, “savings”, etc. you end up with a final amount of disposable income, and this is where I’ve hit a roadblock that’s been annoying me literally for years:

On any given day of the month I want to know how much money I can spend out of my remaining disposable income, e.g. remaining days of the month divided by remaining disposable income. This is annoyingly hard to do with YNAB, because every time I make a transaction I’d have to manually add those transactions to YNAB, see my new disposable amount and calculate my new daily amount. This is not an issue for American YNAB users, because YNAB can automatically import transactions from American banks, meaning you can just check your YNAB app and divide the up-to-date disposable amount with the remaining days of the month. Still somewhat cumbersome, but at least easier than having to manually import transactions.

Also, services like Spiir, that does automatically import transactions from Danish banks, does not help me out here, because the budgetting concept there is built around category spending limits, which I don’t like. So, what I’ve been doing is using the app Pennies, which just allows me to enter an account total, and then it gives me my daily disposable amount. Whenever I buy something I then add a transaction in that app, and it shows me my remaining amount. Obviously, this is not a perfect solution, because Pennies gets out of sync with my actual budget if forget to enter something and I have to manually update the total amount every month.

This all changed, because last week I received a newsletter from YNAB in which they announced their new public API! My eyes lit up! Maybe I could build my own YNAB integration, automatically importing transactions from my bank on a regular schedule, and run that regularly on my Mac Mini server. And if I got that working, maybe I could build a small iOS app that uses the same new API to show me my up-to-date daily disposable amount! The only limitation was how to get my transactions exported from my bank’s online banking service. After a quick Google search I stumbled upon Nordic API Gateway, a service that exposes an API for almost all banks in Scandinavia. I couldn’t believe my luck.

After spending some hours scripting, I now have the following setup:

  • Node.js script that exports transactions from my bank and imports these into YNAB. YNAB is really clever about detecting duplicates, so I don’t have to worry about filtering out transactions that were already imported. I just import everything going back 30 days. This runs every 15 minutes using cron.
  • Node.js script that fetches my YNAB budget, gets the remaining disposable amount, calculates the daily amount for the remainder of the month and then pushes this info as an iOS Push Notification. This also runs every 15 minutes using cron.
  • iOS app which has Push Notifications enabled and receives these from the 2nd Node.js script. When it receives the notification it updates its badge with the amount of money I can spend that day.

This now means I have a nice app icon on my iPhone that always shows a badge number with the up-to-date daily disposable amount I can use.

Review: PEBA 1296P Super HD Dash Cam

I recently bought my first car dash cam, a PEBA 1296P Super HD dash cam. I did a lot of research and ended up with the following features I wanted:

  • At least 4 stars on Amazon
  • At least 1296p resolution
  • Circa 150 degrees field of view
  • Suction mounting, not glue
  • At least a 2-inch screen
  • Cabled power

But that was just the technical specifications. It is surprisingly hard to find in-depth reviews of dash cams which actually focus on what’s most important when it comes to cameras that you operate while driving: the user interface and the user experience. Unfortunately when you haven’t owned a dash cam before, it’s hard to predict what kind of UX cases will be important to you.

Before I go on it’s worth noting that dash cams usually work in the following way: the camera fills up the memory card as you drive, automatically deleting the oldest recordings to make space for new ones.

Having used the camera for a while, two common use cases very quickly became obvious:

  • Something happens on the road and you quickly want to save and lock the last X amount of minutes from deletion on the memory card.
  • Quick and easy transfer of data from the memory card to computer or smartphone.

Unfortunately the PEBA camera doesn’t support any of these in a meaningful way. It does have a feature where tapping the OK button the camera locks a clip against being automatically being deleted, but the feature is useless because it only stores the clip a few seconds back. So, let’s say someone cuts you off or you see something funny and want to store that clip without going through hours of hours of footage to find it, this is not possible with this camera.

For quick importing of the footage onto my iPhone, I bought a lightning to SD adapter, but apparently this only supports a specific list of video formats, and the format the camera is using is not supported. So, I have to either bring my laptop into the car or bring the SD card with me into the house to import footage. Kind of annoying when you frequently like to watch the footage.

So, I will be buying a new camera which supports both of these use cases.

Thoughts on The Last Jedi

Finn & Rose: This storyline just did nothing for me. Rose is such a flat character, they spend no proper time making her interesting. Compare her introduction with Rey’s in Force Awakens. We see and understand so many things about Rey just by watching her on Jukka; her sledding down the dune, putting on the fighter pilot helmet, defending BB-8 and refusing to sell him even though she really needs the money and we feel she’s orphaned and alone by her actions and by the actions of those around her. It’s such a great character introduction, and Rose gets none of that. She’s sad because her sister is dead and later on we get to know she had a shitty childhood. We get told this, we neither see nor experience it, so it falls flat, at least to me.

The scenes at the casino didn’t do much for me either to the point where I groaned at some of the scenes. The aesthetics of the place are amazing, but the whole rescue operation of the horse creatures and the “it’s worth it now” comment at the end felt so forced, especially when you realize they left behind a whole group of slave kids! It also seems like the movie implies that they just so happen to stumble upon another master hacker in Benicio del Toro, which seems ridiculous after Maz specifically points out that these are incredibly hard to find.

But the worst part of their storyline is that, in the end, they get a bunch of people killed and their plan completely fails, when del Toro betrays them and tells Hux about the escaping transport ships.

This movie really, really does not want you to think that going on a sacrificial, hero-journey to save people, is a good idea, because it fails for more or less everyone who tries it in the movie.

Poe, Holdo & Leia: Why exactly is Poe not in chains at the end of the movie? He disarms a vice admiral at gunpoint and attempts to ruin her escape plan. And why, when Poe is holding Holdo at gunpoint, doesn’t she just TELL HIM that the First Order won’t notice the escaping transport ships? Why doesn’t Poe know this himself?! Such a ridiculous storyline. And I found Leia’s Mary Poppins flight pretty silly, that just did not work for me. I actually felt that would’ve been a decent choice, to have her killed there, to set the stakes for the rest of the movie.

Hux: This guy must be the most incompetent military leader in the movie series’ history. He doesn’t manage to block the evacuation, he doesn’t realize that Finn and Rose escape and he doesn’t see the escaping transport ships until del Toro’s character tells him. I loved the scene in the throne room where he’s just about to shoot Kylo though.

Rey & Luke: I’m not sure what I think of the scenes on the island. I loved Luke’s reaction to Rey’s first meeting with the dark side, that she just plunges in. I feel like they justified her ambivalence between the light and dark very well, but the mirror scene made no sense to me. I assumed that was a source of the dark side of the Force, but she just goes there, looks in a mirror, sees herself and that’s it? I really liked the scene with Yoda, even though it seems like the movie makes a bit of a mockery of that scene’s point when you see that Rey actually brought the Jedi scripture with her.

Kylo & Rey: The best part of the movie for me. The interactions they had actually felt real, their connection felt real and I was convinced by the supposed sympathy they get for each other. Rey’s arc here, where we actually get the feeling she may turn to the dark side – now or later – is the most impressive part of the movie to me. This is exactly what never worked in the prequels, I was never convinced by Anakin’s fall to the dark side, he just felt like a petulant, spoiled child. With Rey, I get the feeling that there’s so much anger just below the surface, that she could explode at any moment, that she’s so deeply hurt by her parents’ abandonment that she’s filled to the brim with a desire to get some sort of resolution even if that means tuning to the dark side.

And Adam Driver fucking nails his performance. Kylo feels so enraged, frustrated and sad that when he and Rey feels a connection, the relief for Kylo is palpable, it feels like this is what he needs and what he’s wanted for so long without knowing it; an ally who understands him and his rage.

During the throne room scene, however, this is all thrown out the window. When Kylo asks Rey to join him, I actually felt like it would be believable if she did. But she doesn’t and then it seems like, for the rest of the movie, all ambiguity is gone and those two characters go back to being exactly how they were at the end of Force Awakens: Kylo bad, Rey good. That felt like such a wasted opportunity to me.

Kylo & Luke: Second-best part of the movie. When they reveal that Luke tried to rage-kill Kylo I was flabbergasted, that felt so stunningly amazing and it made Luke’s shame so much deeper, so much easier to understand. I absolutely loved that I actually had to consider if Luke would now try to kill Rey. I think this movie could’ve been one of my all-time favorites if they had actually gone along with that plotline, if Luke does feel obligated to destroy Rey because of the immense darkness in her, and she then escapes, which pushes her directly into the arms of Kylo, ending up with a stand-off between Kylo and Rey, the disappointed apprentices, on one side, and Luke on the other. Then the final movie could be about getting Rey back or something. As it is now I feel like The Last Jedi ends with Kylo and Rey in basically the exact same place as at the end of Force Awakens. Rey shuts the door on Kylo, she rejects him and what he stands for, but that was already her stance at the beginning of the movie.

Luke: What the fuck was that ending? It finally feels like we will see Luke in battle and then he’s a fucking hologram? He’s not even there? I was ready to see him crush some fucking walkers! And please, for the love of God, someone explain to me why he dies afterward? If he had to die, then why not actually have him there and give that scene with Kylo meaning?

Getting iOS Certificate Expiration Date from Provisioning Profile

If you ever find yourself needing to get the expiration date of a certificate used to create a provisioning profile, this snippet does exactly that.

/usr/libexec/PlistBuddy -c 'Print DeveloperCertificates:0' /dev/stdin <<< $(security cms -D -i /path/to/prov_profile.mobileprovision) | openssl x509 -inform DER -noout -enddate

Resources:
https://www.objc.io/issues/17-security/inside-code-signing/

D&D 5 Spell Book

I’ve been playing Dungeons & Dragons 5 in my sparetime for a while, and it’s very annoying having to look up spells in a big, alphabetized list, so I made an app!

It’s quite simple, it’s just a list of spells, a settings view for inputting the variables needed to calculate how many spells you can have prepared and a couple of filters. It’s very much work in progress, but I wanted to post a short video of its current state here.