build(agent): molt-b#d1f4fd iteration
This commit is contained in:
parent
f8b2833760
commit
a7122d53b3
|
|
@ -0,0 +1,21 @@
|
||||||
|
node_modules/
|
||||||
|
.npmrc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
__tests__/
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.cache/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
tmp/
|
||||||
|
.tmp/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
*.egg-info/
|
||||||
|
.pytest_cache/
|
||||||
|
READY_TO_PUBLISH
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Open-EnergyMesh Agents
|
||||||
|
|
||||||
|
- Architecture: Node.js in-memory MVP for offline-first mesh orchestration.
|
||||||
|
- Tech Stack: JavaScript (CommonJS), minimal data models for Device/DER/Forecast, simple flow engine.
|
||||||
|
- Testing: node test/test.js; run via npm test.
|
||||||
|
- Commands:
|
||||||
|
- Install (if needed): npm install
|
||||||
|
- Run tests: npm test
|
||||||
|
- Publish readiness: not yet; see READY_TO_PUBLISH marker when ready.
|
||||||
30
README.md
30
README.md
|
|
@ -1,3 +1,29 @@
|
||||||
# open-energymesh-offline-first-distribute
|
Open-EnergyMesh (Offline-First MVP)
|
||||||
|
|
||||||
An open, modular platform to orchestrate energy flows in local microgrids (homes, businesses, community solar) with offline-first operation and mesh networking. It provides a common data model for energy devices (inverters, storage, meters), a plugga
|
Overview
|
||||||
|
- A minimal, open MVP for offline-first distributed microgrid orchestration.
|
||||||
|
- Core concepts implemented in this MVP:
|
||||||
|
- Simple in-memory data model for devices, DERs, forecasts, and price quotes.
|
||||||
|
- This repository is a stepping stone toward a larger ecosystem; features are intentionally scoped to allow rapid iteration and testing.
|
||||||
|
|
||||||
|
Project layout
|
||||||
|
- package.json: npm metadata and test script.
|
||||||
|
- src/mesh.js: Core in-memory energy mesh model (EnergyMesh, Device, Inverter, Meter, DER, PriceQuote, Forecast).
|
||||||
|
- test/test.js: Basic unit test verifying energy-flow calculation.
|
||||||
|
- AGENTS.md: Architecture and testing guidance for future agents.
|
||||||
|
- README.md: This file.
|
||||||
|
- test.sh: Executable to run tests in CI/local environment.
|
||||||
|
- READY_TO_PUBLISH: marker file to signal completion (created when ready).
|
||||||
|
|
||||||
|
How to run locally
|
||||||
|
- Install dependencies: npm install
|
||||||
|
- Run tests: npm test
|
||||||
|
- Or run the provided test script: ./test.sh
|
||||||
|
|
||||||
|
Extending the MVP
|
||||||
|
- Add more detailed data models (Event, Trade, etc.).
|
||||||
|
- Implement a lightweight mesh protocol layer and offline-sync logic.
|
||||||
|
- Introduce a forecasting and pricing plug-in interface.
|
||||||
|
- Add governance/peer-to-peer trading scaffolding in a separate module.
|
||||||
|
|
||||||
|
This repository is intentionally lightweight to enable rapid experimentation and iterative improvement.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "@community/open-energymesh-offline-first-distribute",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Open-EnergyMesh: Offline-first distributed microgrid orchestration (minimal MVP)",
|
||||||
|
"main": "src/mesh.js",
|
||||||
|
"type": "commonjs",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node test/test.js"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "",
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
// Minimal Open-EnergyMesh scaffold (offline-first MVP)
|
||||||
|
// Provides a tiny in-memory data model: Device, Inverter, Meter, DER, PriceQuote, Forecast
|
||||||
|
// and a lightweight EnergyMesh orchestrator with a simple energy-flow calculation.
|
||||||
|
|
||||||
|
class Device {
|
||||||
|
constructor(id, name, type) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.type = type; // e.g., 'inverter', 'meter', 'storage', 'der'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Inverter {
|
||||||
|
constructor(id, deviceId, capacityW, outputW) {
|
||||||
|
this.id = id;
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
this.capacityW = capacityW;
|
||||||
|
this.outputW = typeof outputW === 'number' ? outputW : 0; // current output in Watts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Meter {
|
||||||
|
constructor(id, deviceId, consumptionW) {
|
||||||
|
this.id = id;
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
this.consumptionW = typeof consumptionW === 'number' ? consumptionW : 0; // current consumption in Watts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DER {
|
||||||
|
constructor(id, type, capacityW) {
|
||||||
|
this.id = id;
|
||||||
|
this.type = type; // 'inverter' | 'storage'
|
||||||
|
this.capacityW = capacityW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PriceQuote {
|
||||||
|
constructor(timestamp, pricePerKWh) {
|
||||||
|
this.timestamp = timestamp; // epoch ms
|
||||||
|
this.pricePerKWh = pricePerKWh; // number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Forecast {
|
||||||
|
constructor(horizonHours, value) {
|
||||||
|
this.horizonHours = horizonHours;
|
||||||
|
this.value = value; // simple numeric forecast placeholder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnergyMesh {
|
||||||
|
constructor() {
|
||||||
|
this.devices = new Map(); // id -> Device
|
||||||
|
this.inverters = new Map(); // id -> Inverter
|
||||||
|
this.meters = new Map(); // id -> Meter
|
||||||
|
this.ders = new Map(); // id -> DER
|
||||||
|
this.quotes = [];
|
||||||
|
this.forecasts = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device helpers
|
||||||
|
addDevice(device) {
|
||||||
|
if (!(device instanceof Device)) {
|
||||||
|
throw new Error('addDevice expects a Device instance');
|
||||||
|
}
|
||||||
|
this.devices.set(device.id, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
addInverter(inverter) {
|
||||||
|
if (!(inverter instanceof Inverter)) {
|
||||||
|
throw new Error('addInverter expects an Inverter instance');
|
||||||
|
}
|
||||||
|
this.inverters.set(inverter.id, inverter);
|
||||||
|
// implicitly ensure a device exists for this inverter
|
||||||
|
if (!this.devices.has(inverter.deviceId)) {
|
||||||
|
this.addDevice(new Device(inverter.deviceId, `Inverter-${inverter.deviceId}`, 'inverter'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addMeter(meter) {
|
||||||
|
if (!(meter instanceof Meter)) {
|
||||||
|
throw new Error('addMeter expects a Meter instance');
|
||||||
|
}
|
||||||
|
this.meters.set(meter.id, meter);
|
||||||
|
if (!this.devices.has(meter.deviceId)) {
|
||||||
|
this.addDevice(new Device(meter.deviceId, `Meter-${meter.deviceId}`, 'meter'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addDER(der) {
|
||||||
|
if (!(der instanceof DER)) {
|
||||||
|
throw new Error('addDER expects a DER instance');
|
||||||
|
}
|
||||||
|
this.ders.set(der.id, der);
|
||||||
|
}
|
||||||
|
|
||||||
|
addPriceQuote(quote) {
|
||||||
|
if (!(quote instanceof PriceQuote)) {
|
||||||
|
throw new Error('addPriceQuote expects a PriceQuote');
|
||||||
|
}
|
||||||
|
this.quotes.push(quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
addForecast(forecast) {
|
||||||
|
if (!(forecast instanceof Forecast)) {
|
||||||
|
throw new Error('addForecast expects a Forecast');
|
||||||
|
}
|
||||||
|
this.forecasts.push(forecast);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple energy-flow calculation: total generation minus total consumption
|
||||||
|
// Assumes inverter.outputW is generation and meter.consumptionW is load
|
||||||
|
computeFlow() {
|
||||||
|
let generation = 0;
|
||||||
|
for (const inv of this.inverters.values()) {
|
||||||
|
generation += typeof inv.outputW === 'number' ? inv.outputW : 0;
|
||||||
|
}
|
||||||
|
let consumption = 0;
|
||||||
|
for (const m of this.meters.values()) {
|
||||||
|
consumption += typeof m.consumptionW === 'number' ? m.consumptionW : 0;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
generationW: generation,
|
||||||
|
consumptionW: consumption,
|
||||||
|
netFlowW: generation - consumption
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
EnergyMesh,
|
||||||
|
Device,
|
||||||
|
Inverter,
|
||||||
|
Meter,
|
||||||
|
DER,
|
||||||
|
PriceQuote,
|
||||||
|
Forecast
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Running tests..."
|
||||||
|
npm test
|
||||||
|
|
||||||
|
echo "Tests completed."
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
This folder contains simple tests for the Open-EnergyMesh MVP.
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
const assert = require('assert');
|
||||||
|
const {
|
||||||
|
EnergyMesh,
|
||||||
|
Inverter,
|
||||||
|
Meter
|
||||||
|
} = require('../src/mesh.js');
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
// Create mesh
|
||||||
|
const mesh = new EnergyMesh();
|
||||||
|
|
||||||
|
// Create devices and components
|
||||||
|
const inv = new Inverter('inv1', 'dev-inv1', 1000, 600); // generates 600W
|
||||||
|
const meter = new Meter('m1', 'dev-mtr1', 400); // consumes 400W
|
||||||
|
|
||||||
|
mesh.addInverter(inv);
|
||||||
|
mesh.addMeter(meter);
|
||||||
|
|
||||||
|
const result = mesh.computeFlow();
|
||||||
|
|
||||||
|
// Expect generation > consumption by 200W
|
||||||
|
assert.strictEqual(result.generationW, 600);
|
||||||
|
assert.strictEqual(result.consumptionW, 400);
|
||||||
|
assert.strictEqual(result.netFlowW, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
run();
|
||||||
|
console.log('ALL TESTS PASSED');
|
||||||
|
process.exit(0);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('TEST FAILED', err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue