Skip to content

Add support for StreamDeck Plus XL and update product IDs#170

Open
mdlopezme wants to merge 2 commits intoabcminiuser:masterfrom
airacingtech:moises/streamdeck_plus_xl
Open

Add support for StreamDeck Plus XL and update product IDs#170
mdlopezme wants to merge 2 commits intoabcminiuser:masterfrom
airacingtech:moises/streamdeck_plus_xl

Conversation

@mdlopezme
Copy link

Summary

Implements full device driver support for the Elgato Stream Deck + XL (USB PID 0x00c6). The device was reverse-engineered via HID probing on the physical hardware and all functionality has been verified end-to-end.

Hardware Specs (discovered via reverse engineering)

Property Value
Keys 36 (9 columns x 4 rows)
Key resolution 112 x 112 px, JPEG
Dials / Encoders 6 (turn + push)
Touchscreen 1200 x 100 px, JPEG
LCD panel 800 x 1280 (portrait internally, rotated 90 CW by device)
USB PID 0x00c6
Input reports 64 bytes
HID protocol Compatible with Stream Deck + (same event types and command bytes)

Key technical findings

  • LCD is portrait internally: The Plus XL's LCD is 800x1280 in its native orientation. The device rotates display 90 CW. Key images need KEY_ROTATION = 90 (PIL rotates 90 CCW before sending). Touchscreen images are rotated manually in set_touchscreen_image with expand=True because the shared PILHelper._to_native_format uses rotate() without expand=True, which crops non-square images.
  • Touchscreen coordinate system: The 0x0c LCD command uses internal portrait coordinates. User-facing (x, y, w, h) are swapped to (y, x, h, w) in the 0x0c header.
  • Serial/firmware offsets differ from Plus: Serial at report 0x06 offset 2 (Plus uses 5), firmware at report 0x05 offset 6 (Plus uses 5).

Changes

Added

  • src/StreamDeck/Devices/StreamDeckPlusXL.py — Full driver implementation. Includes all abstract method implementations, HID event parsing, image upload, and touchscreen rotation handling.

Test Results

# Full library suite (all device models, dummy transport)
$ python3 test/test.py
51/51 tests finished (17 models x 3 tests)

Hardware validation was performed on a physical Stream Deck + XL with 20 interactive checks — all passed.

How to verify with hardware

If you have a Stream Deck + XL, copy the script below and run it. It walks through 20 checks covering keys, dials, touchscreen, brightness, and callbacks — you confirm each step by observing the device.

Hardware validation script — click to expand, save, and run
#!/usr/bin/env python3

#         Python Stream Deck Library
#      Released under the MIT license
#

# Hardware validation test for Stream Deck + XL.
# Requires a physical device connected. A human operator confirms
# each test step by observing the device and answering prompts.
#
# Usage:
#     pip install streamdeck pillow
#     python3 test_plus_xl_hardware.py

import sys
import time
import threading

from PIL import Image, ImageDraw
from StreamDeck.DeviceManager import DeviceManager
from StreamDeck.ImageHelpers import PILHelper
from StreamDeck.Devices.StreamDeck import DialEventType, TouchscreenEventType

DECK_TYPE = "Stream Deck + XL"

passed = 0
failed = 0
skipped = 0


def ask(question):
    """Ask operator a yes/no question. Returns True for yes."""
    while True:
        answer = input(f"\n  ? {question} [y/n/s(kip)]: ").strip().lower()
        if answer in ('y', 'yes'):
            return 'yes'
        elif answer in ('n', 'no'):
            return 'no'
        elif answer in ('s', 'skip'):
            return 'skip'


def check(name, question):
    """Ask operator to validate, record result."""
    global passed, failed, skipped
    result = ask(question)
    if result == 'yes':
        passed += 1
        print(f"  PASS: {name}")
    elif result == 'skip':
        skipped += 1
        print(f"  SKIP: {name}")
    else:
        failed += 1
        print(f"  FAIL: {name}")


def wait(seconds, msg=""):
    """Wait with a visible countdown."""
    if msg:
        print(f"  ({msg})")
    time.sleep(seconds)


