Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/completion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Completion gates
on: [push, pull_request]
permissions:
contents: read
jobs:
host-board:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: |
pip install PyYAML==6.0.2
python -c "import yaml; yaml.safe_load(open('config/home-assistant.yaml'))"
python tools/validate.py
g++ -std=c++17 tests/usage_test.cpp -o /tmp/alerts
/tmp/alerts
python -m unittest discover -s tests
python tools/validate_completion.py
pip install platformio==6.1.18
pio run -e nodemcuv2
62 changes: 62 additions & 0 deletions .github/workflows/decode-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Decode project image and validate
on:
push:
branches: ['automation/**']
paths:
- 'docs/images/project-overview.png.b64.*'
- 'docs/images/project-overview.manifest.json'
- 'tools/decode_project_image.py'
- '.github/workflows/decode-image.yml'
permissions:
contents: write
concurrency:
group: decode-image-${{ github.ref }}
cancel-in-progress: false
jobs:
recover:
if: startsWith(github.ref, 'refs/heads/automation/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
fetch-depth: 1
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Guard on pending transport
id: pending
run: |
if compgen -G 'docs/images/project-overview.png.b64.*' > /dev/null; then
echo 'present=true' >> "$GITHUB_OUTPUT"
else
echo 'present=false' >> "$GITHUB_OUTPUT"
fi
- name: Validate and losslessly decode PNG
if: steps.pending.outputs.present == 'true'
run: python tools/decode_project_image.py
- name: Commit decoded image to same branch
if: steps.pending.outputs.present == 'true'
env:
BRANCH_REF: ${{ github.ref }}
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add -- docs/images/project-overview.png docs/images/project-overview.png.b64.*
git diff --cached --quiet && exit 0
git commit -m 'Decode validated generated project image'
git push origin "HEAD:$BRANCH_REF"
# GITHUB_TOKEN pushes may not trigger push workflows. Run gates explicitly
# on the resulting image commit here; the cloud agent also verifies that SHA.
- name: Run full completion gates on decoded commit
if: steps.pending.outputs.present == 'true'
run: |
pip install PyYAML==6.0.2
python -c "import yaml; yaml.safe_load(open('config/home-assistant.yaml'))"
python tools/validate.py
g++ -std=c++17 tests/usage_test.cpp -o /tmp/alerts
/tmp/alerts
python -m unittest discover -s tests
python tools/validate_completion.py
pip install platformio==6.1.18
pio run -e nodemcuv2
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ __pycache__/
.idea/
.pio/
build/
firmware/config.private.h
92 changes: 63 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,77 @@
# Smart Entryway Usage Analytics
An ESP8266 NodeMCU displays BME280 temperature/humidity, drives an RGB indicator, and measures **actual enabled-indicator time** in hourly and daily buckets. Matter control is provided through an explicit Home Assistant/Matterbridge bridge, not a native ESP8266 Matter implementation.

Build a smart home prototype that uses RGB LED, BME280, OLED display to summarize daily and weekly usage. Include setup instructions, a circuit diagram, tested firmware, and sample output.
![NodeMCU with BME280 OLED and RGB indicator beside a conceptual local home bridge](docs/images/project-overview.png)

## Project details
## Overview, objectives and features
Learn interval attribution, ring-buffer summaries and time rollover. USB/MQTT ON/OFF requests indicator enable. Valid readings drive red at humidity≥70%, blue below15°C, otherwise green. Invalid sensor data blanks RGB and is not counted. OLED shows readings, request/actual state, current-day minutes and seven-day-slot minutes.

| Field | Value |
| --- | --- |
| Roadmap ID | 17 |
| Category | Smart Home |
| Platform | ESP8266 |
| Difficulty | Intermediate |
| Estimated build time | 32 hours |
| Connectivity | Matter |
| Core components | RGB LED, BME280, OLED display |
| Control mode | data logger |
## Architecture and platform
NodeMCU nodemcuv2 Arduino, real BME280/SSD1306 drivers and PubSubClient. [Usage counter](firmware/usage.h) maintains24hour and7day buckets. [Architecture](docs/architecture.md). MQTT→HA entity→Matterbridge-hass→Matter controller is the interoperability path. Counters and physical display work locally over USB without the bridge.

## Repository layout
## BOM quantities
| Qty | Item |
|---:|---|
| 1 | ESP8266 NodeMCU and USB data/power cable |
| 1 | BME280 I2C breakout0x76 supporting3.3V |
| 1 | SSD1306 I2C128×64 OLED0x3C supporting3.3V |
| 1 | Common-cathode RGB LED |
| 3 | 330Ω resistors |
| 1 each | Breadboard and jumper set |
| 1 each for Matter | MQTT broker, HA instance, Matterbridge host and Matter controller |

- `firmware/smart-entryway-usage-analytics/smart-entryway-usage-analytics.ino`: runnable firmware or application
- `docs/wiring.md`: suggested low-voltage wiring plan
- `docs/architecture.md`: system data flow
- `docs/test-plan.md`: repeatable verification steps
- `sample-data/example.json`: example telemetry record
- `tools/validate.py`: dependency-free repository validation
## Prerequisites and exact pin map
Python3.12, PlatformIO6.1.18 and USB serial driver. All sensor pullups and GPIO must be3.3V.
| NodeMCU | Connection |
|---|---|
| D2/GPIO4 | Both SDA |
| D1/GPIO5 | Both SCL |
| D6/GPIO12 | 330Ω→red anode |
| D7/GPIO13 | 330Ω→green anode |
| D5/GPIO14 | 330Ω→blue anode |
| 3V3 | BME/OLED VCC and BME CSB |
| GND | BME/OLED GND, BME SDO, RGB common cathode |

## Quick start
## Circuit/wiring and assembly
Disconnect USB; follow [editable circuit](docs/circuit-diagram.svg) and [wiring](docs/wiring.md). Connect sharedI2C, inspect addresses and LED pinout. Each color needs a separate resistor. Avoid5V I2C pullups. Verify BME model (BMP280 lacks humidity) and set SDO low for0x76 before power.

1. Open `firmware/smart-entryway-usage-analytics/smart-entryway-usage-analytics.ino` in Arduino IDE or Arduino CLI.
2. Select the board matching **ESP8266**.
3. Compile and upload, then open the serial monitor at 115200 baud.
## Setup and flashing
```sh
python -m pip install platformio==6.1.18
pio run -e nodemcuv2
pio run -e nodemcuv2 -t upload
pio device monitor -b 115200
```
[Source](firmware/smart-entryway-usage-analytics/smart-entryway-usage-analytics.ino). Default Wi-Fi/MQTT strings are blank; USB ON/OFF works independently. For network operation create ignored firmware/config.private.h with WIFI_SSID,WIFI_PASSWORD,MQTT_HOST,MQTT_USER,MQTT_PASSWORD defines, then rebuild privately. Never commit credentials.

## Expected behavior
## Configuration and Matter setup
Use port1883 only on an isolated lab network; secure transport is future work. Configure the HA MQTT integration with your broker. Merge [HA YAML](config/home-assistant.yaml) into existing configuration and restart/reload the integration. Confirm switch.entryway17_indicator exists (adjust entity ID to actual HA discovery naming). Install the official Matterbridge HA Application or standalone Matterbridge and the [matterbridge-hass plugin](https://github.com/Luligu/matterbridge-hass). Configure its HA host and private HA access token **outside Git**, create a filter label, apply it to the Entryway17 switch and temperature/humidity sensors, restart and verify the exported devices, then pair Matterbridge’s QR code with the controller. The plugin explicitly supports HA switches and environmental sensors. This bridge setup and pairing are documented but have not been executed here.

Usage Analytics demonstration with repeatable test steps. The default implementation supports simulated or generic analog inputs so the control path can be exercised before hardware-specific drivers are added.
## Usage, telemetry/data formats and expected output
Send newline-terminated ON/OFF at115200, publish exact non-retained ON/OFF to entry17/command, or toggle the bridged switch. entry17/switch/state reflects the **request**, while JSON active reflects physical indicator eligibility. Actual LED hardware behavior is untested. Retained entry17/state and USB JSON contain id17, uptime_s, valid, temperature_c, humidity_pct, requested, active, hour_ms, day_ms, slots24_ms, slots7day_ms. [Sample](sample-data/telemetry.jsonl). Invalid values use0 placeholders with valid=false; do not interpret them as measurements. LWT/birth entry17/status is offline/online; commands are best-effortQoS0.
After one valid enabled minute, day_ms increases approximately60000; after OFF it stops. The previous sampled state is attributed to each elapsed interval (1s sampling uncertainty). OLED day/week values are integer minutes.

## Hardware adaptation
## Time model and limitations
“Day” is an86400000ms interval since first sampling after reboot, not a calendar day. The24hour summary contains current and23previous hourly slots; the7day summary contains current and6previous day slots. Boundary eviction is exact but summaries are coarse buckets, not exact sliding24h/168h windows. Counts are RAM-only and reset on reboot. millis wrap is extended through periodic samples; a sampling gap≥49.7days is unsupported. This measures indicator enabled time, not occupancy, lamp energy, household usage or calibrated comfort. OLED failure does not diagnose sensor health.

The included code is a safe reference implementation. Update pin assignments and sensor conversions from the exact component datasheets, then repeat the test plan before connecting actuators.
## Actual run tests
[Run results](docs/validation-results.md) record meaningful C++ counter tests,3image tests, artifact/credential checks and actual NodeMCU compilation. [Test plan](docs/test-plan.md). Hardware calibration, MQTT/HA networking and Matter pairing have not been tested.
```sh
g++ -std=c++17 tests/usage_test.cpp -o /tmp/usage
/tmp/usage
python -m unittest discover -s tests
python tools/validate.py
python tools/validate_completion.py
```

## License
## Troubleshooting
No readings: verify BME0x76 and3.3V wiring. OLED blank: verify SSD1306 dimensions/address0x3C. Counter not increasing: requestedON and valid must both be true. Wrong colors: common cathode/anode mapping. Matter missing: confirm HA switch first, plugin filter labels, HA token/host and bridge pairing; ESP8266 itself does not advertise Matter.

MIT
## Domain safety
Low voltage indicator only; never connect mains, locks or critical equipment. Keep3.3V GPIO isolated from5V. Environmental thresholds are demonstration values, not health warnings. Restrict the unencrypted lab MQTT network and keep tokens/passwords out of commits.

## Future work
Add authenticated telemetry, calibrated sensors, qualified flash persistence and real calendar timestamps. Verify bridge interoperability on physical controllers.

## Contributing and license
Keep counter boundary/rollover coverage and pin-map consistency; distinguish mock data from measured data. Full [MIT license](LICENSE).
31 changes: 31 additions & 0 deletions config/home-assistant.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Merge with your existing MQTT configuration; configure broker through HA UI.
mqtt:
switch:
- name: Entryway17 Indicator
unique_id: entryway17_indicator
command_topic: entry17/command
state_topic: entry17/switch/state
payload_on: "ON"
payload_off: "OFF"
availability:
- topic: entry17/status
sensor:
- name: Entryway17 Temperature
unique_id: entryway17_temperature
state_topic: entry17/state
value_template: "{{ value_json.temperature_c if value_json.valid else none }}"
device_class: temperature
unit_of_measurement: "°C"
state_class: measurement
- name: Entryway17 Humidity
unique_id: entryway17_humidity
state_topic: entry17/state
value_template: "{{ value_json.humidity_pct if value_json.valid else none }}"
device_class: humidity
unit_of_measurement: "%"
state_class: measurement
- name: Entryway17 Day Enabled Minutes
unique_id: entryway17_day_minutes
state_topic: entry17/state
value_template: "{{ (value_json.day_ms / 60000) | round(2) }}"
unit_of_measurement: min
8 changes: 1 addition & 7 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
# Architecture

```text
Sensors -> validation and filtering -> data logger -> output/alert
|
+-> Matter telemetry and logs
```

The implementation separates acquisition, decision logic, output handling, and telemetry. Hardware-specific access is kept at the edge so the core behavior can be tested with simulated readings.
Physical BME280 readings select RGB color and OLED values. Requested enable comes from USB ON/OFF or MQTT; actual enabled time only counts when sensor readings are valid. Shared Usage allocates elapsed intervals into24hour/7day ring slots with rollover tests. MQTT→Home Assistant switch→Matterbridge-hass is the explicit Matter path. ESP8266 has no native Matter stack; bridge/radio behavior is untested.
1 change: 1 addition & 0 deletions docs/circuit-diagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions docs/images/project-overview.manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"bytes": 1715468,
"dimensions": [
1254,
1254
],
"sha256": "d7a89620a1c2580bb26f51f897e0f9ce9f9ac2144f998e4e71e052adf864c5e2",
"chunks": [
"project-overview.png.b64.000",
"project-overview.png.b64.001",
"project-overview.png.b64.002",
"project-overview.png.b64.003",
"project-overview.png.b64.004",
"project-overview.png.b64.005",
"project-overview.png.b64.006",
"project-overview.png.b64.007",
"project-overview.png.b64.008",
"project-overview.png.b64.009",
"project-overview.png.b64.010",
"project-overview.png.b64.011",
"project-overview.png.b64.012",
"project-overview.png.b64.013",
"project-overview.png.b64.014",
"project-overview.png.b64.015",
"project-overview.png.b64.016",
"project-overview.png.b64.017",
"project-overview.png.b64.018",
"project-overview.png.b64.019",
"project-overview.png.b64.020",
"project-overview.png.b64.021",
"project-overview.png.b64.022",
"project-overview.png.b64.023",
"project-overview.png.b64.024",
"project-overview.png.b64.025",
"project-overview.png.b64.026",
"project-overview.png.b64.027",
"project-overview.png.b64.028",
"project-overview.png.b64.029",
"project-overview.png.b64.030",
"project-overview.png.b64.031",
"project-overview.png.b64.032",
"project-overview.png.b64.033",
"project-overview.png.b64.034",
"project-overview.png.b64.035",
"project-overview.png.b64.036",
"project-overview.png.b64.037",
"project-overview.png.b64.038",
"project-overview.png.b64.039",
"project-overview.png.b64.040",
"project-overview.png.b64.041",
"project-overview.png.b64.042",
"project-overview.png.b64.043",
"project-overview.png.b64.044",
"project-overview.png.b64.045",
"project-overview.png.b64.046",
"project-overview.png.b64.047",
"project-overview.png.b64.048",
"project-overview.png.b64.049",
"project-overview.png.b64.050",
"project-overview.png.b64.051",
"project-overview.png.b64.052",
"project-overview.png.b64.053",
"project-overview.png.b64.054",
"project-overview.png.b64.055",
"project-overview.png.b64.056",
"project-overview.png.b64.057",
"project-overview.png.b64.058",
"project-overview.png.b64.059",
"project-overview.png.b64.060",
"project-overview.png.b64.061",
"project-overview.png.b64.062",
"project-overview.png.b64.063",
"project-overview.png.b64.064",
"project-overview.png.b64.065",
"project-overview.png.b64.066",
"project-overview.png.b64.067",
"project-overview.png.b64.068",
"project-overview.png.b64.069",
"project-overview.png.b64.070",
"project-overview.png.b64.071"
]
}
Binary file added docs/images/project-overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 1 addition & 20 deletions docs/test-plan.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,3 @@
# Test plan

## Static checks

1. Run `python tools/validate.py`.
2. Confirm the firmware or application starts without missing configuration.
3. Compare the assembled wiring with `docs/wiring.md` and component datasheets.

## Functional checks

1. Start with simulated or disconnected actuators.
2. Feed low, nominal, and high readings into the controller.
3. Confirm the **data logger** behavior matches the serial or console output.
4. Disconnect one sensor and confirm the system enters a safe state.
5. Restore the sensor and verify recovery requires an intentional acknowledgement for latched safety modes.

## Acceptance criteria

- Telemetry includes a timestamp, state, input readings, and output state.
- Invalid readings do not command an actuator on.
- The output changes only after the configured threshold and debounce checks pass.
- The steps in the README reproduce the demonstration.
Host tests cover interval attribution, exact hour/day boundaries,24/7slot eviction, toggles and millis wrap. CI actually compiles NodeMCU firmware and checks image/artifacts. Hardware: confirmI2C0x76/0x3C, enable for60s viaUSBON thenOFF, compareday_ms≈60000; disconnectsensor andconfirmLEDoff/validfalse. Restore, checkcountsresumeonlyifrequested. HA/Matter pairing tests unperformed.
7 changes: 7 additions & 0 deletions docs/validation-results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Actual cloud validation

On 2026-10-10 IST, image recovery run 37996789889 passed its full host/artifact/target gates on decoded commit 20c4b573ecb568d87e157e28148a38b5b9f5f8f1 after lossless PNG SHA256/CRC/dimension verification and transport cleanup.

Final push and PR workflows independently validate C++ usage counter tests, three image tests, Home Assistant YAML syntax, PNG/SVG/links/MIT/credentials and actual PlatformIO NodeMCU firmware build on the exact final head before merge. This commit triggers those final checks; their URLs and results are recorded in durable run state.

Physical hardware and live connectivity tests were not performed.
Loading
Loading