Documentation
¶
Index ¶
- Constants
- Variables
- func FindConfigFile(filename string, envVar string) string
- func ValidateStorageName(name string) error
- type APIEndpoints
- type APIErrorResponse
- type APIResponse
- type ControlUnitConfig
- type ControlUnitEndpoints
- type DBusUnitConfig
- type DBusUnitEndpoints
- type Defaults
- type DeltaSettings
- type DisplayRequest
- type Endpoint
- type EndpointWithStatus
- type EqualizeDefaults
- type EqualizeSettings
- type ExecutedProgram
- type ExecutionStatus
- type ExecutionStorage
- type FileStorage
- func (storage *FileStorage) DeleteProgram(filePath string) error
- func (storage *FileStorage) GetAvailableSpaceMB() int64
- func (storage *FileStorage) ListPrograms(searchPath string) ([]string, error)
- func (storage *FileStorage) LoadProgram(filePath string) (*Program, error)
- func (storage *FileStorage) SaveProgram(filePath string, program *Program) error
- type GlobalOptions
- type HalkoConfig
- type HardwareStatusResponse
- type KilnSensorStrategy
- type PSUStatus
- type PowerCommand
- type PowerOperationResponse
- type PowerPidSettings
- type PowerResponse
- type PowerSettingType
- type PowerStatusResponse
- type PowerUnit
- type PowerUnitEndpoints
- type PowersCommand
- type Program
- type ProgramListing
- type ProgramState
- type ProgramStep
- type ProgramStorage
- type RunHistory
- type RunNote
- type SensorStatus
- type SensorUnitConfig
- type SensorUnitEndpoints
- type ServiceStatus
- type ServiceStatusResponse
- type ShellyStatus
- type ShellySwitchGetStatusResponse
- type ShellySwitchSetResponse
- type StatusRequest
- type StatusResponse
- type StepDuration
- type StepType
- type StorageConfig
- type StoredProgramInfo
- type SystemInfo
- type SystemServicesStatus
- type SystemStatusResponse
- type TemperatureResponse
- type TemperatureStatus
- type VPNOperationResponse
Constants ¶
const ( ServiceNameControlUnit = "controlunit" ServiceNamePowerUnit = "powerunit" ServiceNameSensorUnit = "sensorunit" )
Service name constants
const ( StepTypeHeating StepType = "heating" StepTypeCooling StepType = "cooling" StepTypeAcclimate StepType = "acclimate" // Startup step types. The control unit synthesizes these in front of a // program's own steps; a program may not name them. StepTypeEqualize StepType = "equalize" StepTypeSteamPrewarm StepType = "steam_prewarm" PowerSettingTypeSimple PowerSettingType = "simple" PowerSettingTypeDelta PowerSettingType = "delta" )
const ( // signals invalid temperature reading InvalidTemperatureReading = -273.15 // Absolute zero in Celsius, used to indicate an invalid reading )
const Version = "1.5.0"
Version is the Halko release version.
This constant is the single source of truth for the version. Every service reports it on its status endpoint and logs it at startup, and the release-tag workflow creates the matching git tag once a change lands on master. Releasing therefore means bumping this line -- see CONTRIBUTING.md for which component to bump.
Variables ¶
var ( ErrProgramExists = errors.New("program exists") ErrProgramDoesNotExist = errors.New("program does not exist") )
var ErrInvalidStorageName = errors.New("invalid storage name")
ErrInvalidStorageName is returned for a program or run name that cannot be used as a filename.
Functions ¶
func FindConfigFile ¶
FindConfigFile searches for a configuration file in standard locations filename: name of config file to search for (e.g., "halko.cfg", "simulator.conf") envVar: environment variable name to check (e.g., "HALKO_CONFIG", "SIMULATOR_CONFIG") Returns empty string if not found
func ValidateStorageName ¶
ValidateStorageName reports whether name may be joined to a storage directory to form a file path.
Program and run names arrive over the API and are turned into filenames under base_path. filepath.Join cleans the result but does not confine it, so a name carrying a separator or a parent reference escapes the directory it was meant to land in. Rather than filter the ways of spelling that, the rule is a whitelist: a name must be one plain path segment, which is all any legitimate name has ever needed to be.
Types ¶
type APIEndpoints ¶
type APIEndpoints struct {
ControlUnit ControlUnitEndpoints `json:"controlunit"`
SensorUnit SensorUnitEndpoints `json:"sensorunit"`
PowerUnit PowerUnitEndpoints `json:"powerunit"`
DBusUnit DBusUnitEndpoints `json:"dbusunit"`
}
type APIErrorResponse ¶
type APIErrorResponse struct {
Err string `json:"error"`
}
APIErrorResponse is a generic error response
type APIResponse ¶
type APIResponse[T any] struct { Data T `json:"data"` }
type ControlUnitConfig ¶
type ControlUnitConfig struct {
BasePath string `json:"base_path"`
TickLength string `json:"tick_length"`
NetworkInterface string `json:"network_interface"`
KilnSensorStrategy KilnSensorStrategy `json:"kiln_sensor_strategy"`
Defaults *Defaults `json:"defaults"`
// Resolved from TickLength once, while loading.
TickDuration time.Duration `json:"-"`
}
type ControlUnitEndpoints ¶
type ControlUnitEndpoints struct {
Endpoint `json:",inline"`
Engine string `json:"engine"`
Programs string `json:"programs"`
Status string `json:"status"`
}
func (*ControlUnitEndpoints) GetEngineURL ¶
func (e *ControlUnitEndpoints) GetEngineURL() string
func (*ControlUnitEndpoints) GetProgramsURL ¶
func (e *ControlUnitEndpoints) GetProgramsURL() string
ControlUnitEndpoints methods
func (*ControlUnitEndpoints) GetStatusURL ¶
func (e *ControlUnitEndpoints) GetStatusURL() string
GetStatusURL returns the full status endpoint URL for ControlUnitEndpoints
type DBusUnitConfig ¶
type DBusUnitConfig struct {
SystemBusSocket string `json:"system_bus_socket"`
}
type DBusUnitEndpoints ¶
type DBusUnitEndpoints struct {
Endpoint `json:",inline"`
VPN string `json:"vpn"`
Power string `json:"power"`
Status string `json:"status"`
}
func (*DBusUnitEndpoints) GetStatusURL ¶
func (e *DBusUnitEndpoints) GetStatusURL() string
GetStatusURL returns the full status endpoint URL for DBusUnitEndpoints
type Defaults ¶
type Defaults struct {
Deltas map[StepType]*DeltaSettings `json:"deltas"`
// Power applied to a component a step leaves unspecified. Pointers so
// that "absent" is distinguishable from a deliberate 0.
FanPower *uint8 `json:"fan_power"`
SteamPower *uint8 `json:"steam_power"`
// Everything the startup steps run on, grouped so the block reads as
// one concern rather than two loose keys.
Equalize *EqualizeDefaults `json:"equalize"`
// Highest target any step may ask for.
MaxTargetTemperature *uint8 `json:"max_target_temperature"`
// Temperature steam cannot heat the kiln past. Above it steam is
// thermally neutral; below it steam outruns the heater.
SteamCeiling *uint8 `json:"steam_ceiling"`
// How long a sensor may go without a valid reading before the running
// program is failed and all power switched off.
SensorTimeout string `json:"sensor_timeout"`
// How often a running program appends a line to its execution log.
ExecutionLogInterval string `json:"execution_log_interval"`
// Resolved from the strings above once, while loading. Both are
// compared against second counts, so they are carried as seconds
// rather than durations.
SensorTimeoutSeconds int64 `json:"-"`
ExecutionLogIntervalSeconds int64 `json:"-"`
}
Defaults carries every value the control unit would otherwise have to invent: the delta band each step type falls back to, the power a program gets for components it does not name, and the kiln's own ceiling. All of it is required, so a missing entry fails at load rather than turning into a zero somewhere downstream.
type DeltaSettings ¶
type DeltaSettings struct {
MinDelta float32 `json:"min_delta"`
MaxDelta float32 `json:"max_delta"`
}
DeltaSettings is the band a step type's delta controller starts from when a program names no heater of its own. What the band is measured against depends on the step type, so the numbers are not comparable across them: a heating step bands the kiln around the material and both values are positive, an acclimate bands it around the target and straddles zero.
type DisplayRequest ¶
type DisplayRequest struct {
Message string `json:"message"`
Address string `json:"address,omitempty"`
}
DisplayRequest defines the structure for a display update request body
type Endpoint ¶
func (*Endpoint) GetPort ¶
GetPort extracts the port as a string from the URL or provided urlStr Returns "80" for HTTP and "443" for HTTPS if port is not explicitly specified
func (*Endpoint) GetStatusURL ¶
GetStatusURL returns the full status endpoint URL
type EndpointWithStatus ¶
type EndpointWithStatus interface {
GetStatusURL() string
}
type EqualizeDefaults ¶
type EqualizeDefaults struct {
Delta *float32 `json:"delta"`
// Whether a program that does not say gets the steam warm-up step.
// A pointer so that "absent" is distinguishable from a deliberate
// false, which would otherwise be an invented default.
SteamPrewarm *bool `json:"steam_prewarm"`
SteamPrewarmTimeout string `json:"steam_prewarm_timeout"`
// Resolved from the string above once, while loading. Compared against
// a second count, so carried as seconds rather than a duration.
SteamPrewarmTimeoutSeconds int64 `json:"-"`
}
EqualizeDefaults carries what the startup steps the control unit runs before a program's first step need: the band a program falls back to when it names none of its own, and how long the steam warm-up waits for evidence the generator is producing.
type EqualizeSettings ¶
type EqualizeSettings struct {
Delta *float32 `json:"delta,omitempty"`
SteamPrewarm *bool `json:"steam_prewarm,omitempty"`
}
EqualizeSettings configures the startup steps the control unit runs before a program's own first step. Delta is the band within which the kiln and the material count as equal; SteamPrewarm asks for the steam generator to be brought up to boiling and proved before the run starts.
type ExecutedProgram ¶
type ExecutedProgram struct {
RunHistory
Program Program `json:"program"`
}
type ExecutionStatus ¶
type ExecutionStatus struct {
Program Program `json:"program"`
StartedAt int64 `json:"started_at,omitempty"`
CurrentStep string `json:"current_step,omitempty"`
CurrentStepStartedAt int64 `json:"current_step_started_at,omitempty"`
Temperatures TemperatureStatus `json:"temperatures,omitempty"`
PowerStatus PSUStatus `json:"power_status,omitempty"`
}
type ExecutionStorage ¶
type ExecutionStorage interface {
// Program execution history operations
ListExecutedPrograms() ([]string, error)
LoadExecutedProgram(programName string) (*Program, error)
DeleteExecutedProgram(programName string) error
LoadState(programName string) (ProgramState, int64, error)
GetLogPath(programName string) (string, error)
GetRunningLogPath(programName string) (string, error)
LoadRunNotes(programName string) ([]RunNote, error)
// System resource operations
GetAvailableSpaceMB() int64
}
ExecutionStorage defines the interface for managing execution history and logs
type FileStorage ¶
type FileStorage struct {
BasePath string
}
func NewFileStorage ¶
func NewFileStorage(basePath string) (*FileStorage, error)
func (*FileStorage) DeleteProgram ¶
func (storage *FileStorage) DeleteProgram(filePath string) error
func (*FileStorage) GetAvailableSpaceMB ¶
func (storage *FileStorage) GetAvailableSpaceMB() int64
GetAvailableSpaceMB returns the available disk space in megabytes for the storage base path
func (*FileStorage) ListPrograms ¶
func (storage *FileStorage) ListPrograms(searchPath string) ([]string, error)
func (*FileStorage) LoadProgram ¶
func (storage *FileStorage) LoadProgram(filePath string) (*Program, error)
func (*FileStorage) SaveProgram ¶
func (storage *FileStorage) SaveProgram(filePath string, program *Program) error
type GlobalOptions ¶
type GlobalOptions struct {
ConfigPath string // Path to halko.cfg configuration file
Verbose bool // Enable verbose output
LogLevel log.Level // Log level (0=ERROR, 1=WARN, 2=INFO, 3=DEBUG, 4=TRACE)
}
GlobalOptions represents options common to all modules (controlunit, powerunit, sensorunit, halkoctl)
func ParseGlobalOptions ¶
func ParseGlobalOptions() (*GlobalOptions, error)
ParseGlobalOptions parses command-line options for all modules (controlunit, powerunit, sensorunit, halkoctl)
func (*GlobalOptions) ApplyLogLevel ¶
func (opts *GlobalOptions) ApplyLogLevel()
ApplyLogLevel sets the global log level based on the parsed options
type HalkoConfig ¶
type HalkoConfig struct {
ControlUnitConfig *ControlUnitConfig `json:"controlunit"`
PowerUnit *PowerUnit `json:"power_unit"`
SensorUnit *SensorUnitConfig `json:"sensorunit"`
DBusUnit *DBusUnitConfig `json:"dbusunit"`
APIEndpoints *APIEndpoints `json:"api_endpoints"`
}
func LoadConfig ¶
func LoadConfig(configPath string) (*HalkoConfig, error)
func (*HalkoConfig) ValidateRequired ¶
func (c *HalkoConfig) ValidateRequired() error
type HardwareStatusResponse ¶
type HardwareStatusResponse struct {
Shelly ShellyStatus `json:"shelly"`
}
HardwareStatusResponse contains hardware status information
type KilnSensorStrategy ¶
type KilnSensorStrategy string
KilnSensorStrategy names how the control unit turns the kiln's two sensor readings into the one temperature it controls on. It is a property of the installation, so it is configured rather than defaulted: a control unit that invented one would silently control on a number nobody chose.
const ( KilnSensorLower KilnSensorStrategy = "lower" KilnSensorHigher KilnSensorStrategy = "higher" KilnSensorAverage KilnSensorStrategy = "average" )
type PSUStatus ¶
type PSUStatus struct {
Heater int8 `json:"heater"`
Fan int8 `json:"fan"`
Steam int8 `json:"steam"`
}
PSUStatus represents the power level (in percentage) of the heater, fan, and steam.
type PowerCommand ¶
type PowerCommand struct {
Percent uint8 `json:"percent"`
}
Power controller API
type PowerOperationResponse ¶
type PowerOperationResponse struct {
Message string `json:"message"`
}
Power controller API
type PowerPidSettings ¶
type PowerPidSettings struct {
Type PowerSettingType `json:"type,omitempty"`
MinDelta *float32 `json:"min_delta,omitempty"`
MaxDelta *float32 `json:"max_delta,omitempty"`
Power *uint8 `json:"power,omitempty"`
}
func (*PowerPidSettings) Validate ¶
func (p *PowerPidSettings) Validate(component string) error
type PowerResponse ¶
type PowerResponse struct {
Percent uint8 `json:"percent"`
}
Power controller API
type PowerSettingType ¶
type PowerSettingType string
type PowerUnit ¶
type PowerUnit struct {
ShellyAddress string `json:"shelly_address"`
CycleLength string `json:"cycle_length"`
PowerMapping map[string]int `json:"power_mapping"`
MaxIdleTime string `json:"max_idle_time"`
// Resolved from the strings above once, while loading.
CycleDuration time.Duration `json:"-"`
MaxIdleDuration time.Duration `json:"-"`
}
type PowerUnitEndpoints ¶
type PowerUnitEndpoints struct {
Endpoint `json:",inline"`
Power string `json:"power"`
Status string `json:"status"`
}
func (*PowerUnitEndpoints) GetPowerURL ¶
func (e *PowerUnitEndpoints) GetPowerURL() string
PowerUnitEndpoints methods
func (*PowerUnitEndpoints) GetStatusURL ¶
func (e *PowerUnitEndpoints) GetStatusURL() string
GetStatusURL returns the full status endpoint URL for PowerUnitEndpoints
type Program ¶
type Program struct {
ProgramName string `json:"name"`
// Description is free text the operator writes about the program -
// what charge it suits, why the steps are shaped the way they are. It
// is carried along with the program into a run and its history so the
// note is still there when the run is looked at later.
Description string `json:"description,omitempty"`
Equalize *EqualizeSettings `json:"equalize,omitempty"`
ProgramSteps []ProgramStep `json:"steps"`
DefaultsApplied bool `json:"-"`
// contains filtered or unexported fields
}
func (*Program) ApplyDefaults ¶
func (Program) Duplicate ¶
Duplicate creates a deep copy of a Program using JSON marshaling/unmarshaling
func (*Program) PrependStartupSteps ¶
func (p *Program) PrependStartupSteps()
PrependStartupSteps puts the control unit's own startup steps in front of the program's. They are synthesized rather than authored so that everything downstream - the status endpoint, the execution log, the history record and the webapp - sees ordinary steps instead of special-casing a hidden state.
Call this only after Validate has passed. The authored program is what the rules apply to; a synthesized equalize step in front of it would defeat "first step must be a heating step".
The steps carry no power settings: the FSM states that run them drive the channels from the kiln/material delta, not from a PowerController.
type ProgramListing ¶
type ProgramListing struct {
Programs []RunHistory `json:"programs"`
}
type ProgramState ¶
type ProgramState string
const ( ProgramStateCanceled ProgramState = "canceled" ProgramStateCompleted ProgramState = "completed" ProgramStateFailed ProgramState = "failed" ProgramStatePending ProgramState = "pending" ProgramStateRunning ProgramState = "running" ProgramStateUnknown ProgramState = "unknown" )
controlunit API
type ProgramStep ¶
type ProgramStep struct {
Name string `json:"name"`
StepType StepType `json:"type"`
TargetTemperature uint8 `json:"temperature_target,omitempty"`
Runtime *StepDuration `json:"runtime,omitempty"`
Heater *PowerPidSettings `json:"heater,omitempty"`
Fan *PowerPidSettings `json:"fan,omitempty"`
Steam *PowerPidSettings `json:"steam,omitempty"`
}
func (*ProgramStep) Validate ¶
func (p *ProgramStep) Validate(steamCeiling uint8) error
Validate checks a step in isolation. steamCeiling is the temperature above which steam stops being able to heat the kiln, which the step needs to know to decide whether it may hold steam at constant power.
type ProgramStorage ¶
type ProgramStorage interface {
// Program template operations
ListStoredProgramsWithInfo() ([]StoredProgramInfo, error)
LoadStoredProgram(programName string) (*Program, error)
CreateStoredProgram(programName string, program *Program) error
UpdateStoredProgram(programName string, program *Program) error
DeleteStoredProgram(programName string) error
}
ProgramStorage defines the interface for managing stored program templates
type RunHistory ¶
type RunHistory struct {
Name string `json:"name"`
State ProgramState `json:"state"`
StartedAt int64 `json:"started_at,omitempty"`
CompletedAt int64 `json:"completed_at,omitempty"`
}
type RunNote ¶
type RunNote struct {
Time int64 `json:"time"` // unix seconds
Step string `json:"step"` // step running at the time
Temperatures TemperatureStatus `json:"temperatures"` // kiln and material
Text string `json:"text"`
}
RunNote is an operator's observation, recorded while a run is in progress. Everything but Text is stamped by the control unit at the moment the note is taken, so a note read months later carries the state it was written about instead of having to be matched against the chart by hand.
Notes are only ever created during a live run, so every field is always populated; there is no "context unavailable" case to represent.
type SensorStatus ¶
type SensorStatus string
const ( SensorStatusConnected SensorStatus = "connected" SensorStatusDisconnected SensorStatus = "disconnected" SensorStatusOK SensorStatus = "ok" )
SensorStatus values
type SensorUnitConfig ¶
type SensorUnitEndpoints ¶
type SensorUnitEndpoints struct {
Endpoint `json:",inline"`
Temperatures string `json:"temperatures"`
Display string `json:"display"`
Status string `json:"status"`
}
func (*SensorUnitEndpoints) GetDisplayURL ¶
func (e *SensorUnitEndpoints) GetDisplayURL() string
func (*SensorUnitEndpoints) GetStatusURL ¶
func (e *SensorUnitEndpoints) GetStatusURL() string
GetStatusURL returns the full status endpoint URL for SensorUnitEndpoints
func (*SensorUnitEndpoints) GetTemperaturesURL ¶
func (e *SensorUnitEndpoints) GetTemperaturesURL() string
SensorUnitEndpoints methods
type ServiceStatus ¶
type ServiceStatus string
const ( ServiceStatusHealthy ServiceStatus = "healthy" ServiceStatusDegraded ServiceStatus = "degraded" )
ServiceStatus values for standardized health checks
type ServiceStatusResponse ¶
type ServiceStatusResponse struct {
Status ServiceStatus `json:"status"`
// Version is the reporting service's build version. Services are
// deployed by hand and not always together, so a mismatch between
// two services here is a real signal, not noise.
Version string `json:"version"`
Service string `json:"service"`
Details map[string]interface{} `json:"details,omitempty"`
}
ServiceStatusResponse defines the standard status response for all services
type ShellyStatus ¶
type ShellyStatus struct {
Reachable bool `json:"reachable"`
}
ShellyStatus contains Shelly device status
type ShellySwitchGetStatusResponse ¶
type ShellySwitchGetStatusResponse struct {
ID string `json:"id"`
Source string `json:"source"`
Output bool `json:"output"`
Temperature struct {
TC float32 `json:"tC"`
TF float32 `json:"tF"`
} `json:"temperature"`
}
Shelly API responses
type ShellySwitchSetResponse ¶
type ShellySwitchSetResponse struct {
WasOn bool `json:"was_on"`
}
Shelly API responses
type StatusRequest ¶
type StatusRequest struct {
Message string `json:"message"`
}
StatusRequest defines the structure for a set status request body
type StatusResponse ¶
type StatusResponse struct {
Status SensorStatus `json:"status"`
}
StatusResponse defines the structure for a status API response (legacy sensorunit)
type StepDuration ¶
func (StepDuration) MarshalJSON ¶
func (d StepDuration) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler for StepDuration
func (*StepDuration) UnmarshalJSON ¶
func (d *StepDuration) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler for StepDuration
type StorageConfig ¶
type StorageConfig struct {
BasePath string `json:"base_path"`
}
type StoredProgramInfo ¶
type SystemInfo ¶
type SystemInfo struct {
MemoryUsedMB int64 `json:"memory_used_mb"`
MemoryTotalMB int64 `json:"memory_total_mb"`
SwapUsedMB int64 `json:"swap_used_mb"`
SwapTotalMB int64 `json:"swap_total_mb"`
DiskSpaceMB int64 `json:"disk_space_mb"`
UptimeSeconds int64 `json:"uptime_seconds"`
}
SystemInfo contains general system information
type SystemServicesStatus ¶
type SystemServicesStatus struct {
ControlUnit ServiceStatusResponse `json:"controlunit"`
PowerUnit ServiceStatusResponse `json:"powerunit"`
SensorUnit ServiceStatusResponse `json:"sensorunit"`
}
SystemServicesStatus contains health status of all services
type SystemStatusResponse ¶
type SystemStatusResponse struct {
Services SystemServicesStatus `json:"services"`
System SystemInfo `json:"system"`
}
SystemStatusResponse aggregates all system information
type TemperatureStatus ¶
type TemperatureStatus struct {
Material float32 `json:"material"`
// Kiln is what the control unit resolved from the two sensors below
// and acted on, by controlunit.kiln_sensor_strategy.
Kiln float32 `json:"kiln"`
KilnPrimary float32 `json:"kiln_primary"`
KilnSecondary float32 `json:"kiln_secondary"`
}
TemperatureStatus represents the current temperature of the material and kiln in Celsius.
type VPNOperationResponse ¶
D-Bus unit API