Skip to content

πŸŽ› Receive events and control the light on a Griffin PowerMate USB device

License

Notifications You must be signed in to change notification settings

Aldaviva/PowerMate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

27 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

PowerMate

Nuget GitHub Workflow Status Testspace Coveralls

Receive events and control the light on a Griffin PowerMate USB device

  1. Quick Start
  2. Prerequisites
  3. Installation
  4. Usage
  5. Notifications
  6. Light Control
  7. Demos
  8. Acknowledgements

Griffin PowerMate

Quick Start

dotnet install PowerMate
using PowerMate;

using IPowerMateClient powerMate = new PowerMateClient();

powerMate.LightBrightness = 255;

powerMate.InputReceived += (_, input) => {
    switch (input) {
        case { IsPressed: true, RotationDirection: RotationDirection.None }:
            Console.WriteLine("PowerMate was pressed");
            break;
        case { RotationDirection: RotationDirection.Clockwise }:
            Console.WriteLine($"PowerMate was rotated clockwise {input.RotationDistance:N0} increment(s)");
            break;
        case { RotationDirection: RotationDirection.Counterclockwise }:
            Console.WriteLine($"PowerMate was rotated counterclockwise {input.RotationDistance:N0} increment(s)");
            break;
        default:
            break;
    }
};

Prerequisites

Installation

You can install this library into your project from NuGet Gallery:

  • dotnet add package PowerMate
  • Install-Package PowerMate
  • Go to Project β€Ί Manage NuGet Packages in Visual Studio and search for PowerMate

Usage

  1. Construct a new instance of the PowerMateClient class.

    using IPowerMateClient powerMate = new PowerMateClient();

    You should dispose of instances when you're done with them by calling Dispose(), or with a using statement or declaration.

  2. Now you can listen for InputReceived events from the client.

    powerMate.InputReceived += (_, input) => Console.WriteLine($"Received PowerMate event: {input}");

Connections

This library will automatically try to connect to one of the PowerMate devices that are plugged into your computer. If no device is connected, it will automatically wait until one appears and then connect to it. If a device disconnects, this library will reconnect automatically when one reappears.

If multiple PowerMate devices are present simultaneously, this library will pick one of them arbitrarily and use it until it disconnects.

The connection state is exposed by the bool IsConnected property, and changes to this state are emitted by the IsConnectedChanged event.

Console.WriteLine(powerMate.IsConnected 
    ? "Listening for movements from PowerMate." 
    : "Waiting for a PowerMate to be connected.");

powerMate.IsConnectedChanged += (_, isConnected) => Console.WriteLine(isConnected 
    ? "Reconnected to a PowerMate." 
    : "Disconnected from the PowerMate, attempting reconnection...");

Notifications

InputReceived event

Fired whenever the PowerMate knob is rotated, pressed, or released.

powerMate.InputReceived += (_, input) => Console.WriteLine($"Received PowerMate event: {input}");

The event argument is a PowerMateInput struct with the following fields.

Field name Type Example values Description
IsPressed bool true false true if the knob is being held down, or false if it is up. Pressing and releasing the knob will generate two events, with true and false in order. Will also be true when the knob is rotated while being held down.
RotationDirection enum None Clockwise Counterclockwise The direction the knob is being rotated when viewed from above, or None if it is not being rotated.
RotationDistance uint 0 1 2 How far, in arbitrary angular units, the knob was rotated since the last update. When you rotate the knob slowly, you will receive multiple events, each with this set to 1. As you rotate it faster, updates are batched and this number increases to 2 or more. The highest value I have seen is 8. This is always non-negative, regardless of the rotation direction; use RotationDirection to determine the direction. If the knob is pressed without being rotated, this is 0.

IsConnectedChanged event

Fired whenever the connection state of the PowerMate changes. Not fired when constructing or disposing the PowerMateClient instance.

The event argument is a bool which is true when a PowerMate has reconnected, or false when it has disconnected.

To get the value of this state at any time, read the IsConnected property on the IPowerMateClient instance.

powerMate.IsConnectedChanged += (_, isConnected) => Console.WriteLine(isConnected 
    ? "Reconnected to a PowerMate." 
    : "Disconnected from the PowerMate, attempting reconnection...");

Light Control

