# Seeblue / LEDGlow Motorcycle LED Controller device spec
# Copyright 2026 Pigs Can Fly Labs LLC
# SPDX-License-Identifier: Apache-2.0

device:
  name: "Seeblue Motorcycle LED Controller"
  manufacturer: "Seeblue / LEDGlow"
  manufacturer_status: "active"
  protocol: "ble"
  notes: >
    White-label BLE module used by LEDGlow Motorcycle, LEDGlow V2, and other
    Seeblue-powered brands. SEEBlue transport envelope: [0x95, len, index,
    protocol_id, data..., checksum]. Checksum is 8-bit two's complement over
    bytes from message_length through last payload byte. Static analysis sources:
    com.seeblue.ledglow_moto v1.9.1 and com.seeblue.ledglowv2 v1.4.1.
  identification:
    service_uuids:
      - "0000ffe5-0000-1000-8000-00805f9b34fb"

  discovery:
    methods:
      - type: "ble_scan"
        ble:
          service_uuids:
            - "0000ffe5-0000-1000-8000-00805f9b34fb"
    identity:
      stable_keys: ["address"]
      display: "service_uuid"
    static_ip_required: false

  setup:
    required: false
    confidence: "medium"
    notes: >
      No provisioning. Identify by service UUID; these controllers advertise
      without a stable local name.
    methods:
      - type: "ble_direct"
        verified: false
        description: >
          No provisioning step: the device advertises as soon as it is powered
          on and accepts a connection from any central. There is no account, no
          network credential exchange and no pairing PIN, so a replacement
          client only has to scan, connect and write.
        ble:
          pairing_required: false
        steps:
          - action: "Power the device and scan for its advertisement."
            actor: "client"
            request:
              protocol: "ble_gatt"
          - action: "Connect and run the documented initialization sequence, if the spec defines one."
            actor: "client"
    factory_reset:
      confidence: "low"
      effect: >
        No credential state to clear. Power-cycling drops the current
        connection, which is the actual remedy for the common failure mode:
        the device already being connected to another central.
      procedures:
        - name: "Power cycle"
          steps:
            - action: "Remove power (or battery) for a few seconds and reapply."
              actor: "user"
              expect: "Device advertises again and accepts a new connection."
    rejoin:
      in_place_supported: true
      requires_factory_reset: false
      notes: >
        Nothing binds this device to a network or an owner, so switching
        controllers is just connecting from the new one. If the old client is
        an Android or iOS phone, remove the device from the OS Bluetooth list
        as well — a cached bond can keep the phone reconnecting automatically
        and holding the single available link.
    credentials:
      wifi_passphrase_protection: "not_applicable"
      stored_on_device: []
      issued_to_client: []
      notes: >
        Anything within radio range can connect and issue commands. Physical
        proximity is the only access control.
services:
  - uuid: "0000ffe5-0000-1000-8000-00805f9b34fb"
    name: "LEDGlow BLE Data Service"
    characteristics:
      - uuid: "0000ffe9-0000-1000-8000-00805f9b34fb"
        name: "TX Write"
        properties: ["write"]
        notes: "SEEBlue envelope writes target this characteristic."
        seeblue_envelope:
          header: 0x95
          layout: ["header", "message_length", "message_index", "protocol_id", "payload", "checksum"]
          checksum: "8-bit two's complement over message_length through last payload byte"
          protocols:
            direct_lighting: 0x01
            ledglow_snapshot: 0x10
            ui_changed: 0x05
        commands:
          direct_brake_feature:
            description: "Direct protocol 0x01 command 0: brake feature"
            template: [0x95, "{message_length}", "{message_index}", 0x01, 0x00, "{value}", "{checksum}"]
            protocol_id: 0x01
            command_id: 0
            payload_template: [0x00, "{value}"]
            parameters:
              value:
                type: "uint8"
                min: 0
                max: 255
          direct_set_color_1:
            description: "Direct protocol 0x01 command 1: set RGB color 1"
            template: [0x95, "{message_length}", "{message_index}", 0x01, 0x01, "{red}", "{green}", "{blue}", "{checksum}"]
            protocol_id: 0x01
            command_id: 1
            payload_template: [0x01, "{red}", "{green}", "{blue}"]
            parameters:
              color_order: "rgb"
              red:
                type: "uint8"
                min: 0
                max: 255
              green:
                type: "uint8"
                min: 0
                max: 255
              blue:
                type: "uint8"
                min: 0
                max: 255
          direct_set_brightness_1:
            description: "Direct protocol 0x01 command 2: set brightness 1"
            template: [0x95, "{message_length}", "{message_index}", 0x01, 0x02, "{brightness}", "{checksum}"]
            protocol_id: 0x01
            command_id: 2
            payload_template: [0x02, "{brightness}"]
            parameters:
              brightness:
                type: "uint8"
                min: 0
                max: 255
          direct_set_speed:
            description: "Direct protocol 0x01 command 3: set animation speed"
            template: [0x95, "{message_length}", "{message_index}", 0x01, 0x03, "{speed}", "{checksum}"]
            protocol_id: 0x01
            command_id: 3
            payload_template: [0x03, "{speed}"]
            parameters:
              speed:
                type: "uint8"
                min: 0
                max: 255
          direct_set_mode:
            description: "Direct protocol 0x01 command 4: set effect mode; app sends mode_index + 1"
            template: [0x95, "{message_length}", "{message_index}", 0x01, 0x04, "{mode_plus_1}", "{checksum}"]
            protocol_id: 0x01
            command_id: 4
            payload_template: [0x04, "{mode_plus_1}"]
            parameters:
              mode_plus_1:
                type: "uint8"
                min: 1
                max: 255
          direct_command_5_reserved:
            description: "Direct protocol 0x01 command 5: not observed in analyzed APKs; reserved gap between mode and color 2"
            protocol_id: 0x01
            command_id: 5
            observed: false
          direct_set_color_2:
            description: "Direct protocol 0x01 command 6: set RGB color 2"
            template: [0x95, "{message_length}", "{message_index}", 0x01, 0x06, "{red}", "{green}", "{blue}", "{checksum}"]
            protocol_id: 0x01
            command_id: 6
            payload_template: [0x06, "{red}", "{green}", "{blue}"]
            parameters:
              color_order: "rgb"
              red:
                type: "uint8"
                min: 0
                max: 255
              green:
                type: "uint8"
                min: 0
                max: 255
              blue:
                type: "uint8"
                min: 0
                max: 255
          direct_set_brightness_2:
            description: "Direct protocol 0x01 command 7: set brightness 2"
            template: [0x95, "{message_length}", "{message_index}", 0x01, 0x07, "{brightness}", "{checksum}"]
            protocol_id: 0x01
            command_id: 7
            payload_template: [0x07, "{brightness}"]
            parameters:
              brightness:
                type: "uint8"
                min: 0
                max: 255
          direct_additional_feature:
            description: "Direct protocol 0x01 command 8: additional feature"
            template: [0x95, "{message_length}", "{message_index}", 0x01, 0x08, "{value}", "{checksum}"]
            protocol_id: 0x01
            command_id: 8
            payload_template: [0x08, "{value}"]
            parameters:
              value:
                type: "uint8"
                min: 0
                max: 255
          full_settings_snapshot:
            description: "Full LEDGlow snapshot protocol 0x10: complete two-color state payload"
            template: [0x95, "{message_length}", "{message_index}", 0x10, 0x01, "{on_off}", "{brightness1}", "{brightness2}", "{speed}", "{mode_plus_1}", "{red1}", "{green1}", "{blue1}", "{red2}", "{green2}", "{blue2}", "{checksum}"]
            protocol_id: 0x10
            payload_template: [0x01, "{on_off}", "{brightness1}", "{brightness2}", "{speed}", "{mode_plus_1}", "{red1}", "{green1}", "{blue1}", "{red2}", "{green2}", "{blue2}"]
            parameters:
              color_order: "rgb"
              on_off:
                type: "uint8"
                min: 0
                max: 1
              brightness1:
                type: "uint8"
                min: 0
                max: 255
              brightness2:
                type: "uint8"
                min: 0
                max: 255
              speed:
                type: "uint8"
                min: 0
                max: 255
              mode_plus_1:
                type: "uint8"
                min: 1
                max: 255
              red1:
                type: "uint8"
                min: 0
                max: 255
              green1:
                type: "uint8"
                min: 0
                max: 255
              blue1:
                type: "uint8"
                min: 0
                max: 255
              red2:
                type: "uint8"
                min: 0
                max: 255
              green2:
                type: "uint8"
                min: 0
                max: 255
              blue2:
                type: "uint8"
                min: 0
                max: 255
          ledglow_v2_power:
            description: "LEDGlow V2 UI change ID 0: power"
            protocol_id: 0x05
            ui_id: 0
            payload_template: [0x00, "{value}"]
          ledglow_v2_color_a:
            description: "LEDGlow V2 UI change ID 2: color A RGB"
            protocol_id: 0x05
            ui_id: 2
            payload_template: [0x02, "{red}", "{green}", "{blue}"]
          ledglow_v2_color_b:
            description: "LEDGlow V2 UI change ID 3: color B RGB"
            protocol_id: 0x05
            ui_id: 3
            payload_template: [0x03, "{red}", "{green}", "{blue}"]
          ledglow_v2_color_c:
            description: "LEDGlow V2 UI change ID 4: color C RGB"
            protocol_id: 0x05
            ui_id: 4
            payload_template: [0x04, "{red}", "{green}", "{blue}"]
          ledglow_v2_brightness_a:
            description: "LEDGlow V2 UI change ID 5: brightness A"
            protocol_id: 0x05
            ui_id: 5
            payload_template: [0x05, "{brightness}"]
          ledglow_v2_brightness_b:
            description: "LEDGlow V2 UI change ID 6: brightness B"
            protocol_id: 0x05
            ui_id: 6
            payload_template: [0x06, "{brightness}"]
          ledglow_v2_brightness_c:
            description: "LEDGlow V2 UI change ID 7: brightness C"
            protocol_id: 0x05
            ui_id: 7
            payload_template: [0x07, "{brightness}"]
          ledglow_v2_million_color_a:
            description: "LEDGlow V2 UI change ID 8: million color A"
            protocol_id: 0x05
            ui_id: 8
            payload_template: [0x08, "{value}"]
          ledglow_v2_million_color_b:
            description: "LEDGlow V2 UI change ID 9: million color B"
            protocol_id: 0x05
            ui_id: 9
            payload_template: [0x09, "{value}"]
          ledglow_v2_million_color_c:
            description: "LEDGlow V2 UI change ID 10: million color C"
            protocol_id: 0x05
            ui_id: 10
            payload_template: [0x0A, "{value}"]
          ledglow_v2_fade:
            description: "LEDGlow V2 UI change ID 11: fade"
            protocol_id: 0x05
            ui_id: 11
            payload_template: [0x0B, "{value}"]
          ledglow_v2_speed:
            description: "LEDGlow V2 UI change ID 12: speed"
            protocol_id: 0x05
            ui_id: 12
            payload_template: [0x0C, "{speed}"]
          ledglow_v2_music:
            description: "LEDGlow V2 UI change ID 13: music"
            protocol_id: 0x05
            ui_id: 13
            payload_template: [0x0D, "{value}"]
          ledglow_v2_mode:
            description: "LEDGlow V2 UI change ID 14: mode"
            protocol_id: 0x05
            ui_id: 14
            payload_template: [0x0E, "{mode}"]
          ledglow_v2_strobe:
            description: "LEDGlow V2 UI change ID 15: strobe"
            protocol_id: 0x05
            ui_id: 15
            payload_template: [0x0F, "{value}"]
          ledglow_v2_white_wire_ui:
            description: "LEDGlow V2 white wire UI IDs 64-79; payload is [ui_id, values...]"
            protocol_id: 0x05
            ui_id_range: [64, 79]
            payload_template: ["{ui_id}", "{values}"]
          ledglow_v2_interior_ui:
            description: "LEDGlow V2 interior UI IDs 0x80+; payload is [ui_id, values...]"
            protocol_id: 0x05
            ui_id_min: 0x80
            payload_template: ["{ui_id}", "{values}"]
          ledglow_v2_system_power:
            description: "LEDGlow V2 system UI ID 0xF0: system power"
            protocol_id: 0x05
            ui_id: 0xF0
            payload_template: [0xF0, "{value}"]
          ledglow_v2_courtesy_light:
            description: "LEDGlow V2 system UI ID 0xF1: courtesy light"
            protocol_id: 0x05
            ui_id: 0xF1
            payload_template: [0xF1, "{value}"]
          ledglow_v2_low_voltage:
            description: "LEDGlow V2 system UI ID 0xF3: low voltage"
            protocol_id: 0x05
            ui_id: 0xF3
            payload_template: [0xF3, "{value}"]
          ledglow_v2_auto_dim:
            description: "LEDGlow V2 system UI ID 0xF4: auto dim"
            protocol_id: 0x05
            ui_id: 0xF4
            payload_template: [0xF4, "{value}"]
          ledglow_v2_display_brightness:
            description: "LEDGlow V2 system UI ID 0xF5: display brightness"
            protocol_id: 0x05
            ui_id: 0xF5
            payload_template: [0xF5, "{brightness}"]
          ledglow_v2_courtesy_color:
            description: "LEDGlow V2 system UI ID 0xF6: courtesy color"
            protocol_id: 0x05
            ui_id: 0xF6
            payload_template: [0xF6, "{red}", "{green}", "{blue}"]
          ledglow_v2_backlight_color:
            description: "LEDGlow V2 system UI ID 0xF7: backlight color"
            protocol_id: 0x05
            ui_id: 0xF7
            payload_template: [0xF7, "{red}", "{green}", "{blue}"]
          ledglow_v2_white_wire_system:
            description: "LEDGlow V2 system UI ID 0xF9: white wire"
            protocol_id: 0x05
            ui_id: 0xF9
            payload_template: [0xF9, "{value}"]
          ledglow_v2_auto_off:
            description: "LEDGlow V2 system UI ID 0xFA: auto off"
            protocol_id: 0x05
            ui_id: 0xFA
            payload_template: [0xFA, "{value}"]
      - uuid: "0000ffe4-0000-1000-8000-00805f9b34fb"
        name: "RX Notify"
        properties: ["notify"]

entities:
  - platform: "light"
    name: "Motorcycle LEDs"
    features: ["brightness", "color", "effect"]
    state_characteristic: "0000ffe4-0000-1000-8000-00805f9b34fb"
    commands:
      turn_on: "full_settings_snapshot"
      set_color: "direct_set_color_1"
      set_brightness: "direct_set_brightness_1"
      set_effect: "direct_set_mode"
