SteelSeries / gamesense-sdk

471 stars 145 forks source link

Help Needed Troubleshooting Inaccurate Data Display on OLED with Keyboard #138

Closed DavidN-Work closed 5 months ago

DavidN-Work commented 5 months ago

Hey Guys,

I'm developing an app using the GameSense SDK to display music data from Sonos on the SteelSeries OLED screen. However, I'm encountering an issue where the OLED only shows "1" under "EVENT VALUE" regardless of the actual music data.

------------------------------
| EVENT VALUE                |
| 1                          |
------------------------------

Details:

Event Binding:

def bind_event_handler():
    handler = {
        "game": "HELLO_APP",
        "event": "HELLO_WORLD",
        "min_value": 0,
        "max_value": 1,
        "icon_id": 1,
        "handlers": [
            {
                "device-type": "screened-128x40",
                "zone": "one",
                "mode": "screen",
                "datas": [{"has-text": True, "lines": [{"text": "Hello, World!"}]}],
            }
        ],
    }
    return send_request("bind_game_event", handler) 

Payload:

def create_hello_world_payload():
    return {
        "game": "HELLO_APP",
        "event": "HELLO_WORLD",
        "data": {
            "value": 1,
            "frame": {"lines": [{"has-text": True, "text": "Hello, World!"}]},
        },
    }

System Information:

Additional Notes:

Full Code

import requests
import json
import time

# Function to send a request to the SteelSeries Engine
def send_request(endpoint, payload):
    try:
        with open(
            "/Library/Application Support/SteelSeries Engine 3/coreProps.json", "r"
        ) as file:
            core_props = json.load(file)
    except FileNotFoundError:
        try:
            with open(
                "C:\\ProgramData\\SteelSeries\\SteelSeries Engine 3\\coreProps.json",
                "r",
            ) as file:
                core_props = json.load(file)
        except FileNotFoundError:
            print("coreProps.json not found. Is SteelSeries Engine running?")
            return False

    sse_address = f"http://{core_props['address']}/{endpoint}"
    headers = {"Content-Type": "application/json"}
    response = requests.post(sse_address, headers=headers, data=json.dumps(payload))

    if response.status_code == 200:
        print(f"Successfully sent request to {endpoint}")
        return True
    else:
        print(
            f"Failed to send request to {endpoint}: {response.status_code}, {response.text}"
        )
        return False

# Unregister the event from SteelSeries Engine
def unregister_event():
    event_metadata = {"game": "HELLO_APP", "event": "HELLO_WORLD"}
    return send_request("remove_game_event", event_metadata)

# Unregister the game from SteelSeries Engine
def unregister_game():
    game_metadata = {"game": "HELLO_APP"}
    return send_request("remove_game", game_metadata)

# Register the game with SteelSeries Engine
def register_game():
    game_metadata = {
        "game": "HELLO_APP",
        "game_display_name": "Hello App",
        "developer": "David",
    }
    return send_request("game_metadata", game_metadata)

# Register the event with SteelSeries Engine
def register_event():
    event_metadata = {
        "game": "HELLO_APP",
        "event": "HELLO_WORLD",
        "min_value": 0,
        "max_value": 1,
        "icon_id": 1,
        "value_optional": True,
    }
    return send_request("register_game_event", event_metadata)

# Bind the event handler with SteelSeries Engine
def bind_event_handler():
    handler = {
        "game": "HELLO_APP",
        "event": "HELLO_WORLD",
        "min_value": 0,
        "max_value": 1,
        "icon_id": 1,
        "handlers": [
            {
                "device-type": "screened-128x40",
                "zone": "one",
                "mode": "screen",
                "datas": [{"has-text": True, "lines": [{"text": "Hello, World!"}]}],
            }
        ],
    }
    return send_request("bind_game_event", handler)

# Create the JSON payload for "Hello, World!" event
def create_hello_world_payload():
    return {
        "game": "HELLO_APP",
        "event": "HELLO_WORLD",
        "data": {
            "value": 1,
            "frame": {"lines": [{"has-text": True, "text": "Hello, World!"}]},
        },
    }

# Send the "Hello, World!" event to SteelSeries Engine
def send_hello_world_event():
    payload = create_hello_world_payload()
    return send_request("game_event", payload)

# Main function
def main():
    # unregister_event()
    # unregister_game()
    register_game()
    register_event()
    bind_event_handler()
    time.sleep(2)  # Wait for a few seconds to ensure everything is registered
    send_hello_world_event()

if __name__ == "__main__":
    main()

CleanShot 2024-06-04 at 09 42 03

I know the app says Sonos App. The above code is a Hello World Project, as the Sonos App has the same issue.

CleanShot 2024-06-04 at 09 41 16

DavidN-Work commented 5 months ago

Switched to Bitmap using PIL for text rendering.

JHunz commented 5 months ago

For posterity, to do it without bitmap rendering you would want your setup to look something like this:

    handler = {
        "game": "HELLO_APP",
        "event": "HELLO_WORLD",
        "min_value": 0,
        "max_value": 1,
        "icon_id": 1,
        "value_optional": true,
        "handlers": [
            {
                "device-type": "screened-128x40",
                "zone": "one",
                "mode": "screen",
                "datas": [{"has-text": True, "lines": [{"context-frame-key": "line1"}]}],
            }
        ],
    }
    return send_request("bind_game_event", handler) 

The "text" key you were using before isn't actually a part of the API, you need to tell it where in the frame to access the data (or use "prefix" or "suffix" for static text segments). Also note the value_optional key, you'll want this if sending the same event value (but different associated data) in subsequent payloads.

    return {
        "game": "HELLO_APP",
        "event": "HELLO_WORLD",
        "data": {
            "value": 1,
            "frame": {"line1": "Hello, World!"},
        },
    }

And this is a payload associated with how I set it up above.

DavidN-Work commented 5 months ago

Thanks for quick response. I misinterpreted the docs and thought that the context-frame-key was a generic placeholder to which you could assign a custom key to.