OK maybe I was a bit too quick to judge Gemini; we seem to be sending data to emoncms.org in the form of:
https://emoncms.org/input/post?node=vaillant&apikey=[apireadandwritekeyremoved]&fulljson={"flow_temp":36.86,"return_temp":35.54,"elec_power":511.73,"outside_temp":5.67,"inside_temp":18.94,"target_temp":19.0,"dhw_temp":53.0,"heat_output":1311.73}
I just have no idea what to do next (and my brain is fried)
For the record, this is my docker compose.yaml
services:
ebusd:
image: john30/ebusd:latest
container_name: ebusd
restart: always
network_mode: host
command: >
--scanconfig
-d ens:10.0.1.23:9999
--accesslevel=*
--httpport=8080
hpm_bridge:
image: python:3.9-slim
container_name: hpm_bridge
restart: always
network_mode: host
depends_on:
- ebusd
volumes:
- ./heatpump-monitor/main.py:/app/main.py
command: sh -c "pip install requests && python -u /app/main.py"
And this is the python program main.py Gemini wrote:
import requests
import time
import json
import sys
# --- CONFIGURATION ---
EBUSD_URL = "http://localhost:8080/data"
API_KEY = "[read&writeAPIkey removed]"
EMONCMS_URL = "https://emoncms.org/input/post"
NODE_NAME = "vaillant"
INTERVAL = 30
# --- SENSORS MAP ---
# Format: "EmonCMS_Name": ("Circuit", "Register_Name")
SENSORS = {
# 1. Physics (Temps & Power)
"flow_temp": ("hmu", "RunDataFlowTemp"),
"return_temp": ("hmu", "RunDataReturnTemp"),
"elec_power": ("hmu", "RunDataElectricPowerConsumption"),
# 2. Calculation Source (Internal variable, not sent to dashboard)
"env_yield": ("hmu", "CurrentYieldPower"),
# 3. Environmental
"outside_temp": ("basv", "DisplayedOutsideTemp"),
"inside_temp": ("basv", "Z1RoomTemp"),
"target_temp": ("basv", "Z1ActualRoomTempDesired"),
"dhw_temp": ("basv", "HwcStorageTemp"),
}
def fetch_value(circuit, name):
"""Fetches a value from ebusd, handling caching rules."""
try:
# Force a fresh read (maxage=0) for Yield to ensure accurate Physics
# Use cache (maxage=60) for Temps to reduce bus load
is_yield = (name == "CurrentYieldPower")
maxage = "0" if is_yield else "60"
url = f"{EBUSD_URL}/{circuit}/{name}?def&maxage={maxage}"
r = requests.get(url, timeout=5)
if r.status_code != 200: return None
data = r.json()
# Recursive finder for "value"
def find_val_recursive(d):
if isinstance(d, dict):
if "value" in d:
v = d["value"]
if isinstance(v, (int, float)) and not isinstance(v, bool):
return float(v)
for item in d.values():
res = find_val_recursive(item)
if res is not None: return res
return None
return find_val_recursive(data)
except:
return None
def main():
print("--- Vaillant Bridge (Final Clean) ---")
while True:
payload = {}
raw_data = {}
# 1. Fetch All Sensors
for key, (circuit, reg_name) in SENSORS.items():
val = fetch_value(circuit, reg_name)
if val is not None:
raw_data[key] = val
# Add to payload automatically, unless it's a calc variable
if key != "env_yield":
payload[key] = val
# 2. Calculate True Heat Output (Physics)
# Total Heat (W) = Environmental Yield (kW * 1000) + Electrical Input (W)
elec = raw_data.get("elec_power", 0.0)
env_yield_kw = raw_data.get("env_yield", 0.0)
if env_yield_kw > 0:
payload["heat_output"] = (env_yield_kw * 1000.0) + elec
else:
payload["heat_output"] = 0.0
# 3. Safety Defaults
if "elec_power" not in payload: payload["elec_power"] = 0.0
# 4. Upload
if len(payload) > 1:
try:
params = {
"node": NODE_NAME,
"fulljson": json.dumps(payload),
"apikey": API_KEY
}
requests.post(EMONCMS_URL, params=params, timeout=10)
print(f"Uploaded: {payload}")
except Exception as e:
print(f"Upload failed: {e}")
else:
print("Waiting for valid data...")
sys.stdout.flush()
time.sleep(INTERVAL)
if __name__ == "__main__":
main()
Edit: see the updated main.py in my reply below, and follow the replies for the next steps