Skip to content

Instantly share code, notes, and snippets.

@agzam
Created January 28, 2024 02:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agzam/ee0e4a1132940ff39123403147fed679 to your computer and use it in GitHub Desktop.
Save agzam/ee0e4a1132940ff39123403147fed679 to your computer and use it in GitHub Desktop.
Update Kinesis 360 ZMK helper
// OMG. This all so stupidly dirty and so needlessly tangled.
// I imagined this would be a tiny, simple thing, but it came out as this crap.
// Fuck Javascript, I should rewrite the whole thing in Clojurescript (but I'm lazy)
//
// This is a helper script to update Kinesis 360 firmware.
// When you put the keyboard in the bootloader mode it becomes unavailable, and you can't type
// but the mouse remains connected.
// This thing basically loads a tiny html page where you can click things to update and unblock your keyboard.
//
// Prerequisites:
// - needs `gh` cmd-line tool installed
// - working gpg, and sudoer password in ~/.authinfo.gpg - change it to your host and user, I'm too lazy to make that configurable.
//
// When you run the script:
// 1. It can grab the latest build from the repository (change down below to point at yours)
// 2. Then you put the board into the bootloader mode - Mod+1 for left and Mod+3 for right side, or use the hidden push button
// 3. Wait for a few seconds
// 4. Mount the drive (click the link on the page)
// 5. Upload the file
// 6. Repeat for the other half
// 7. Clean up
const { exec } = require("child_process");
const http = require("http");
const url = require("url");
const fs = require("fs");
function errorCheck(err) {
if (err) {
console.log(`error: ${err.message}`);
process.exit(1);
}
}
function fetchArtifact(cb) {
exec(
"gh api repos/agzam/Adv360-Pro-ZMK/actions/artifacts",
(err, stdout, stderr) => {
if (err) {
console.log(`error: ${err.message}`);
return;
}
try {
const result = JSON.parse(stdout);
return cb(result.artifacts[0]);
} catch (err) {
errorCheck(err);
}
},
);
}
function getPaths(dir) {
let files = fs
.readdirSync(dir)
.filter((f) => f !== "firmware.zip")
.map((f) => `${dir}/${f}`);
let left = files.filter((f) => f.includes("-left.uf2"))[0];
let right = files.filter((f) => f.includes("-right.uf2"))[0];
return {
left,
right,
};
}
function home(req, res) {
fetchArtifact((artifact) => {
res.writeHead(200, { "Content-Type": "text/html" });
const queryObj = url.parse(req.url, true).query;
const dir = decodeURIComponent(queryObj.download_directory);
let isDir = dir !== undefined && dir !== "" && dir !== "undefined";
let pointer = isDir ? "pointer" : "none";
let files = isDir ? getPaths(dir) : {};
res.end(
`
<h2>Update Advantage 360 Firmware<h2/>
<a href="download?url=${artifact.archive_download_url}">
Download Firmware files for: ${artifact.updated_at}
<a/>
<br>
<a href="mount_bootloader?download_directory=${dir}"
style="pointer-events: ${pointer}">
Mount Bootloader Drive
<a/>
<br>
<a href="/update?file=${encodeURIComponent(files.left)}&download_directory=${dir}"
style="pointer-events: ${pointer}"
>Upload Left Half ${files.left}
</a>
<br>
<a href="/update?file=${encodeURIComponent(files.right)}&download_directory=${dir}"
style="pointer-events: ${pointer}"
>Upload Right Half ${files.right}
</a>
<br>
<a href="cleanup?download_directory=${dir}"> Cleanup <a/>`,
);
});
}
function download(req, res) {
let queryObj = url.parse(req.url, true).query;
let downloadUrl = queryObj.url;
exec(
`tmp_dir=$(mktemp -d -t ci-XXXXXXXXXX) && \
gh api -X GET ${downloadUrl} > $tmp_dir/firmware.zip
unzip $tmp_dir/firmware.zip -d $tmp_dir && echo $tmp_dir`,
(err, stdout, stderr) => {
errorCheck(err);
let dir = encodeURIComponent(
stdout
.split("\n")
.filter((x) => x !== "")
.slice(-1),
);
res.writeHead(302, { Location: `/?download_directory=${dir}` });
res.end();
},
);
}
function mountBootloader(req, res) {
console.log("mounting the bootloader drive");
const queryObj = url.parse(req.url, true).query;
const dir = decodeURIComponent(queryObj.download_directory);
exec(
`
mkdir -p $HOME/media/ADVKBD &&
pass=$(gpg2 -qd ~/.authinfo.gpg 2> /dev/null | grep 'machine arch-machina login ag' | awk '{print $NF}') &&
echo $pass | sudo -S mount /dev/sdd $HOME/media/ADVKBD/
`,
(err, stdout, stderr) => {
errorCheck(err);
console.log(stdout);
console.log("drive mounted");
},
);
res.writeHead(302, { Location: `/?download_directory=${dir}` });
res.end();
}
function update(req, res) {
const queryObj = url.parse(req.url, true).query;
const file = decodeURIComponent(queryObj.file);
const dir = queryObj.download_directory;
exec(`
pass=$(gpg2 -qd ~/.authinfo.gpg 2> /dev/null | grep 'machine arch-machina login ag' | awk '{print $NF}') &&
echo $pass | sudo -S cp ${file} $HOME/media/ADVKBD/
`, (err, stdout, stderr) => {
errorCheck(err);
console.log(stdout);
});
res.writeHead(302, { Location: `/?download_directory=${dir}` });
res.end();
}
function cleanup(req, res) {
const queryObj = url.parse(req.url, true).query;
const dir = decodeURIComponent(queryObj.download_directory);
exec(`
rm -rf ${dir} &&
pass=$(gpg2 -qd ~/.authinfo.gpg 2> /dev/null | grep 'machine arch-machina login ag' | awk '{print $NF}') &&
echo $pass | sudo -S umount -R $HOME/media/ADVKBD &&
rm -rf $HOME/media/ADVKBD/
`, (err, stdout, stderr) => {
errorCheck(err);
console.log(stdout);
});
res.writeHead(302, { Location: `/` });
res.end();
}
const routes = {
"/": home,
"/download": download,
"/mount_bootloader": mountBootloader,
"/update": update,
"/cleanup": cleanup,
};
const server = http
.createServer((req, res) => {
let routeFn = routes[url.parse(req.url).pathname];
if (routeFn) {
routeFn(req, res);
}
})
.listen(3000, () => exec("xdg-open http://localhost:3000"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment