<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Twopercent Blog</title><link href="https://blog.twopercent.xyz/" rel="alternate"/><link href="https://blog.twopercent.xyz/feeds/all.atom.xml" rel="self"/><id>https://blog.twopercent.xyz/</id><updated>2026-06-17T00:00:00-05:00</updated><subtitle>“Fear is the path to the dark side. Fear leads to anger. Anger leads to hate. Hate leads to suffering.” -Yoda</subtitle><entry><title>DJ on a Bike</title><link href="https://blog.twopercent.xyz/dj-bike.html" rel="alternate"/><published>2025-06-07T00:00:00-05:00</published><updated>2025-06-07T00:00:00-05:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2025-06-07:/dj-bike.html</id><summary type="html">&lt;h2&gt;Music on the Move&lt;/h2&gt;
&lt;h3&gt;Backgrond&lt;/h3&gt;
&lt;p&gt;Inspired by this &lt;a href="https://www.youtube.com/watch?v=UPPuy-vna5o"&gt;video&lt;/a&gt; of a person playing the piano on the back of a cargo bike, I have wanted to add music to our Spicy Curry since we bought it. No one in my family plays piano and even a novice DJ can play …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Music on the Move&lt;/h2&gt;
&lt;h3&gt;Backgrond&lt;/h3&gt;
&lt;p&gt;Inspired by this &lt;a href="https://www.youtube.com/watch?v=UPPuy-vna5o"&gt;video&lt;/a&gt; of a person playing the piano on the back of a cargo bike, I have wanted to add music to our Spicy Curry since we bought it. No one in my family plays piano and even a novice DJ can play coherent music so I decided on a DJ setup.&lt;/p&gt;
&lt;h2&gt;Parts&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Raspberry Pi 5&lt;/li&gt;
&lt;li&gt;10" touchscreen display (touchscreen isn't &lt;em&gt;necessary&lt;/em&gt; but it helps with adjusting mixxx settings)&lt;/li&gt;
&lt;li&gt;DJ Controller&lt;/li&gt;
&lt;li&gt;Bike with passenger seating&lt;/li&gt;
&lt;li&gt;Batter pack&lt;/li&gt;
&lt;li&gt;Portable speaker&lt;/li&gt;
&lt;li&gt;Cables&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;DJ Software&lt;/h2&gt;
&lt;p&gt;I used fayaaz's &lt;a href="https://fayaaz.github.io/mixxx-pi-gen/"&gt;mixx-pi-gen fork&lt;/a&gt; because it handled a lot of the installation and configuration of &lt;a href="https://mixxx.org/"&gt;mixxx&lt;/a&gt;. The controller I am using, a Pioneer DDJ-SB3, had a &lt;a href="https://manual.mixxx.org/2.5/en/hardware/controllers/pioneer_ddj_sb3"&gt;mapping&lt;/a&gt; already built-in to mixxx so no additional configuration was needed. The DDJ-SB3 also has a build in audio card so I connected it to the portalble speaker with a 2-channel RCA &amp;lt;-&amp;gt; 3.5mm cable. The controller is connected (and gets its power) from the RPI with a USB Type-A &amp;lt;-&amp;gt; USB Type-B cable. I suspect that connecting the RPI to the speaker with bluetooth would insert so much lag that mixxing would be near impossible.&lt;/p&gt;
&lt;h2&gt;Speaker&lt;/h2&gt;
&lt;p&gt;I found a ION Sport Go portable speaker for cheap. I replaced the battery and it is good to go. I looked for RCA inputs on the speaker as I thought they would be a little sturdier on bumpy paths. The the ION was the right price as well as weather resistant. Make sure the speaker fits in the front basket of your bike without hindering the handelbars. A few pool noodles on the bottom and sides of the bread basket (yuba's term for the front basket) offer a softer ride for the speaker.&lt;/p&gt;
&lt;h2&gt;Power&lt;/h2&gt;
&lt;p&gt;During initial tests the RPI would power cycle as soon as the controller was plugged in. My assumption was that the controller was drawing too much current for the 3 amp chunky I was using. I read that the RPI 5 can handle up to 5 amps at 5 volts so I ordered a semi-official 5.1 volt 5 amp chunky. Using this wall plug, it worked properly. There were no 5V5A power banks that I could find so we experimented with some USB-PD voltage selectors and a buck converter to request 9V from a USB-PD battery pack and convert to 5V with the buck converter. If we can draw 9V3A we should get close to 5V5A after conversion. This solution also didn't work.
We later disovered that power on the RPI 5 USB ports is limited to 600mA unless the official power supply is used. Here is a good &lt;a href="https://www.reddit.com/r/UsbCHardware/comments/16v1ub2/pi_5_5v5a/"&gt;reddit thread&lt;/a&gt; on the topic. You can add a line to your &lt;code&gt;/boot/config.txt&lt;/code&gt; file to enable up to 1.6 amps on the USB ports even with other power supply's&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;usb_max_current_enable=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I considered powering the 10" display from an alternate source but the mouse touches are handled throught same USB interface and the touchscreen is handy.
You can use a wide variety of portable batttery packs to power the RPI now. I found a reasonable priced 20,000 mAh Anker battery pack (A1383) on eBay that works well.  &lt;/p&gt;
&lt;h2&gt;Orientation&lt;/h2&gt;
&lt;p&gt;The hdmi and usb connectors on the 10" display are on the bottom left side of the screen. Construction of the screen holder proved easier of the display was upside down and the ports were at the top left. Many resources suggest adding &lt;code&gt;lcd_rotate=2&lt;/code&gt; to &lt;code&gt;/boot/config.txt&lt;/code&gt; but this only works with official RPI display. Another suggested route is within the display settings of the Raspberry PI OS GUI. Since &lt;code&gt;mixxx-pi-gen&lt;/code&gt; uses a stripped down OS and the sway window manager, there is no GUI configuration.
After figuring out the name of my display, I added the following to &lt;code&gt;~/.config/sway/config&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HDMI&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;
&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;touch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calibration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-1.0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;0.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;1.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;0.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-1.0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first line rotates the display and the second transforms the mouse clicks to match the rotated display.&lt;/p&gt;
&lt;h2&gt;Construction&lt;/h2&gt;
&lt;p&gt;Brother Joe helped with the design and building of the wood table to hold the controller and batter pack. Some small wood segments hold the controller centered. A velcro strap holds the battery in place. Two 2x4's with a slot routered in them hold the touchscreen. The RPI is attached to the back of the touchscreen.&lt;/p&gt;
&lt;p&gt;&lt;img alt="man operating a woodworking router on a two by four" src="images/djbike_1.jpg"&gt;
&lt;img alt="wood board with velcro strap and wood guide pieces" src="images/djbike_4.jpg"&gt;&lt;/p&gt;
&lt;p&gt;We raised the monkey bars to accomodate a rider with longer legs. We added a small section (~17 cm) of 2x4 in between the monkey bar and the bike frame. A longer 5mm x 60mm screw is needed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="dj controller, raspberry pi, touchscreen display on a wood tray on the back of a cargo bicycle" src="images/djbike_3.jpg"&gt;
&lt;img alt="dj controller, touchscreen display on a wood tray on the back of a cargo bicycle" src="images/djbike_2.jpg"&gt;&lt;/p&gt;
&lt;p&gt;The first ride was challending because we hadn't figured out the screen rotation yet so the display was upside down. But we still had fun!&lt;/p&gt;
&lt;p&gt;&lt;img alt="selfy of two men on a cargo bike, wearing reflective vests. The passenger is operating a dj controller" src="images/djbike_5.jpg"&gt;&lt;/p&gt;</content><category term="posts"/><category term="cycling"/><category term="dj"/><category term="rpi"/><category term="hardware"/></entry><entry><title>RAID 1 with LVM</title><link href="https://blog.twopercent.xyz/lvm-raid.html" rel="alternate"/><published>2024-12-18T00:00:00-06:00</published><updated>2024-12-18T00:00:00-06:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2024-12-18:/lvm-raid.html</id><summary type="html">&lt;h2&gt;Convert an existing linear logical volume to a raid1 or mirror&lt;/h2&gt;
&lt;h3&gt;Backgrond&lt;/h3&gt;
&lt;p&gt;I have a server running on a single disk. I now find myself relying on it more and want redundant storage.
The server installer created a single volume group on a single physical volume.&lt;/p&gt;
&lt;p&gt;I have added a …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Convert an existing linear logical volume to a raid1 or mirror&lt;/h2&gt;
&lt;h3&gt;Backgrond&lt;/h3&gt;
&lt;p&gt;I have a server running on a single disk. I now find myself relying on it more and want redundant storage.
The server installer created a single volume group on a single physical volume.&lt;/p&gt;
&lt;p&gt;I have added a second disk to the system. I will duplicate the paritioning of the original drive, create a physical volume on it, and convert the existing volume group into a raid1 or mirror across both physical volumes.&lt;/p&gt;
&lt;h2&gt;Tools&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt install lvm2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Examine disks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;lsblk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Examine physical volumes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pvscan
pvs
pvdisplay
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Examine volume groups:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vgscan
vgs
vgdisplay
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Examine logical volumes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;lvscan
lvs
lvdisplay
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Prepare the new disk&lt;/h3&gt;
&lt;p&gt;The new disk is identical in size to the original. Use &lt;code&gt;sfdisk&lt;/code&gt; to dump the partition table from the original disk and apply it to the new one. THIS WILL DESTROY ALL THE DATA ON /dev/sdb.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo sfdisk --dump /dev/sda &amp;gt; part_table
sudo sfdisk /dev/sdb &amp;lt; part_table
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I have two additional partitions (EFI and boot) that I will attempt to manually sync at a later time. For now, create a new lvm physical volume on the new disk.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo pvcreate /dev/sdb3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And add it to the existing volume group.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo vgextend &amp;lt;vg_name&amp;gt; /dev/sdb3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;lvconvert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;raid1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;mirrors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;vg_name&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;lv_name&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="posts"/><category term="raid"/><category term="lvm"/></entry><entry><title>Raspberry Jira</title><link href="https://blog.twopercent.xyz/raspberry-jira.html" rel="alternate"/><published>2024-02-09T00:00:00-06:00</published><updated>2024-02-09T00:00:00-06:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2024-02-09:/raspberry-jira.html</id><summary type="html">&lt;h2&gt;Raspberry Pi with an e-ink display to show the status of a Jira board&lt;/h2&gt;
&lt;h3&gt;Parts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raspberrypi.com/products/raspberry-pi-3-model-b-plus/"&gt;Raspberry Pi 3 Model B+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.waveshare.com/product/displays/e-paper/epaper-2/2.7inch-e-paper-hat.htm"&gt;Waveshare 2.7inch e-Paper HAT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;I used &lt;a href="https://www.raspberrypi.com/software/"&gt;rpi-imager&lt;/a&gt; to flash Raspberry Pi OS Lite (64-bit) onto a 4GB SD card.
Since we don't require a desktop, we can use …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Raspberry Pi with an e-ink display to show the status of a Jira board&lt;/h2&gt;
&lt;h3&gt;Parts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raspberrypi.com/products/raspberry-pi-3-model-b-plus/"&gt;Raspberry Pi 3 Model B+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.waveshare.com/product/displays/e-paper/epaper-2/2.7inch-e-paper-hat.htm"&gt;Waveshare 2.7inch e-Paper HAT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;I used &lt;a href="https://www.raspberrypi.com/software/"&gt;rpi-imager&lt;/a&gt; to flash Raspberry Pi OS Lite (64-bit) onto a 4GB SD card.
Since we don't require a desktop, we can use the smaller "Lite" image which will fit onto a 4GB card.&lt;/p&gt;
&lt;p&gt;Configure a user name and password on first boot. Then set localisation and network settings with &lt;code&gt;sudo raspi-config&lt;/code&gt; You can get the latest packages with &lt;code&gt;raspi-config&lt;/code&gt; as well&lt;/p&gt;
&lt;p&gt;Follow the &lt;a href="https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT_Manual#Enable_SPI_Interface"&gt;Waveshare wiki&lt;/a&gt; to enable the SPI interface:&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;raspi-config&lt;/code&gt; tool. SPI is enabled in &lt;code&gt;Interfacing Options&lt;/code&gt; -&amp;gt; &lt;code&gt;SPI&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Skip the &lt;code&gt;C&lt;/code&gt; section in the wiki and proceed with the &lt;code&gt;Python&lt;/code&gt; installation.
Skip python2 packages (we'll use python3) as well as &lt;code&gt;pip&lt;/code&gt;. 
All of the python requirements can be installed with the Debian package manager &lt;code&gt;apt&lt;/code&gt;.
In Debian Bookworm and later, the python3 interpreter is marked &lt;code&gt;externally-managed&lt;/code&gt;.
This means &lt;code&gt;pip&lt;/code&gt; will not work unless extra steps are taken.
Read more about it &lt;a href="https://www.debian.org/releases/bookworm/amd64/release-notes/ch-information.en.html#python3-pep-668"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt update
sudo apt install git python3-pil python3-numpy python3-rpi.gpio python3-spidev python3-dotenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Review the demo code from Waveshare or &lt;/p&gt;</content><category term="posts"/><category term="rpi"/><category term="hardware"/></entry><entry><title>Pandoc 3 breaks Pandoc Resume</title><link href="https://blog.twopercent.xyz/pandoc3-braeks-pandoc-resume.html" rel="alternate"/><published>2023-12-26T00:00:00-06:00</published><updated>2026-06-17T00:00:00-05:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2023-12-26:/pandoc3-braeks-pandoc-resume.html</id><summary type="html">&lt;p&gt;I've been using Mark Szepieniec's &lt;a href="http://mszep.github.io/pandoc_resume/"&gt;Markdown Resume&lt;/a&gt; but recently noticed a degradation in the styling. &lt;/p&gt;
&lt;!---
It might look better to use HTML for the columns, I didn't try it
--&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="files/johnnycoder.pdf"&gt;Original&lt;/a&gt; version of the resume:&lt;/th&gt;
&lt;th&gt;&lt;a href="files/johnnycoder_broken.pdf"&gt;Current&lt;/a&gt; version of the resume:&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;object data="files/johnnycoder.pdf" type="application/pdf" height=300px&gt;&lt;/object&gt;&lt;/td&gt;
&lt;td&gt;&lt;object data="files/johnnycoder_broken.pdf" type="application/pdf" height=300px&gt;&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The tool relies on pandoc and context&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pandoc --version&lt;/code&gt; and &lt;code&gt;context --version&lt;/code&gt; reveal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pandoc 3.0.1
mtx-context     | ConTeXt Process Management 1.04
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pandoc was …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've been using Mark Szepieniec's &lt;a href="http://mszep.github.io/pandoc_resume/"&gt;Markdown Resume&lt;/a&gt; but recently noticed a degradation in the styling. &lt;/p&gt;
&lt;!---
It might look better to use HTML for the columns, I didn't try it
--&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="files/johnnycoder.pdf"&gt;Original&lt;/a&gt; version of the resume:&lt;/th&gt;
&lt;th&gt;&lt;a href="files/johnnycoder_broken.pdf"&gt;Current&lt;/a&gt; version of the resume:&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;object data="files/johnnycoder.pdf" type="application/pdf" height=300px&gt;&lt;/object&gt;&lt;/td&gt;
&lt;td&gt;&lt;object data="files/johnnycoder_broken.pdf" type="application/pdf" height=300px&gt;&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The tool relies on pandoc and context&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pandoc --version&lt;/code&gt; and &lt;code&gt;context --version&lt;/code&gt; reveal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pandoc 3.0.1
mtx-context     | ConTeXt Process Management 1.04
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pandoc was just updated to major release 3 in &lt;a href="https://packages.debian.org/search?keywords=pandoc&amp;amp;searchon=names&amp;amp;exact=1&amp;amp;suite=all&amp;amp;section=all"&gt;Debian Trixie&lt;/a&gt; (testing) and could be causing the issue.&lt;/p&gt;
&lt;p&gt;This &lt;a href="https://github.com/mszep/pandoc_resume/issues/94"&gt;issue&lt;/a&gt; in the &lt;a href="https://github.com/mszep/pandoc_resume"&gt;pandoc_resume&lt;/a&gt; github repo also suggests updated pandoc is the cause.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;sudo apt-cache policy pandoc&lt;/code&gt; to see if a previous version is still available. &lt;/p&gt;
&lt;p&gt;None were available so I added (uncommented) the stable release in my /etc/apt/sources.list file.&lt;/p&gt;
&lt;p&gt;Then &lt;code&gt;sudo apt update&lt;/code&gt;, check the policy again and I can see pandoc &lt;code&gt;2.17.1.1-2~deb12u1&lt;/code&gt; is available.&lt;/p&gt;
&lt;p&gt;Remove pandoc 3 as well as the pandoc-data depencancy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt remove pandoc
sudo apt autoremove
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally install pandoc 2:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt install pandoc=2.17.1.1-2~deb12u1 pandoc-data=2.17.1.1-2~deb12u1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hold the pandoc 2 package:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-mark hold pandoc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, use the included docker-compose.yml file to build your resume with a docker container that contains a pandoc 2&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Update&lt;/h3&gt;
&lt;p&gt;I asked claude how to fix the regression introduced with pandoc 3. There is a one line fix to add &lt;code&gt;--top-level-division=section \&lt;/code&gt; after line 14 in the Makefile.&lt;/p&gt;
&lt;p&gt;I added claude's full response to the github issue linked above.&lt;/p&gt;</content><category term="posts"/><category term="debian"/><category term="apt"/></entry><entry><title>TimeFlip2 and Harvest</title><link href="https://blog.twopercent.xyz/timeflip2-and-harvest.html" rel="alternate"/><published>2023-01-26T00:00:00-06:00</published><updated>2023-01-26T00:00:00-06:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2023-01-26:/timeflip2-and-harvest.html</id><content type="html">&lt;p&gt;We use &lt;a href="htps://getharvest.com"&gt;Harvest&lt;/a&gt; timetracking at work.
I have a &lt;a href="https://timeflip.io"&gt;TimeFlip 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It would really be great if I could use my timeflip with harvest.&lt;/p&gt;</content><category term="Ideas"/><category term="programming"/></entry><entry><title>Lodestar on Ubuntu</title><link href="https://blog.twopercent.xyz/lodestar-on-ubuntu-pt0.html" rel="alternate"/><published>2022-02-10T00:00:00-06:00</published><updated>2022-02-11T00:00:00-06:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2022-02-10:/lodestar-on-ubuntu-pt0.html</id><summary type="html">&lt;p&gt;In order to create a robust decentralized blockchain, Ethereum 2 will require a diverse set of clients. Prysm and Lighthouse are currently dominating. I will install &lt;code&gt;Lodestar&lt;/code&gt;, a Eth2 implementation written in Typescript maintained by ChainSafe Systems via npm  &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://chainsafe.github.io/lodestar/"&gt;Lodestar Documentation&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Install Ubuntu&lt;/h3&gt;
&lt;p&gt;Install the latest Ubuntu LTS release (currently …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In order to create a robust decentralized blockchain, Ethereum 2 will require a diverse set of clients. Prysm and Lighthouse are currently dominating. I will install &lt;code&gt;Lodestar&lt;/code&gt;, a Eth2 implementation written in Typescript maintained by ChainSafe Systems via npm  &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://chainsafe.github.io/lodestar/"&gt;Lodestar Documentation&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Install Ubuntu&lt;/h3&gt;
&lt;p&gt;Install the latest Ubuntu LTS release (currently &lt;a href="https://ubuntu.com/download/server"&gt;Ubuntu 20.04.3 LTS
Focal Fossa&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Ubuntu desktop and server are the same but offer different default packages. I started with server because we don't need a desktop environment, office suite, web browser...&lt;/p&gt;
&lt;p&gt;We will need &lt;code&gt;make&lt;/code&gt;. This is achieved with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo apt install build-essential&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Lodestar requirements&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;nodejs&lt;/code&gt; package in Ubuntu repositories is crusty. Install nvm (node version manager) Check their &lt;a href="https://github.com/nvm-sh/nvm#installing-and-updating"&gt;Github page&lt;/a&gt; for the latest version of the install script. Update your environment variables after running the script.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;source .bashrc&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now install node Long Term Support and yarn&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nvm install --lts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nvm use --lts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install -g yarn&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Install lodestar&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;npm install -g @chainsafe/lodestar-cli&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Run&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;lodestar --help&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you get a &lt;code&gt;-bash: /home/USER/.nvm/versions/node/v16.14.0/bin/lodestar: Permission denied&lt;/code&gt; error, run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;chmod +x ~/.nvm/versions/node/v16.14.0/lib/node_modules/@chainsafe/lodestar-cli/lib/index.js&lt;/code&gt;&lt;/p&gt;</content><category term="crypto"/><category term="crypto"/><category term="Typescript"/></entry><entry><title>Ansible for Windows</title><link href="https://blog.twopercent.xyz/ansible-for-windows.html" rel="alternate"/><published>2016-05-31T16:24:00-05:00</published><updated>2016-05-31T16:24:00-05:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2016-05-31:/ansible-for-windows.html</id><summary type="html">&lt;p class="first last"&gt;Install Anbiel on CentOS 7 and ping some Windows hosts&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A co-worker of mine is using &lt;a class="reference external" href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; to manage some windows hosts.
I set up my own environment to do some testing for him.
Recently released Ansible 2.1 has some new features for Windows management so I installed this from epel-testing.
sshpass is also required.&lt;/p&gt;
&lt;div class="section" id="installation"&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;On CentOS 7:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;yum&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;--enablerepo&lt;span class="w"&gt; &lt;/span&gt;epel&lt;span class="w"&gt; &lt;/span&gt;--enablerepo&lt;span class="w"&gt; &lt;/span&gt;epel-testing&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ansible&lt;span class="w"&gt; &lt;/span&gt;sshpass
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You will also need pywinrm as well:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pywinrm
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="configure-windows-clients"&gt;
&lt;h2&gt;Configure Windows Clients&lt;/h2&gt;
&lt;p&gt;I followed the &lt;a class="reference external" href="http://docs.ansible.com/ansible/intro_windows.html#windows-system-prep/"&gt;Windows System Prep&lt;/a&gt; guide on &lt;a class="reference external" href="http://docs.ansible.com/"&gt;docs.ansible.com&lt;/a&gt;.
The first suggestion is to run a powershell script on each Windows host.
I have no reason to believe Ansible would be mailicious but I don't like the idea of running this script without needing to.
Getting started, we only need to verify that powershell 3.0.0 or later is installed and that the WinRM service is installed and running.
Open powershell and run the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;$PSVersionTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PSVersion&lt;/span&gt;
&lt;span class="nb"&gt;Get-Service&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;WinRM&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If powershell is too old or the WinRM service is not installed or not running, remedy these before moving on.&lt;/p&gt;
&lt;p&gt;Create a local administrator account for Ansible to use.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configure-ansible"&gt;
&lt;h2&gt;Configure Ansible&lt;/h2&gt;
&lt;p&gt;Change to /etc/ansible and modify the &amp;quot;hosts&amp;quot; file.
Declare a [windows] group and list one or more Windows hosts.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;windows&lt;span class="o"&gt;]&lt;/span&gt;
workPC.mydomain.com
homePC.mydonain.com
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create the /etc/ansible/group_vars directory and create a &amp;quot;windows.yml&amp;quot; file in it.
This will describe the local account to use, password, and connection settings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;ansible_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ansible&lt;/span&gt;
&lt;span class="nt"&gt;ansible_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;secret_password&lt;/span&gt;
&lt;span class="nt"&gt;ansible_port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;5985&lt;/span&gt;
&lt;span class="nt"&gt;ansible_connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;winrm&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Port 5985 is the default http port for WS-Management.
We can switch to 5986 after an SSL certificate is arranged and installed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-results"&gt;
&lt;h2&gt;The Results&lt;/h2&gt;
&lt;p&gt;That should be all we need.
On the control server issue the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ansible&lt;span class="w"&gt; &lt;/span&gt;windows&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;win_ping
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;workPC.mydomain.com&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SUCCESS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;changed&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;false,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ping&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pong&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
homePC.mydomain.com&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SUCCESS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;changed&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;false,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ping&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pong&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
</content><category term="blog"/><category term="ops"/><category term="CentOS"/><category term="Ansible"/></entry><entry><title>Metro Transit CLI Bus Locations Part 2</title><link href="https://blog.twopercent.xyz/metro-transit-cli-bus-locations-part-2.html" rel="alternate"/><published>2015-10-20T16:37:00-05:00</published><updated>2015-10-20T16:37:00-05:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2015-10-20:/metro-transit-cli-bus-locations-part-2.html</id><summary type="html">&lt;p class="first last"&gt;busloc.py displays a geojson.io web page with locations of vehicles on a specified route&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Continuing with the mtcli package, I added busloc.py which will display the locations of vehicles on a specified route.
There are two main functions, getLocations(busNum) and openMap(busLocations).&lt;/p&gt;
&lt;p&gt;First the user is prompted for a bus number.
A call is made to the metro transit API to return an array of arrays of information about all the vehicles currently active on that route.
Two of the attributes of interest are the latitude and longitude.
I build a GeoJSON MultiPoint array of the GPS coordinates of each vehicle.
More info on the GeoJSON format can be foud &lt;a class="reference external" href="http://geojson.org"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was recommended to a great resource in &lt;a class="reference external" href="http://geojson.io"&gt;GeoJSON.io&lt;/a&gt; to display the locations on map in a browser.
You can upload data in many forms to geojson.io or point it to an online datasets.
Since I wanted a fluid experience with my application I chose to encode the coordinate data into the URL used to request the site.
I used the urllib.parse.quote module to replace special characters in the coordinate data using %xx escape format.
Finally webbrowser.open_new_tab(url) is called to open geojson.org with the location data.&lt;/p&gt;
&lt;p&gt;Metro Transit allows you to specity &amp;quot;0&amp;quot; as a route number and it will return data on all vehicles currently in operation.&lt;/p&gt;
&lt;p&gt;I am thinking about ways to combine mtcli.py and busloc.py into a single application.&lt;/p&gt;
&lt;p&gt;All Metro Transit vehicles in operation at 16:00 on a Tuesday&lt;/p&gt;
&lt;img alt="bus location image" src="images/metro_transit.png" /&gt;
</content><category term="blog"/><category term="python"/><category term="dev"/><category term="maps"/></entry><entry><title>Metro Transit CLI Bus Locations</title><link href="https://blog.twopercent.xyz/metro-transit-cli-bus-locations.html" rel="alternate"/><published>2015-09-22T16:37:00-05:00</published><updated>2015-09-22T16:37:00-05:00</updated><author><name>Chris</name></author><id>tag:blog.twopercent.xyz,2015-09-22:/metro-transit-cli-bus-locations.html</id><summary type="html">&lt;p class="first last"&gt;Start of one of my early python projects to interact with the Metro Transit API&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="http://www.metrotransit.org"&gt;Metro Transit&lt;/a&gt; is the main transit operator in the twin cities.
They have a fine website but a little too much clicking is required to find out when the next bus is going to roll by.
I found the &lt;a class="reference external" href="http://svc.metrotransit.org/"&gt;Metro Transit Web Services&lt;/a&gt; page to be just what I needed.&lt;/p&gt;
&lt;p&gt;I wrote three functions to request the applicable directions of a specific route, the time stops on a route, and the next arrivals at a time stop.
There is also some logic to present the user with the results of each query and enter input to craft the next request.
I used&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;urllib.request&amp;lt;/code&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;xml.etree.ElementTree
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;from the python 3.4 standard library.&lt;/p&gt;
&lt;p&gt;First the user is prompted for a route number and a call is made to the API to return the directions, either North - South or East - West, of that route.
The data is returned in XML format as an array of text value pairs, the direction and a direcion code.
ElementTree is used to parse them.
I load the data into a list of lists so that I can display the options with a &amp;lt;code&amp;gt;0.&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;1.&amp;lt;/code&amp;gt; identifier.
The users input is used to select the index of the parent list, taking the first index of the child list, which is the direction code.&lt;/p&gt;
&lt;p&gt;The direction code is used alond with the bus number to craft the next API call.
An array of pairs is again returned.
Time stops and a four character stop code are returned in order of the direction that was specified.
A list of lists is again created and the users is promtped for a seletion.&lt;/p&gt;
&lt;p&gt;The final call combines the route number, the direction of travel and the time stop location.
A collection of &amp;quot;NexTripDeparture&amp;quot; are returned which contain information about the next vehicles scheduled to stop at the requested location.
I print the arrival time, bus number, route description, and a &amp;quot;BlockNumber&amp;quot; designator for the vehicle. (maybe a bus number)&lt;/p&gt;
&lt;p&gt;The code is on &lt;a class="reference external" href="https://github.com/twopercent/mtcli"&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Use git clone to clone is locally:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/twopercent/mtcli
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some more work can be done to pull out the XML parsing from the API call functions as it is very simiar in all three.&lt;/p&gt;
</content><category term="blog"/><category term="python"/><category term="REST"/><category term="dev"/></entry></feed>