10 Posts for keyboard Programming close
"Letting AI do some things."
2026/06/21 3:12 PM

AI Comfort Levels

For quite a while I have been reluctant to invest much time into using AI for more than setting alarms, timers, and adding things to my reminders. But, I've been playing around with some of the free tools lately and I'm pretty impressed with whats possible now.

Siri AI

With the beta versions of iOS 27 and MacOS 27 I have been able to ask Siri to find info buried in my personal data. The assistant has been much more useful than in the past. It can perform more complex tasks with multiple steps.

One thing Siri AI doesn't seem to be very good at is programming. They seem to have limited it for these initial releases. You can use the other popular models with X-Code now, but I haven't given that a try yet.

Codex

I have also been playing with ChatGPT and Codex. I'm only using the free version for now. It looks like it can get expensive quick. That's one of the reasons I hope Apples new OS's will be able to get close to whats possible with Codex.

Using Codex to fix filename issues

One thing I used Codex for recently was fixing the names of music video mp4s I have in my collection. There are around 4500 files in this folder. Some files are named poorly and there are even a few duplicates.

I started by asking Codex to look at the folder and verify how many files there were. Then I told it what the file name pattern should be and asked it to compile a list of files that did not meet this pattern. There were about 250 files it found that were not named correctly. I was going to turn this into a renaming script, but I asked Codex to do this for me.

Not only did Codex create the script, but it also properly renamed the files based on the pattern I previously described. It was able to smartly determine which parts were the artist name, which parts were the song name, and when there was extranous data that was ok to remove. I verified that the file was correct and ran the script in the terminal.

The next task was to identify duplicate files. It was able to find 12 or so files that might have duplicates. Some of these were live versions that I did not change. But, again it did find a few files that were duplicates, even though the file names were different.

This all saved me several hours.

Using Codex to create an application

I use Jellyfin to serve up video files, movies, tv shows, and Music Videos on our local network. I'm not really happy with most of the clients on the Apple TV. They work, but never in the way I want.

I spent a few hours over the last couple weeks creating a proof of concept client in Javascript and HTML to login to our server and play content. I was able to get shuffle play working properly for our music video library.

What I want to do is the same thing in an Apple TV App that works the way I want it to. I decided to use Codex to create the first version of this App. I described what I wanted to Codex. It created a new project for me, that almost worked. I had to tell it to fix a few things to make it work correctly in X-Code. A few minutes later I had a basic App that will let me login to our Jellyfin server, navigate through the Libraries, view items in the libraries, and play MP4s.

There were a few issues with images that I was able to get it to fix with another prompt. It was also loading all the items in the music video library at once. So I asked it to add pagination to that view. Again, in just a few minutes it made the change and had most of the functionality my prototype had.

Unfortunately that was when I received a warning that I was about to run our of usage for the month. So, in a 10-15 minutes I was able to duplicate most of what I had done by hand in 4-5 hours. Kind of amazing really.

All of this is easier to manage when you know what to ask for and how to describe what you want.

I'm constantly amazed by what these tools can and can't do. Some things that seem incredible are easy for them, while other things that we take for granted are very hard for these tools to get right. Hmm, so I wonder how much it would be to run my own models for development locally?

Stream Deck and Homekit update.

I mentioned previously using HomeBridge to help control a fan with the Elgato Stream Deck. It felt like overkill and I later found a much more stable solution for this that I'll talk about below. However, Robert Hawdon sent me an update to the original code I was using that saves the current state of the switch so that it can restore the state through a HomeBridge restart or crash.


const port = 3000;

const express = require('express');
const bodyParser = require('body-parser');
const stateFile = './state.json';
var fs = require('fs');
let app = express();
app.use(bodyParser.text({ type: 'text/*' }))

var switches = {};

function saveState() {

	var data = JSON.stringify(switches);

	fs.writeFile(stateFile, data, function (err) {
		if (err) {
			console.log('There has been an error saving the switch state.');
			console.log(err.message);
			return;
		}
		console.log('State saved successfully.')
	});
}

function loadState() {

	if (fs.existsSync(stateFile)) {

		var data = fs.readFileSync(stateFile);

		try {
			switches = JSON.parse(data);
		}
		catch (err) {
			console.log('There has been an error reading the switch state.');
			console.log(err);
		}

	}
}

function getSwitchValue(switchName, trueValue, falseValue) {

	let value = switches[switchName];

	if (trueValue === undefined) {

		trueValue = true;
	} 

	if (falseValue == undefined) {

		falseValue = false;
	}

	if (value === undefined || value == false) {

		return falseValue;

	} else {
		
		return trueValue;
	}
}

function setSwitchValue(switchName, valueAsBoolean) {

	// Don't care about current state of switch - or if it exists or not at this point.
	switches[switchName] = valueAsBoolean;
	saveState();
}

// ==================
// General purpose getter to show the state of all switches in the browser.

app.get('/', (request, response) => {
	
	// return current state of all known switches
	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify(switches, null, 4));	
});


// HomeBridge http switch end points.
app.get('/switches/:switchName/on', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchOn called for switch: ", switchName);
	setSwitchValue(switchName, true);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});

app.get('/switches/:switchName/off', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchOff called for switch: ", switchName);

	setSwitchValue(switchName, false);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});

app.get('/switches/:switchName/status', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchStatus called for switch: ", switchName);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});


// ==================
// Stream Deck MCControl endpoints.

app.get('/switches/:switchName', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("got status request for switch: ", switchName);

	let switchValue = getSwitchValue(switchName, "ON", "OFF");

	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify({ state: switchValue }));
});


app.post('/switches/:switchName', (request, response) => {
	
	let switchName = request.params.switchName;

	//console.log("switch set post called for switch: ", switchName);

	var data = request.body;
	console.log("Data: ", data);

	if (data == "ON") {

		switchState = true;

	} else if (data == "OFF") {

		switchState = false;
	}

	setSwitchValue(switchName, switchState);

	let switchValue = getSwitchValue(switchName, "ON", "OFF");
	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify({ state: switchValue }));
});



app.listen(port, (err) => {
	if (err) {
		return console.log('something bad happened', err)
	}

	loadState();
	console.log(`server is listening on ${port}`)
});


These days I'm not using HomeBridge at all. But, I still use the fan and turn it on and off with the StreamDeck. I do this with HomeControl for the Mac. This app lets you control all of your home kit devices and scenes with a menu on your mac. It also lets you use control devices with callback urls via the command line. You can create a script and execute that script with a button on the StreamDeck. However, It doesn't show the current state of the devine on the StreamDeck button with this technique.


#!/bin/bash

open -g "homecontrol://x-callback-url/run-action?action-type=switch-device-status&item-type=device&item-name=Fan&home-name=Home&activation-mode=toggle&authentication-token=1234"

You need to get the url to use here with the HomeControl app. There is an auth token that will be unique to your computer. I hope this update helps anyone trying to do something simmilar.

Hello Computer

Lets use the Stream Deck to control a HomeKit device.

I get warm in the computer room occasionaly, so I have a small fan I turn on and off frequently. I also like to wear big wired headphones. I never take my headphones off when I reach for the fan. So I'm constantly reaching the end of my headphone cord. Well, I just got this Elgato Stream Deck. And, I have some smart outlets that I can control with HomeKit. But, The Stream Deck does not have a plugin for HomeKit, at least not that I found.

The smart plug I'm using is the koogeek Smart Plug It's a pain to setup with homekit, I have one that I can not get to work. :) But, I have the fan connected to one and I can control it with homekit.

Now how do I connect it to the Stream Deck?

I have HomeBridge running on a server. And I have added it to HomeKit. In HomeBridge I added the Homebridge Http Switch plugin. This plugin has 3 rest inputs that it can call for turning the light on, off, and checking it's status. It expected 1 and 0 for the status call. No idea why it's not expecting json and boolean, but so be it. I'll wright my own plugin later.

The Stream Deck has an action called MCControl this can connect to a rest endpoint as well. But it has some quirks as well. It does do a post for setting, but instead of json it sends plain/text. I'll end up writing an action that works better here, but again this works, if you have an endpoint that can handle the quirky behavior. Maybe this is based on some standard I'm not aware of but, its not how I would have done it.

In between these I have a small Node JS app. It keeps track of switch state, and provides apis for the Stream Deck action and the HomeBridge plugin to talk to.

const port = 3000;

const express = require('express');
const bodyParser = require('body-parser');
let app = express();
app.use(bodyParser.text({ type: 'text/*' }))

var switches = {};


function getSwitchValue(switchName, trueValue, falseValue) {

	let value = switches[switchName];

	if (trueValue === undefined) {

		trueValue = true;
	} 

	if (falseValue == undefined) {

		falseValue = false;
	}

	if (value === undefined || value == false) {

		return falseValue;

	} else {
		
		return trueValue;
	}
}

function setSwitchValue(switchName, valueAsBoolean) {

	// Don't care about current state of switch - or if it exists or not at this point.
	switches[switchName] = valueAsBoolean;
}

// ==================
// General purpose getter to show the state of all switches in the browser.

