Open-source local control

Tilt Local Bridge

Connect compatible Tilt and SmarterHome roller shades to Home Assistant with a nearby Raspberry Pi. Day-to-day control stays local, exact positions work, and Google Home can sit on top.

Runtime
Local BLE + MQTT
Control
Open, close, 0-100%
Pairing
One-shot cloud exchange
License
MIT

Give the job to your coding agent

Paste this into Claude Code, Codex, or another coding tool that can reach your Raspberry Pi. It starts read-only and keeps privileged setup, pairing, and movement behind explicit gates.

Setup prompt
Help me install Tilt Local Bridge on a Raspberry Pi and connect my compatible
Tilt or SmarterHome roller shades to Home Assistant.

Use https://github.com/Sunrise-Labs-Dot-AI/tilt-local-bridge as the source of
truth. Read README.md, SECURITY.md, docs/SETUP.md, docs/PAIRING.md,
docs/HOME_ASSISTANT.md, and docs/TROUBLESHOOTING.md before acting. Inspect my
environment before changing it, confirm which machine is the Raspberry Pi, and
use SSH only after I confirm any new or changed host key. Never bypass SSH host
key verification.

Work in stages. Begin with inspection only. Ask before each privileged package
installation, service change, or edit under /etc. Start the bridge read-only.
The CLI's explicit read, write, and pairing flags are the enforced safety gates;
do not bypass them. Run the offline tests and check-runtime before contacting a
shade, then use probe-status for the first live check because it does not move
the shade.

Treat pairing and movement as separate approval gates. Do not pair, rekey, or
replace an existing shade key until I explicitly approve that step. Pair exactly
one advertising shade at a time. If pairing completion is ambiguous, do not
retry. Preserve the pending key and explain the read-only recovery check. Do not
send any movement command until I separately approve a small first movement
with the shade path clear.

Have me enter account details, MQTT credentials, device addresses, and other
private values directly in the local terminal or protected local files when
needed. Do not ask me to paste them into chat. Never put passwords, access
tokens, MQTT credentials, pairing keys, real BLE addresses, private hostnames,
home-network details, or unredacted terminal output in command arguments, git,
source files, logs, screenshots, issues, or other public output. Store keys and
MQTT credentials only in the protected files documented by the repository and
verify their ownership and permissions.

Stop and explain what you need if protected-file validation fails, more than one
shade advertises pairing mode, an SSH host key changes, the hardware or firmware
differs from the documented flow, or any step would require replacing an
existing key. Tell me what access or physical action you need only when you
reach that step, and keep all reported checks concise and redacted.

If the tool cannot reach the Pi or Home Assistant, it should stop and give you the smallest next action. The complete manual path is below.

01Home AssistantCover + position
02Raspberry PiBridge + BlueZ
03Roller shadeEncrypted session

The legacy Tilt service is contacted only while pairing a shade. It is not in the control path after the key is stored on your Pi.

Step 0

Before you start

You need a Raspberry Pi with Bluetooth, Raspberry Pi OS Bookworm, Home Assistant admin access, physical access to each shade, and the owner's Tilt account if a new pairing key is required.

Pairing changes the shade key.

The original Tilt app may stop controlling that shade. Pair one shade at a time, keep the travel path clear, and begin with movement disabled.

Hardware button timing varies by shade revision. Use the physical pairing instructions for your model. The tool waits for the exact Tilt pairing advertisement and refuses ambiguous scans.

Step 1

Prepare Home Assistant

  1. Open Settings, Apps, Install app and install the official Mosquitto broker app.
  2. Create a dedicated bridge user under Settings, People, Users. Enable Advanced mode in your profile if Users is hidden.
  3. Accept the discovered MQTTintegration under Devices & services and leave discovery enabled.

Maintained reference: Home Assistant MQTT

Step 2

Install the bridge on the Pi

Place the Pi in the same room for the first run. The installer creates a locked-down service account and a disabled system service. It does not contact or move a shade.

git clone https://github.com/Sunrise-Labs-Dot-AI/tilt-local-bridge.git
cd tilt-local-bridge
sudo ./scripts/install.sh --activate --install-system-packages

Store the dedicated MQTT username and password in/etc/tilt-local-bridge/. Keep both files readable only by root and the tiltbridge service group.

Step 3

Pair exactly one shade

Put one shade into physical pairing mode, then run the one-shot tool. Your password is entered without echo and is never written to disk. The resulting 32-byte shade key is stored privately.

sudo env PYTHONPATH=/opt/tilt-local-bridge/src \
  python3 -m tilt_local_bridge.tilt_pairing \
  --output /var/lib/tilt-local-pairing/office_shade.key \
  --scan-timeout 30 \
  --permit-live-pairing

Enter the account email and password at the interactive prompts. Success prints the shade's BLE address. Record it only in the protected local config, then install the key under/etc/tilt-local-bridge/keys/ and remove the staging copy. Do not paste pairing output into chat, logs, screenshots, or public issues. The email prompt is visible on screen, so redact it from any terminal output you share. The detailed pairing guide explains protected key import and ambiguous final acknowledgements.

Read pairing and key recovery

Step 4

Configure, then verify without movement

Replace every sample value. The BLE address below is deliberately fake. Start with position writes set to false.

{
  "version": 1,
  "access": {
    "allow_reads": true,
    "allow_position_writes": false
  },
  "mqtt": {
    "host": "homeassistant.local",
    "port": 1883,
    "username_file": "/etc/tilt-local-bridge/mqtt.username",
    "password_file": "/etc/tilt-local-bridge/mqtt.password"
  },
  "shades": [{
    "id": "office_shade",
    "name": "Office Shade",
    "mac": "02:00:00:00:00:01",
    "pairing_key_file": "/etc/tilt-local-bridge/keys/office_shade.key"
  }]
}

Run check-runtime --expect-shade-reads, followed by a single probe-status --allow-shade-reads. A valid probe reports position, battery, charging state, and calibration without moving the shade.

Enable the read-only service. Home Assistant should discover one device per shade with a Cover, Position slider, and Battery sensor.

Step 5

Enable exact position control

Change allow_position_writes to true in the config, then add the independent launch-time gate:

sudo ./scripts/install.sh \
  --activate \
  --enable \
  --allow-position-writes

Start from a known calibrated endpoint and request a small change, such as 100 to 95 percent. During movement, the bridge publishes observed positions, keeps the cover available, blocks overlapping commands, and performs bounded readback checks.

Step 6

Add the covers to Google Home

Home Assistant Cloud

The shorter route. It is paid after the trial. Connect Google Assistant under Home Assistant Cloud, then expose only each shade's Cover entity.

Manual integration

No Home Assistant subscription fee. You need a public HTTPS Home Assistant URL, a Google Home developer project, HomeGraph, and a service account.

For the free route, follow Home Assistant's maintained manual Google Assistant flow, then allowlist only your cover entities:

google_assistant:
  project_id: YOUR_PROJECT_ID
  service_account: !include SERVICE_ACCOUNT.json
  report_state: true
  expose_by_default: false
  entity_config:
    cover.office_shade:
      name: Office Shade
      expose: true
      room: Office

Maintained reference: manual Google Assistant setup

Recovery

Know the exit path

Stop all local control withsudo systemctl disable --now tilt-local-bridge.service. This does not change the key again. To keep status but remove movement, set the write gate to false and reinstall without--allow-position-writes.

Pairing relies on legacy account endpoints that Sunrise Labs does not operate. If those endpoints disappear, an existing protected shade key can continue to support local control, but this project cannot manufacture a replacement key offline.

Community-built and experimental. Not affiliated with or endorsed by Tilt or SmarterHome. Use only with hardware you own.