[Guide] Adding a physical power button to a VM


bobbintb

Recommended Posts

I've been wanting a power button for a VM for a while since my UnRAID server is my main/only computer at this point. It runs everything, including a gaming VM that has multiseating for the whole family. But trying to show (and trust) everyone how to start a VM in the GUI is not ideal. I've done WOL with an app and I know there are ways to wake it from sleep but it's just not the same to me. Yes, I know I can just leave it on as well but I've taken to shutting it down since a foreign malicious user or two was attempting to access it while I was sleeping.

This guide will be a bit pretty really rough for now. I'm really busy with some other stuff so I'll try to add photos and improvements when I get the time.

Hardware needed:

Raspberry Pi Pico

A button (you can harvest one from an old PC or buy a new one. It should be a momentary switch)

1. Connect your momentary switch to your Pico. It should be two wires, one goes to pin 16 and the other to ground. Doesn't matter which. They should be in one of the corners. (photos to come)

 

2. Follow this guide to get the basics set up on your computer to program the Pico. You just need Thonny set up and MicroPython installed.
https://projects.raspberrypi.org/en/projects/getting-started-with-the-pico

 

3. Copy this script:

from machine import Pin
import uasyncio as asyncio
from primitives import Pushbutton

vm_name = "\'Windows 11\'"
start_command = "virsh start " + vm_name
reboot_command = "virsh reboot " + vm_name
force_shutdown_command = "virsh destroy " + vm_name

btn = Pin(16, Pin.IN, Pin.PULL_UP)
pb = Pushbutton(btn, suppress=True)

async def main():
    short_press = pb.release_func(print, (start_command,))
    double_press = pb.double_func(print, (reboot_command,))
    long_press = pb.long_func(print, (force_shutdown_command,))
    while True:
        await asyncio.sleep(1)

asyncio.run(main())

 

4. Name the script "main.py" and save it to your Pico. It will run automatically as soon as it is plugged into a machine when it is named as such so name it something else if you are still testing.

 

5. Go to the following link. Go to the v3 folder and download the directory named "primitives".

https://github.com/peterhinch/micropython-async

 

6. In Thonny, click the view menu at the top and select "Files".

7. Copy the primitives folder to the lib folder on your Pico.

8. Create a user script in UnRAID and set it to run when the array starts. The script is as follows:

#!/bin/sh
sh /dev/ttyACM0 > sh /dev/tty

NOTE: You may have to give /dev/ttyACM0 execute permissions with `chmod +x /dev/ttyACM0`
 

9. Plug the Pico into your server and run the user script you just made in the background, or stop and restart your array.

 

That should be it. I wrote most of this from memory, so let me know if there are issues. I will improve it and add photos when I get the time but I have school to focus on. The user script just listens on the USB port and forwards anything it gets to the terminal. I don't have a reset button set up yet but may add one later. Currently pressing the button once turns it on, long press will force a shutdown, and double press resets the machine. Theoretically, the button can be programmed to run any command in the terminal, such as a docker. Shouldn't be too hard to adjust the code for that.

Edited by bobbintb
  • Like 3
Link to comment

Wow, I was just looking for something like this! Do you have a photo of your Pico setup?  Trying to think of how I can add some buttons to my hardware without looking out of place.

 

I have an Arduino Uno lying around, would it be good for this kind of project?  I'm new to Arduino, but not clueless when it comes to embedded devices.

Link to comment
10 hours ago, tombunraider said:

Wow, I was just looking for something like this! Do you have a photo of your Pico setup?  Trying to think of how I can add some buttons to my hardware without looking out of place.

 

I have an Arduino Uno lying around, would it be good for this kind of project?  I'm new to Arduino, but not clueless when it comes to embedded devices.

I don't just yet. I've been busy finishing my masters so I've been pretty slammed. Right now, it's just rudimentary, a wire hanging down with a spare button from some front panel. Just enough for a non-technical user. I have an actual button on the way but it is taking forever to get here. I have an open frame for my server and plan to 3d print a mount. It'll look nice and pretty soon. I've never used an Arduino but know a bit about them. An Uno is kind of overkill for just a button but it should work and you've got a spare so, why not? This is my first experience with any kind of embedded device and it didn't take me long so I'm sure you could do it.

