Building a Binary Clock with PowerShell

I was busy surfing the web when I could have been working on some PowerShell scripts (or doing yard work outside) when I came across a binary clock. I don’t quite remember how I got to the page showing a binary clock, but regardless, it happened. Here is one page showing a binary clock.  This got me thinking about what it would take to write some code to create my very own binary clock using PowerShell. At first I thought about just making a “proof of concept” by just displaying a single instance of time in the console. But of course that is not very impressive, so I knew I would need to build out a WPF version of the clock.

If you want to know more about a binary clock, read this.

Now I know a lot of you will be saying “Boe, why don’t you use Show-UI since it is fricking amazing?”. My answer to that is , yes I know and will eventually make the leap over to it. But at this time I am not able to put a lot of time into using Show-UI as where I work restricts my ability to run modules and such downloaded from the internet without jumping through several hurdles That being said, I do have it downloaded and ready to run on my laptop and have plans to port my various GUI projects over to it when time permits.

Building the GUI

As I stated, I am not using Show-UI, so therefore I am working with XAML and PowerShell to build out my GUI and make the necessary control and event connections. Having built a few GUI’s now, I am more comfortable in writing the XAML and deciding what I will need to make it work the way that I want it to. For those of you unfamiliar with XAML, I suggest using your favorite search engine to learn more about it. It is a great way to build out your front end GUI! I decided on a Grid as it would make aligning everything much easier, especially since I can show the grid lines and see where I am at. I also used labels to display some other information that I will cover later on in the post.

image

Clock showing the grid lines.

One item I decided on was to use Radio Buttons to act as my on/off buttons for the binary clock. The issue that I first ran into was figuring out how to make sure that when one button was turned ‘on’, that the other buttons would not turn ‘off’. Turns out the answer was fairly simple: put each Radio Button into a group by itself so each button is independent of the other buttons when turned on or off.

As I started to put this together, I also had to find a way to convert a time into binary and then have it represented correctly on my clock. Luckily, I found the system.convert class to be my ally in converting a decimal value into Binary. By using the static method, ToString, I can easily perform the conversion of decimal values 7 and 9 into binary.

[convert]::ToString(7,2)

111

[convert]::ToString(9,2)

1001

Pretty simple, isn’t it?

Because I have the binary clock represented by 6 columns, I split the time out into 6 chunks by using –Split. That way, I can work with each value by itself and convert it into binary that can be used for the setting the clock to the correct time. Lets see an example o this by grabbing the hours:

$hourA,$hourB = [string](Get-Date -f HH) -split "" | Where {$_}

image

This trick allows me to store each piece of the array in its own variable rather than using the [0] and [1] method.

Another trick I use is to filter using Where-Object to only grab the parts of the array that actually contains the data. All of the extra empty space is removed.

Another gotcha that I ran into is that I first had to reverse the order of the binary array by using the [array]::Reverse() method. This allows you to supply an existing array into the method and reverse the order without having to save the results to a new variable! Very helpful stuff!

image

By reversing the array before using it for the GUI will help to ensure that my clock now behaves properly by managing the buttons in the correct fashion.

I configured a timer on the WPF window to run at every .1 second. I found timing issues and a lot of inconsistency if I made the value any higher. Sometimes a little trial and error can help out when working on these kind of items.

Extra Items

After finishing all of this, I decided to add a few little extra items just because I thought they would be useful to folks. The first item is a helper column that makes it easier to translate what each binary value means. This can be accomplished by hitting the ‘h’ key while the window for the clock is active.

The second item was to add a date on the top that can be hidden or visible using the ‘d’ key.

Lastly, if you want to see what the time is in a more “human” format, clicking the ‘t’ key will bring up a digital clock for you.

I chose to run the clock in a different runspace so the console would be free to use without having to stop the clock to run other commands or opening a second PowerShell console.

Conclusion

Hopefully everyone finds this to be a pretty cool application. While nothing too special, it was a lot of fun putting this together and working through a few roadblocks along the way. I have no doubt that there are better ways that I could have gone about building this clock, but I also encourage everyone that likes this to take the code and expand upon it. By expanding I mean make the code shorter and more efficient, port it over to Show-UI, make it look fancier and just have fun doing it!

Examples

Here are a few screenshots showing what the Binary clock looks like and also using the various keys.

binaryclock

Default clock

binaryclock_date

Clock showing the date using the ‘d’ key.

binaryclock_help

Clock showing the helper values using the ‘h’ key.

binaryclock_time

Clock showing the time in a “human readable” format using the ‘t’ key.

 

Code

Poshcode

Script Repository

<#
.SYNOPSIS
    This is a binary clock that lists the time in hours, minutes and seconds
    
.DESCRIPTION
    This is a binary clock that lists the time in hours, minutes and seconds. Also available is the ability to 
    display the time in a "human readable" format, display the date and display a helper display showoing how to read
    the binary numbers to determine the time.
    
    Tips:
    Use the "h" key show and hide the helper column to better understand what the binary values are.
    Use the "d" key to show and hide the current date.
    Use the "t" key to show the time in a more "human readable" format.

.NOTES  
    Name: BinaryClock/ps1
    Author: Boe Prox
    DateCreated: 07/05/2011
    Version 1.0 
#>
$rs = [RunspaceFactory]::CreateRunspace()
$rs.ApartmentState = “STA”
$rs.ThreadOptions = “ReuseThread”
$rs.Open() 
$psCmd = {Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase}.GetPowerShell() 
$psCmd.Runspace = $rs 
$psCmd.Invoke() 
$psCmd.Commands.Clear() 
$psCmd.AddScript({ 

#Load Required Assemblies
Add-Type –assemblyName PresentationFramework
Add-Type –assemblyName PresentationCore
Add-Type –assemblyName WindowsBase


[xml]$xaml = @"
<Window
    xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
    x:Name='Window' Title='Binary Clock' WindowStartupLocation = 'CenterScreen' Width = '205' Height = '196' ShowInTaskbar = 'True' ResizeMode = 'NoResize' >
        <Window.Background>
        <LinearGradientBrush StartPoint='0,0' EndPoint='0,1'>
            <LinearGradientBrush.GradientStops> <GradientStop Color='#C4CBD8' Offset='0' /> <GradientStop Color='#E6EAF5' Offset='0.2' /> 
            <GradientStop Color='#CFD7E2' Offset='0.9' /> <GradientStop Color='#C4CBD8' Offset='1' /> </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
    </Window.Background>
        <Grid x:Name = 'Grid1' HorizontalAlignment="Stretch" ShowGridLines='False'>
            <Grid.ColumnDefinitions>
                <ColumnDefinition x:Name = 'Column1' Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>                
                <ColumnDefinition x:Name = 'helpcolumn' Width="0"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition x:Name = 'daterow' Height = '0'/>
                <RowDefinition Height = '*'/>
                <RowDefinition Height = '*'/>
                <RowDefinition Height = '*'/>                
                <RowDefinition Height = '*'/>
                <RowDefinition x:Name = 'timerow' Height = '0'/>
            </Grid.RowDefinitions>
            <RadioButton x:Name = 'HourA0' IsChecked = 'False' GroupName = 'A' Grid.Row = '4' Grid.Column = '0' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'HourA1' IsChecked = 'False' GroupName = 'B' Grid.Row = '3' Grid.Column = '0' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'HourB0' IsChecked = 'False' GroupName = 'C' Grid.Row = '4' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'HourB1' IsChecked = 'False' GroupName = 'D' Grid.Row = '3' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'HourB2' IsChecked = 'False' GroupName = 'E' Grid.Row = '2' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'HourB3' IsChecked = 'False' GroupName = 'F' Grid.Row = '1' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'MinuteA0' IsChecked = 'False' GroupName = 'G' Grid.Row = '4' Grid.Column = '3' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'MinuteA1' IsChecked = 'False' GroupName = 'H' Grid.Row = '3' Grid.Column = '3' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'MinuteA2' IsChecked = 'False' GroupName = 'I' Grid.Row = '2' Grid.Column = '3' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'MinuteB0' IsChecked = 'False' GroupName = 'J' Grid.Row = '4' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />                                                
            <RadioButton x:Name = 'MinuteB1' IsChecked = 'False' GroupName = 'K' Grid.Row = '3' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'MinuteB2' IsChecked = 'False' GroupName = 'L' Grid.Row = '2' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />                         
            <RadioButton x:Name = 'MinuteB3' IsChecked = 'False' GroupName = 'M' Grid.Row = '1' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'SecondA0' IsChecked = 'False' GroupName = 'N' Grid.Row = '4' Grid.Column = '6' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'SecondA1' IsChecked = 'False' GroupName = 'O' Grid.Row = '3' Grid.Column = '6' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondA2' IsChecked = 'False' GroupName = 'P' Grid.Row = '2' Grid.Column = '6' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondB0' IsChecked = 'False' GroupName = 'Q' Grid.Row = '4' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondB1' IsChecked = 'False' GroupName = 'R' Grid.Row = '3' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondB2' IsChecked = 'False' GroupName = 'S' Grid.Row = '2' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondB3' IsChecked = 'False' GroupName = 'T' Grid.Row = '1' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '4' Grid.Column = '8' Content = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '3' Grid.Column = '8' Content = '2' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '2' Grid.Column = '8' Content = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '1' Grid.Column = '8' Content = '8' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'H1Label' Grid.Row = '5' Grid.Column = '0' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'H2Label' Grid.Row = '5' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '5' Grid.Column = '2' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' Content = ":" />
            <Label FontWeight = 'Bold' x:Name = 'M1Label' Grid.Row = '5' Grid.Column = '3' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'M2Label' Grid.Row = '5' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '5' Grid.Column = '5' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' Content = ":" />
            <Label FontWeight = 'Bold' x:Name = 'S1Label' Grid.Row = '5' Grid.Column = '6' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'S2Label' Grid.Row = '5' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'datelabel' Grid.Row = '0' Grid.Column = '1' Grid.ColumnSpan = "6" HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
        </Grid>
</Window>
"@ 

$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Global:Window=[Windows.Markup.XamlReader]::Load( $reader )

$datelabel =  $Global:window.FindName("datelabel")
$H1Label =  $Global:window.FindName("H1Label")
$H2Label =  $Global:window.FindName("H2Label")
$M1Label =  $Global:window.FindName("M1Label")
$M2Label =  $Global:window.FindName("M2Label")
$S1Label =  $Global:window.FindName("S1Label")
$S2Label =  $Global:window.FindName("S2Label")
$timerow =  $Global:window.FindName("timerow")
$daterow =  $Global:window.FindName("daterow")
$helpcolumn =  $Global:window.FindName("helpcolumn")
$Global:Column1 = $Global:window.FindName("Column1")
$Global:Grid = $column1.parent

##Events
#Show helper column   
$Global:Window.Add_KeyDown({
    If ($_.Key -eq "h") {
        Switch ($helpcolumn.width) {
            "*" {$helpcolumn.width = "0"}
            0 {$helpcolumn.width = "*"}
            }
        }
    }) 

#Show time column   
$Global:Window.Add_KeyDown({
    If ($_.Key -eq "t") {
        Switch ($timerow.height) {
            "*" {$timerow.height = "0"}
            0 {$timerow.height = "*"}
            }
        }
    }) 
#Show date column   
$Global:Window.Add_KeyDown({
    If ($_.Key -eq "d") {
        Switch ($daterow.height) {
            "*" {$daterow.height = "0"}
            0 {$daterow.height = "*"}
            }
        }
    })     

$update = {
$datelabel.content = Get-Date -f D
$hourA,$hourB = [string](Get-Date -f HH) -split "" | Where {$_}
$minuteA,$minuteB = [string](Get-Date -f mm) -split "" | Where {$_}
$secondA,$secondB = [string](Get-Date -f ss) -split "" | Where {$_}

$hourAradio = $grid.children | Where {$_.Name -like "hourA*"}
$minuteAradio = $grid.children | Where {$_.Name -like "minuteA*"}
$secondAradio = $grid.children | Where {$_.Name -like "secondA*"}
$hourBradio = $grid.children | Where {$_.Name -like "hourB*"}
$minuteBradio = $grid.children | Where {$_.Name -like "minuteB*"}
$secondBradio = $grid.children | Where {$_.Name -like "secondB*"}

#hourA
$H1Label.content = $hourA
[array]$splittime = ([convert]::ToString($houra,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $hourAradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#hourB
$H2Label.content = $hourB
[array]$splittime = ([convert]::ToString($hourb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $hourBradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#minuteA
$M1Label.content = $minuteA
[array]$splittime = ([convert]::ToString($minutea,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $minuteAradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#minuteB
$M2Label.content = $minuteB
[array]$splittime = ([convert]::ToString($minuteb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $minuteBradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#secondA
$S1Label.content = $secondA
[array]$splittime = ([convert]::ToString($seconda,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $secondAradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#secondB
$S2Label.content = $secondB
[array]$splittime = ([convert]::ToString($secondb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $secondBradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0
}

$Global:Window.Add_KeyDown({
    If ($_.Key -eq "F5") {
        &$update 
        }
    })

#Timer Event
$Window.Add_SourceInitialized({
    #Create Timer object
    Write-Verbose "Creating timer object"
    $Global:timer = new-object System.Windows.Threading.DispatcherTimer 

    Write-Verbose "Adding interval to timer object"
    $timer.Interval = [TimeSpan]"0:0:.10"
    #Add event per tick
    Write-Verbose "Adding Tick Event to timer object"
    $timer.Add_Tick({
        &$update
        Write-Verbose "Updating Window"
        })
    #Start timer
    Write-Verbose "Starting Timer"
    $timer.Start()
    If (-NOT $timer.IsEnabled) {
        $Window.Close()
        }
    })

&$update
$window.Showdialog() | Out-Null            
}).BeginInvoke() | out-null

About Boe Prox

Microsoft Cloud and Datacenter MVP working as a SQL DBA.
This entry was posted in powershell, scripts and tagged , , , , . Bookmark the permalink.

3 Responses to Building a Binary Clock with PowerShell

  1. Pingback: PowerShell Binary Clock: Round 2 | Learn Powershell | Achieve More

  2. Ryan Grant says:

    Hey Boe, cool post! I like the idea of using PowerShell to create a binary clock.

    Since ShowUI v1.1 was just released, I decided to make a binary coded sexagesimal clock, where hour, minute and second each have a single row.

    As you said, the downside to ShowUI is that it’s not available on every computer you use; the nice thing about it is that it takes very little code to design the UI. The Window content in the clock I wrote was only 3 lines (101 to 103).

    I uploaded the script to PoshCode at: http://poshcode.org/2778

    • Boe Prox says:

      Hi Ryan!
      That is an excellent script using Show-UI! Glad to see someone giving this a shot and building their own version of a binary clock. 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s