app.get('/', (request, response) => {
	
	// return current state of all known switches
	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify(switches, null, 4));	
});


// HomeBridge http switch end points.
app.get('/switches/:switchName/on', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchOn called for switch: ", switchName);
	setSwitchValue(switchName, true);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});

app.get('/switches/:switchName/off', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchOff called for switch: ", switchName);

	setSwitchValue(switchName, false);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});

app.get('/switches/:switchName/status', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchStatus called for switch: ", switchName);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});


// ==================
// Stream Deck MCControl endpoints.

app.get('/switches/:switchName', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("got status request for switch: ", switchName);

	let switchValue = getSwitchValue(switchName, "ON", "OFF");

	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify({ state: switchValue }));
});


app.post('/switches/:switchName', (request, response) => {
	
	let switchName = request.params.switchName;

	//console.log("switch set post called for switch: ", switchName);

	var data = request.body;
	console.log("Data: ", data);

	if (data == "ON") {

		switchState = true;

	} else if (data == "OFF") {

		switchState = false;
	}

	setSwitchValue(switchName, switchState);

	let switchValue = getSwitchValue(switchName, "ON", "OFF");
	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify({ state: switchValue }));
});



app.listen(port, (err) => {
	if (err) {
		return console.log('something bad happened', err)
	}

	console.log(`server is listening on ${port}`)
});

I run this little server on the same server where I have HomeBridge.

Now for the final piece. Automations in HomeKit. I have a couple of automations in HomeKit that do the following:

  • When the Switch turns on, turn the fan on.
  • When the Switch turns off, turn the fan off.

There is a bit of a delay when I use the Stream Deck due, to a configured polling interval, but it works like a charm. And, if I use HomeKit to control the fan directly. It updates via HomeBridge, and the Stream Deck switch reflects the correct state.

Nice and Simple...

It's just that simple

Fun to Learn

I haven't been keeping up with the latest changes to Swift and the iOS. This has been keeping me from updating existing apps on the store. I have also wanted to update and bring some of my old apps back. I have a few ideas for new apps that I would like to create as well. So, I have been spending a bunch of time working on improving my iOS development skills, and getting up to speed on the current state of iOS development.

Wow

Wow, things have come along way. I was never a big fan of Objective C. It has some great features that really helped, but it always felt old and cumbersome to me. Swift seemed like the future right from the beginning. It feels like a modern language to me. The new versions of Swift have improved and evolved the language in wonderful ways. Perhaps more importantly, I enjoy writing code in Swift.

I have been using a Paul Hudson's Hacking With Swift site to get back up to speed. I think his teaching style and pragmatic approach is very compatible with my own coding preferences. He has a large number of guided videos on YouTube that are great when combined with the content on his site and in his books. He also does an amazing job at updating everything for the latest language updates.

DispatchQueue.global().async {
	do {
		let data = try Data(contentsOf: url)
		let downloadedData = try self.decode(type, from: data)
		DispatchQueue.main.async {
			completion(downloadedData)
		}
	} catch {
		print(error.localizedDescription)
	}
}

I have headed down this path before. What makes it different this time?

Honestly I don't know, but it feels different. I feel more committed to getting something new released this time. But, only time will tell. Will I stick with it, and dedicate my free time to it? I intend too, and I hope I do. There is nothing like the feeling of setting a difficult goal for your self and reaching it.

Get to Work!

iOS 11 Beta 1 and the iPad Pro

I took the unrecommended step of installing the iOS 11 beta on my iPad Pro last night.  I normally wait till the 3rd or 4th beta before taking a look at whats new. This time things are different.  I could not wait to give the dramatic changes to the OS a try.

A small disclaimer first, do not try this at home, i'm what you call a professional. This is an early beta and I have already experienced a few unexplainable crashes. It was entirely likely that my iPad could have turned into an unresponsive block of aluminum. There were multiple reports of developers being unable to use their devices after the install.

DO NOT INSTALL THIS ON YOUR MAIN DEVICE!

Here are some of my initial impressions.

Files

The iCloud Drive app is now a part of a new application called Files. This is a major change by Apple that will vastly improve productivity on iOS.  It's still an early beta though and there are some issues with knowing where a file you save is going.  I did not see a way to control where saved files go first? I also do not understand how the new "On my iPad" section works. Hopefully this will become clear as new functionality is completed and bugs are fixed.

The Dock

Big changes to the dock, functionality was intuitive. Seems to work very well without any apparent bugs.  One of those changes that seems obvious after you see it.  I could see some variation of this coming to the iPhone in the future.

Multitasking Improvements

This works in conjunction with the new Dock, but it took me a bit to figure out how to use the new multitasking gestures. I ended up having to Google it. Overall I think these changes make it easier to use multitasking on the iPad. However, I think there may be some ways to make things easier to discover, maybe a tutorial video that you can watch when you first update?

Drag and Drop

This is another tent-pole feature that should have been here from day one. This will help to increase the number of people who are able to use an iPad as their primary computing device. Works like you would expect, drag selected text, images, etc... The applications have to support it, but I'm sure every app will as soon as possible.

Apple Pencil

I don't have the new iPad Pro yet, so I can't see how the new refresh rates impact drawing and writing responsiveness yet.  The new markup features in the Notes app and in multiple other locations are big improvement. I'm sure these changes will make the Apple Pencil a required accessory for anyone trying get work done on the iPad. It's not just for people that want to draw or take a few hand written notes.

Being able to search for hand written notes is a game changer for me, and may drive me back to the notes app from Notability.  I hope they are able to take advantage of some of these features.

I can't wait to explore the other changes in iOS 11. I was already using my iPad daily. These changes make it an even more important tool for me.  Now, if they can figure out how I can to desktop style development tasks on an iPad without feeling like I have one hand tied behind my back...

I'm starting to agree

I have been doing more JavaScript programming over the last few years that I would ever have imagined.  Node JS and React have really changed the way I think about programming.  I can honestly say I would prefer to create a web application with NodeJS, SailsJS, EJS, and React over Java, Spring, and JSP.   Know there are other options and variations on these. But, the JavaScript based stacks is just more enjoyable, and more importantly quicker for me.   Realizing and acclimating to this new reality is an on going process.

I was just sent these two articles today. I should have already been aware of.  If you haven't read them yet, give them some attention, there are some interesting ideas to mull over.

The Two Pillars of JavaScript - PT 1,  and PT2.

NodeJS, SailsJS, ReactJS, EJS - JS all the way down

Started a new GitHub project with a generic random name.

Say hello to creepy-squeegee.

There really isn't much to see there yet, and what is there will be changing dramatically.  I needed a place to put some training material and thought it would be good to share it.

The purpose of this app is to consolidate several code samples related to Client and Server web technologies.  Focusing mainly on Node.js, Sails.js, and React.

Feedback welcome.

Angular Js and the New World Order

JavaScript is Dead, Long Live JavaScript.

Over the last couple of months my day job has been undergoing a transition. We decided to change our thinking about Web Applications. We realized just how far behind we were. Web Applications have changed. The wild west is being tamed. the structured MVC world we are used to in the back in, has moved into the browser. Instead of single pages being rendered after each request to the server, we have full featured stateful applications running in the browser. Meanwhile the servers, where much of this logic and state used to exist, have now become reusable restful data providers.  The whole stack has shifted.

None of this is new of course. It's been going on for years, but I've only just poked my head out of the trenches and realized. In a broader sense it's the same transition that has been going on for years. So if like me you had not been paying attention Start Here:

Update

Well so much for Angular. I know many people are still using it. I personaly have become very into using React for small reusuable components. But, Angular was not at all popular in my company, very few people liked working with it. It was also very difficult for us to admit that there was any real benefit to using it for the projects that we had at the time.

"Java"
2013/05/17 12:56 PM

Java

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class WordsToLiveBy {

	public static void main(String[] args) {
	
		String value = "2UEnLAFR7aICwCQ8q6LfE2b7EGNZLExRtOb7FbI6R-0let8dqHnTB-R" +
			"dIwM82gKaEwBzOhvyI2e7oqGWTTwbx2Krb54txM6juMH5FNpwNxbKCM" +
			"scaqf3N2G5kglXPMhzONnaWfp0Wxf5Kk7NZF0yAcLiPPkK3CTSbUDAk" +
			"PLVa7kbZGjbTG1cjM0-1rvEB8mvwbPEH-GDcLg8uU7v-EzBAblRMovk" +
			"_olXAklRo4AKNuikee7MPldNQf3zmWL2WsIyDOmgMc9LWKR1cq9rAyB" +
			"744xfsxFLB1FYueyhTBKeezynxuLuYd5UUovXSUEbk8DL";
		
		try {
		
			SecretKey key = new SecretKeySpec("Rush2112FreeWill".getBytes(), "AES");
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "SunJCE");
			Base64 coder = new Base64(32, new byte[] {}, true);
			cipher.init(Cipher.DECRYPT_MODE, key);
			byte[] decrypted = cipher.doFinal(coder.decode(value.getBytes()));
			System.out.println(new String(decrypted));
		
		} catch (Throwable t) {
		
			t.printStackTrace();
		}
	}
}