Edited by bobbintb
  • Like 1
Link to comment
21 hours ago, okkies said:

so complicated. 

just run home assistant and a zigbee buttons. 

 

 

It's really not. It's about as simple as it can get and only takes a few minutes. I really don't need to invest in a whole home automation setup with all that hardware just for this. That's actually quite a bit complicated and expensive.

  • Like 1
Link to comment
  • 5 months later...

Works like charm :)

I changed the code a little to blink led :)

 

from machine import Pin
import uasyncio as asyncio
from primitives import Pushbutton

vm_name = "\'Windows 11\'"
start_command = "virsh start " + vm_name
reboot_command = "virsh reboot " + vm_name
force_shutdown_command = "virsh destroy " + vm_name

# PIN13 is D7 on DFROBOT ESP32 Beetle V1.0
btn = Pin(13, Pin.IN, Pin.PULL_UP)
# PIN27 is D4 on DFROBOT ESP32 Beetle V1.0
led = Pin(27, Pin.OUT)
pb = Pushbutton(btn, suppress=True)
led.on()

async def reboot_blinky():  # This is a coroutine
    print(reboot_command)
    for i in range(10):
      led.on()
      await asyncio.sleep_ms(300)
      led.off()
      await asyncio.sleep_ms(300)
      led.on()

async def kill_off():  # This is a coroutine
    print(force_shutdown_command)
    led.off()

async def start_blink():  # This is a coroutine
    print(start_command)
    led.on()
 
async def main():
    short_press = pb.release_func(start_blink)
    double_press = pb.double_func(reboot_blinky)
    long_press = pb.long_func(kill_off)
    while True:
        await asyncio.sleep(1)

asyncio.run(main())

 

300518201_570169228176330_3631807859047470255_n.jpg

300458374_3199292233667513_174750279706372031_n.jpg

301054011_439378258147093_7602559603595763448_n.jpg

300449832_475933487389406_214011964959515722_n.jpg

300427524_3302430370034672_1835978130666194387_n.jpg

300507625_653046079147018_7179499084474934145_n.jpg

300606949_739926560403719_3796864915173851857_n.jpg

  • Like 1
Link to comment
On 8/23/2022 at 2:17 PM, VladoPortos said:

Works like charm :)

I changed the code a little to blink led :)

 

Very nice. It's really simple to make any tweaks or changes to the script, such as blinking lights, or even to run docker containers. Basically anything you can do in the command line. I was booting over the network as well but this just gives it a bit of polish and ease of use for the rest of the family. Plus they don't bug me to turn it on because they don't remember how to do it. Nice looking enclosure you got too.

Link to comment
  • 4 weeks later...

I have been thinking of this also, there are some HA integrations, HA supports zigbee buttons etc. But I think this could be implemented without adding any additional hardware if we figure out how we disable the default action of the RESET button on our PC cases, this is done on the OS level. We could in theory remap the reset button to any command

Link to comment
On 9/23/2022 at 10:10 AM, BoKKeR said:

I have been thinking of this also, there are some HA integrations, HA supports zigbee buttons etc. But I think this could be implemented without adding any additional hardware if we figure out how we disable the default action of the RESET button on our PC cases, this is done on the OS level. We could in theory remap the reset button to any command

image.thumb.png.83343b375fb6556a3385735dcc4c500e.png

 

image.png.a9292b2a2cbd4a78f60d818af1105665.png

image.png.b48966c976eb44a74f3adb856ddbb1ce.png

image.png.f0a761383588eec540333f9bbb4f72d6.png

 

image.png.32068a0f2b6d8f27b78e4ad4dd5469c5.png

 

This is how i set it up in nodered trough home assistant. 

On 9/23/2022 at 10:10 AM, BoKKeR said:

I have been thinking of this also, there are some HA integrations, HA supports zigbee buttons etc. But I think this could be implemented without adding any additional hardware if we figure out how we disable the default action of the RESET button on our PC cases, this is done on the OS level. We could in theory remap the reset button to any command

 

  • Like 1
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.