By default, the blue/cyan LED in the base of the PowerMate lights up when it's plugged in. You can change the intensity of the light, make it pulse at different frequencies, or turn it off entirely by setting the following properties. The values that you set are retained across reconnections.

LightBrightness property

A writable byte property with valid values in the range [0, 255]. 0 represents off, and 255 is the brightest.

The default value is 80, which matches the brightness the device uses when no program has instructed it to change its brightness since it has been plugged in.

Changes to LightBrightness will not take effect while the LED is pulsing with a LightAnimation of Pulsing.

powerMate.LightBrightness = 0;
powerMate.LightAnimation = LightAnimation.Solid;
powerMate.LightBrightness = 255;
powerMate.LightAnimation = LightAnimation.Solid;

LightAnimation property

A writable enum property that controls how the light is animated. Available values are

  • Solid (default) β€” the light shines at a constant brightness, controlled by LightBrightness
  • Pulsing β€” the light raises and lowers its brightness to the highest and lowest levels in a cyclical animation, with the frequency controlled by LightPulseSpeed
  • SolidWhileAwakeAndPulsingDuringComputerStandby β€” while the computer to which the device is connected is awake, the light shines at a constant brightness (controlled by LightBrightness), but while the computer is asleep, the light displays the pulsing animation (with the frequency controlled by LightPulseSpeed)
powerMate.LightPulseSpeed = 12;
powerMate.LightAnimation = LightAnimation.Pulsing;

LightPulseSpeed property

A writable int property with valid values in the range [0, 24]. Values outside that range are clamped. The default value is 12.

This property controls how fast the light pulses when LightAnimation is set to Pulsing or SolidWhileAwakeAndPulsingDuringComputerStandby. The slowest pulse speed, 0, is about 0.03443 Hz, or a 29.04 sec period. The fastest pulse speed, 24, is about 15.63 Hz, or a 64 ms period.

powerMate.LightPulseSpeed = 12;
powerMate.LightAnimation = LightAnimation.Pulsing;

PowerMate light state loss on resume

When the computer goes into standby mode and then resumes, the PowerMate loses all of its state and resets its settings to their default values, erasing your light control changes. There are two techniques to fix this, and you should use both of them.

Automatically reapply settings on stale input

Each time the PowerMate device sends an input to the computer, such as a knob turn or press, it also sends the current state of the lights. This library checks that device state and compares it to the values you set using LightBrightness, LightAnimation, and LightPulseSpeed. If any of them differ, it will automatically send the correct values to the PowerMate. You don't have to do anything to enable this behavior.

Manually reapply settings on resume

Unfortunately, the user is likely to see the incorrect light state before they send an input with the PowerMate: it will be wrong as soon as the computer resumes, and they may not need to touch the PowerMate until much later.

To fix this, your program should also wait for the computer to resume from standby, and when it does, force this library to resend all of the light control property values to the device by calling SetAllFeaturesIfStale().

To detect when a Windows computer resumes from standby, a successful strategy is to listen for event ID 107 from the Kernel-Power source in the System log.

using IStandbyListener standbyListener = new EventLogStandbyListener();
standbyListener.Resumed += (_, _) => powerMate.SetAllFeaturesIfStale();

β›” You should not listen for SystemEvents.PowerModeChanged events because they are unreliable and do not get sent about 5% of the time.

Demos

Simple demo

This console program just prints out each event it receives from the PowerMate.

> Demo-x64.exe
Listening for PowerMate events
Received event from PowerMate: Turning clockwise 1 increment while not pressed
Received event from PowerMate: Turning clockwise 1 increment while not pressed
Received event from PowerMate: Turning clockwise 1 increment while not pressed
^C

Volume control

This background program increases and decreases the output volume of the default Windows audio output device when you turn the PowerMate knob clockwise and counterclockwise. It also toggles the output mute when you press the knob.

PowerMateVolume.exe

By default, it increments and decrements the volume by 1 percentage point for each rotation event. You can customize this amount by passing a different percentage as a command-line argument, such as Β½ or 2 percentage points:

PowerMateVolume.exe 0.005
PowerMateVolume.exe 0.02

Acknowledgements

Luke Ma Luke Ma for giving me a PowerMate as a Christmas gift in 2013. Thanks Luke!