using System.Device.Gpio; namespace GPIORaspberryPI4; public sealed class RelayOutput : IRelayOutput { private readonly GpioController _gpio; private readonly int _pin; private readonly bool _activeLow; private bool _disposed; public bool IsOn { get; private set; } public RelayOutput(int pin, bool activeLow = false) { _pin = pin; _activeLow = activeLow; _gpio = new GpioController(); _gpio.OpenPin(_pin, PinMode.Output); // Safe default state WriteInternal(false); } public void On() { ThrowIfDisposed(); WriteInternal(true); } public void Off() { ThrowIfDisposed(); WriteInternal(false); } public void Set(bool on) { ThrowIfDisposed(); WriteInternal(on); } private void WriteInternal(bool on) { var pinValue = _activeLow ? (on ? PinValue.Low : PinValue.High) : (on ? PinValue.High : PinValue.Low); _gpio.Write(_pin, pinValue); IsOn = on; } private void ThrowIfDisposed() { if (_disposed) throw new ObjectDisposedException(nameof(RelayOutput)); } public void Dispose() { if (_disposed) return; try { // Fail-safe: relay OFF on dispose WriteInternal(false); } catch { // Ignore cleanup errors } if (_gpio.IsPinOpen(_pin)) _gpio.ClosePin(_pin); _gpio.Dispose(); _disposed = true; } }