A real-time energy monitoring dashboard that tracks power consumption using Eagle-200 smart meter data. Built with modern web technologies, comprehensive testing, enhanced security, and deployed on Fly.io.
Production URLs:
- Main Site: https://linknode.com
- Grafana Dashboard: https://linknode-grafana.fly.dev
- Eagle Monitor API: https://linknode-eagle-monitor.fly.dev/api/stats
- Health Check: https://linknode-eagle-monitor.fly.dev/health
-
Real-time Power Monitoring
- Eagle-200 smart meter integration (XML format)
- Live power consumption with 5-second refresh
- Time-series data storage (InfluxDB)
- Public Grafana dashboard (no login required)
- Data staleness detection with age indicators
-
Outage Alerting
- Data staleness monitor detects when the power meter stops reporting
- Slack notifications on outage and recovery (fires once per state change, no spam)
- Pushover emergency-priority siren that repeats until acknowledged
-
Failover & Resilience (the Eagle-200 meter is failing; see docs/THEORY_OF_OPERATION.md)
- Local-API bypass: a Raspberry Pi on the home network polls the meter's LAN API and ships synthetic Rainforest XML to the ingest endpoint whenever the meter's own cloud uploader stalls (hot standby, no duplicate data). See deploy/README.md
- Persistent, CRC-checkpointed reliability stats survive reboots;
--reportprints a human-readable outage and uptime report - Live uptime on the dashboard: real Data Uptime and device health figures driven by a heartbeat from the bypass, replacing a previously hardcoded 100%
-
Modern Web Interface
- Dark theme with animated gradients
- Embedded Grafana dashboard
- API status indicators
- Responsive design (mobile to 4K)
-
Cloud Native on Fly.io
- 4 microservices: web, eagle-monitor, grafana, influxdb
- Automatic SSL/TLS termination
- Global edge network deployment
- Automated CI/CD via GitHub Actions
-
Comprehensive Testing
- 3-phase E2E test suite (Playwright)
- Visual regression testing
- Performance profiling
- Accessibility testing
-
Security
- CSP headers and HSTS
- API authentication with rate limiting
- Grafana role-based access (Viewer for anonymous)
- Security scanning in CI/CD
# Clone and install
git clone https://github.com/murr2k/linknode-com.git
cd linknode-com
npm install
# Run tests
npm test # All tests
npm run baseline:compare # Regression comparison
# Deploy (automated via GitHub Actions on push to main)
git push origin mainlinknode-com/
├── fly/ # Fly.io services (production)
│ ├── web/ # Main website (nginx + index.html)
│ ├── eagle-monitor/ # Power monitoring API (Python/Flask)
│ ├── grafana/ # Metrics dashboard
│ └── influxdb/ # Time-series database
├── e2e/ # E2E test suite
│ ├── tests/ # Test specifications
│ ├── pages/ # Page object models
│ └── utils/ # Test utilities
├── scripts/ # Utility scripts
│ ├── capture-baseline.ts # Baseline capture
│ ├── compare-baseline.ts # Baseline comparison
│ └── [deployment scripts]
├── test-baselines/ # Regression baselines
│ ├── visual/ # Screenshot baselines
│ └── baseline.json # Performance/feature baseline
├── docs/ # Documentation
│ ├── THEORY_OF_OPERATION.md # System architecture
│ ├── archive/ # Historical docs
│ ├── e2e/ # Testing guides
│ └── slack/ # Slack integration
├── monitoring/ # Operations toolkit
├── .github/workflows/ # CI/CD pipelines
└── websites/ # Related website projects
# Test categories
npm test # All tests
npm run test:api # API integration
npm run test:visual # Visual regression
npm run test:perf # Performance
npm run test:a11y # Accessibility
npm run test:phase3 # Advanced tests
# Regression testing
npm run baseline:capture # Capture new baseline
npm run baseline:compare # Compare to baseline
npm run test:regression # Full regression suiteBaseline: v1.0.0-baseline (2025-07-23)
- 8 visual screenshots (desktop, tablet, mobile)
- Performance metrics: FCP 148ms
- All API endpoints validated
Push to main triggers deployment via GitHub Actions:
git push origin maincd fly/web && flyctl deploy
cd fly/eagle-monitor && flyctl deploy
cd fly/grafana && flyctl deploy
cd fly/influxdb && flyctl deployGitHub Secrets:
FLY_API_TOKEN- Fly.io deployment tokenINFLUXDB_TOKEN- InfluxDB authenticationGRAFANA_ADMIN_PASSWORD- Grafana admin access
Fly.io Secrets (per service):
INFLUXDB_TOKEN- Database accessEAGLE_PASSWORD- Eagle-200 authenticationGF_SECURITY_ADMIN_PASSWORD- Grafana admin
| Workflow | Trigger | Purpose |
|---|---|---|
deploy-fly.yml |
Push to main | Deploy all services |
e2e-tests.yml |
PRs, pushes | Multi-browser E2E tests |
e2e-tests-phase3.yml |
Manual | Advanced testing |
regression-tests.yml |
PRs | Baseline comparison |
security-scan.yml |
PRs, pushes | Security validation |
| Document | Description |
|---|---|
| docs/THEORY_OF_OPERATION.md | System architecture and data flow |
| deploy/README.md | Eagle-200 failover bypass: install and operate on the Pi |
| docs/HEALTH_CHECKS.md | Service health endpoints |
| docs/REGRESSION_TESTING.md | Baseline management guide |
| docs/e2e/ | E2E testing guides |
| fly/grafana/README.md | Grafana setup and security |
| CHANGELOG.md | Version history |
flowchart LR
subgraph Home Network
Eagle[Eagle-200<br/>Smart Meter]
Pi[Raspberry Pi<br/>failover bypass]
end
subgraph Fly.io
Monitor[Eagle Monitor<br/>Python/Flask]
DB[(InfluxDB)]
Grafana[Grafana]
Web[Web<br/>nginx]
end
subgraph Users
Browser[Browser]
end
Eagle -->|XML/HTTP| Monitor
Eagle -.->|LAN API| Pi
Pi -.->|synthetic XML<br/>during outages| Monitor
Monitor -->|Write| DB
DB -->|Query| Grafana
Browser -->|HTTPS| Web
Web -->|Embed| Grafana
The dashed path is the failover bypass: when the Eagle's own cloud uploader stalls, the Pi reads the meter over the LAN and fills the gap. See deploy/README.md.
See docs/THEORY_OF_OPERATION.md for detailed architecture diagrams.
- Real Data Uptime and device health figures from a bypass heartbeat, replacing a hardcoded 100%
- Outage log with reliability analytics for the bypass (
--report: uptime %, MTBF, duration histogram, hour-of-day pattern)
- A Raspberry Pi polls the meter's LAN API and ships synthetic XML to fill gaps when the meter's cloud uploader stalls
- Added because the Eagle-200 hardware is failing (storage wear causing repeated data outages)
- Persistent, CRC-checkpointed statistics that survive reboots
- Fixed critical Grafana vulnerability (anonymous Admin → Viewer)
- Rotated InfluxDB credentials
- Removed hardcoded secrets from scripts
- Major project cleanup (~43M removed)
- Reorganized root directory (84 → 12 files)
- Dashboard shows dashes when data is stale (>2 min)
- Age indicator: "Live", "Updated Xs ago", "No data for Xm"
See CHANGELOG.md for full history.
MIT License - see LICENSE
Murray Kopit - @murr2k
Built with Claude | Deployed on Fly.io | Tested with Playwright