-
Apr 9, 2018
Setting Emacs Theme Based on Ambient Light
I sit next to a window at work. On sunny days it’s easier to see a light editor theme, and when the sky is dark, a dark theme is easier on my eyes. So I decided to try to have my MacBook automatically switch the Emacs theme based on readings from the ambient light sensor.
There are two parts to this solution: a command-line executable to read data from the sensor, and then a small elisp function to do the theme switching.
The program below is from StackOverflow and slightly modified. It gets the
AppleLMUController
IO service, then, when the service is ready, prints the light sensor data to stdout and exits.// lmutracker.mm // // clang -o lmutracker lmutracker.mm -framework IOKit -framework CoreFoundation #include <mach/mach.h> #import <IOKit/IOKitLib.h> #import <CoreFoundation/CoreFoundation.h> static double updateInterval = 0.1; static io_connect_t dataPort = 0; void updateTimerCallBack(CFRunLoopTimerRef timer, void *info) { kern_return_t kr; uint32_t outputs = 2; uint64_t values[outputs]; kr = IOConnectCallMethod(dataPort, 0, nil, 0, nil, 0, values, &outputs, nil, 0); if (kr == KERN_SUCCESS) { printf("%8lld", values[0]); exit(0); } if (kr == kIOReturnBusy) { return; } mach_error("I/O Kit error:", kr); exit(kr); } int main(void) { kern_return_t kr; io_service_t serviceObject; CFRunLoopTimerRef updateTimer; serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleLMUController")); if (!serviceObject) { fprintf(stderr, "failed to find ambient light sensors\n"); exit(1); } kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &dataPort); IOObjectRelease(serviceObject); if (kr != KERN_SUCCESS) { mach_error("IOServiceOpen:", kr); exit(kr); } setbuf(stdout, NULL); updateTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + updateInterval, updateInterval, 0, 0, updateTimerCallBack, NULL); CFRunLoopAddTimer(CFRunLoopGetCurrent(), updateTimer, kCFRunLoopDefaultMode); CFRunLoopRun(); exit(0); }
The accompanying elisp code will invoke that executable on a timer and change the theme based on the light reading.
(setq current-theme "dark") (defconst light-theme 'majapahit-light) (defconst dark-theme 'majapahit-dark) ;; will apply a dark theme if the room is dark, and a light theme if the room is ;; bright (defun change-theme-for-lighting () (let* ((current-light-sensor-reading (string-to-number (shell-command-to-string "./lmutracker")))) (if (< current-light-sensor-reading 100000) (when (not (string-equal current-theme "dark")) (load-theme dark-theme 1) (setq current-theme "dark")) (when (not (string-equal current-theme "light")) (load-theme light-theme 1) (setq current-theme "light"))))) ;; probably want to run this less frequently than every second (run-with-timer 0 1 #'change-theme-for-lighting)
Edit 4/7/2019: Read a Russian translation of this post here.
-
Apr 2, 2018
Creating a new blog post in Emacs
Now that my blog is based on markdown text files, some new tooling options have opened!
- entire blog is under source control (done)
make
targets for common actions (e.g. create new post, deploy, serve dev version) (done)- git hooks for publishing (need to think about this more)
- blogging via emacs! (done!)
I just wrote this function to create a new posts file and open a buffer for editing it. In fact, it’s how I’m editing this post right now!
(defun new-blog-post () (interactive) (let ((post-title (read-string "Enter new post title: "))) (let* ((posts-dir "/ssh:vps:~/projects/blog/_posts/") (clean-title (replace-regexp-in-string "[^[:alpha:][:digit:]_-]" "" (s-replace " " "-" (downcase post-title)))) (new-post-filename (concat (format-time-string "%Y-%m-%d") "-" clean-title ".md")) (frontmatter-template "---\nlayout: post\ntitle: {title}\ndate: {date}\n---\n\n") (frontmatter (s-replace "{date}" (format-time-string "%Y-%m-%d %H:%m %z") (s-replace "{title}" post-title frontmatter-template))) (new-post-file (expand-file-name new-post-filename posts-dir))) (if (file-exists-p new-post-file) (message "A post with that name already exists.") (write-region frontmatter nil new-post-file) (find-file new-post-file)))))
This was also the first elisp function I wrote :)
-
Mar 31, 2018
under construction
I was having some headaches with my existing shared hosting provider, so I decided to move this site to a VPS. I also didn’t want to run a LAMP stack, so I’ve moved this blog from WordPress to Jekyll. Please pardon the appearance as I find time to iron things out.
-
Feb 8, 2018
How to set up free SSL on shared-hosting with Let’s Encrypt
I just updated this domain to use HTTPS with Let’s Encrypt as a certificate authority. Presently this site is on a shared-hosting provider and I had to generate a cert manually and then upload it. Here are instructions for doing that.
Note: some shared-hosting providers may offer a way to automatically generate and install a Let’s Encrypt (or other CA) certificate directly through the cPanel. I’d recommend doing that if it’s an option :)
First, download and install
certbot
. On a separate computer (i.e., not the website host), runcertbot
to generate a certificate:brew install certbot mkdir ~/letstencrypt && cd ~/letstencrypt/ certbot --config-dir . --work-dir . --logs-dir . certonly --manual
After displaying some prompts,
certbot
will produce a challenge string and ask you to upload a file to your host containing that content (using thehttp
challenge). This is to prove control of the website.On the host, create the file as instructed. E.g. copy the challenge text, then:
pbpaste > challengefile ssh myhost 'mkdir -p ~/public_html/.well-know/acme-challenge/' scp challengefile myhost:~/public_html/.well-known/acme-challenge/rPs-CyPusl...
Then, confirm you’ve uploaded the file and complete the
certbot
setup to create the certificate. There will be alive
directory containing the generated certificate and secret.live └── my-website.com ├── README ├── cert.pem -> ../../archive/my-website.com/cert1.pem ├── chain.pem -> ../../archive/my-website.com/chain1.pem ├── fullchain.pem -> ../../archive/my-website.com/fullchain1.pem └── privkey.pem -> ../../archive/my-website.com/privkey1.pem
Copy the contents of
fullchain.pem
and paste them into the certificate text box of your cPanel’s SSL configuration settings or upload the certificate file directly.Finally, install the certificate and upload
privkey.pem
.Once the process is complete the challenge file can be removed from the server. You should now be able to access your domain over
https
. -
Jan 31, 2018
Assuming an IAM role from an EC2 instance with its own assumed IAM role
In AWS IAM’s authentication infrastructure, it’s possible for one IAM role to assume another. This is useful if, for example, a service application runs as an assumed role on EC2, but then wishes to assume another role.
We wanted one of our applications to be able to get temporary credentials for a role via the AWS Security Token Service (STS).
In our application (Clojure) code, this looked something like:
(defn- get-temporary-assumee-role-creds [] (let [sts-client (AWSSecurityTokenServiceClient.) assume-role-req (-> (AssumeRoleRequest.) (.withRoleArn "arn:aws:iam::111111111111:role/assumee") (.withRoleSessionName "assumer-service")) assume-role-result (.assumeRole sts-client assume-role-req)] (.getCredentials assume-role-result)))
This code is running as role
assumer
and wants to assume IAM roleassumee
.The
assumer
role must have an attached policy giving it the ability to assume theassumee
role:{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::111111111111:role/assumee" } ] }
And, importantly, the
assumee
role must have a trust policy like:{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111111111111:role/assumer" }, "Action": "sts:AssumeRole" } ] }
The above trust policy is for the
assumee
role and explicitly grants theassumer
role the ability to assume theassumee
role (whew).Without the roles and policies properly configured, it isn’t possible to assume the role. An example error:
assumer-box$ aws sts assume-role --role-arn arn:aws:iam::111111111111:role/assumee --role-session-name sess A client error (AccessDenied) occurred when calling the AssumeRole operation: User: arn:aws:sts::111111111111:assumed-role/assumer/i-06b6226fe698e565e is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::111111111111:role/assumee
Recent Posts
- MacWhisper
- Coding with agent helper scripts
- Vibecoding’s allure: the inventor and the fiend
- Philadelphia Data Explorer
- Plant identification
- Let’s Flip an Unfair Coin
- Flight Focus
- Generative AI Accelerates Our Exploration of the Search Space
- AI Assistant Use-Case: Performance Feedback
- Poor Man's Computer Use with Execute Arbitrary AppleScript MCP Server