def make_solid_key(deck, r, g, b):
    """Create a solid color key image."""
    img = Image.new('RGB', (deck.KEY_PIXEL_WIDTH, deck.KEY_PIXEL_HEIGHT), (r, g, b))
    return PILHelper.to_native_key_format(deck, img)


def make_labeled_key(deck, label, bg=(20, 20, 40), fg='white'):
    """Create a key image with centered text."""
    img = Image.new('RGB', (deck.KEY_PIXEL_WIDTH, deck.KEY_PIXEL_HEIGHT), bg)
    draw = ImageDraw.Draw(img)
    bbox = draw.textbbox((0, 0), label)
    tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
    x = (deck.KEY_PIXEL_WIDTH - tw) // 2
    y = (deck.KEY_PIXEL_HEIGHT - th) // 2
    draw.text((x, y), label, fill=fg)
    return PILHelper.to_native_key_format(deck, img)


def make_touchscreen_bar(deck, color, label=""):
    """Create a full-width touchscreen image with optional label."""
    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT
    img = Image.new('RGB', (w, h), color)
    if label:
        draw = ImageDraw.Draw(img)
        draw.text((20, h // 2 - 6), label, fill='white')
    return PILHelper.to_native_touchscreen_format(deck, img)


# ---------------------------------------------------------------------------
# Test steps
# ---------------------------------------------------------------------------

def test_device_info(deck):
    print("\n=== 1. Device Info ===")
    serial = deck.get_serial_number()
    firmware = deck.get_firmware_version()
    print(f"  Serial:   {serial}")
    print(f"  Firmware: {firmware}")
    print(f"  Keys:     {deck.key_count()} ({deck.KEY_COLS}x{deck.KEY_ROWS})")
    print(f"  Dials:    {deck.DIAL_COUNT}")
    print(f"  Screen:   {deck.TOUCHSCREEN_PIXEL_WIDTH}x{deck.TOUCHSCREEN_PIXEL_HEIGHT}")
    check("device_info", "Does the info above look correct for your Stream Deck + XL?")


def test_brightness(deck):
    print("\n=== 2. Brightness Control ===")

    # Set a visible image first so brightness changes are observable
    for key in range(deck.key_count()):
        deck.set_key_image(key, make_solid_key(deck, 100, 100, 100))

    print("  Setting brightness to 100%...")
    deck.set_brightness(100)
    wait(1.5)

    print("  Setting brightness to 10%...")
    deck.set_brightness(10)
    wait(1.5)

    print("  Setting brightness to 50%...")
    deck.set_brightness(50)
    wait(1.5)

    print("  Setting brightness to 100%...")
    deck.set_brightness(100)

    check("brightness", "Did the keys get bright, then dim, then medium, then bright again?")

    # Clear
    for key in range(deck.key_count()):
        deck.set_key_image(key, None)


def test_all_keys_light_up(deck):
    print("\n=== 3. All Keys Light Up ===")
    print("  Lighting all 36 keys white...")

    for key in range(deck.key_count()):
        deck.set_key_image(key, make_solid_key(deck, 255, 255, 255))
    wait(1)

    check("all_keys_white", "Are ALL 36 keys lit up white? (9 columns x 4 rows)")

    # Clear
    for key in range(deck.key_count()):
        deck.set_key_image(key, None)


def test_key_numbering(deck):
    print("\n=== 4. Key Numbering ===")
    print("  Displaying key index numbers 0-35...")

    for key in range(deck.key_count()):
        deck.set_key_image(key, make_labeled_key(deck, str(key)))

    check("key_numbers",
          "Do you see numbers 0-35? Top-left=0, top-right=8, bottom-left=27, bottom-right=35?")

    # Clear
    for key in range(deck.key_count()):
        deck.set_key_image(key, None)


def test_key_colors_by_row(deck):
    print("\n=== 5. Row Colors ===")
    colors = [
        (255, 0, 0),      # Row 0: Red
        (0, 255, 0),      # Row 1: Green
        (0, 0, 255),      # Row 2: Blue
        (255, 255, 0),    # Row 3: Yellow
    ]
    names = ["RED", "GREEN", "BLUE", "YELLOW"]

    print("  Row 0 (top):    RED")
    print("  Row 1:          GREEN")
    print("  Row 2:          BLUE")
    print("  Row 3 (bottom): YELLOW")

    for key in range(deck.key_count()):
        row = key // deck.KEY_COLS
        r, g, b = colors[row]
        deck.set_key_image(key, make_solid_key(deck, r, g, b))

    check("row_colors",
          "Is the top row RED, second GREEN, third BLUE, bottom YELLOW?")

    # Clear
    for key in range(deck.key_count()):
        deck.set_key_image(key, None)


def test_key_colors_by_column(deck):
    print("\n=== 6. Column Gradient ===")
    print("  Columns should go from dark (left) to bright (right)...")

    for key in range(deck.key_count()):
        col = key % deck.KEY_COLS
        brightness = int(255 * col / (deck.KEY_COLS - 1))
        deck.set_key_image(key, make_solid_key(deck, 0, brightness, brightness))

    check("column_gradient",
          "Do the keys get brighter from left to right (dark cyan to bright cyan)?")

    # Clear
    for key in range(deck.key_count()):
        deck.set_key_image(key, None)


def test_single_key_highlight(deck):
    print("\n=== 7. Corner Keys ===")
    corners = {
        0: "top-left",
        8: "top-right",
        27: "bottom-left",
        35: "bottom-right",
    }

    for key_idx, position in corners.items():
        # All black except the target
        for k in range(deck.key_count()):
            deck.set_key_image(k, make_solid_key(deck, 0, 0, 0))
        deck.set_key_image(key_idx, make_solid_key(deck, 255, 0, 255))
        check(f"corner_{position}",
              f"Is the ONLY lit key (magenta) in the {position} corner?")

    # Clear
    for key in range(deck.key_count()):
        deck.set_key_image(key, None)


def test_touchscreen_colors(deck):
    print("\n=== 8. Touchscreen Colors ===")
    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT

    print("  Showing RED on the touchscreen strip...")
    deck.set_touchscreen_image(make_touchscreen_bar(deck, (255, 0, 0), "RED"), 0, 0, w, h)
    wait(1.5)

    print("  Showing GREEN...")
    deck.set_touchscreen_image(make_touchscreen_bar(deck, (0, 255, 0), "GREEN"), 0, 0, w, h)
    wait(1.5)

    print("  Showing BLUE...")
    deck.set_touchscreen_image(make_touchscreen_bar(deck, (0, 0, 255), "BLUE"), 0, 0, w, h)
    wait(1.5)

    check("touchscreen_colors",
          "Did the touchscreen strip show RED, then GREEN, then BLUE?")


def test_touchscreen_gradient(deck):
    print("\n=== 9. Touchscreen Gradient ===")
    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT
    print(f"  Drawing a horizontal gradient ({w}x{h})...")

    img = Image.new('RGB', (w, h))
    draw = ImageDraw.Draw(img)
    for x in range(w):
        r = int(255 * x / w)
        draw.line([(x, 0), (x, h)], fill=(r, 0, 255 - r))

    native = PILHelper.to_native_touchscreen_format(deck, img)
    deck.set_touchscreen_image(native, 0, 0, w, h)

    check("touchscreen_gradient",
          "Does the touchscreen show a smooth gradient from blue (left) to red (right)?")


def test_touchscreen_partial(deck):
    print("\n=== 10. Touchscreen Composited Update ===")
    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT

    # Build a full-strip image with a yellow rectangle composited in the center
    canvas = Image.new('RGB', (w, h), (30, 30, 30))
    draw = ImageDraw.Draw(canvas)
    mid_w, mid_h = 300, 60
    mid_x, mid_y = (w - mid_w) // 2, (h - mid_h) // 2
    draw.rectangle([mid_x, mid_y, mid_x + mid_w, mid_y + mid_h], fill=(255, 200, 0))
    draw.text((mid_x + 100, mid_y + 20), "PARTIAL", fill='black')

    native = PILHelper.to_native_touchscreen_format(deck, canvas)
    deck.set_touchscreen_image(native, 0, 0, w, h)

    check("touchscreen_partial",
          "Do you see a yellow rectangle with 'PARTIAL' centered on a dark touchscreen?")


def test_key_press_callback(deck):
    print("\n=== 11. Key Press Detection ===")
    print("  Press any 3 different keys. They should turn GREEN when pressed, BLACK when released.")

    events = []
    event = threading.Event()

    def on_key(d, key, state):
        action = "PRESSED" if state else "RELEASED"
        print(f"    Key {key} {action}")
        if state:
            d.set_key_image(key, make_solid_key(d, 0, 255, 0))
            events.append(key)
        else:
            d.set_key_image(key, make_solid_key(d, 0, 0, 0))
        if len(events) >= 3:
            event.set()

    # Show all keys as black
    for k in range(deck.key_count()):
        deck.set_key_image(k, make_solid_key(deck, 0, 0, 0))

    deck.set_key_callback(on_key)
    print("  Waiting for 3 key presses (15s timeout)...")
    event.wait(timeout=15)
    deck.set_key_callback(None)

    if len(events) >= 3:
        check("key_press",
              "Did the correct keys flash green when you pressed them?")
    else:
        print(f"  Only got {len(events)} presses.")
        check("key_press", "Did the keys you pressed flash green?")


def test_dial_turn_callback(deck):
    print("\n=== 12. Dial Turn Detection ===")
    print("  Turn each of the 6 dials at least once. The touchscreen will show which dial and direction.")

    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT
    dials_seen = set()
    event = threading.Event()

    def update_display(label):
        img = Image.new('RGB', (w, h), (10, 10, 40))
        draw = ImageDraw.Draw(img)
        draw.text((20, 15), label, fill='white')
        seen_str = f"Dials turned: {sorted(dials_seen)}"
        draw.text((20, 55), seen_str, fill=(150, 255, 150))
        native = PILHelper.to_native_touchscreen_format(deck, img)
        deck.set_touchscreen_image(native, 0, 0, w, h)

    def on_dial(d, dial, evt, value):
        if evt == DialEventType.TURN:
            direction = "CW" if value > 0 else "CCW"
            print(f"    Dial {dial} turned {direction} ({value:+d})")
            dials_seen.add(dial)
            update_display(f"Dial {dial} turned {direction} ({value:+d})")
            if len(dials_seen) >= deck.DIAL_COUNT:
                event.set()

    update_display("Turn each dial...")
    deck.set_dial_callback(on_dial)
    print(f"  Waiting for all {deck.DIAL_COUNT} dials to be turned (20s timeout)...")
    event.wait(timeout=20)
    deck.set_dial_callback(None)

    if len(dials_seen) >= deck.DIAL_COUNT:
        check("dial_turn",
              f"Did all {deck.DIAL_COUNT} dials register correctly? Dials seen: {sorted(dials_seen)}")
    else:
        check("dial_turn",
              f"Detected {len(dials_seen)}/{deck.DIAL_COUNT} dials: {sorted(dials_seen)}. Did they respond correctly?")


def test_dial_push_callback(deck):
    print("\n=== 13. Dial Push Detection ===")
    print("  Push (click) each of the 6 dials. The touchscreen will show which one.")

    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT
    dials_pushed = set()
    event = threading.Event()

    def update_display(label):
        img = Image.new('RGB', (w, h), (40, 10, 10))
        draw = ImageDraw.Draw(img)
        draw.text((20, 15), label, fill='white')
        seen_str = f"Dials pushed: {sorted(dials_pushed)}"
        draw.text((20, 55), seen_str, fill=(255, 150, 150))
        native = PILHelper.to_native_touchscreen_format(deck, img)
        deck.set_touchscreen_image(native, 0, 0, w, h)

    def on_dial(d, dial, evt, value):
        if evt == DialEventType.PUSH and value:
            print(f"    Dial {dial} pushed")
            dials_pushed.add(dial)
            update_display(f"Dial {dial} pushed!")
            if len(dials_pushed) >= deck.DIAL_COUNT:
                event.set()

    update_display("Push each dial...")
    deck.set_dial_callback(on_dial)
    print(f"  Waiting for all {deck.DIAL_COUNT} dials to be pushed (20s timeout)...")
    event.wait(timeout=20)
    deck.set_dial_callback(None)

    if len(dials_pushed) >= deck.DIAL_COUNT:
        check("dial_push",
              f"Did all {deck.DIAL_COUNT} dials register push? Dials: {sorted(dials_pushed)}")
    else:
        check("dial_push",
              f"Detected {len(dials_pushed)}/{deck.DIAL_COUNT} pushes: {sorted(dials_pushed)}. Did they respond?")


def test_touchscreen_tap_callback(deck):
    print("\n=== 14. Touchscreen Tap Detection ===")
    print("  Tap the touchscreen in 3 different spots. Dots will appear where you tap.")

    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT
    canvas = Image.new('RGB', (w, h), (10, 10, 30))
    draw = ImageDraw.Draw(canvas)
    draw.text((20, h // 2 - 6), "Tap me in 3 places!", fill=(100, 100, 100))
    native = PILHelper.to_native_touchscreen_format(deck, canvas)
    deck.set_touchscreen_image(native, 0, 0, w, h)

    taps = []
    event = threading.Event()

    def on_touch(d, evt, value):
        if evt == TouchscreenEventType.SHORT:
            x, y = value['x'], value['y']
            print(f"    Tap at ({x}, {y})")
            taps.append((x, y))

            # Draw a dot at the tap location
            draw.ellipse([x - 8, y - 8, x + 8, y + 8], fill=(0, 255, 255))
            draw.text((x + 12, y - 6), f"({x},{y})", fill='white')
            native = PILHelper.to_native_touchscreen_format(d, canvas)
            d.set_touchscreen_image(native, 0, 0, w, h)

            if len(taps) >= 3:
                event.set()

    deck.set_touchscreen_callback(on_touch)
    print("  Waiting for 3 taps (15s timeout)...")
    event.wait(timeout=15)
    deck.set_touchscreen_callback(None)

    if len(taps) >= 3:
        check("touchscreen_tap",
              "Did cyan dots appear at the spots you tapped?")
    else:
        check("touchscreen_tap",
              f"Only got {len(taps)} taps. Did they appear in the right spots?")


def test_touchscreen_drag_callback(deck):
    print("\n=== 15. Touchscreen Drag Detection ===")
    print("  Drag your finger across the touchscreen. A line will be drawn.")

    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT
    canvas = Image.new('RGB', (w, h), (10, 10, 30))
    draw = ImageDraw.Draw(canvas)
    draw.text((20, h // 2 - 6), "Drag across me!", fill=(100, 100, 100))
    native = PILHelper.to_native_touchscreen_format(deck, canvas)
    deck.set_touchscreen_image(native, 0, 0, w, h)

    drags = []
    event = threading.Event()

    def on_touch(d, evt, value):
        if evt == TouchscreenEventType.DRAG:
            x1, y1 = value['x'], value['y']
            x2, y2 = value['x_out'], value['y_out']
            print(f"    Drag from ({x1},{y1}) to ({x2},{y2})")
            drags.append((x1, y1, x2, y2))

            draw.line([(x1, y1), (x2, y2)], fill=(255, 100, 0), width=3)
            draw.ellipse([x1 - 4, y1 - 4, x1 + 4, y1 + 4], fill=(0, 255, 0))
            draw.ellipse([x2 - 4, y2 - 4, x2 + 4, y2 + 4], fill=(255, 0, 0))
            native = PILHelper.to_native_touchscreen_format(d, canvas)
            d.set_touchscreen_image(native, 0, 0, w, h)

            if len(drags) >= 1:
                event.set()

    deck.set_touchscreen_callback(on_touch)
    print("  Waiting for a drag gesture (15s timeout)...")
    event.wait(timeout=15)
    deck.set_touchscreen_callback(None)

    if drags:
        check("touchscreen_drag",
              "Did a line appear following your drag? (green=start, red=end)")
    else:
        check("touchscreen_drag", "No drag detected. Did you swipe across the touchscreen?")


def test_rapid_key_update(deck):
    print("\n=== 16. Rapid Key Animation ===")
    print("  Running a sweep animation across all keys...")

    for frame in range(3):
        for key in range(deck.key_count()):
            deck.set_key_image(key, make_solid_key(deck, 0, 200, 255))
            time.sleep(0.03)
            deck.set_key_image(key, make_solid_key(deck, 0, 0, 0))

    # Leave all off
    check("rapid_update",
          "Did you see a cyan dot sweep across the keys left-to-right, top-to-bottom, 3 times?")


def test_reset(deck):
    print("\n=== 17. Device Reset ===")
    # Set some state
    for key in range(deck.key_count()):
        deck.set_key_image(key, make_solid_key(deck, 255, 128, 0))
    w, h = deck.TOUCHSCREEN_PIXEL_WIDTH, deck.TOUCHSCREEN_PIXEL_HEIGHT
    deck.set_touchscreen_image(make_touchscreen_bar(deck, (255, 128, 0), "BEFORE RESET"), 0, 0, w, h)
    wait(1)

    print("  Calling reset()...")
    deck.reset()
    wait(1)

    check("reset",
          "Did the device clear all keys and show the Elgato logo / default state?")


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    print("=" * 60)
    print("  Stream Deck + XL — Hardware Validation Test")
    print("=" * 60)
    print()
    print("This test requires a physical Stream Deck + XL.")
    print("You will be asked to observe the device and confirm behavior.")
    print()

    streamdecks = DeviceManager().enumerate()
    deck = None
    for d in streamdecks:
        if d.DECK_TYPE == DECK_TYPE:
            deck = d
            break

    if not deck:
        found = [d.deck_type() for d in streamdecks]
        print(f"ERROR: No {DECK_TYPE} found. Devices: {found}")
        sys.exit(1)

    deck.open()
    deck.reset()
    deck.set_brightness(100)

    print(f"Found: {deck.deck_type()} (serial: {deck.get_serial_number()})")

    tests = [
        test_device_info,
        test_brightness,
        test_all_keys_light_up,
        test_key_numbering,
        test_key_colors_by_row,
        test_key_colors_by_column,
        test_single_key_highlight,
        test_touchscreen_colors,
        test_touchscreen_gradient,
        test_touchscreen_partial,
        test_key_press_callback,
        test_dial_turn_callback,
        test_dial_push_callback,
        test_touchscreen_tap_callback,
        test_touchscreen_drag_callback,
        test_rapid_key_update,
        test_reset,
    ]

    try:
        for test_fn in tests:
            test_fn(deck)
    except KeyboardInterrupt:
        print("\n\nInterrupted by user.")
    finally:
        deck.reset()
        deck.close()

    total = passed + failed + skipped
    print()
    print("=" * 60)
    print(f"  Results: {passed} passed, {failed} failed, {skipped} skipped (of {total})")
    print("=" * 60)

    if failed > 0:
        sys.exit(1)
    else:
        sys.exit(0)

Test coverage breakdown

# Test What it validates
1 Device Info Serial, firmware, key/dial counts
2 Brightness 100% → 10% → 50% → 100% transitions
3 All Keys All 36 keys light up white (9x4)
4 Key Numbering Numbers 0-35 in correct grid positions
5 Row Colors Red/Green/Blue/Yellow by row
6 Column Gradient Dark-to-bright left-to-right
7 Corner Keys 4 individual corner key checks
8 Touchscreen Colors Red → Green → Blue on strip
9 Touchscreen Gradient Smooth blue-to-red horizontal gradient
10 Composited Update Yellow "PARTIAL" box on dark background
11 Key Press Press 3 keys — flash green on press
12 Dial Turn Turn all 6 dials — screen shows which
13 Dial Push Push all 6 dials — screen shows which
14 Touchscreen Tap Tap 3 spots — cyan dots appear at position
15 Touchscreen Drag Swipe — orange line with green/red endpoints
16 Rapid Animation Cyan sweep across all keys 3x
17 Reset Device clears to default/logo state

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant