Marshall Stanmore 2 Speaker - with source code


I finally have some time to complete the documentation of my project and have made it public.

https://github.com/rabbit-aaron/marshall-stanmore-2

This library allows you to control your Marshall Stanmore 2 speaker using Python, and there’s an MQTT client that allows you to integrate this with Home Assistant or other home automation tools.

To run it, you will need some basic Python knowledge.

python -m venv .venv
  • Activate the virtual environment
source .venv/bin/activate 
  • Install the dependencies
pip install -r requirements.txt
  • Run the MQTT server with environment variables, you can turn the below into a bash script so you can run it like an executable.
#!/usr/bin/env bash
export BLE_ADDRESS=<your speaker BLE address>
export MQTT_HOSTNAME=<your mqtt host>
export MQTT_PORT=<your mqtt port> # optional, default is 1883
export MQTT_USERNAME=<username> #optional default is None, if you didn't enable authentication
export MQTT_PASSWORD =<password> #optional default is None, if you didn't enable authentication
export MQTT_TOPIC_PREFIX = <mqtt topic prefix> # optional, default is "stanmore2"
export MQTT_RETAIN=<TRUE or FALSE> # optional, default is False, this sets the mqtt message retain flag
export ALLOW_PAIRING=<TRUE or FALSE> # optional, default is FALSE, whether to allow MQTT to turn the speaker into Bluetooth pairing mode. Note that when entering the pairing mode, BLE control disconnects.

/path/to/.venv/bin/python -m marshallstanmore2

See the readme for the MQTT commands and output you can use. Here’s my configuration.

mqtt:
  - number:
        command_topic: stanmore2/command/set_eq_profile/160hz
        state_topic: stanmore2/info/eq_profile/160hz
        availability_topic: stanmore2/lwt
        min: 0
        max: 10
        step: 1
        name: Stanmore2 EQ 160hz
        icon: mdi:equalizer
  - number:
        command_topic: stanmore2/command/set_eq_profile/400hz
        state_topic: stanmore2/info/eq_profile/400hz
        availability_topic: stanmore2/lwt
        min: 0
        max: 10
        step: 1
        name: Stanmore2 EQ 400hz
        icon: mdi:equalizer
  - number:
        command_topic: stanmore2/command/set_eq_profile/1000hz
        state_topic: stanmore2/info/eq_profile/1000hz
        availability_topic: stanmore2/lwt
        min: 0
        max: 10
        step: 1
        name: Stanmore2 EQ 1khz
        icon: mdi:equalizer
  - number:
        command_topic: stanmore2/command/set_eq_profile/2500hz
        state_topic: stanmore2/info/eq_profile/2500hz
        availability_topic: stanmore2/lwt
        min: 0
        max: 10
        step: 1
        name: Stanmore2 EQ 2.5khz
        icon: mdi:equalizer
  - number:
        command_topic: stanmore2/command/set_eq_profile/6250hz
        state_topic: stanmore2/info/eq_profile/6250hz
        availability_topic: stanmore2/lwt
        min: 0
        max: 10
        step: 1
        name: Stanmore2 EQ 6.25khz
        icon: mdi:equalizer
  - number:
        command_topic: stanmore2/command/set_volume
        state_topic: stanmore2/info/volume
        availability_topic: stanmore2/lwt
        min: 0
        max: 32
        step: 1
        name: Stanmore2 Volume
        icon: mdi:knob
  - select:
        name: Stanmore2 EQ Preset
        command_topic: stanmore2/command/set_eq_preset
        state_topic: stanmore2/info/eq_preset
        availability_topic: stanmore2/lwt
        icon: mdi:equalizer
        value_template: "{{ value.upper() }}"
        command_template: "{{ value.lower() }}"
        options:
          - FLAT
          - ROCK
          - METAL
          - POP
          - HIPHOP
          - ELECTRONIC
          - JAZZ
          - CUSTOM
  - select:
        name: Stanmore2 Audio Source
        command_topic: stanmore2/command/set_source
        state_topic: stanmore2/info/audio_source
        availability_topic: stanmore2/lwt
        icon: mdi:audio-input-rca
        value_template: "{{ value.upper() }}"
        command_template: "{{ value.lower() }}"
        options:
          - BLUETOOTH
          - AUX
          - RCA

Let me know if you have trouble running it, if more people are interested in this project I can make it into a docker container so you can just pull and run.

Note, the top integration in the screenshot is [an integration with Shairport-sync].(GitHub - parautenbach/hass-shairport-sync: A custom media player for Home Assistant that allows you to control and get updates from a Shairport Sync installation using MQTT.)

I have another program running to capture shairport-sync’s MQTT message and use that to transform and forward MQTT message to my program, and that allow me to directly control speaker volume from my iPhone/Mac. If anyone’s interested I can open source this too. Alternatively you can do this via HA automation or NodeRed.

2 Likes

I understand that quite a lot of time has passed. I don’t understand programming at all, but I really wanted to put my speaker in ha. I tried to use chat gpt but something still doesn’t work. I would be immensely grateful if you shared a ready-made container or video instructions on how to do this

Same here! I would like to create an automation to keep the speaker awake (maybe by sending a command to turn up or down the volume a bit, every now and then). I have tried playing an inaudible sound, but it doesn’t work for me very well.

I come up with a janky way of waking up the speaker when needed. I was messing about with just using bluetoothctl connect / disconnect inside a bash script ran from a separate linux machine, but it was unreliable.
So this is what worked for me, (I used LLM)

  1. Install bleak on the secondary machine (in my case sudo apt install python3-bleak )

  2. Create /usr/local/bin/stanmore_ble_poke.py:

sudo nano /usr/local/bin/stanmore_ble_poke.py

Paste:

#!/usr/bin/env python3
import asyncio
from bleak import BleakClient

STANMORE_MAC = ""  # BLE MAC of your Stanmore

async def main():
    # Connect once over BLE, wait a bit, then disconnect
    async with BleakClient(STANMORE_MAC) as client:
        if not client.is_connected:
            raise RuntimeError("Failed to connect over BLE")
        # Keep the BLE link up briefly so the speaker can react
        await asyncio.sleep(3)

if __name__ == "__main__":
    asyncio.run(main())

Make it executable:

sudo chmod +x /usr/local/bin/stanmore_ble_poke.py
  1. Adapt your bash toggle to use that script when disconnected:
#!/usr/bin/env bash
set -euo pipefail

STANMORE_MAC=""

if bluetoothctl info "$STANMORE_MAC" 2>/dev/null | grep -q "Connected: yes"; then
    echo "Speaker is connected; disconnecting once..."
    bluetoothctl disconnect "$STANMORE_MAC" || true
else
    echo "Speaker is disconnected; opening a BLE connection once to wake..."
    python3 /usr/local/bin/stanmore_ble_poke.py
fi

This does exactly your requested logic:

  • If connected → disconnect once, no reconnect.
  • If disconnected → open one BLE connection, hold for ~3 s, then drop.

Then I put it inside my config.yaml under shell command and set up a web hook automation, which I can trigger both in other scripts / automations or by Siri Shortcuts