- Simulator rewritten to use flexmeasures_client (works!) - flexmeasures-entsoe installed (ENTSO-E data import) - flexmeasures-weather installed (weather data) - FlexMeasures Redis connection fixed (DNS resolution) - Dashboard Grafana updated with Cariflex asset types - Simulator running in background, posting to 40 sensors TODO: - S2 CEM deployment - Scheduler FlexMeasures - Logo Cariflex in FM UI
111 lines
3.5 KiB
Python
111 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Cariflex Simulator - Publishes simulated data to FlexMeasures API.
|
|
Uses flexmeasures_client for authentication and data posting.
|
|
Simulates 40 assets: 10 PV, 10 Battery, 10 EV Charger, 10 EV V2G
|
|
"""
|
|
import asyncio
|
|
import time
|
|
import random
|
|
import math
|
|
from datetime import datetime, timezone, timedelta
|
|
from flexmeasures_client import FlexMeasuresClient
|
|
|
|
FM_HOST = "https://flexmeasures.digitribe.fr"
|
|
FM_EMAIL = "admin@digitribe.fr"
|
|
FM_PASSWORD = "Digitribe972"
|
|
|
|
# Sensor mapping: sensor_id -> (name, type, unit, min, max)
|
|
SENSORS = {}
|
|
for i in range(1, 11):
|
|
SENSORS[40 + i] = {"name": f"pv_{i:02d}_power", "type": "pv", "unit": "kW", "min": 0, "max": 5}
|
|
for i in range(1, 11):
|
|
SENSORS[50 + i] = {"name": f"bat_{i:02d}_power", "type": "battery", "unit": "kWh", "min": 10, "max": 100}
|
|
for i in range(1, 11):
|
|
SENSORS[60 + i] = {"name": f"chg_{i:02d}_power", "type": "ev_charger", "unit": "kW", "min": 0, "max": 22}
|
|
for i in range(1, 11):
|
|
SENSORS[70 + i] = {"name": f"ev_{i:02d}_power", "type": "ev_v2g", "unit": "kWh", "min": 15, "max": 75}
|
|
|
|
def generate_value(cfg, hour):
|
|
"""Generate realistic value based on asset type and time of day."""
|
|
t = cfg["type"]
|
|
if t == "pv":
|
|
if 6 <= hour <= 18:
|
|
factor = max(0, math.sin((hour - 6) * math.pi / 12))
|
|
else:
|
|
factor = 0
|
|
return round(max(0, cfg["max"] * factor + random.gauss(0, 0.3)), 2)
|
|
elif t == "battery":
|
|
base = 50 + 30 * math.sin((hour - 6) * math.pi / 12)
|
|
return round(max(cfg["min"], min(cfg["max"], base + random.gauss(0, 2))), 2)
|
|
elif t == "ev_charger":
|
|
if 8 <= hour <= 22:
|
|
factor = random.uniform(0.2, 1.0)
|
|
else:
|
|
factor = random.uniform(0, 0.15)
|
|
return round(max(0, cfg["max"] * factor + random.gauss(0, 0.5)), 2)
|
|
elif t == "ev_v2g":
|
|
if 0 <= hour <= 6:
|
|
base = 60
|
|
elif 17 <= hour <= 21:
|
|
base = 30
|
|
else:
|
|
base = 45
|
|
return round(max(cfg["min"], min(cfg["max"], base + random.gauss(0, 3))), 2)
|
|
return 0
|
|
|
|
async def post_all_data(client):
|
|
"""Post data for all 40 sensors."""
|
|
now = datetime.now(timezone.utc)
|
|
hour = now.hour
|
|
start = now - timedelta(minutes=5)
|
|
|
|
success = 0
|
|
failed = 0
|
|
|
|
for sensor_id, cfg in SENSORS.items():
|
|
value = generate_value(cfg, hour)
|
|
try:
|
|
await client.post_sensor_data(
|
|
sensor_id=sensor_id,
|
|
values=[value],
|
|
start=start.isoformat(),
|
|
duration="PT5M",
|
|
unit=cfg["unit"]
|
|
)
|
|
success += 1
|
|
except Exception as e:
|
|
failed += 1
|
|
if failed <= 3:
|
|
print(f" ⚠️ Sensor {sensor_id}: {e}")
|
|
|
|
return success, failed
|
|
|
|
async def main():
|
|
print("🚗 Cariflex Simulator → FlexMeasures API")
|
|
print(f" Sensors: {len(SENSORS)} (10 PV, 10 Bat, 10 Chg, 10 EV)")
|
|
print(f" FM API: {FM_HOST}")
|
|
print()
|
|
|
|
client = FlexMeasuresClient(
|
|
email=FM_EMAIL,
|
|
password=FM_PASSWORD,
|
|
host="flexmeasures.digitribe.fr",
|
|
ssl=True,
|
|
request_timeout=60.0
|
|
)
|
|
|
|
print("✅ Connected to FlexMeasures")
|
|
|
|
iteration = 0
|
|
while True:
|
|
success, failed = await post_all_data(client)
|
|
iteration += 1
|
|
now = datetime.now(timezone.utc)
|
|
print(f" 📊 Iteration {iteration}: {success} OK, {failed} failed (hour={now.hour})")
|
|
|
|
await asyncio.sleep(30)
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|