An intelligent relay control system for managing power states of hardware devices with optimized boot/shutdown sequences.
- Dependency Management: Devices can depend on other devices (e.g., computer needs network switch)
- Optimized Boot Timing: Calculates optimal start times for dependent devices to minimize total boot time
- Lock-based Control: Multiple users/processes can request device power with lock IDs
- Graceful Shutdown: Devices shut down after a delay when all locks are released
- Cooldown Protection: Prevents rapid on/off cycling to protect hardware
- Event Hooks: Observe and react to relay state changes
- Flexible Logging: Integrate with your favorite logging library
go get github.com/endreszabo/relayctlpackage main
import (
"context"
"fmt"
"time"
"github.com/endreszabo/relayctl/relay"
)
func main() {
// Create a network switch that takes 30 seconds to boot
network := relay.NewRelay("network", nil,
relay.WithBootDelay(30*time.Second),
relay.WithCooldownPeriod(2*time.Second),
relay.WithHardwareControl(
func() error { fmt.Println("Turning on network"); return nil },
func() error { fmt.Println("Turning off network"); return nil },
),
)
// Create a computer that depends on the network
// Will start at T=5s (30s - 25s) so both are ready at T=30s
computer := relay.NewRelay("computer", nil,
relay.WithBootDelay(25*time.Second),
relay.WithDependencies(network),
relay.WithHardwareControl(
func() error { fmt.Println("Turning on computer"); return nil },
func() error { fmt.Println("Turning off computer"); return nil },
),
)
ctx := context.Background()
// Turn on the computer (network starts automatically)
if err := computer.TurnOn(ctx, "user1"); err != nil {
fmt.Printf("Error: %v\n", err)
}
// ... use the devices ...
// Turn off when done
computer.TurnOff("user1")
}Each relay goes through these states:
StateOff → StateBooting → StateOn → [locks empty, wait powerOffDelay] → StateCoolingDown → StateOff
When a relay has dependencies, they are started automatically with optimized timing:
// Computer needs network to be ready
computer := relay.NewRelay("computer", nil,
relay.WithBootDelay(25*time.Second),
relay.WithDependencies(network),
)The system calculates the optimal start time so the computer finishes booting exactly when the network is ready, minimizing total startup time.
Multiple users can hold locks on a relay simultaneously. The relay only turns off after all locks are released:
computer.TurnOn(ctx, "user1")
computer.TurnOn(ctx, "user2") // Second lock
computer.TurnOff("user1") // Still stays on (user2 still has lock)
computer.TurnOff("user2") // Now it turns off after powerOffDelayUse functional options to configure relays:
| Option | Description | Default |
|---|---|---|
WithBootDelay(d) |
Time required for the relay to boot | 0 |
WithCooldownPeriod(d) |
Minimum time between off and on | 0 |
WithPowerOffDelay(d) |
Delay before actual shutdown after last lock released | 0 |
WithHardwareControl(on, off) |
Hardware control functions | No-op |
WithDependencies(...) |
Dependency relays | none |
WithLogger(logger) |
Logger for operations | NoOpLogger |
WithEventHandler(handler) |
Event handler for state changes | NoOpEventHandler |
WithController(ctrl) |
Central controller (optional) | nil |
type MyLogger struct{}
func (l *MyLogger) Debug(msg string, args ...interface{}) {
// Your logging implementation
}
func (l *MyLogger) Info(msg string, args ...interface{}) {
// Your logging implementation
}
func (l *MyLogger) Error(msg string, args ...interface{}) {
// Your logging implementation
}
relay := relay.NewRelay("id", nil,
relay.WithLogger(&MyLogger{}),
)type MyEventHandler struct{}
func (h *MyEventHandler) OnBootComplete(relayID string, duration time.Duration) {
fmt.Printf("🎉 %s booted in %v\n", relayID, duration)
}
func (h *MyEventHandler) OnStateChange(relayID string, oldState, newState relay.RelayState) {
fmt.Printf("%s: %s → %s\n", relayID, oldState, newState)
}
// Implement other EventHandler methods...
relay := relay.NewRelay("id", nil,
relay.WithEventHandler(&MyEventHandler{}),
)Build and run the demo application:
go run cmd/demo/main.goFull API documentation is available at godoc.org.
[Your License Here]