From a7122d53b397787ac4eb0731db0987c53d8bc22d Mon Sep 17 00:00:00 2001 From: agent-d1f4fdedbc508482 Date: Wed, 15 Apr 2026 01:00:54 +0200 Subject: [PATCH] build(agent): molt-b#d1f4fd iteration --- .gitignore | 21 ++++++++ AGENTS.md | 9 ++++ README.md | 30 ++++++++++- package.json | 13 +++++ src/mesh.js | 139 +++++++++++++++++++++++++++++++++++++++++++++++++ test.sh | 7 +++ test/README.md | 1 + test/test.js | 34 ++++++++++++ 8 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 package.json create mode 100644 src/mesh.js create mode 100644 test.sh create mode 100644 test/README.md create mode 100644 test/test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd5590b --- /dev/null +++ b/.gitignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e9d46fb --- /dev/null +++ b/AGENTS.md @@ -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. diff --git a/README.md b/README.md index aaebf8a..e894ca3 100644 --- a/README.md +++ b/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 \ No newline at end of file +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. diff --git a/package.json b/package.json new file mode 100644 index 0000000..102b398 --- /dev/null +++ b/package.json @@ -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": {} +} diff --git a/src/mesh.js b/src/mesh.js new file mode 100644 index 0000000..9c13735 --- /dev/null +++ b/src/mesh.js @@ -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 +}; diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..90e18b1 --- /dev/null +++ b/test.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Running tests..." +npm test + +echo "Tests completed." diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..ba212c2 --- /dev/null +++ b/test/README.md @@ -0,0 +1 @@ +This folder contains simple tests for the Open-EnergyMesh MVP. diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..f278a02 --- /dev/null +++ b/test/test.js @@ -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); +}