Documentation ¶
Overview ¶
Package lights provides automated control of lighting. Information is read from sensors, occupancy and/or brightness, that then informs decisions about whether lights should be on and at what brightness.
This package provides these automatic lighting functions:
- Lights in occupied spaces are on, lights in unoccupied spaces are off
- Lights that are on are dimmer if it's bright outside
Implementation approach ¶
The core automation logic can be found in the processState function in logic.go. This code receives the combined knowledge of the system and works out what needs to be done to satisfy the automatic outcomes we desire. Here you'll find the code that says "if any sensor reports a state of OCCUPIED, then the lights should be on".
Collecting and updating the shared knowledge is a process we call patching, inspired by version control systems. ReadState holds all the information we can know and it is updated by a sequence of Patcher.Patch calls. These Patcher instances come from the different sensors and are applied to the shared state one at a time in state.go#readStateChanges.
Invoking the logic when the state changes happens in processStateChanges which takes care to avoid unnecessary work by skipping analysing state that has already been superseded by more state changes.
Implementation finer details ¶
To avoid duplicating commands and to provide some sense of determinism the logic function is allowed to complete before being called again, even if more state changes are available. The context passed to the func is only cancelled if the automation as a whole is stopped. This means that we shouldn't get into a state where the logic has turns some lights on but not others because a new version of the state is available. Actions successfully performed by the logic are recorded in WriteState which is consulted before new actions are performed. If the logic wants to turn the lights on, then it can check WriteState to see if the light has already been asked to turn on, the action can then be skipped as needed.
One of the clauses for the logic is to wait a configured time after a space becomes unoccupied before turning the lights off. To accomplish this the logic could use time.After but that would cause the logic to block for a long time, during which the automation is not reacting to new information. Instead the logic returns a TTL value that indicates to the calling func that the analysis of the state and the subsequent actions are only correct until TTL expires, at which point the logic should be rerun.
Index ¶
Constants ¶
const ( ModeAuto = "auto" ModeDefault = "default" ModeValueKey = "lighting.mode" )
const AutoType = "lights"
Variables ¶
var Factory = auto.FactoryFunc(func(services auto.Services) service.Lifecycle { logger := services.Logger.Named("lights") impl := PirsTurnLightsOn(services.Node, logger) return autoToService(impl, logger) })
Functions ¶
This section is empty.
Types ¶
type BrightnessAutomation ¶
type BrightnessAutomation struct {
// contains filtered or unexported fields
}
BrightnessAutomation implements turning lights on or off based on occupancy readings from PIRs and other devices.
func PirsTurnLightsOn ¶
func PirsTurnLightsOn(clients node.Clienter, logger *zap.Logger) *BrightnessAutomation
PirsTurnLightsOn creates an automation that controls light brightness based on PIR occupancy status.
func (*BrightnessAutomation) Configure ¶
func (b *BrightnessAutomation) Configure(configData []byte) error
Configure updates the configured devices and settings this automation uses.
func (*BrightnessAutomation) Start ¶
func (b *BrightnessAutomation) Start(_ context.Context) error
Start implements Starter and initialises this automation. Start may be called before or after Configure.
func (*BrightnessAutomation) Stop ¶
func (b *BrightnessAutomation) Stop() error
Stop stops the automation from running. You must call Start to have automated action occur again.
type BrightnessSensorPatches ¶
type BrightnessSensorPatches struct {
// contains filtered or unexported fields
}
BrightnessSensorPatches contributes patches for changing the state based on brightness sensor readings.
func (*BrightnessSensorPatches) Poll ¶
func (o *BrightnessSensorPatches) Poll(ctx context.Context, changes chan<- Patcher) error
type BrightnessWriteState ¶
type BrightnessWriteState struct { WriteTime time.Time Brightness *traits.Brightness }
type ButtonPatches ¶
type ButtonPatches struct {
// contains filtered or unexported fields
}
func (*ButtonPatches) Poll ¶
func (p *ButtonPatches) Poll(ctx context.Context, changes chan<- Patcher) error
type ButtonType ¶
type ButtonType int
const ( UndefinedButton ButtonType = iota OnButton OffButton ToggleButton )
func (ButtonType) String ¶
func (s ButtonType) String() string
type ModePatches ¶
type ModePatches struct {
// contains filtered or unexported fields
}
ModePatches contributes patches for changing the state based on mode changes.
func (*ModePatches) Poll ¶
func (o *ModePatches) Poll(ctx context.Context, changes chan<- Patcher) error
type OccupancySensorPatches ¶
type OccupancySensorPatches struct {
// contains filtered or unexported fields
}
OccupancySensorPatches contributes patches for changing the state based on occupancy sensor readings.
func (*OccupancySensorPatches) Poll ¶
func (o *OccupancySensorPatches) Poll(ctx context.Context, changes chan<- Patcher) error
type Patcher ¶
type Patcher interface {
Patch(s *ReadState)
}
Patcher represents a single patch that adjusts ReadState.
type ReadState ¶
type ReadState struct { Config config.Root AutoStartTime time.Time // time that the automation started up Occupancy map[string]*traits.Occupancy // used for daylight dimming AmbientBrightness map[string]*traits.AmbientBrightness Buttons map[string]*gen.ButtonState // used for selecting the run modes, aka "modes" config property Modes *traits.ModeValues }
ReadState models everything we have read from the system. For example if we PullBrightness, then the responses will be recoded here.
func NewReadState ¶
type WriteState ¶
type WriteState struct { Brightness map[string]BrightnessWriteState LastButtonAction time.Time // used for button press deduplication, the last time we did anything due to a button press LastButtonOnTime time.Time // used for occupancy related darkness, the last time lights were turned on due to button press ActiveMode string }
WriteState models the state of the system based on the changes we've made to it. For example if we UpdateBrightness, then the response to that call is recorded in this state.
func NewWriteState ¶
func NewWriteState(startTime time.Time) *WriteState
func (*WriteState) MergeFrom ¶
func (s *WriteState) MergeFrom(other *WriteState)