Config
Icarus uses a single instance of a NodeConfig
(found in icarus/app/internal/config.py
) object to control all of its functionality.
Instantiation
Heimdall stores every node's NodeConfig
as a record in its database, this record is the single source of truth of the node's config.
On startup, Icarus will request its NodeConfig
from Heimdall by sending a HTTP request.
If the request is successful, Icarus will create a NodeConfig
instance by deserializing the database record (returned in the request response).
A successful node config request response from Heimdall looks like this:
curl -i -X GET http://heimdall/v1/nodes/omaha/config
HTTP/1.1 200 OK
Server: heimdall
Date: Thu, 24 Apr 2025 23:43:03 GMT
Content-Type: application/json
Content-Length: 189
{"config_id":20,"dev_name":"/dev/video0","video_retention_days":3,"min_event_duration":3,"threshold":150,"start_time":"06:55:00","stop_time":"10:48:17","auto_start":false,"auto_stop":false}%
config_id
, auto_start
, and auto_stop
.
If the request is not successful for whatever reason (Heimdall is down, node is not connected to internet, request timeout, etc.), then Icarus will create a NodeConfig
instance by deserializing its config backup file: icarus/app/config.bak
.
In both of these scenarios (successful response and unsuccessful response), the NodeConfig
instance is stored in the app.state.CONFIG
variable.
The config backup file is also synced with the NodeConfig
instance whenever the NodeConfig
instance is mutated (on creation or on modification). This ensures that at least the config backup file has recent values that Icarus can use in case of a later unsuccessful response.
Config Fields
Fields
The node config has few fields, when deserialization happens it expects no other fields except for the following.
dev_name
: The path of the camera device being used. This value cannot and should not be changed unless you are planning on changing the camera.video_retention_days
: The number of days to keep videos. This is the number of days to keep videos before they are deleted. The default is 1 day. The minumum is 1 day and the maximum is 3 days.min_event_duration
: The minimum duration of an event to be recorded. This is the minimum duration of an event to be recorded. The default is 3 seconds. The minumum is 1 second and the maximum is 120 seconds.start_time
: The time to start recording in UTC time. This must be formatted in a string as "HH:MM:SS".stop_time
: The time to stop recording in UTC time. This must be formatted in a string as "HH:MM:SS".
NOTE: start_time
and stop_time
cannot be the same value.
threshold
: The threshold that an event needs to reach in order to be classified as an event. The default is 175. The minumum is 0 and the maximum is 255.
Field Validators
Each of the above fields has validators. These validators are simply imput validation, making sure that the values are of the correct type and are in within certain ranges.
A NodeConfig
instance cannot be created if any of the input fails its validators.
The validators are run automatically by Pydantic when creating a NodeConfig
instance.
Modifying Fields
Adding
To add a node config field, add it in the NodeConfig
class in icarus/app/internal/config.py
.
Be sure to add a type hint and a default value.
If the value needs input validation (value range, formatting, correct type, etc), use a Pydantic validator.
Example of adding a new node config field foo
of type int
with a default value of 88
.
In this example lets also bound foo
to only be accepted if it is even.
In icarus/app/internal/config.py
:
class NodeConfig(BaseModel):
"""
Config class for the node.
Its values are used to configure the node and its behavior.
"""
class Config:
extra = "forbid"
# Default values
dev_name: str = "/dev/video0"
video_retention_days: int = 3
min_event_duration: int = 5 # Mininum event duration in seconds
start_time: str = "19:00:00" # HH:MM:SS UTC
stop_time: str = "06:00:00" # HH:MM:SS UTC
threshold: int = 175 # Threshold for event detection, 0-255
# Add the new field with a type hint, default value, and a useful comment
foo: int = 88 # Very useful comment!
# Add a field validator for `foo`
@field_validator("foo")
def validate_foo(cls, value):
if value % 2 != 0: # Check if the value of `foo` is even
raise ValueError("Foo value must be even")
return value
NOTE: Any config field added here must also be added in Heimdall's NodeConfig
schema struct, found in heimdall/server/internal/database/database.go
.
Let us make sure we do this for our new node config field!
In heimdall/server/internal/database/database.go
:
type NodeConfig struct {
ConfigID uint `gorm:"primaryKey" json:"config_id"`
DevName string `form:"devName" json:"dev_name"`
VideoRetentionDays int `attrs:"class=\"w-full\"" control:"dropdown" form:"videoRetentionDays" json:"video_retention_days" options:"1,2,3" validate:"min=1,max=3"`
MinEventDuration int `attrs:"min=1 max=120" control:"number" form:"minEventDuration" json:"min_event_duration" validate:"gt=0"`
Threshold int `attrs:"min=0 max=255" control:"number" form:"threshold" json:"threshold" validate:"min=0,max=255"`
StartTime string `attrs:"min=00:00 max=23:59" control:"time" form:"startTime" json:"start_time" validate:"time-format"`
StopTime string `attrs:"min=00:00 max=23:59" control:"time" form:"stopTime" json:"stop_time" validate:"time-format,nefield=StartTime"`
AutoStart bool `json:"auto_start"`
AutoStop bool `json:"auto_stop"`
/*
Add new field: struct field name MUST be capitalized, add the type,
Field tags:
Add attrs if needed (used by the frontend to know which values to display), Add validator tags if you want to validate this data
All new node config fields must have a json tag that exactly matches the field name in the `NodeConfig` python class
This is necessary for marshaling and unmarshaling
*/
Foo int `json:"foo"`
}
Check out the documentation of the validator Heimdall uses for more information.
Deleting
To delete a node config field, delete it from the NodeConfig
class in icarus/app/internal/config.py
.
If the field has any field validator(s), remove them.
Be sure to make sure that Icarus is not actually using your deleted field anywhere, this can be done with a simple grep search.
In icarus/
:
grep -r -E '[^[:space:]]+\.deleted_field' app/ test/ scripts/
and/or:
grep -r "deleted_field" app/ test/ scripts/
Make sure to delete the field from Heimdall's NodeConfig
schema struct, found in heimdall/server/internal/database/database.go
To be safe, make sure that Heimdall is not using the deleted field with a simple grep search.
In heimdall/
:
grep -r -E '[^[:space:]]+\.DeletedField' server/
and/or:
grep -r "DeletedField" server/
Tweaking
Tweaking a node config field could be anything from changing a default value to changing its field validator(s).
No matter how you tweak it, it is imperative that the changes are reflected in Heimdall.