A DJ controller is the best home-automation remote
Physical faders, pads, and encoders beat a phone screen for lighting, media, and scenes. The bridge is a bidirectional MIDI-to-everything sync loop with state feedback.
- problem
- Touch-screen control of lighting and media is slow, context-free, and requires looking at a screen. A physical controller with tactile feedback is faster and more satisfying, but needs a reliable bridge between MIDI events and the target systems.
- scope
- A bidirectional MIDI bridge: controller input maps to service calls, device state maps back to controller LED and fader feedback, and the loop stays honest when devices are offline.
- environment
- A DDJ-FLX10 or similar class-compliant MIDI controller connected via USB, a Home Assistant instance, and a small daemon process bridging MIDI I/O to HA WebSocket events.
Assumptions
- The controller is class-compliant and exposes a MIDI input and output port without proprietary drivers.
- The bridge process runs persistently and can reconnect to both the MIDI device and HA after interruptions.
- Device state changes are observable through the HA state machine, so feedback can flow back to the controller.
Limitations
- MIDI is one-directional per message; bidirectional sync requires explicit feedback writes, which some controllers do not support for all controls.
- Controller-specific LED SysEx messages are vendor-specific and may require reverse engineering.
- The bridge is a single point of failure; a crashed daemon means the controller goes dark.
Table of contents 4 sections
Physical control is faster than touch
A fader is a positional control: the current brightness is where the fader physically sits. A touch slider is a velocity control: it starts at zero and you drag until the screen shows the target. Positional controls have a massive advantage for "set to a known level" because the position is the state. You do not look at the fader; you grab it and the room is already roughly right.
Pads are momentary scene triggers. The tactile click confirms the scene fired without looking. Encoders are relative adjustments that do not need absolute position. The combination of positional, momentary, and relative controls maps naturally to lighting (brightness, scenes, color temperature) and media (volume, play/pause, source).
Feedback keeps the surface honest
A one-directional bridge fires MIDI to the system and ignores the return path. That means if a scene is triggered from a phone, the controller fader still shows the old position. The bridge must also subscribe to device state and write the current value back to the controller. This closes the loop: the physical surface always reflects the actual device state.
The sync loop is the hard part. MIDI writes to a controller can fight the human hand: if the bridge writes a fader position while the operator is holding the fader, the motor fights the hand (if motorized) or the position jumps when released (if not). The practical rule is to write feedback only when the source of the state change was not the controller itself, and to debounce rapid state transitions.
Keep the surface useful when devices are down
When a light is offline, the controller should not pretend it is in scene. The feedback LED for that control should reflect the outage: dimmed, amber, or off. The operator can still trigger the scene; the bridge queues the target and applies it on recovery, but the physical surface communicates "this did not fully take."
This is the same availability contract as the lighting engine. The controller is a view of the system state; if the view lies, the operator loses trust in the surface. A controller that shows green when the light is offline is worse than no controller at all.
Make the bridge survive USB disconnects
A USB controller can disconnect, sleep, or be power-cycled. The bridge must detect MIDI port disappearance, attempt reconnection, and re-sync the full controller state on reconnect. This is the same pattern as reconnecting to a WebSocket: the connection is a resource that can drop, and the bridge treats reconnection as a normal event.
The re-sync on reconnect is important: the controller may have been moved while disconnected. The bridge reads no state from the controller on reconnect; it writes the current device state to the controller. This prevents a stale fader position from firing a brightness change the moment the cable is plugged back in.
def on_midi_port_lost(): log.warning('controller disconnected') schedule_reconnect(interval=2) def on_midi_port_recovered(): # do NOT read controller state; write current state for control, entity in mapping.items(): state = ha.get_state(entity) write_feedback(control, state) log.info('controller re-synced')- implemented Production bridge running on desk
A DDJ-FLX10 maps faders to room brightness, pads to scenes, and encoders to color temperature, with LED feedback reflecting live device state.
- reference MIDI 1.0 specificationopen ↗
The MIDI Manufacturers Association spec covers channel voice messages, control change, pitch wheel, and SysEx.