Floating Status Window

The software side of LaundryMonitor is currently pretty straightforward. The C.H.I.P. 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 HomeStatusWindow can watch and messages are sent to a family Telegram channel.

I use the onoff 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.

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 “home monitor” 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.

Next up in the “floating status window” category is SystemTemperatureStatusWindow. This one does exactly what it says on the tin – it shows various system temperature values.

The sensor data is read using the Open Hardware Monitor 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.

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.

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.

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 “-install” and everything would get setup properly with just one UAC prompt.

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.

Now that I’ve written about my FloatingStatusWindow library I can start to talk about the projects that use it. First up is the ProcessCpuUsageStatusWindow project.

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.

CPU usage is calculated by taking a snapshot of “% processor time” for the “Process” performance counter category every 2 seconds and having the counter sample calculate the percent usage for each process based on the previous snapshot.

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.

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.

This was also the first project where I tried using Squirrel for installation and updates. I had used both WiX (with custom update detection and installation) and ClickOnce in other projects and I think I’ll probably go with Squirrel for most things going forward.

Something that shows up in a lot of my projects is my FloatingStatusWindow 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:

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 GitHub and I’m working to add more.

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:

Basic appearance settings are built into the library itself along with a dialog to change them:

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.

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.

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.

There will be more about the library later when I write about the various applications that use it.