Posted in

Control your Bed Motors with Home Assistant

Last year my wife and I bought a new bed. We decided to go with a bed that contained bed motors so we can move the headrest and legrest up and down. It came with a nice remote control and as always I was wondering what the protocol was of the remote control. After reviewing the manual of the motors on the internet, I’ve figured out that it actually had bluetooth control! This got me hooked. Would it be possible to control the bed with Home Assistant? The answer: Yes!

Smartbed-MQTT

Before diving into my own setup, you should absolutely take a look at the fantastic Home Assistant add-on Smartbed-MQTT by Richard Hopton. Richard is doing an amazing job trying to connect as many different bed motors as possible to Home Assistant through a bluetooth proxy. At the moment, he supports bed motors from Okin/Okimat, Richmat, Linak, Solace, MotoSleep, Reverie, Leggett & Platt, Keeson, Octo, ErgoMotion and Logicdata.

I helped test and contribute support for my own bed model, but I ran into issues with my second motor that prevented full integration through the add-on. Still, if your goal is simply to get your bed into Home Assistant, start with Smartbed-MQTT. Even if your model isn’t supported yet, the repository is an excellent resource for learning about Bluetooth services and control commands.

My Bed: An Okin / Okimat motor

My bed uses the Okimat 4 IPS from DewertOkin GmbH. Each bed motor model behaves slightly differently over Bluetooth, so this configuration may not work with yours. Again, check the Smartbed-MQTT repository—if not for the add-on itself, then for its extensive documentation on control characteristics and message structures.

Note: The latest version of my set-up can be found here.

Bluetooth Discovery

The biggest challenge in this project was discovering the correct Bluetooth services and characteristics. Since I’m relatively new to low-level Bluetooth control, it took some trial and error, but with the right tools, it’s very manageable.

I gathered information from two main sources:

1. Smartbed-MQTT Source Code

Many supported bed models already have their control commands mapped out. Browsing through the code often reveals exactly which service UUIDs and characteristics you need to write to.

nRF Connect (Android / iOS)

Using nRF Connect (Android & iOS), you can monitor BLE traffic from the manufacturer’s app. By pressing buttons in the official mobile app and watching the BLE writes in nRF Connect, you can reverse-engineer the command payloads.
There are many tutorials online, and the process is surprisingly straightforward once you get the hang of it.

Walkthrough of My Setup

I’m using an M5 Atom Lite running ESPHome as a Bluetooth proxy. Below is the relevant part of my configuration.

1. BLE Advertisements

This section scans for the bed motor based on its MAC address and logs all discovered info. It’s purely for debugging: to confirm the proxy sees the correct device.

esp32_ble_tracker:
  on_ble_advertise:
    - mac_address:
        - YOUR MAC ADDRESS
      then:
        - lambda: |-
            ESP_LOGD("ble_adv", "New BLE device");
            ESP_LOGD("ble_adv", "  address: %s", x.address_str().c_str());
            ESP_LOGD("ble_adv", "  name: %s", x.get_name().c_str());
            ESP_LOGD("ble_adv", "  Advertised service UUIDs:");
            for (auto uuid : x.get_service_uuids()) {
                ESP_LOGD("ble_adv", "    - %s", uuid.to_string().c_str());
            }
            ESP_LOGD("ble_adv", "  Advertised service data:");
            for (auto data : x.get_service_datas()) {
                ESP_LOGD("ble_adv", "    - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size());
            }
            ESP_LOGD("ble_adv", "  Advertised manufacturer data:");
            for (auto data : x.get_manufacturer_datas()) {
                ESP_LOGD("ble_adv", "    - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size());
            }

2. BLE Client Connection

Once connected, a binary sensor is updated indicating the link status. Useful for dashboards and troubleshooting.

ble_client:
  - mac_address: YOUR MAC ADDRESS
    id: bed_1
    on_connect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Connected to BLE device");
        - binary_sensor.template.publish:
            id: bed_1_connection_status
            state: True
    on_disconnect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Disconnected from BLE device");
        - binary_sensor.template.publish:
            id: bed_1_connection_status
            state: False

Binary sensor:

binary_sensor:
  - platform: template
    name: "Bed 1 connection status"
    id: bed_1_connection_status
    device_class: connectivity
    entity_category: diagnostic
    device_id: motor_bed_1

3. Device Information

I also pull in all standard BLE Device Information characteristics (model, serial, firmware, etc.). This is optional but handy, especially when you have multiple motors and want to compare firmware versions.

text_sensor:
  - platform: ble_client
    ble_client_id: bed_1
    service_uuid: '0000180a-0000-1000-8000-00805f9b34fb'
    characteristic_uuid: '00002a24-0000-1000-8000-00805f9b34fb'
    name: "Model Number"
    id: bed_1_model_number
    entity_category: diagnostic
    icon: mdi:identifier
    device_id: motor_bed_1

  - platform: ble_client
    ble_client_id: bed_1
    service_uuid: '0000180a-0000-1000-8000-00805f9b34fb'
    characteristic_uuid: '00002a25-0000-1000-8000-00805f9b34fb'
    name: "Serial Number"
    id: bed_1_serial_number
    entity_category: diagnostic
    icon: mdi:numeric
    device_id: motor_bed_1

  - platform: ble_client
    ble_client_id: bed_1
    service_uuid: '0000180a-0000-1000-8000-00805f9b34fb'
    characteristic_uuid: '00002a26-0000-1000-8000-00805f9b34fb'
    name: "Firmware Revision"
    id: bed_1_firmware_revision
    entity_category: diagnostic
    icon: mdi:chip
    device_id: motor_bed_1

  - platform: ble_client
    ble_client_id: bed_1
    service_uuid: '0000180a-0000-1000-8000-00805f9b34fb'
    characteristic_uuid: '00002a27-0000-1000-8000-00805f9b34fb'
    name: "Hardware Revision"
    id: bed_1_hardware_revision
    entity_category: diagnostic
    icon: mdi:memory
    device_id: motor_bed_1

  - platform: ble_client
    ble_client_id: bed_1
    service_uuid: '0000180a-0000-1000-8000-00805f9b34fb'
    characteristic_uuid: '00002a28-0000-1000-8000-00805f9b34fb'
    name: "Software Revision"
    id: bed_1_software_revision
    entity_category: diagnostic
    icon: mdi:application
    device_id: motor_bed_1

  - platform: ble_client
    ble_client_id: bed_1
    service_uuid: '0000180a-0000-1000-8000-00805f9b34fb'
    characteristic_uuid: '00002a29-0000-1000-8000-00805f9b34fb'
    name: "Manufacturer Name"
    id: bed_1_manufacturer_name
    entity_category: diagnostic
    icon: mdi:factory
    device_id: motor_bed_1

4. Light Control

The light on my bed is a simple toggle command with no feedback state. Since it also turns off automatically after one hour, exposing it as a “button” is the most reliable option.

  # Lights
  - platform: template
    name: "Bed light"
    icon: "mdi:lightbulb-outline"
    device_id: motor_bed_1
    on_press:
      - ble_client.ble_write:
          id: bed_1
          service_uuid: 62741523-52f9-8864-b1ab-3b3a8d65950b
          characteristic_uuid: 62741525-52f9-8864-b1ab-3b3a8d65950b
          value: [0x04, 0x02, 0x00, 0x02]

5. Movement Controls

Each movement direction is implemented as a button that triggers a repeating script until stopped.
This mirrors how the original remote holds a button down to keep the motor moving.

Buttons:

# Movement
  - platform: template
    name: "Bed head up"
    icon: "mdi:arrow-up-bold"
    device_id: motor_bed_1
    on_press:
      - script.execute:
          id: bed_1_move
          move_value: 0x01
          description: "Head UP"

  - platform: template
    name: "Bed head down"
    icon: "mdi:arrow-down-bold"
    device_id: motor_bed_1
    on_press:
      - script.execute:
          id: bed_1_move
          move_value: 0x02
          description: "Head DOWN"

  - platform: template
    name: "Bed feet up"
    icon: "mdi:arrow-up-bold"
    device_id: motor_bed_1
    on_press:
      - script.execute:
          id: bed_1_move
          move_value: 0x04
          description: "Feet UP"

  - platform: template
    name: "Bed feet down"
    icon: "mdi:arrow-down-bold"
    device_id: motor_bed_1
    on_press:
      - script.execute:
          id: bed_1_move
          move_value: 0x08
          description: "Feet DOWN"

  - platform: template
    name: "Bed stop"
    icon: "mdi:stop-circle-outline"
    device_id: motor_bed_1
    on_press:
      - script.stop: bed_1_move

Movement script:

script:
  # Movement script
  - id: bed_1_move
    mode: restart
    parameters:
      move_value: int
      description: string
    then:
      - repeat:
          count: 100
          then:
            - ble_client.ble_write:
                id: bed_1
                service_uuid: 62741523-52f9-8864-b1ab-3b3a8d65950b
                characteristic_uuid: 62741525-52f9-8864-b1ab-3b3a8d65950b
                value: !lambda |-
                  return std::vector<uint8_t>{0x02, 0x02, 0x00, static_cast<uint8_t>(move_value)};
            - delay: 140ms
            - logger.log:
                format: "Bed 1: %s"
                args: [ 'description.c_str()' ]

You can adjust the delay to make the movement feel smoother, but it still won’t be as seamless as the manufacturer’s app. The Smartbed-MQTT add-on handles this more gracefully with fine-tuned timing logic.

Conclusion

Connecting my bed motors to Home Assistant turned out to be one of those projects that starts as a curiosity and ends as a useful home automation upgrade.

If you’re attempting a similar project, I highly recommend:

  • Starting with the Smartbed-MQTT add-on
  • Using nRF Connect (Android & iOS) to inspect BLE traffic
  • Taking the time to properly map out services and characteristics

In the end, bringing your bed into Home Assistant isn’t just a neat trick: it’s a great example of how open-source tools let you take control of hardware that was never designed to be part of a smart home.

Leave a Reply

Your email address will not be published. Required fields are marked *