types

package module
v0.0.0-...-455825a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: GPL-3.0 Imports: 12 Imported by: 12

Documentation

Index

Constants

View Source
const (
	ServiceNameControlUnit = "controlunit"
	ServiceNamePowerUnit   = "powerunit"
	ServiceNameSensorUnit  = "sensorunit"
)

Service name constants

View Source
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"
)
View Source
const (
	// signals invalid temperature reading
	InvalidTemperatureReading = -273.15 // Absolute zero in Celsius, used to indicate an invalid reading
)
View Source
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

View Source
var (
	ErrProgramExists       = errors.New("program exists")
	ErrProgramDoesNotExist = errors.New("program does not exist")
)
View Source
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

func FindConfigFile(filename string, envVar string) string

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

func ValidateStorageName(name string) error

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

type Endpoint struct {
	URL    string `json:"url"`
	Status string `json:"status"`
}

func (*Endpoint) GetPort

func (e *Endpoint) GetPort(urlStr ...string) (string, error)

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

func (e *Endpoint) GetStatusURL() string

GetStatusURL returns the full status endpoint URL

func (*Endpoint) GetURL

func (e *Endpoint) GetURL() string

GetURL returns the base URL for this endpoint

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 PowerStatusResponse

type PowerStatusResponse map[string]PowerResponse

Power controller API

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 PowersCommand

type PowersCommand map[string]PowerCommand

Power controller API

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 (p *Program) ApplyDefaults(defaults *Defaults)

func (Program) Duplicate

func (p Program) Duplicate() (Program, error)

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.

func (*Program) Validate

func (p *Program) Validate() error

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 SensorUnitConfig struct {
	SerialDevice string `json:"serial_device"`
	BaudRate     int    `json:"baud_rate"`
}

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"
	ServiceStatusUnavailable ServiceStatus = "unavailable"
)

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

type StepDuration struct {
	time.Duration
}

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 StepType

type StepType string

type StorageConfig

type StorageConfig struct {
	BasePath string `json:"base_path"`
}

type StoredProgramInfo

type StoredProgramInfo struct {
	Name         string `json:"name"`
	LastModified string `json:"last_modified"`
}

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 TemperatureResponse

type TemperatureResponse map[string]float32

Temperature sensor API

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

type VPNOperationResponse struct {
	Message string `json:"message"`
	Name    string `json:"name"`
}

D-Bus unit API

Directories

Path Synopsis
log module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL