Keybow : Virtual Desktop Controller

The Pimoroni Keybow Kit is a DIY mini mechanical keyboard powered by a Raspberry Pi Zero WH with twelve illuminated RGB keys. The behaviour of the keys is setup in a Lua script. It come with severals examples, but you are free to experiment and setup something that match your needs.

The first application I had in mind was to use this beautiful keyboard to control my Virtual Desktops on macOS (Mission Control), and on my other Un*x system : GNUX Linux and OpenBSD (I use ratpoison window manager).

The main idea was to use :

  • 8 keys (the 2 first line in green) for direct access to my 8 Virtual Desktops.
  • The bottom left key (blue) for Dashboard access on macOS, and WTF on GNU Linux distributions / OpenBSD.
  • The bottom right key (violet) for ScreenLock
  • The 2 bottom middle keys (yellow) to switch between previous / next Virtual Desktop.

I wanted to keep a visual information of which Desktop is active : so the active Desktop and Dashboard key stay in red when active. While Previous / Next Desktop keys and ScreenLock key are red only when pressed.

The Lua script take place on your microSD card on folder ‘layouts’ where you can find other examples of keyboard layouts Lua script.

Here is my first (and ugly) Lua script :

require "keybow"

function setup() -- Set custom lights up
    keybow.auto_lights(false)
    keybow.clear_lights()
    
    keybow.set_pixel(11, 0, 255, 0) -- Green
    keybow.set_pixel(08, 0, 255, 0) -- Green
    keybow.set_pixel(05, 0, 255, 0) -- Green
    keybow.set_pixel(02, 0, 255, 0) -- Green
    
    keybow.set_pixel(10, 0, 255, 0) -- Green
    keybow.set_pixel(07, 0, 255, 0) -- Green
    keybow.set_pixel(04, 0, 255, 0) -- Green
    keybow.set_pixel(01, 0, 255, 0) -- Green
    
    keybow.set_pixel(09, 0, 0, 255) -- Blue
    keybow.set_pixel(06, 255, 255, 0) -- Yellow
    keybow.set_pixel(03, 255, 255, 0) -- Yellow
    
    keybow.set_pixel(00, 255, 0, 255) -- Purple
end

if desktop_number == nil then
    desktop_number = 1
end


-- Standard number pad mapping --

-- Physical Horizontal : 
--        USB
--        ||
-- [11][08][05][02]
-- [10][07][04][01]
-- [09][06][03][00]
--


-- Key mappings --

function desktop_light( number )
 prev_desktop_number = desktop_number
 desktop_number = number
 if desktop_number == 1 then
        keybow.set_pixel(11, 255, 0, 0)
 else
        keybow.set_pixel(11, 0, 255, 0)
 end
 if desktop_number == 2 then
        keybow.set_pixel(08, 255, 0, 0)
 else
        keybow.set_pixel(08, 0, 255, 0)
 end
 if desktop_number == 3 then
        keybow.set_pixel(05, 255, 0, 0)
 else
        keybow.set_pixel(05, 0, 255, 0)
 end
 if desktop_number == 4 then
        keybow.set_pixel(02, 255, 0, 0)
 else
        keybow.set_pixel(02, 0, 255, 0)
 end
 
 if desktop_number == 5 then
        keybow.set_pixel(10, 255, 0, 0)
 else
        keybow.set_pixel(10, 0, 255, 0)
 end
 if desktop_number == 6 then
        keybow.set_pixel(07, 255, 0, 0)
 else
        keybow.set_pixel(07, 0, 255, 0)
 end
 if desktop_number == 7 then
        keybow.set_pixel(04, 255, 0, 0)
 else
        keybow.set_pixel(04, 0, 255, 0)
 end
 if desktop_number == 8 then
        keybow.set_pixel(01, 255, 0, 0)
 else
        keybow.set_pixel(01, 0, 255, 0)
 end
 if desktop_number == 0 then
        keybow.set_pixel(09, 255, 0, 0)
 else
        keybow.set_pixel(09, 0, 0, 255)
 end
 
end

-- First Line :

function handle_key_11(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F1, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 1
    end
    desktop_light(desktop_number)
end

function handle_key_08(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F2, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 2
    end
    desktop_light(desktop_number)
end

function handle_key_05(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F3, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 3
    end
    desktop_light(desktop_number)
end

function handle_key_02(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F4, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 4
    end
    desktop_light(desktop_number)
end



-- Second Line :

function handle_key_10(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F5, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 5
    end
    desktop_light(desktop_number)
end

function handle_key_07(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F6, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 6
    end
    desktop_light(desktop_number)
end

function handle_key_04(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F7, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 7
    end
    desktop_light(desktop_number)
end

function handle_key_01(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F8, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 8
    end
    desktop_light(desktop_number)
end



-- Third Line :

function handle_key_09(pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)	
    keybow.set_key(keybow.F10, pressed)
    keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
    if pressed then
     desktop_number = 0
    end
    desktop_light(desktop_number)
end

function handle_key_06(pressed)
    if pressed then
        keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)
        keybow.tap_key(keybow.LEFT_ARROW)
        keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
        keybow.set_pixel(06, 255, 0, 0)
	desktop_number = desktop_number - 1
	if desktop_number < 0 then
	 desktop_number = 0
	end
	desktop_light(desktop_number)
    else
        keybow.set_pixel(06, 255, 255, 0)
    end
end

function handle_key_03(pressed)
    if pressed then
        keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN)
        keybow.tap_key(keybow.RIGHT_ARROW)
        keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP)
        keybow.set_pixel(03, 255, 0, 0)
	desktop_number = desktop_number + 1
	if desktop_number > 8 then
	 desktop_number = 8
	end
	desktop_light(desktop_number)
    else
        keybow.set_pixel(03, 255, 255, 0)
    end
end

function handle_key_00(pressed)
    -- lock screen
    if pressed then
        keybow.set_modifier(keybow.LEFT_META, keybow.KEY_DOWN)
        keybow.set_modifier(keybow.LEFT_CTRL, keybow.KEY_DOWN)
        keybow.tap_key("q")
        keybow.set_modifier(keybow.LEFT_META, keybow.KEY_UP)
        keybow.set_modifier(keybow.LEFT_CTRL, keybow.KEY_UP)
        keybow.set_pixel(00, 255, 0, 0)
    else
        keybow.set_pixel(00, 255, 0, 255)
    end
end

For information, my Shortcuts settings in macOS :

It do the job, but have some limitations : They Keybow keyboard send information to your computer, but receive any. So if you change Virtual Desktop from another way (main keyboard or gesture for example), it will not have knowledge of the changes, so can’t change the color for active desktop key.

I hope this example will give you some ideas about how to use this nice little keyboard kit 🙂