SIMPL# Pro program that loads onto a Crestron MPC3-302 and exposes its buttons, LEDs, and volume to Home Assistant over MQTT (with HA auto-discovery) and a small REST API on the processor's built-in web server.
Tested target: MPC3-302 at 192.168.1.150. The same code targets the MPC3-301
without changes because both use MPC3x30xTouchscreenSlot.
Just want to install it? See docs/DEPLOY.md — build, deploy, configure, and verify in three commands.
Once the bridge connects to your MQTT broker, HA's MQTT integration discovers
one device named after FriendlyName with these entities:
| HA entity | Type | Purpose |
|---|---|---|
switch.<id>_led_power |
switch | Power-button LED |
switch.<id>_led_mute |
switch | Mute-button LED |
switch.<id>_led_btn01..btn10 |
switch (×10) | Programmable button LEDs |
number.<id>_volume |
number | Volume 0..100 (also drives bargraph) |
switch.<id>_mute |
switch | Soft mute state (separate from LED) |
Every physical press emits an MQTT device-trigger event — visible in HA's automation editor as “When Power is pressed”, “When Button 3 is released”, etc. (12 buttons × 2 edges = 24 triggers).
Open http://<processor-ip>/cws/api/ui in any browser. Single page that:
- Shows live volume / mute / per-LED state (LEDs are clickable to toggle)
- Edits every setting in
appsettings.json - Save writes through to
/user/appsettings.jsonimmediately - "Restart program" issues
progreseton this slot so MQTT/REST plumbing picks up the new values
If you change Rest.RoutePrefix, the UI's own URL moves with it.
Served at http://<processor-ip>/cws/api/:
GET /cws/api/state -> {"leds":{...},"volume":50,"muted":false,"recent_events":[...]}
GET /cws/api/buttons -> {"events":[{"name":"btn03","pressed":true,"at_utc":"..."}]}
POST /cws/api/led/power ?on=true
POST /cws/api/volume ?level=75
POST /cws/api/volume/mute ?on=true
GET /cws/api/ui -> settings web page (HTML)
GET /cws/api/settings -> current settings JSON (password redacted)
POST /cws/api/settings -> persists to /user/appsettings.json
POST /cws/api/restart -> progreset on this slot
JSON bodies work too: curl -XPOST http://.../volume -d '{"level":75}'.
The easiest path is to deploy the program once with the default settings, then
open http://<processor-ip>/cws/api/ui and edit from there. The UI writes
directly to /user/appsettings.json and offers a one-click program restart.
For unattended first-boot, you can pre-stage /user/appsettings.json over
SCP. Start from crestron/Mpc3HaBridge/appsettings.sample.json:
{
"DeviceId": "mpc3-living-room",
"FriendlyName": "MPC3 Living Room",
"Mqtt": {
"Enabled": true,
"Host": "192.168.1.10",
"Port": 1883,
"Username": "",
"Password": "",
"BaseTopic": "mpc3",
"DiscoveryPrefix": "homeassistant",
"KeepAliveSeconds": 30
},
"Rest": {
"Enabled": true,
"RoutePrefix": "api"
},
"Volume": {
"DefaultLevel": 50
}
}DeviceId must be unique per MPC3 on the same broker — it’s the prefix for
every MQTT topic and the HA unique_id. Renaming it after first install
creates duplicate HA entities; delete the old retained discovery topics first.
See docs/DEPLOY.md for the full guide. Short version, from the repo root:
.\tools\Build-Cpz.ps1 # VS 2008 DTE → .cpz
$env:MPC_PASS = 'your-admin-password'
.\tools\Deploy-Cpz.ps1 -Target 192.168.1.150 # SCP + progloadYou need VS 2008 + the Crestron SIMPL# Pro plugin to build —
headless msbuild produces a .dll but the plugin's post-build
packaging step needs a live IDE host. The PowerShell scripts wrap that
with COM automation.
SSH into the processor and run mpc help to see commands:
mpc state dump current state
mpc led btn03 on drive a single LED
mpc vol 75 set volume
mpc mute on set mute
Useful for verifying hardware wiring before involving MQTT or HA.
Under <BaseTopic>/<DeviceId>/:
status online | offline (retained, last-will)
led/<name>/state ON | OFF (retained) names: power, mute, btn01..btn10
led/<name>/set subscribe — ON | OFF
volume/state 0..100 (retained)
volume/set subscribe — integer 0..100
mute/state ON | OFF (retained)
mute/set subscribe — ON | OFF
button/<name>/event "pressed" | "released" (non-retained, per press)
- TLS / MQTTS — the MQTT client is plaintext. If you need TLS, terminate
it at a reverse proxy on the HA host or replace
MqttClient.cswith M2Mqtt's SIMPL# build. - QoS 1/2 — every publish is QoS 0. HA can lose a button event on broker restart. Acceptable for room-control; not OK for safety-critical use.
- AV scene logic — there is no "press btn03 → switch to HDMI 2" mapping in the program. The bridge is intentionally a thin passthrough; do that in HA automations.
crestron/
├── Mpc3HaBridge.sln VS 2008 solution
└── Mpc3HaBridge/
├── ControlSystem.cs entry point + console commands
├── Config/AppSettings.cs /user/appsettings.json loader
├── Hardware/Mpc3Wrapper.cs buttons, LEDs, volume <-> DeviceState
├── State/DeviceState.cs in-memory model, event source
├── State/Parse.cs CF 3.5 int.TryParse polyfill
├── Mqtt/MqttClient.cs minimal MQTT 3.1.1 client (no deps)
├── Mqtt/HaBridge.cs HA discovery + topic routing
├── Rest/RestApi.cs CWS-backed REST endpoints
├── Rest/SettingsUi.cs inline HTML/JS settings page
├── ProgramInfo.config
├── appsettings.sample.json
└── Properties/
tools/
├── Build-Cpz.ps1 drive VS 2008 DTE to produce .cpz
├── Deploy-Cpz.ps1 pscp upload + plink progload
└── Build-And-Deploy.ps1 chain the two for inner-loop dev
docs/
└── DEPLOY.md full deploy walkthrough