Skip to content

Instantly share code, notes, and snippets.

@todbot
Last active January 25, 2024 23:51
Show Gist options
  • Save todbot/5cd232e16c7a67bd2172d6719f8a8518 to your computer and use it in GitHub Desktop.
Save todbot/5cd232e16c7a67bd2172d6719f8a8518 to your computer and use it in GitHub Desktop.
Sleep a Windows box with CircuitPython using USB HID
# SPDX-FileCopyrightText: Copyright (c) 2023 Tod Kurt
#
# SPDX-License-Identifier: MIT
"""
`sleepy_keys`
=============
HID System Control Powerdown / Sleep / Wake device helper library
* Author(s): Tod Kurt
Implementation Notes
--------------------
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
**Reference:***
* "Input Device Class Power Management Reference Specification, v. 2.0 "
http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/Inpmspc.rtf
* "HID Usage Tables, section 4.5.1"
https://www.usb.org/document-library/hid-usage-tables-14
"""
import time
import usb_hid
from adafruit_hid import find_device
def device(report_id: int = 4) -> usb_hid.Device:
"""Create a `usb_hid.Device` for System Control Power buttons
:param int report_id: The report ID to use for the device.
"""
return usb_hid.Device(
# fmt: off
report_descriptor=bytes((
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x09, 0x80, # Usage (Sys Control)
0xA1, 0x01, # Collection (Application)
0x85, 0x04, # Report ID (4)
0x75, 0x02, # Report Size (2)
0x95, 0x01, # Report Count (1)
0x15, 0x01, # Logical Minimum (1)
0x25, 0x03, # Logical Maximum (3)
0x09, 0x82, # Usage (Sys Sleep)
0x09, 0x81, # Usage (Sys Power Down)
0x09, 0x83, # Usage (Sys Wake Up)
0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State)
0x75, 0x06, # Report Size (6)
0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
)),
# fmt: on
usage_page=0x01,
usage=0x80,
report_ids=(report_id,),
in_report_lengths=(1,),
out_report_lengths=(0,),
)
powerdown_report = bytes( (0x81, ) ) # powerdown
sleep_report = bytes( (0x82, ) ) # sleep
wakeup_report = bytes( (0x83, ) ) # wake
class SleepyKeys:
"""Send System Control Power HID reports"""
def __init__(self, devices):
"""Create a SleepyKeys object that will send System Control HID reports.
Devices can be a list of devices that includes a keyboard device or a keyboard device
itself. A device is any object that implements ``send_report()``, ``usage_page`` and
``usage``.
"""
self._device = find_device(devices, usage_page=0x01, usage=0x80)
def send_powerdown(self):
"""From the spec:
Should send usage “System Power Down” when the button is
pressed to power down the system. The system power policy manager
will look up the POWER button action in the current power policy
(by default, shutdown) and take that action.
"""
self._device.send_report( powerdown_report )
def send_sleep(self):
"""From the spec:
Should send usage “System Sleep” when the button is pressed to put
the system to sleep. The system power policy manager will look up
the SLEEP button action in the current power policy
(by default, sleep) and take that action.
"""
self._device.send_report( sleep_report )
def send_wakeup(self):
"""From the spec:
Should send usage “System Wake Up” when the SLEEP button is pressed
again to wake up the system.
"""
self._device.send_report( wakeup_report )
# SPDX-FileCopyrightText: Copyright (c) 2023 Tod Kurt
"""
Demo of SleepyKeys -- boot.py
"""
import usb_hid
import sleepy_keys
sleep_keys_device = sleepy_keys.device()
usb_hid.enable( (usb_hid.Device.KEYBOARD,
usb_hid.Device.MOUSE,
usb_hid.Device.CONSUMER_CONTROL,
sleep_keys_device) )
print("enabled sleep keys")
# SPDX-FileCopyrightText: Copyright (c) 2023 Tod Kurt
"""
Demo of SleepyKeys on Adafruit MacroPad RP2040 -- code.py
"""
import time, board, keypad
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
import sleepy_keys
sleep_keys = sleepy_keys.SleepyKeys(usb_hid.devices)
kbd = Keyboard(usb_hid.devices)
key_pins = [getattr(board, "KEY%d" % (num + 1)) for num in range(12)]
keys = keypad.Keys(key_pins, value_when_pressed=False, pull=True)
print("ready to go, press bottom keys to go to sleep")
while True:
event = keys.events.get()
if event:
key_number = event.key_number
print("key:", key_number, event.pressed)
if event.pressed:
if key_number in (9,10,11): # bottom three keys
print("sleeping... ")
sleep_keys.send_sleep()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment