mirror of
https://github.com/ckaczor/Blog.git
synced 2026-01-14 01:25:37 -05:00
220 lines
51 KiB
XML
220 lines
51 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
|
||
<title>Chris Kaczor</title>
|
||
<subtitle>Code, Critters, and whatever I feel like writing about.</subtitle>
|
||
<link href="https://chriskaczor.com/feed/feed.xml" rel="self" />
|
||
<link href="https://chriskaczor.com/" />
|
||
<updated>2024-11-11T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/</id>
|
||
<author>
|
||
<name>Chris Kaczor</name>
|
||
</author>
|
||
<entry>
|
||
<title>Where did the time go?</title>
|
||
<link href="https://chriskaczor.com/blog/where-did-the-time-go/" />
|
||
<updated>2024-11-11T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/where-did-the-time-go/</id>
|
||
<content type="html"><p>Whoa - where the hell did all the time go?! I knew in the back of my mind that I was being a huge slacker by not updating in a while, but almost 5 years? That's crazy! My initial reaction was that the timestamp of my last post had gotten screwed up somehow, but the details of what I wrote proves that the time is right.</p>
|
||
<p>I'll fill in more details in upcoming posts but some highlights of the last 5 years:</p>
|
||
<ul>
|
||
<li>Continued working on my software and hardware projects</li>
|
||
<li>Starting playing around with 3D printing</li>
|
||
<li>Took a fairly intensive master's degree program in software engineering</li>
|
||
<li>Cataract surgery for my right eye (my left was April 2018 which I never wrote about)</li>
|
||
<li>Made it through all the craziness of 2020 and beyond</li>
|
||
</ul>
|
||
<p>I'm sure there are other things, but they're not coming to mind right now - I'll bring them up if I think of them later.</p>
|
||
<p>If you've been here before you might notice that this site looks completely different now. I had been using WordPress because I wanted something easy - I had a feeling that if I picked something that needed some coding I'd end up getting lost in all of that rather that writing, but with dispute between WordPress and WP Engine (and some stuff done by Mullenweg that I think is pretty shady) I decided it was time to bail on it completely. The site has switched to Eleventy, and I've done some minimal tweaking to make it look the way I want for now. I'm sure I'll end up making some adjustments as I go, but I need to just make the switch and go with it.</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>Back Again</title>
|
||
<link href="https://chriskaczor.com/blog/back-again/" />
|
||
<updated>2019-12-13T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/back-again/</id>
|
||
<content type="html"><p>When I started this blog I had recently been laid off and figured it was as good a time as any to give some visibility to the code I had been creating over the years rather than hoarding it like a jealous dragon. I intended to keep up with it but then I started a new job and the writing sort of fell by the wayside. I kept working on projects when I could but never really got the chance to write about them. I'm hoping to be a bit more consistent this time around but only time will tell.</p>
|
||
<p>I've been mostly working on completely recreating my home monitoring project with both new hardware and software - I'll write more about that in upcoming posts.</p>
|
||
<p>For the last week or so I've been doing <a href="https://adventofcode.com/">Advent of Code 2019</a> so that I can follow along with my son. He got a good head start due to a few snow days at the beginning of the month but I've gotten ahead of him a bit as the challenges have gotten harder. We just spent some time getting his Intcode computer for day 9 working properly and I nearly swore off the entire contest while doing the second part of day 12 - the coding for that part wasn't really a problem but it took me a bit to figure out the concept for doing it without brute force. I think it would have been more fair if the challenge better explained how to solve the problem and left the coding up to the participant but that's probably just me being whiny. =) We've been posting our code in our GitHub repos in case anyone wants a peek:</p>
|
||
<ul>
|
||
<li><a href="https://github.com/ckaczor/Advent2019">https://github.com/ckaczor/Advent2019</a></li>
|
||
<li><a href="https://github.com/Zakury/Advent-of-Code">https://github.com/Zakury/Advent-of-Code</a></li>
|
||
</ul>
|
||
<p>I've been working in C# and he has been doing it in Python so it has been interesting to compare the two languages. I've been trying to get him to debug with breakpoints in Visual Studio Code rather than going old-school with print statements. It is definitely a process but we're getting there!</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>Etsy Shop Widget</title>
|
||
<link href="https://chriskaczor.com/blog/etsy-shop-widget/" />
|
||
<updated>2018-04-20T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/etsy-shop-widget/</id>
|
||
<content type="html"><p>My wife and a friend run The Crafty Coop (edit: now closed) - an event planning business that also sells handmade party decorations/favors online and at craft shows. One of the ways I help out is as the designated &quot;IT guy&quot; by handling the web/email hosting and other technical stuff.</p>
|
||
<p>When we were initially setting up the website using WordPress we looked for a widget that would generate a listing of their Etsy shop items but we weren't really happy with the ones we found - they either required that the Etsy shop be broken up into sections or they didn't quite display the way we wanted. I decided to try to write my own instead of tweaking one of the existing ones and <a href="https://github.com/ckaczor/etsy-shop-widget">Etsy Shop Widget</a> was born.</p>
|
||
<p><a href="https://chriskaczor.com/blog/etsy-shop-widget/images/etsy-shop-widget-widget.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/ZuxNaFUA1y-280.avif 280w"><source type="image/webp" srcset="https://chriskaczor.com/img/ZuxNaFUA1y-280.webp 280w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/ZuxNaFUA1y-280.png" alt="" width="280" height="391"></picture></a></p>
|
||
<p>I decided early on that I wanted to avoid generating the HTML in PHP as much as possible. I knew I'd have use some PHP for the WordPress admin settings but I wanted to use a front-end framework for the widget itself. I was already using <a href="https://vuejs.org/">Vue.js</a> in other projects so when I found a <a href="https://github.com/caldera-learn/vue-webpack-wordpress-plugin">template project</a> for creating a WordPress plugin that used Vue.js I was good to go.</p>
|
||
<p>The <a href="https://www.etsy.com/developers/documentation/getting_started/api_basics">Etsy API</a> requires an API key and is rate limited so I wanted to cache the results rather than fetch them each time the page loaded. I added standard WordPress settings for the API key, the shop name, and the cache time and put them all in a settings section.</p>
|
||
<p><a href="https://chriskaczor.com/blog/etsy-shop-widget/images/etsy-shop-widget-settings.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/rbvGYcCS3i-544.avif 544w"><source type="image/webp" srcset="https://chriskaczor.com/img/rbvGYcCS3i-544.webp 544w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/rbvGYcCS3i-544.png" alt="" width="544" height="289"></picture></a></p>
|
||
<p>Right now this limits the widget to only one shop per WordPress installation but that's all we need right now. In the future I'll move the shop name to an attribute of the widget instead.</p>
|
||
<p>On the back end I created a custom WordPress action that uses the WordPress <a href="https://codex.wordpress.org/Transients_API">transients API</a> to store the cached data from Etsy. Basically if get_transient returns some data then that data is simply returned to the caller, otherwise wp_remote_request is used to make the call to the Etsy API and the returned data is stored using set_transient with the appropriate cache duration.</p>
|
||
<pre class="language-php" tabindex="0"><code class="language-php"><span class="token keyword">function</span> <span class="token function-definition function">ESW_Listings_request</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
|
||
<span class="token punctuation">{</span>
|
||
<span class="token variable">$listings</span> <span class="token operator">=</span> <span class="token function">get_transient</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'etsy_shop_widget_listings'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
||
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$listings</span> <span class="token operator">===</span> <span class="token constant boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
||
<span class="token variable">$options</span> <span class="token operator">=</span> <span class="token function">get_option</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'ESW_settings'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
||
<span class="token variable">$response</span> <span class="token operator">=</span> <span class="token function">wp_remote_request</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'https://openapi.etsy.com/v2/shops/'</span> <span class="token operator">.</span> <span class="token variable">$options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'ESW_Etsy_Shop_Name'</span><span class="token punctuation">]</span> <span class="token operator">.</span> <span class="token string single-quoted-string">'/listings/active?includes=MainImage&amp;amp;amp;amp;amp;amp;api_key='</span> <span class="token operator">.</span> <span class="token variable">$options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'ESW_Etsy_API_Key'</span><span class="token punctuation">]</span> <span class="token operator">.</span> <span class="token string single-quoted-string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
||
<span class="token variable">$listings</span> <span class="token operator">=</span> <span class="token variable">$response</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'body'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
||
|
||
<span class="token function">set_transient</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'etsy_shop_widget_listings'</span><span class="token punctuation">,</span> <span class="token variable">$listings</span><span class="token punctuation">,</span> <span class="token variable">$options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'ESW_Cache_Time'</span><span class="token punctuation">]</span> <span class="token operator">*</span> <span class="token number">60</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
<span class="token punctuation">}</span>
|
||
|
||
<span class="token keyword">echo</span> <span class="token variable">$listings</span><span class="token punctuation">;</span>
|
||
|
||
<span class="token keyword">die</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
<span class="token punctuation">}</span>
|
||
|
||
<span class="token function">add_action</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'admin_post_esw_listings'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'ESW_Listings_request'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
<span class="token function">add_action</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'admin_post_nopriv_esw_listings'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'ESW_Listings_request'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
|
||
<p>The rest of the PHP side is pretty straightforward - there's an &quot;[etsy-shop-widget]&quot; shortcode that generates an empty div with an ID of &quot;etsy-shop-widget&quot; that is used as the root of the Vue.js application and some code that links the styles and scripts generated by webpack when building the Vue.js code.</p>
|
||
<p>The front-end code is currently pretty basic as well. The Vue.js application just makes an AJAX request to the WordPress custom action, stores the resulting data in a component, and then uses the data to render a list of the item names, pictures, and prices.</p>
|
||
<pre class="language-jscript" tabindex="0"><code class="language-jscript">@Component
|
||
export default class App extends Vue {
|
||
listings: Array<EtsyListing> | null = null;
|
||
|
||
async mounted() {
|
||
const response = await Axios.get<EtsyResult>(window['esw_wp'].siteurl + '/wp-admin/admin-post.php?action=esw_listings');
|
||
|
||
this.listings = response.data.results.sort((a, b) => a.last_modified_tsz - b.last_modified_tsz);
|
||
}
|
||
}</EtsyResult></EtsyListing></code></pre>
|
||
<pre class="language-xml" tabindex="0"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>esw-listing-container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>esw-listing-item<span class="token punctuation">"</span></span> <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listing in listings<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">v-bind:</span>key</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listing.listing_id<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">:href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listing.url<span class="token punctuation">"</span></span> <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>esw-listing-item-image<span class="token punctuation">"</span></span> <span class="token attr-name">:src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listing.MainImage.url_170x135<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
|
||
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">:href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listing.url<span class="token punctuation">"</span></span> <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>esw-listing-item-title<span class="token punctuation">"</span></span> <span class="token attr-name">v-html</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listing.title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
|
||
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>esw-listing-item-price<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
|
||
|
||
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
|
||
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span></code></pre>
|
||
<p>At some point I'd like the make the listing a little fancier - maybe with a single image and previous/next buttons rather than a simple scrolling list but we're happy with the way it is working for now.</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>Feed Center</title>
|
||
<link href="https://chriskaczor.com/blog/feed-center/" />
|
||
<updated>2018-04-04T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/feed-center/</id>
|
||
<content type="html"><p>One of my most complete projects is <a href="https://github.com/ckaczor/FeedCenter">Feed Center</a> - an RSS feed reader designed to blend in with the Windows desktop.</p>
|
||
<p><a href="https://chriskaczor.com/blog/feed-center/images/Feed-Center.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/uZeAJYpepW-300.avif 300w"><source type="image/webp" srcset="https://chriskaczor.com/img/uZeAJYpepW-300.webp 300w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/uZeAJYpepW-300.png" alt="" width="300" height="188"></picture></a></p>
|
||
<p>Years ago when push technology was the hot new thing I got my online news from applications like PointCast and MSNBC News Alert - but they were eventually discontinued as web sites moved to other delivery mechanisms like RSS feeds. I tried to find an RSS reader but never really found one that I liked. I didn't want some Outlook lookalike that I had to specifically open and read through. I wanted an app that would blend in with my desktop as much as possible and be something that I could glance at every so often for the latest news. At some point I started fiddling with creating my own and Feed Center is the result.</p>
|
||
<p>In theory Feed Center supports all of the common RSS versions and Atom with some special handling for common errors. The fact that I'm parsing the feeds with an XML parser makes it a little harder to handle the edge cases but it works well enough so far.</p>
|
||
<p>Feeds are added in a &quot;default&quot; category but they can optionally be organized into any number of other categories. The top of the main window has a selector for the current category - I'm not sure I like the way it looks but I haven't come up with anything better yet.</p>
|
||
<p><a href="https://chriskaczor.com/blog/feed-center/images/Options-Feed.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/4GWlZwTcq7-706.avif 706w"><source type="image/webp" srcset="https://chriskaczor.com/img/4GWlZwTcq7-706.webp 706w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/4GWlZwTcq7-706.png" alt="" width="706" height="353"></picture></a></p>
|
||
<p>The arrows at the top of the window (as well as mouse buttons 4 and 5 if you have them) will scroll through the feeds in the current category in alphabetical order. The current feed will also scroll automatically every minute unless the mouse is hovering over the window - it would be annoying to have the feed change when you're getting ready to read one.</p>
|
||
<p>Double clicking an article will open the web page for the article. By default the system default browser will be used but there's an option to choose another browser instead. There's also buttons to open all of the articles for the current feed and to mark all of the articles as having been read.</p>
|
||
<p>The code base has had some major changes over the years - originally the UI was done using WinForms and I used XML files as storage. After a few corrupt XML files from power failures I switched to using SQLite and then to SQL Server Compact. At some point I rewrote the UI using WPF so I could get rid of a bunch of custom painting for the feed list.</p>
|
||
<p>The project has a full installer created with <a href="http://wixtoolset.org/">WiX</a> that uses a <a href="https://github.com/ckaczor/WixBalExtensionExt">modified bootstrapper</a> to automatically relaunch the application after an upgrade.</p>
|
||
<p>Other than the code on GitHub I haven't made anything public yet but I plan to use <a href="https://www.appveyor.com/">AppVeyor</a> for build and deployment at some point - probably with the installer served as a GitHub release.</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>Laundry Monitor – Software</title>
|
||
<link href="https://chriskaczor.com/blog/laundry-monitor-software/" />
|
||
<updated>2018-04-02T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/laundry-monitor-software/</id>
|
||
<content type="html"><p>The software side of <a href="https://github.com/ckaczor/LaundryMonitor">LaundryMonitor</a> is currently pretty straightforward. The <a href="https://getchip.com/pages/chip">C.H.I.P.</a> runs a Node.js application that keeps track of a GPIO pin for each current switch - when the switch is closed the appliance is on and when the switch is open the appliance is off. The application exposes a websocket that applications like <a href="https://github.com/ckaczor/HomeStatusWindow">HomeStatusWindow</a> can watch and messages are sent to a family Telegram channel.</p>
|
||
<p><a href="https://chriskaczor.com/blog/laundry-monitor-software/images/HomeStatusWindow.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/t0izVudIyW-107.avif 107w"><source type="image/webp" srcset="https://chriskaczor.com/img/t0izVudIyW-107.webp 107w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/t0izVudIyW-107.png" alt="" width="107" height="48"></picture></a></p>
|
||
<p><a href="https://chriskaczor.com/blog/laundry-monitor-software/images/LaundryBot.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/MWYQsdWfr9-238.avif 238w"><source type="image/webp" srcset="https://chriskaczor.com/img/MWYQsdWfr9-238.webp 238w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/MWYQsdWfr9-238.png" alt="" width="238" height="300"></picture></a></p>
|
||
<p>I use the <a href="https://github.com/fivdi/onoff">onoff</a> library to poll the GPIO pins every 500 milliseconds and wait for a pin to be steady for 10 seconds before registering the new state. Originally I tried to use an interrupt but during testing I found that the onoff library could get stuck if switch events came in too fast. I decided that I didn't need real-time updates so a polling mechanism would be enough. I also originally didn't have the 10 second wait time but I found that the washer had a few periods where it would stop completely between wash phases and it made the Telegram alerts somewhat spammy. It looks like there have been a lot of updates to onoff since I first created the application so I plan to see if the interrupt issue has been fixed and if I can use the new debounce support rather than my own custom code.</p>
|
||
<p>Since I'm planning to monitor more devices in the future I'm thinking about making the application that runs on each C.H.I.P. generic and moving the alert and websocket implementation to a central &quot;home monitor&quot; service. The monitor application on each C.H.I.P. would be the same (other than configuration settings) and just report the status to the central service which would be in charge of everything else.</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>Laundry Monitor - Hardware</title>
|
||
<link href="https://chriskaczor.com/blog/laundry-monitor-hardware/" />
|
||
<updated>2018-03-29T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/laundry-monitor-hardware/</id>
|
||
<content type="html"><p>The next project up is <a href="https://github.com/ckaczor/LaundryMonitor">LaundryMonitor</a> which uses the most hardware of anything I've done so far.</p>
|
||
<p>I'd always wanted something that would let us tell if the washing machine or dryer was running but I never knew exactly the right way to detect it. After we got our solar panels I was doing research on how to track power usage and came across an article about current sensors and had the idea that I could hook up a current sensor to each appliance and watch the output voltage to know if the appliance was running. While trying to find a decent sensor I stumbled across a current switch which was more or less the same idea but had everything in one package - when the current was over a certain threshold the switch would close and that could be easily detected.</p>
|
||
<p>My first thought was to use a <a href="http://www.powerswitchtail.com/powerstate-tail">PowerState Tail</a> but only a 120V version was available - there was nothing like it I could use for the 240V of the dryer. Eventually I settled on a <a href="https://smile.amazon.com/gp/product/B00I9IFJOM/ref=oh_aui_search_detailpage?ie=UTF8&amp;psc=1">Dwyer Miniature Current Switch</a> and I'd use one each for both the washer and dryer just to be consistent.</p>
|
||
<p>Next up was to figure out how to monitor the switch. I was going to use a <a href="https://www.phidgets.com/?tier=1&amp;catid=2&amp;pcid=1">Phidget I/O Board</a> attached to a central server and run wires across the house from the washer and dryer to the I/O board but the idea of running all the wires wasn't too appealing. I was about to try some sort of <a href="https://www.sparkfun.com/pages/xbee_guide">XBee</a> setup when I found the <a href="https://getchip.com/pages/chip">C.H.I.P.</a> Kickstarter and it seemed like it was worth a try - the C.H.I.P. was cheap, had digital I/O, had WiFi, and I could use anything that'd run on Linux to make it all work. The full board is probably a bit of overkill for just monitoring two switches but at $9 a device it really didn't bother me that much.</p>
|
||
<p>Hooking up the dryer was relatively easy - I opened the control panel, used the schematic to find the wire that powers the motor, disconnected the wire, ran the wire through the current switch, and reconnected the wire. I mounted the sensor on the back of the dryer for easy access in case I needed to adjust it but the motor draws enough current that the switch was able to detect it easily.</p>
|
||
<p><a href="https://chriskaczor.com/blog/laundry-monitor-hardware/images/Dryer.jpg"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/7D8kV0Tyqw-225.avif 225w"><source type="image/webp" srcset="https://chriskaczor.com/img/7D8kV0Tyqw-225.webp 225w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/7D8kV0Tyqw-225.jpeg" alt="" width="225" height="300"></picture></a></p>
|
||
<p>The washer was pretty much the same but I decided to monitor the main power line so I could detect if the washer was running at any point in the cycle. The trouble was that at certain times (like when the washer was filling) the current draw was way too low for the switch to detect even with the sensitivity set as low as possible. I solved this by looping the wire through the sensor several times to amplify the current to the point where the switch could pick it up.</p>
|
||
<p><a href="https://chriskaczor.com/blog/laundry-monitor-hardware/images/Washer.jpg"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/Qvfav4t14w-225.avif 225w"><source type="image/webp" srcset="https://chriskaczor.com/img/Qvfav4t14w-225.webp 225w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/Qvfav4t14w-225.jpeg" alt="" width="225" height="300"></picture></a></p>
|
||
<p>From here on out things were simple - I just had to run some smaller wires from the switch to the digital I/O ports on the C.H.I.P. and then start working on the software. I ended up sticking the C.H.I.P. a few feet away by the power outlet so I didn't need too long a USB cable to power it.</p>
|
||
<p><a href="https://chriskaczor.com/blog/laundry-monitor-hardware/images/Chip.jpg"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/y6v3-1HpL1-225.avif 225w"><source type="image/webp" srcset="https://chriskaczor.com/img/y6v3-1HpL1-225.webp 225w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/y6v3-1HpL1-225.jpeg" alt="" width="225" height="300"></picture></a></p>
|
||
<p>So far this has been running for a little over two years and the hardware continues to work well. I'm planning to do a similar build to monitor two sump pumps - the only change is that I'm planning to use the PowerState Tail since they're both 120V with standard plugs so it'll be a lot easier to hook up.</p>
|
||
<p>I'll cover the software side of the project in my next post - right now the monitor is a standalone service that does all the work but since I'm going to be adding more devices I'm thinking that I need to break things up a bit.</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>Work Indicator</title>
|
||
<link href="https://chriskaczor.com/blog/work-indicator/" />
|
||
<updated>2018-03-19T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/work-indicator/</id>
|
||
<content type="html"><p>I'm going to take a break from status windows for a little bit to cover my <a href="https://github.com/ckaczor/WorkIndicator">WorkIndicator</a> project.</p>
|
||
<p>I've been working remotely for almost 14 years now and my family found it hard to tell when I was working or on the phone. It often looks the same whether I'm working or not - I'm sitting at my computer, sometimes with my headset on. At some point I came across a <a href="https://www.hanselman.com/blog/IsDaddyOnACallABusyLightPresenceIndicatorForLyncForMyHomeOffice.aspx">blog post</a> by Scott Hanselman that described hooking up a status light to Lync and I was inspired to create something similar.</p>
|
||
<p>The first task was to find something I could use as a status light - after some research I settled on a <a href="https://www.delcomproducts.com/productdetails.asp?PartNumber=907241">USB HID Green/Yellow/Red Visual Indicator</a> from Delcom. I liked that it had a stoplight design and it came with a C# sample - perfect!</p>
|
||
<p>My main workflow is to connect to my work system using Microsoft Remote Desktop so the easiest way to detect if I am working or not is to look for the remote desktop window. I created a class that uses SetWinEventHook to get ObjectCreate, ObjectDestroy, and ObjectNameChanged events and watch for a window with the title of my remote desktop window. If the window was found I'd set the indicator lights to yellow, otherwise I'd set them to green. Lately I've been using a VM as well so I updated the code to be able to look for multiple window titles - if any of them are detected the light is set to yellow.</p>
|
||
<p>Originally there was also Skype integration - I was able to use the Skype API to detect if I was on a call and if the call was muted. If I was on a muted call the right light would be on, and if I wasn't muted the red light would blink. This made it easy for the kids to tell how quiet they needed to be when coming into the room. Unfortunately Skype no longer supports this API (and I'm not using Skype anymore anyways) so I removed this code from the project.</p>
|
||
<p>The application also has a tray icon with a menu so I can manually set my status - for example, I can select &quot;talking&quot; and the red light will blink. This is my workaround until I come up with something that'll work for my current phone setup.</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>System Temperature Status Window</title>
|
||
<link href="https://chriskaczor.com/blog/system-temperature-status-window/" />
|
||
<updated>2018-03-19T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/system-temperature-status-window/</id>
|
||
<content type="html"><p>Next up in the &quot;floating status window&quot; category is <a href="https://github.com/ckaczor/SystemTemperatureStatusWindow">SystemTemperatureStatusWindow</a>. This one does exactly what it says on the tin - it shows various system temperature values.</p>
|
||
<p><a href="https://chriskaczor.com/blog/system-temperature-status-window/images/SystemTemperatureStatusWindow.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/LBCvSAQh1L-121.avif 121w"><source type="image/webp" srcset="https://chriskaczor.com/img/LBCvSAQh1L-121.webp 121w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/LBCvSAQh1L-121.png" alt="" width="121" height="118"></picture></a></p>
|
||
<p>The sensor data is read using the <a href="http://openhardwaremonitor.org/">Open Hardware Monitor</a> library which is pretty straightforward - the one catch is that it requires running as an administrator and getting a UAC prompt each time the application starts got a bit annoying after a while.</p>
|
||
<p>I first tried to solve this by splitting the application into two parts - a Windows service that ran with administrator privileges to read the sensors and a normal application to handle the display. While this got past the annoying UAC prompts (after the service was installed) I found that Windows doesn't let you get GPU information from inside a Windows service.</p>
|
||
<p>After doing some more research I came across a workaround of using the Windows Task Scheduler to run the service as an administrator. I just turned the service into a regular application and set up the Task Scheduler to run it on system startup - problem solved.</p>
|
||
<p>Later on I added some command line switches to the service application that'd do the installation automatically. All I had to do was run the service once with a command line of &quot;-install&quot; and everything would get setup properly with just one UAC prompt.</p>
|
||
<p>I haven't gotten around to adding an installer for this one yet but I definitely have plans to do so. I expect some minor complications setting up the service in Squirrel due to the need for administrator access but I think it'll end up being doable.</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>CPU Usage Status Window</title>
|
||
<link href="https://chriskaczor.com/blog/cpu-usage-status-window/" />
|
||
<updated>2018-03-08T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/cpu-usage-status-window/</id>
|
||
<content type="html"><p>Now that I've written about my <a href="https://github.com/ckaczor/FloatingStatusWindow">FloatingStatusWindow</a> library I can start to talk about the projects that use it. First up is the <a href="https://github.com/ckaczor/ProcessCpuUsageStatusWindow">ProcessCpuUsageStatusWindow</a> project.</p>
|
||
<p><a href="https://chriskaczor.com/blog/cpu-usage-status-window/images/ProcessCpuUsageStatusWindow.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/L-UgyKZ-NI-373.avif 373w"><source type="image/webp" srcset="https://chriskaczor.com/img/L-UgyKZ-NI-373.webp 373w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/L-UgyKZ-NI-373.png" alt="" width="373" height="139"></picture></a></p>
|
||
<p>It is basically a mini task manager showing the top X processes by CPU usage and the total CPU usage of the system. The values update automatically every 2 seconds.</p>
|
||
<p>CPU usage is calculated by taking a snapshot of &quot;% processor time&quot; for the &quot;Process&quot; performance counter category every 2 seconds and having the counter sample calculate the percent usage for each process based on the previous snapshot.</p>
|
||
<p>This ended needing a lot more calculation than I had hoped although I don't remember all the details as to why - one of the perils of blogging so long after the code was written. From what I remember what I first tried was built into .NET and was easier to code but used a lot more processor time than I was comfortable with. Perhaps that has since been fixed in a later framework version - someday I'll have to try to recreate what I was doing.</p>
|
||
<p>One caveat - the code doesn't always work quite right. I have seen a few times where the calculation comes out way over 100% but I haven't been able to reliably reproduce it. It seems to be when either something hangs Windows or there's exceptionally high disk usage. Either way - it works well enough for getting a quick look at CPU usage so I haven't spent much time on it.</p>
|
||
<p>This was also the first project where I tried using <a href="https://github.com/Squirrel/Squirrel.Windows">Squirrel</a> for installation and updates. I had used both <a href="http://wixtoolset.org/">WiX</a> (with custom update detection and installation) and <a href="https://en.wikipedia.org/wiki/ClickOnce">ClickOnce</a> in other projects and I think I'll probably go with Squirrel for most things going forward.</p>
|
||
</content>
|
||
</entry>
|
||
<entry>
|
||
<title>Floating Status Window</title>
|
||
<link href="https://chriskaczor.com/blog/floating-status-window/" />
|
||
<updated>2018-02-21T00:00:00Z</updated>
|
||
<id>https://chriskaczor.com/blog/floating-status-window/</id>
|
||
<content type="html"><p>Something that shows up in a lot of my projects is my <a href="https://github.com/ckaczor/FloatingStatusWindow">FloatingStatusWindow</a> library. It allows the creation of something akin to a desktop widget that displays simple text and blends in with the Windows desktop. This is what several of them look like in the corner of my secondary monitor:</p>
|
||
<p><a href="https://chriskaczor.com/blog/floating-status-window/images/Windows-Locked.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/-IG3SLpr1H-770.avif 770w"><source type="image/webp" srcset="https://chriskaczor.com/img/-IG3SLpr1H-770.webp 770w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/-IG3SLpr1H-770.png" alt="" width="770" height="303"></picture></a></p>
|
||
<p>Each one is a separate project that uses the core library to create and manage the window. The code for a few of these is on <a href="https://github.com/ckaczor">GitHub</a> and I'm working to add more.</p>
|
||
<p>The windows are locked by default to prevent accidentally moving them but they can be unlocked in order to move or resize them. When moving or resizing the windows will snap to each other and the sides of the screen. This is what the example above looks like unlocked:</p>
|
||
<p><a href="https://chriskaczor.com/blog/floating-status-window/images/Windows-Unlocked.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/2zWtBO5eyV-1358.avif 1358w"><source type="image/webp" srcset="https://chriskaczor.com/img/2zWtBO5eyV-1358.webp 1358w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/2zWtBO5eyV-1358.png" alt="" width="1358" height="354"></picture></a></p>
|
||
<p>Basic appearance settings are built into the library itself along with a dialog to change them:</p>
|
||
<p><a href="https://chriskaczor.com/blog/floating-status-window/images/Window-Appearance.png"><picture><source type="image/avif" srcset="https://chriskaczor.com/img/mcAwKnxf5g-436.avif 436w"><source type="image/webp" srcset="https://chriskaczor.com/img/mcAwKnxf5g-436.webp 436w"><img loading="lazy" decoding="async" src="https://chriskaczor.com/img/mcAwKnxf5g-436.png" alt="" width="436" height="298"></picture></a></p>
|
||
<p>Each individual project is responsible for the text, the text layout, using a non-default text color, and keeping the text up to date. Some projects will update using a timer while others will wait for some sort of event and update as needed.</p>
|
||
<p>I had tried a number of applications that could do custom widgets but none of them quite worked the way I wanted. I read an article about transparent WPF windows and decided to create something myself.</p>
|
||
<p>Originally it was implemented as a single application that could load a number of plugins but that ended up being a bit of a pain - when working on a new window I had to close and restart everything and if a plugin crashed it'd take down all of the rest. I decided to convert it into a library that could detect other instances of the window so they worked together but were implemented as separate binaries.</p>
|
||
<p>There will be more about the library later when I write about the various applications that use it.</p>
|
||
</content>
|
||
</entry>
|
||
</feed> |