Ecosystem-agnostic smart home lighting & AC automation using Matter protocol
Project description
Home Lighting Programmer
Ecosystem-agnostic smart home automation using the Matter protocol. Replaces physical switches with sensor-driven control of lights (circadian-aware brightness + color temperature schedules) and air conditioners (temperature-driven hysteresis with occupancy gating).
Note: For domestic use only. Not hardened for production/commercial deployment.
How It Works
The system runs a 1Hz loop that:
- Reads real-time sensor data (motion/presence) via Server-Sent Events
- Interpolates the configured schedule to determine target brightness and color temperature
- Sends commands only when the target state changes
Three lighting modes are supported:
- Decoration - Static color and intensity
- Utility - Sensor-triggered, low-latency response
- Ambient - Time-based color temperature and brightness that follows natural daylight
Requirements
- Python 3
- A running
matter-web-controllerinstance - Matter-compatible lights and sensors
Installation
pip install light-programmer
Quick Start
# Step 1: Auto-generate config from your hardware
light-genconfig --ip 192.168.1.220 --port 8080 --out config.json
# Step 2: Edit config.json to customize schedules and sensor logic
# Step 3: Run
light-programmer --server 192.168.1.220:8080 --config config.json
Configuration
Each device entry in the config JSON has:
{
"id": "dev_kitchen_sink", // Matter node ID
"note": "Sink area light", // Human-readable description
"schedule": [ // Time-based control points
{ "time": "06:30", "level": 50, "kelvin": 4000 },
{ "time": "12:00", "level": 100, "kelvin": 4000 },
{ "time": "21:30", "level": 100, "kelvin": 2700 }
],
"sensor": [ // Simple sensor trigger
{ "id": "kitchen_motion", "timeout": 5 }
]
}
level: Brightness 0-100%kelvin: Color temperature 2700-6500K (omit for non-color lights)timeout: Seconds to keep light on after sensor clears- Values between schedule points are linearly interpolated
See sample.json for a full working example.
Advanced Sensor Logic
For complex scenarios, use sensor_condition instead of sensor. It supports a tree of boolean operators:
| Node Type | Description |
|---|---|
sensor |
true if occupied or within timeout |
time_window |
true if current time is between start and end (cross-midnight supported) |
AND |
All operands must be true |
OR |
At least one operand must be true |
NOT |
Inverts its operand |
Example: Light on only when at desk AND not in bed
{
"sensor_condition": {
"operator": "AND",
"operands": [
{ "type": "sensor", "id": "desk_presence", "timeout": 15 },
{
"operator": "NOT",
"operands": [
{ "type": "sensor", "id": "bed_presence", "timeout": 5 }
]
}
]
}
}
Example: Follow schedule during day, sensor-only at night
{
"sensor_condition": {
"operator": "OR",
"operands": [
{ "type": "time_window", "start": "06:00", "end": "22:00" },
{ "type": "sensor", "id": "room_motion", "timeout": 5 }
]
}
}
During 06:00-22:00 the light follows its schedule regardless of sensors. Outside that window, it only turns on when the sensor detects motion.
Air Conditioner Control
AC entries are temperature-driven (no time schedule). They use a Matter thermostat (/api/ac) plus a separate climate sensor (/api/climate) for the ambient reading.
{
"id": "dev_ac_livingroom",
"type": "ac", // marks this entry as an AC
"climate_sensor": "dev_temp_livingroom",
"mode": "cool", // cool / heat / dry / fan / auto
"setpoint": 26.0, // °C sent to the thermostat
"on_above": 29.0, // turn on when ambient ≥ 29 °C
"off_below": 26.5, // turn off when ambient ≤ 26.5 °C
"on_delay_minutes": 5, // require continuous occupancy ≥ 5 min before turning on
"active_window": {"start": "10:00", "end": "23:30"},
"sensor": [
{"id": "dev_occ_livingroom", "timeout": 15}
]
}
Bring-up rule (off → on): ambient ≥ on_above AND occupancy continuously satisfied for on_delay_minutes AND time within active_window.
Bring-down rule (on → off): ambient ≤ off_below, OR occupancy fails (after each sensor's timeout hold), OR outside active_window.
In the dead band between off_below and on_above the AC holds its previous state — this is the hysteresis. For mode: "heat", swap to on_below / off_above (turn on when cold enough, off when warm enough).
The same sensor / sensor_condition AST used by lights applies — combine multiple occupancy sensors with AND/OR/NOT as needed. If the climate sensor is unreachable, the AC holds its last decision rather than flapping.
MCP Server (AI Agent Configuration)
An MCP server is bundled so AI agents (Claude Desktop, Claude Code, etc.) can discover devices and edit the config for you.
pip install light-programmer[mcp]
light-programmer-mcp # stdio transport
Example Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"light-programmer": {
"command": "light-programmer-mcp",
"env": { "MATTER_SRV_KEY": "your-api-key" }
}
}
}
Tools exposed:
| Tool | Purpose |
|---|---|
list_devices |
Discover lights / sensors / climate / AC from /api/metadata |
read_climate, read_ac_state, read_status |
Live readings |
read_config, write_config, validate_config |
Whole-file CRUD with schema validation |
upsert_entry, remove_entry |
Per-entry edits keyed by device id |
set_light, set_ac |
Direct device control for quick tests |
config_schema (prompt) |
Schema reference an agent can pull when authoring entries |
Validation rejects writes with bad time formats, out-of-range levels/Kelvin, missing thresholds, or hysteresis bands where off_below ≥ on_above (cool) / off_above ≤ on_below (heat).
Project Structure
| File | Purpose |
|---|---|
light_programmer/programmer.py |
Main automation controller — runs the 1Hz loop for lights and ACs |
light_programmer/matter_lib.py |
Device abstractions: LightDevice, SensorDevice, ClimateSensorDevice, ACDevice |
light_programmer/genconfig.py |
Generates config JSON from hardware discovery |
light_programmer/mcp_server.py |
MCP server exposing discovery + config CRUD tools to AI agents |
sample.json |
Example configuration with 11 devices |
pyproject.toml |
Package configuration and CLI entry points |
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file light_programmer-0.2.0.tar.gz.
File metadata
- Download URL: light_programmer-0.2.0.tar.gz
- Upload date:
- Size: 18.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dbc53d9ded7746d0747df8d5f4ef198faee80a13c1bea012a3f08db69c427712
|
|
| MD5 |
513763791c22afe6032dc54cd1bddbfa
|
|
| BLAKE2b-256 |
e45afde68f829736f262a13815dfa3409bff4a8b3156f3b8117cf521d1bdd118
|
Provenance
The following attestation bundles were made for light_programmer-0.2.0.tar.gz:
Publisher:
python-publish.yml on dongnh/light_programmer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
light_programmer-0.2.0.tar.gz -
Subject digest:
dbc53d9ded7746d0747df8d5f4ef198faee80a13c1bea012a3f08db69c427712 - Sigstore transparency entry: 1436984826
- Sigstore integration time:
-
Permalink:
dongnh/light_programmer@ef76ce10007b488e450c14df7589a7b5244232e3 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/dongnh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@ef76ce10007b488e450c14df7589a7b5244232e3 -
Trigger Event:
release
-
Statement type:
File details
Details for the file light_programmer-0.2.0-py3-none-any.whl.
File metadata
- Download URL: light_programmer-0.2.0-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
187a8446c0e0af3371d666bdaade6d2db7e174056a979e81b0efdaf170c5551d
|
|
| MD5 |
f3e51b08332cb81db3b4bd635375ef4c
|
|
| BLAKE2b-256 |
fd35fe1ccee92c0d93daf02f19c7fee0cff7f25720ee2cc321c477b43d41fad7
|
Provenance
The following attestation bundles were made for light_programmer-0.2.0-py3-none-any.whl:
Publisher:
python-publish.yml on dongnh/light_programmer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
light_programmer-0.2.0-py3-none-any.whl -
Subject digest:
187a8446c0e0af3371d666bdaade6d2db7e174056a979e81b0efdaf170c5551d - Sigstore transparency entry: 1436984834
- Sigstore integration time:
-
Permalink:
dongnh/light_programmer@ef76ce10007b488e450c14df7589a7b5244232e3 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/dongnh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@ef76ce10007b488e450c14df7589a7b5244232e3 -
Trigger Event:
release
-
Statement type: