Companion plugin for Logi Options+ — opens a pipe server, forwards events to the host SDK's haptic waveforms on the MX Master 4.
Unofficial community plugin. Not affiliated with Logitech.
-
Logi Options+ installed (provides the plugin service +
PluginApi.dll)- Windows:
C:\Program Files\Logi\LogiPluginService\PluginApi.dll - macOS:
/Applications/Utilities/LogiPluginService.app/Contents/MonoBundle/PluginApi.dll
- Windows:
-
.NET 8 SDK
-
LogiPluginTool global .NET tool (for packaging):
dotnet tool install --global LogiPluginTool
logi-plugin/
├── src/
│ ├── Plugin.cs ← Loupedeck.Plugin entry
│ ├── Application.cs ← ClientApplication companion
│ ├── PipeServer.cs ← Named Pipe / Unix socket server
│ ├── HapticMapper.cs ← event → waveform mapping
│ ├── PluginLog.cs ← SDK log wrapper
│ ├── HapticBridgeForUnity.Plugin.csproj
│ └── package/
│ ├── metadata/
│ │ ├── LoupedeckPackage.yaml ← manifest (plugin4, HasHapticsMapping)
│ │ └── Icon256x256.png
│ └── events/
│ ├── DefaultEventSource.yaml ← 15 waveform events
│ └── extra/
│ └── eventMapping.yaml ← haptic UI registration (haptics block)
└── build/ ← .lplug4 output
cd logi-plugin/src
dotnet build -c ReleaseThe csproj automatically writes a .link file into the plugin service's Plugins/ directory and triggers a reload, so the host hot-reloads the plugin.
cd logi-plugin/src
dotnet build -c Release
logiplugintool pack ../bin/Release ../build/HapticBridgeForUnity_0.1.1.lplug4Output: logi-plugin/build/HapticBridgeForUnity_<version>.lplug4 — double-clickable installer.
PluginApi.dll ships with Logi Options+, so it cannot be fetched in CI. Releases are cut locally from the repo root:
./scripts/release-plugin.sh 0.2.0The script:
- Syncs the manifest version and pushes the commit
- Runs
dotnet build -c Release - Packs the
.lplug4 - Creates and pushes the
plugin-v<version>tag - Creates a GitHub Release with the
.lplug4attached as an asset
Requirements: dotnet, logiplugintool, gh (GitHub CLI), a clean working tree.
Load()registers the 15 haptic waveforms viaPluginEvents.AddEventand starts the pipe server.- The Unity client writes an event name (e.g.
"success") to the pipe. HapticMapperresolves it to an SDK waveform (e.g.success → completed).PluginEvents.RaiseEvent("completed")fires the haptic pulse on the MX Master 4.
Two files must line up or Logi Options+ will not show the plugin on its Haptic Feedback screen:
LoupedeckPackage.yaml→pluginCapabilitiesmust includeHasHapticsMapping(note the plural "s").HasHapticMappingalone is not enough.events/extra/eventMapping.yaml→ ahaptics:block withDEFAULT: <waveform>for every waveform the plugin registers.
Without both, events still flow through the pipe but the mouse never vibrates, because the user has no way to enable them in Logi Options+.
.NET's NamedPipeServerStream opens a Unix Domain Socket at $TMPDIR/CoreFxPipe_<name> on macOS. Dispose() does not remove the socket file, which blocks the next plugin instance from binding (IO_AllPipeInstancesAreBusy). PipeServer catches that error, unlinks the socket explicitly, and retries.
15 SDK waveforms: sharp_collision, sharp_state_change, knock, damp_collision, mad, ringing, subtle_collision, completed, jingle, damp_state_change, firework, happy_alert, wave, angry_alert, square.
The Unity side can use the HapticEvent enum (9 generic events) or TriggerRaw("firework") to send a waveform name directly.