tcltk9 ai lol system tray battery indicator hack

seven segment battery indicator for win11 with ashok's tcltk magicsplat runtime and claude 4 sonnet in vscode (plain text editing )

one hour hack in 2025 after ten years away

enjoy

package require Tk
package require Img
package require twapi

# --- 1) Create the systray icon once (Windows needs a *photo*). ---
#     The -text is tooltip only; the actual digits are drawn into the image.
proc TrayCreate {} {
    variable ::bat
    array set ::bat {}
    # Start with a dummy
    set ph [image create photo -width 16 -height 16]
    tk systray create -image $ph -text "Battery"
}

# --- 2) Create a photo image with seven-segment display numbers ---
proc MakeNumIcon {n {size 16}} {
    # clamp + format
    if {$n < 0} { set n 0 } elseif {$n > 100} { set n 100 }
    
    # Create a photo image directly
    set ph [image create photo -width $size -height $size]
    
    # Fill with black background
    $ph put black -to 0 0 $size $size
    
    # Seven-segment display patterns for digits 0-9
    # Each digit is represented as 7 segments: a,b,c,d,e,f,g
    #     aaa
    #    f   b
    #    f   b
    #     ggg
    #    e   c
    #    e   c
    #     ddd
    
    array set segments {
        0 {a b c d e f}
        1 {b c}
        2 {a b g e d}
        3 {a b g c d}
        4 {f g b c}
        5 {a f g c d}
        6 {a f g e d c}
        7 {a b c}
        8 {a b c d e f g}
        9 {a b c d f g}
    }
    
    # Convert number to string with leading zero for single digits, and 100 shows as 00
    if {$n == 100} {
        set numstr "00"
    } elseif {$n < 10} {
        set numstr [format "%02d" $n]
    } else {
        set numstr [format "%d" $n]
    }
    set numlen [string length $numstr]
    
    # Calculate digit size and positioning - always two digits
    set digit_width [expr {($size - 6) / 2}]
    set digit_height [expr {$size - 4}]
    set start_y 2
    
    # First digit
    set start_x 1
    DrawSevenSegmentDigit $ph [string index $numstr 0] $start_x $start_y $digit_width $digit_height segments
    
    # Second digit
    set start_x [expr {$start_x + $digit_width + 2}]
    DrawSevenSegmentDigit $ph [string index $numstr 1] $start_x $start_y $digit_width $digit_height segments
    
    return $ph
}

# Helper function to draw a single seven-segment digit
proc DrawSevenSegmentDigit {ph digit x y width height segmentsArray} {
    upvar $segmentsArray segments
    
    # Skip if digit is invalid
    if {![info exists segments($digit)]} return
    
    # Calculate segment dimensions
    set seg_length [expr {$width - 2}]
    set seg_height [expr {($height - 3) / 2}]
    
    # Calculate actual coordinate values
    set w [expr {$width - 1}]
    set h [expr {$height - 1}]
    set h1 [expr {$height / 2}]
    set h2 [expr {$h1 + 1}]
    
    # Define segment positions and draw them
    foreach seg_name $segments($digit) {
        switch $seg_name {
            a {
                # Top horizontal segment
                DrawHorizontalSegment $ph [expr {$x + 1}] $y $seg_length
            }
            b {
                # Upper right vertical segment
                DrawVerticalSegment $ph [expr {$x + $w}] [expr {$y + 1}] $seg_height
            }
            c {
                # Lower right vertical segment
                DrawVerticalSegment $ph [expr {$x + $w}] [expr {$y + $h2}] $seg_height
            }
            d {
                # Bottom horizontal segment
                DrawHorizontalSegment $ph [expr {$x + 1}] [expr {$y + $h}] $seg_length
            }
            e {
                # Lower left vertical segment
                DrawVerticalSegment $ph $x [expr {$y + $h2}] $seg_height
            }
            f {
                # Upper left vertical segment
                DrawVerticalSegment $ph $x [expr {$y + 1}] $seg_height
            }
            g {
                # Middle horizontal segment
                DrawHorizontalSegment $ph [expr {$x + 1}] [expr {$y + $h1}] $seg_length
            }
        }
    }
}

# Draw horizontal segment
proc DrawHorizontalSegment {ph x y length} {
    for {set i 0} {$i < $length} {incr i} {
        $ph put yellow -to [expr {$x + $i}] $y
    }
}

# Draw vertical segment
proc DrawVerticalSegment {ph x y height} {
    for {set i 0} {$i < $height} {incr i} {
        $ph put yellow -to $x [expr {$y + $i}]
    }
}

# --- 3) Cache icons and update the tray quickly. ---
proc TraySetBattery {percent} {
    variable ::bat
    set key [format %d [expr {int($percent)}]]
    if {![info exists ::bat(icons,$key)]} {
        set ::bat(icons,$key) [MakeNumIcon $key 16]
    }
    tk systray configure -image $::bat(icons,$key) -text "$key%"
}

# --- Demo function (for testing) ---
proc demoTick {} {
    global x
    TraySetBattery $x
    incr x -1
    if {$x < 0} { set x 100 }
    after 100 demoTick
}

# --- Real battery monitoring function ---
proc batteryGo {} {
    # Get battery status using TWAPI
    try {
        set power_status [twapi::get_power_status]
        set battery_percent [dict get $power_status -batterylifepercent]
        
        # Print timestamp and battery value to console
        set timestamp [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S"]
        puts "$timestamp Battery: \"$battery_percent\""
        
        # Handle case where battery percent might be unknown
        if {$battery_percent eq "unknown" || $battery_percent eq "" || $battery_percent < 0} {
            set battery_percent 0
        }
        
        # Update the system tray icon
        TraySetBattery $battery_percent
        
    } trap {TWAPI} {result options} {
        # If TWAPI fails, show error indicator
        TraySetBattery 00
    } on error {result options} {
        # Generic error handling
        TraySetBattery 00
    }
    
    # Schedule next update in 5 seconds (5000ms)
    after 5000 batteryGo
}

console show

# --- Start real battery monitoring ---
TrayCreate
batteryGo

# --- Demo Mode ---
# To run the demo countdown instead of real battery monitoring, comment out
# the "batteryGo" line above and uncomment the following lines:
#
# set x 100
# demoTick