This document defines where code belongs in the shelly-cli codebase. For how to write code (patterns, standards, conventions), see development.md.
| I need to... | Put it in... |
|---|---|
| Define a cobra command | cmd/<domain>/<action>/<action>.go |
| Print formatted output to terminal | term/ |
| Format data to string (no I/O) | output/ |
| Implement device operations | shelly/ |
| Implement auth operations | shelly/auth/ |
| Implement Modbus operations | shelly/modbus/ |
| Implement provisioning operations | shelly/provision/ |
| Implement network operations | shelly/network/ (mqtt.go, ethernet.go, wifi.go) |
| Export data to external format | shelly/export/ |
| Define domain types | model/ |
| Add CLI infrastructure (flags, runners) | cmdutil/ |
| Detect/categorize errors | errutil/ |
| Add shared utilities | utils/ |
| Manage configuration | config/ |
internal/
βββ branding/ # ASCII banner utilities
β βββ branding.go # Banner, StyledBanner(), BannerLines()
β
βββ browser/ # Cross-platform URL opening
β βββ browser.go # Browser interface, New()
β
βββ client/ # Low-level shelly-go SDK wrapper
β βββ client.go # Client struct, Gen2 connection
β βββ detect.go # Device detection
β βββ gen1.go # Gen1Client for legacy devices
β βββ switch.go # SwitchComponent accessor
β βββ light.go # LightComponent accessor
β βββ rgb.go # RGBComponent accessor
β βββ cover.go # CoverComponent accessor
β βββ input.go # InputComponent accessor
β βββ kvs.go # KVS accessor
β βββ thermostat.go # ThermostatComponent accessor
β
βββ cmd/ # Cobra commands ONLY
β β # Each command: NewCommand(f *Factory) + run()
β β # NO helper functions - move them elsewhere!
β βββ root.go
β βββ device/
β β βββ device.go # Parent command
β β βββ info/ # shelly device info
β β βββ status/ # shelly device status
β β βββ reboot/ # shelly device reboot
β βββ switchcmd/ # shelly switch (on/off/toggle/status)
β βββ light/ # shelly light (on/off/set/status)
β βββ rgb/ # shelly rgb (on/off/set/status)
β βββ cover/ # shelly cover (open/close/stop/status)
β βββ sensor/ # shelly sensor (temp/humidity/flood/smoke)
β βββ energy/ # shelly energy (status/history/export)
β βββ config/ # shelly config (get/set/edit)
β βββ backup/ # shelly backup (create/restore/list)
β βββ export/ # shelly export (ansible/terraform)
β βββ scene/ # shelly scene (activate/list/import)
β βββ schedule/ # shelly schedule (list/create/delete)
β βββ script/ # shelly script (list/run/stop)
β βββ batch/ # shelly batch (on/off/command)
β βββ discover/ # shelly discover (scan/mdns/ble)
β βββ monitor/ # shelly monitor (power/status/events)
β βββ version/ # shelly version
β βββ ...
β
βββ cmdutil/ # Command infrastructure
β βββ factory.go # Factory (DI container)
β β # IOStreams(), Config(), ShellyService()
β β # ResolveAddress(), ExpandTargets()
β β # GetDevice(), GetGroup(), GetAlias()
β β
β βββ runner.go # Execution helpers
β β # RunWithSpinner(), RunBatch()
β β # RunStatus[T](), RunList[T]()
β β # PrintResult[T](), PrintListResult[T]()
β β
β βββ flags.go # Flag re-exports from flags/ subpackage
β β # BatchFlags, SceneFlags, ComponentFlags, etc.
β β
β βββ helpers.go # Cobra helpers
β β # AddCommandsToGroup()
β β
β βββ log.go # Logging utilities
β β
β βββ flags/ # Embeddable flag structs (subpackage)
β β βββ flags.go # Core flag adders (AddTimeoutFlag, etc.)
β β βββ batch.go # BatchFlags, AddBatchFlags()
β β βββ scene.go # SceneFlags, AddSceneFlags()
β β βββ component.go # ComponentFlags, QuickComponentFlags
β β βββ output.go # OutputFlags, AddOutputFlags()
β β βββ confirm.go # ConfirmFlags, AddConfirmFlags()
β β βββ device.go # DeviceListFlags, AddDeviceListFlags()
β β
β βββ factories/ # Generic command factories
β βββ component.go # NewComponentCommand (switch/light/rgb on/off/toggle)
β βββ batch.go # NewBatchComponentCommand
β βββ status.go # NewStatusCommand[T]
β βββ sensor.go # NewSensorCommand[T]
β βββ cover.go # NewCoverCommand
β βββ config.go # NewConfigDeleteCommand, NewConfigListCommand
β βββ delete.go # NewDeviceDeleteCommand
β
βββ completion/ # Shell completion
β βββ completion.go # Completers for bash/zsh/fish
β
βββ config/ # Configuration management
β βββ config.go # Config struct, Load(), Save()
β βββ manager.go # Manager - config mutations
β βββ devices.go # Device registry
β βββ aliases.go # Alias management
β β # ExpandAliasArgs(), ExecuteShellAlias()
β βββ scenes.go # Scene management, ParseSceneFile()
β βββ template.go # Template management
β βββ alerts.go # Alert configuration
β βββ validation.go # ValidateName() - shared validation
β
βββ download/ # HTTP download utilities
β βββ download.go # Download(), DownloadWithProgress()
β
βββ errutil/ # Shared error detection and categorization
β βββ detect.go # IsTimeout(), IsDNS(), IsNetwork(), IsAuth(), etc.
β
βββ github/ # GitHub integration
β βββ releases.go # Release checking, downloads
β βββ updates.go # Update checking, FindPreviousRelease()
β βββ install.go # InstallRelease(), binary replacement
β
βββ mock/ # Demo mode mock server
β βββ server.go # MockServer for demo/testing
β βββ demo.go # Demo mode utilities
β βββ config.go # Mock device configuration
β βββ fixtures.go # Test fixtures
β βββ fleet.go # Fleet mock data
β βββ discovery.go # Mock discovery responses
β
βββ ratelimit/ # Rate limiting with circuit breaker
β βββ ratelimit.go # RateLimiter, TokenBucket
β βββ circuit.go # CircuitBreaker
β βββ options.go # Configuration options
β
βββ telemetry/ # Opt-in anonymous usage analytics
β βββ telemetry.go # Telemetry collection (disabled by default)
β
βββ utils/ # Shared utilities (batch, device conversion)
β βββ batch.go # ResolveBatchTargets() - args/stdin/group/all
β βββ device.go # DiscoveredDeviceToConfig(), RegisterDiscoveredDevices()
β β # UnmarshalJSON() - RPC response helper
β βββ must.go # Must() - panic on error for init-time failures
β
βββ iostreams/ # I/O abstraction
β βββ iostreams.go # IOStreams struct
β β # In, Out, ErrOut (io.Reader/Writer)
β β # IsStdinTTY(), IsStdoutTTY(), ColorEnabled()
β β # Printf(), Println(), Success(), Error()
β β # StartProgress(), StopProgress()
β β
β βββ color.go # Color detection, theme handling
β βββ progress.go # Progress indicator management
β βββ prompt.go # Confirm(), Prompt() - user input
β βββ debug.go # DebugErr() - non-fatal error logging
β βββ log.go # Structured logging
β βββ multiwriter.go # Multi-line concurrent output
β
βββ model/ # Domain types
β βββ device.go # Device struct
β β # Name, Address, Generation, Type, Model, Auth
β β
β βββ component.go # Component struct, *Status types, *Config types
β β # SwitchStatus, CoverStatus, LightStatus, RGBStatus
β β
β βββ sensor.go # Sensor types
β β # AlarmSensor interface (generic)
β β # AlarmSensorReading, TemperatureReading
β β # HumidityReading, IlluminanceReading
β β # VoltmeterReading, DevicePowerReading
β β
β βββ errors.go # Error types
β
βββ output/ # Pure formatters (data β string, NO IOStreams)
β βββ format.go # Output format routing
β β # Format type (JSON, YAML, Table, Text, Template)
β β # FormatOutput()
β β
β βββ state.go # Boolean/state renderers
β β # Case enum (CaseLower, CaseTitle, CaseUpper)
β β # RenderOnOff(), RenderYesNo(), RenderOnline()
β β # RenderActive(), RenderAlarmState(), RenderMuteState()
β β # RenderUpdateStatus(), RenderGeneration()
β β
β βββ devices.go # Device formatters
β β # FormatDiscoveredDevices(), FormatAlarmSensors()
β β # FormatPower(), FormatPowerColored(), FormatEnergy()
β β
β βββ table.go # Table builder utilities
β βββ backup.go # FormatBackupsTable()
β βββ benchmark.go # Benchmark result formatters
β βββ diff.go # Config/script diff formatters
β βββ prompt.go # Prompt formatting helpers
β βββ report.go # Report formatters
β βββ writer.go # Writer utilities
β β
β βββ jsonfmt/ # JSON formatting with syntax highlighting
β βββ yamlfmt/ # YAML formatting with syntax highlighting
β βββ synfmt/ # Syntax highlighting utilities
β βββ tmplfmt/ # Go template formatting
β
βββ plugins/ # Plugin system
β βββ registry.go # Registry - plugin management
β βββ loader.go # Plugin loading
β βββ executor.go # Plugin execution
β βββ manifest.go # Plugin metadata
β βββ migrate.go # Plugin migration
β βββ upgrader.go # Upgrader - plugin upgrade functionality
β βββ hooks.go # Plugin hooks
β βββ types.go # Shared types
β βββ scaffold/ # Plugin scaffolding
β
βββ shelly/ # Business logic service layer
β βββ shelly.go # Service struct
β β # Connect(), WithConnection(), RawRPC()
β β # ResolveWithGeneration() - Gen1/Gen2 detection
β β # Service accessors for subpackages
β β
β βββ connection/ # Connection management subpackage
β β βββ connection.go # Manager struct, Provider/Resolver/Discoverer interfaces
β β βββ client.go # DeviceClient - unified Gen1/Gen2 wrapper
β β βββ executor.go # ExecuteGen2(), ExecuteGen1() with IP remapping
β β βββ remap.go # tryIPRemap(), isConnectionError() helpers
β β βββ batch.go # WithDevice(), WithDevices() batch operations
β β
β βββ resolver.go # DeviceResolver interface
β βββ device.go # Device operations
β βββ devicedata.go # DeviceData collection
β βββ switch.go # Switch operations
β βββ light.go # Light operations
β βββ rgb.go # RGB operations
β βββ cover.go # Cover operations
β βββ input.go # Input operations
β βββ sensor.go # Sensor operations
β βββ power.go # Power operations
β βββ energy.go # Energy meter operations
β βββ quick.go # QuickOn(), QuickOff(), QuickToggle()
β βββ config.go # Config get/set, WiFi, BLE, cloud, webhooks
β βββ wifi.go # WiFi operations (WiFiStatusFull, WiFiConfigFull, etc.)
β βββ backup.go # Service methods using backup/ types
β βββ schedule.go # Schedule operations
β βββ script.go # Script operations
β βββ firmware.go # Firmware operations
β βββ cloud.go # Cloud operations
β βββ monitoring.go # MonitoringSnapshot, FetchAllSnapshots()
β βββ kvs.go # Service methods using kvs/ types
β βββ template.go # Template operations
β βββ zigbee.go # Zigbee operations
β βββ bthome.go # BTHome operations
β βββ lora.go # LoRa operations
β βββ matter.go # Matter operations
β βββ migrate.go # Migration operations
β β
β βββ auth/ # Authentication configuration
β β βββ auth.go # Service: GetStatus(), Set(), Disable()
β β # Status type, CalculateHA1()
β β
β βββ automation/ # Scripts, schedules, webhooks
β β βββ ... # Automation-related operations
β β
β βββ backup/ # Backup domain types
β β βββ types.go # DeviceBackup, Options, RestoreOptions, DeviceInfo
β β # Validate(), LoadAndValidate(), IsFile()
β β
β βββ component/ # Component operations
β β βββ ... # Switch, Light, Cover, RGB, Input
β β
β βββ device/ # Device operations
β β βββ ... # Reboot, Info, Status, List
β β
β βββ export/ # Export format builders
β β βββ ansible.go # BuildAnsibleInventory(), AnsibleInventory
β β βββ terraform.go # BuildTerraformConfig(), TerraformDevice
β β βββ backup.go # BackupExporter, ScanBackupFiles(), WriteBackupFile()
β β βββ energy.go # FormatEMDataCSV(), FormatEM1DataCSV()
β β
β βββ firmware/ # Firmware checking and updates
β β βββ ... # Check, Update, Rollback, Cache
β β
β βββ kvs/ # KVS domain types
β β βββ types.go # Item, ListResult, GetResult, Export
β β # ParseValue(), ParseImportFile()
β β
β βββ modbus/ # Modbus configuration
β β βββ modbus.go # Service: GetStatus(), GetConfig(), SetConfig()
β β # Status, Config types
β β
β βββ network/ # Network-related services
β β βββ wifi.go # WiFiService: GetStatusFull(), GetConfigFull(), etc.
β β βββ mqtt.go # MQTTService: GetStatus(), GetConfig(), SetConfig()
β β βββ ethernet.go # EthernetService: GetStatus(), GetConfig(), SetConfig()
β β βββ cloud.go # CloudClient: Login(), GetAllDevices(), etc.
β β βββ cloudcontrol.go # Cloud device control operations
β β
β βββ provision/ # Device provisioning
β β βββ provision.go # Service: GetDeviceInfoByAddress(), ConfigureWiFi()
β β # GetBTHomeStatus(), StartBTHomeDiscovery()
β β # DeviceInfo, BTHomeDiscovery types
β β
β βββ sensoraddon/ # Sensor add-on operations
β β βββ ... # Temperature/humidity sensor add-on handling
β β
β βββ wireless/ # Wireless protocol services
β βββ ... # BLE, BTHome, Matter, Zigbee, LoRa
β
βββ term/ # Terminal presentation (composed displays)
β βββ term.go # Package doc, shared helpers (printTable, formatTemp)
β βββ action.go # DisplayActions - Gen1 action URL display
β βββ alert.go # DisplayAlerts*, DisplayAlertStatus
β βββ audit.go # DisplayAuditResults
β βββ automation.go # DisplayScript*, DisplaySchedule*, DisplayWebhook*
β βββ backup.go # DisplayBackupSummary, DisplayRestorePreview, etc.
β βββ benchmark.go # DisplayBenchmarkResults
β βββ ble.go # DisplayBLEProvisionResult
β βββ bthome.go # DisplayBTHomeDevices*, DisplayBTHomeSensor*
β βββ component.go # DisplaySwitch*, DisplayLight*, DisplayCover*, DisplayInput*
β βββ config.go # DisplayConfigTable, DisplaySceneList, DisplayAliasList
β βββ debug.go # PrintJSONResult, DisplayWebSocketEvent
β βββ device.go # DisplayDeviceStatus, DisplayAllSnapshots
β βββ diff.go # DisplayConfigDiffs, DisplayScriptDiffs, etc.
β βββ discovery.go # DisplayDiscoveredDevices, DisplayBLEDevices
β βββ doctor.go # DisplayDoctorResults, DisplayCheckResult*
β βββ energy.go # DisplayEnergyStatus, DisplayEnergyHistory
β βββ event.go # DisplayEvent, OutputEventJSON
β βββ firmware.go # DisplayFirmwareStatus, DisplayFirmwareInfo
β βββ fleet.go # DisplayFleetStatus, DisplayFleetHealth, DisplayFleetStats
β βββ kvs.go # DisplayKVS*
β βββ network.go # DisplayWiFi*, DisplayEthernet*, DisplayMQTT*, DisplayCloud*
β βββ power.go # DisplayPowerMetrics, DisplayDashboard, DisplayComparison
β βββ repl.go # REPL session display and command handling
β βββ scene.go # DisplaySceneDetails
β βββ script.go # DisplayScriptStatus, DisplayScriptCode
β βββ sensor.go # Generic sensor displays (partial application pattern)
β β # DisplayTemperature*, DisplayHumidity*, etc.
β βββ shell.go # Shell session display and command handling
β βββ version.go # DisplayVersionInfo, DisplayUpdateAvailable, RunUpdateCheck
β
βββ testutil/ # Testing utilities
β βββ testutil.go # Test helpers
β βββ mockclient.go # MockClient for testing
β βββ mockresolver.go # MockResolver for testing
β
βββ theme/ # Theming (bubbletint)
β βββ theme.go # Style builders
β # Bold(), Dim(), Highlight()
β # StatusOK(), StatusWarn(), StatusError()
β # StyledPower(), StyledEnergy()
β # FalseStyle (FalseError, FalseDim)
β
βββ tui/ # Terminal UI (BubbleTea)
β βββ app.go # Main TUI application
β βββ keys.go # Key bindings
β βββ styles.go # TUI styles
β βββ components/ # TUI components (39 total)
β βββ backup/ # Backup management
β βββ batch/ # Batch operations
β βββ ble/ # BLE device management
β βββ cloud/ # Cloud integration
β βββ cmdmode/ # Command mode input
β βββ confirm/ # Confirmation dialogs
β βββ devicedetail/ # Device detail view
β βββ deviceinfo/ # Device info panel
β βββ devicelist/ # Device list
β βββ discovery/ # Device discovery
β βββ energy/ # Energy dashboard
β βββ energybars/ # Energy bar charts
β βββ energyhistory/# Energy history view
β βββ errorview/ # Error display
β βββ events/ # Events stream
β βββ firmware/ # Firmware management
β βββ fleet/ # Fleet management
β βββ form/ # Form inputs
β βββ help/ # Help overlay
β βββ inputs/ # Input components
β βββ jsonviewer/ # JSON viewer
β βββ kvs/ # KVS management
β βββ loading/ # Loading indicators
β βββ modal/ # Modal dialogs
β βββ monitor/ # Real-time monitoring
β βββ protocols/ # Protocol views
β βββ provisioning/ # Device provisioning
β βββ schedules/ # Schedule management
β βββ scripts/ # Script management
β βββ search/ # Search interface
β βββ security/ # Security settings
β βββ smarthome/ # Smart home integrations
β βββ statusbar/ # Status bar
β βββ system/ # System info
β βββ tabs/ # Tab navigation
β βββ toast/ # Toast notifications
β βββ virtuals/ # Virtual components
β βββ webhooks/ # Webhook management
β βββ wifi/ # WiFi configuration
β
βββ webhook/ # Webhook server
β βββ server.go # HTTP webhook receiver
β
βββ wizard/ # Interactive setup wizard
β βββ wizard.go # Main wizard flow
β βββ steps.go # Wizard step definitions
β βββ discovery.go # Device discovery step
β βββ check.go # Validation checks
β βββ helpers.go # Wizard utilities
β
βββ version/ # Build-time version info
βββ version.go # Version, Commit, Date variables
β # Get(), Short(), Long(), String()
βββ cache.go # ReadCachedVersion() - version cache management
βββ notification.go # ShowUpdateNotification() - update availability
Is it a cobra command definition?
βββ Yes β cmd/<domain>/<action>/<action>.go
β (ONLY NewCommand + run, NO helpers!)
β
βββ No β Does it write to IOStreams?
β
βββ Yes β term/
β (composed display functions wrapping output/)
β
βββ No β Is it a pure formatter (data β string)?
β
βββ Yes β output/
β (format.go, state.go, devices.go, etc.)
β
βββ No β Is it business logic (device operations)?
β
βββ Yes β shelly/
β (or shelly/export/ for external formats)
β
βββ No β Is it a domain type?
β
βββ Yes β model/
β
βββ No β Is it CLI infrastructure?
β
βββ Yes β cmdutil/
β (factory, flags, runner, factories/)
β
βββ No β Is it error detection/categorization?
β
βββ Yes β errutil/
β (shared by CLI and TUI)
β
βββ No β Is it config-related?
β
βββ Yes β config/
β
βββ No β utils/
(shared utilities)
The separation between output/ and term/:
| Package | Signature | Purpose |
|---|---|---|
output/ |
FormatX(data) β string |
Pure formatter, no I/O |
term/ |
DisplayX(ios, data) |
Prints to terminal |
Example:
// output/devices.go - pure formatter (no IOStreams)
func FormatDiscoveredDevices(devices []discovery.DiscoveredDevice) *Table {
// Returns table data structure
}
// term/discovery.go - IOStreams printer
func DisplayDiscoveredDevices(ios *iostreams.IOStreams, devices []discovery.DiscoveredDevice) {
table := output.FormatDiscoveredDevices(devices)
if table == nil {
ios.NoResults("devices")
return
}
table.PrintTo(ios.Out)
ios.Count("device", len(devices))
}Command factories in cmdutil/factories/ reduce boilerplate:
// Instead of writing 200+ lines per status command:
func NewCommand(f *cmdutil.Factory) *cobra.Command {
return factories.NewStatusCommand(f, factories.StatusOpts[SwitchStatus]{
Component: "Switch",
Aliases: []string{"st", "s"},
SpinnerMsg: "Fetching switch status...",
Fetcher: fetchStatus,
Display: displayStatus,
})
}Available factories:
NewComponentCommand()- switch/light/rgb on/off/toggleNewStatusCommand[T]()- generic status with type safetyNewSensorCommand[T]()- sensor commands with list/statusNewBatchComponentCommand()- batch operationsNewCoverCommand()- cover open/close/stopNewConfigDeleteCommand(),NewConfigListCommand()
Flag structs in cmdutil/flags/ reduce Options boilerplate:
import "github.com/tj-smith47/shelly-cli/internal/cmdutil/flags"
type Options struct {
flags.BatchFlags // Embed common batch flags
flags.ComponentFlags // Embed component ID flag
Factory *cmdutil.Factory
}
func NewCommand(f *cmdutil.Factory) *cobra.Command {
opts := &Options{Factory: f}
cmd := &cobra.Command{...}
flags.AddBatchFlags(cmd, &opts.BatchFlags)
flags.AddComponentFlags(cmd, &opts.ComponentFlags, "Switch")
return cmd
}Available embeddable structs:
BatchFlags- GroupName, All, Timeout, SwitchID, ConcurrentSceneFlags- Timeout, Concurrent, DryRunComponentFlags- ID (for component selection via --id/-i flag)QuickComponentFlags- ID with -1 default for "all components" (on/off/toggle)OutputFlags- Format (table/json/yaml display output)ConfirmFlags- Yes, Confirm (for destructive operations)DeviceListFlags- All, Group, Quiet, JSON (for device list filtering)
Note: For backward compatibility, these are also re-exported from cmdutil directly (e.g., cmdutil.BatchFlags). New code should import from cmdutil/flags directly.
Dynamic helpers for custom defaults:
AddOutputFlagsCustom(cmd, flags, default, allowed...)- custom default and allowed valuesAddOutputFlagsNamed(cmd, flags, name, short, default, allowed...)- custom flag name
Export builders live under shelly/export/ because they:
- Transform
shelly.DeviceDataβ external format - Are tightly coupled to shelly types
- Semantically represent "shelly data export"
Contents:
ansible.go- Ansible inventory builderterraform.go- Terraform config builderbackup.go- Backup file operationsenergy.go- Energy data CSV export
RunWithSpinner(ctx, ios, msg, action) error
RunBatch(ctx, ios, svc, targets, concurrent, action) error
RunStatus[T](ctx, f, device, id, fetcher, display) error
RunList[T](ctx, f, device, fetcher, display) error
PrintResult[T](ios, format, data, displayFn) error
PrintListResult[T](ios, format, items, displayFn) errortype Case int // CaseLower, CaseTitle, CaseUpper
RenderOnOff(on bool, c Case, fs FalseStyle) string
RenderYesNo(value bool, c Case, fs FalseStyle) string
RenderOnline(online bool, c Case) string
RenderActive(active bool, c Case, fs FalseStyle) string
RenderAlarmState(alarm bool, msg string) string
RenderMuteState(muted bool) string
RenderUpdateStatus(available bool, version string) stringtype AlarmSensor interface {
GetID() int
IsAlarm() bool
IsMuted() bool
GetErrors() []string
}
type TemperatureReading struct { ID int; TC, TF *float64; Errors []string }
type HumidityReading struct { ID int; RH *float64; Errors []string }
type IlluminanceReading struct { ID int; Lux *float64; Errors []string }
type VoltmeterReading struct { ID int; Voltage *float64; Errors []string }Commands in cmd/ should contain ONLY:
NewCommand(f *cmdutil.Factory) *cobra.Command- constructorrun(ctx, f, ...)- execution logic (simple orchestration)
Move ALL other private functions OUT of cmd/ using the above decision tree
- configuration.md - Configuration management
- development.md - Coding patterns and standards
- testing.md - Testing strategy
- plugins.md - Plugin system