A Java service that bridges a home solar installation to MQTT. It polls two data sources, computes how much solar power is safely available for EV charging, and publishes everything to one or more MQTT brokers.
- Schneider Conext Combox (Modbus TCP) – inverter/charger, battery and PV data
- Brultech Dashbox (HTTP) – per-circuit power monitoring, including the EV charger
- Solar analyzer – derives
availableSolarPower, the number of watts the EV charger can draw without discharging the house battery
Note: This app is very specific to my own solar setup (a Schneider Combox, a Brultech Dashbox, and a particular EV charger and battery bank). It could easily be modified to support a different system by changing the register map in
Combox.json, the circuit mapping inBrultechDevice.java, and the constants inSolarAnalyzer.java.
The MQTT data it publishes is consumed by the mobile app in eric-vickery/solar-monitor.
SolarServer starts independent timers and then blocks until shutdown:
| Timer | Default period | Job |
|---|---|---|
| Combox | 2000 ms | Reads Modbus registers defined in src/main/resources/Combox.json |
| Dashbox | 2000 ms | Fetches circuit wattages from /index.php/pages/search/getWattVals |
| Analyzer | 1500 ms | Pulls the latest values from the other providers and updates the available-solar figure |
| MQTT publisher (one per broker) | 5000 ms | Publishes every field from all providers, whether or not it changed |
The target is MAX_SOLAR_WATTS (9200) − house load − battery charge allowance, clamped to 0–7200 W (the EV charger maximum). The published value ramps toward it (up 250 W every 3 s, down 500 W every 2 s). Safeguards:
- Gate – output drops to 0 unless the charger is in Bulk/Absorb/Float/Equalize and battery voltage is above a threshold.
- Equilibrium hold – ramp-up stops once the battery surplus falls below a threshold (smaller in Float).
- Slight-negative nudge – if the battery is mildly negative, step down once and wait 20 s before another step.
- Discharge delay – sustained discharge beyond −175 W for 3 minutes triggers a ramp to 0.
- Backoff – after ramping to 0, hold for 10 minutes. After 3 backoffs the analyzer gives up for the day.
- Full-charge rule – once the battery has reached full charge, a drop in SOC below 97.8% gives up for the day.
- Midnight reset – all state is cleared at midnight.
Tuning constants live at the top of SolarAnalyzer.java.
Topics are retained. Combox topics are defined per register in Combox.json (e.g. combox/lastPublishedTime).
combox/…– Combox registersdashbox/…– circuit power (main,evCharger,wellPump,dryer, …) andsolarRoomTempF/solarRoomTempCsolarAnalyzer/availableSolarPower,inDischargeDelay,inDischargeBackoff,numberOfBackoffs,houseBatteryHitFullCharge,givenUpForDay
All configuration is via environment variables, kept in SolarServer.env. That file contains sensitive information (broker credentials) and is git-ignored, so never commit it.
| Variable | Default | Description |
|---|---|---|
COMBOX_ADDRESS |
required | Combox IP address |
COMBOX_PORT |
502 |
Modbus TCP port |
DASHBOX_HOST |
required | Dashbox hostname |
COMBOX_POLLING_FREQ |
2000 |
Combox poll interval (ms) |
DASHBOX_POLLING_FREQ |
2000 |
Dashbox poll interval (ms) |
ANALYZER_CALCULATION_FREQ |
1500 |
Analyzer interval (ms) |
MQTT_CONSOLE_MODE |
false |
Print payloads to stdout instead of connecting to a broker |
MQTT_BROKER |
– | Primary broker URL, e.g. tcp://host:1883 or ssl://host:8883 |
MQTT_BROKER_NAME |
– | Label used in logs |
MQTT_USERNAME / MQTT_PASSWORD |
– | Optional credentials |
MQTT_PUBLISHING_FREQ |
5000 |
Publish interval (ms) |
Add more brokers by suffixing the MQTT variables with _2, _3, … (MQTT_BROKER_2, MQTT_BROKER_NAME_2, MQTT_USERNAME_2, MQTT_PASSWORD_2, MQTT_PUBLISHING_FREQ_2). Each broker publishes on its own timer. Numbering must be contiguous; the first missing MQTT_BROKER_N ends the search. If MQTT_PUBLISHING_FREQ_N is omitted it falls back to MQTT_PUBLISHING_FREQ.
Requires JDK 24 and Maven.
EasyModbusJavaClient.jar is not on Maven Central, so install it from lib/ once:
mvn install:install-file -Dfile=lib/EasyModbusJavaClient.jar -DgroupId=de.re.easymodbus -DartifactId=EasyModbus -Dversion=2.8.0 -Dpackaging=jarThen build a shaded jar and run it:
mvn packageset -a; source SolarServer.env; set +a; java -jar target/SolarServer-1.0-SNAPSHOT.jarSet MQTT_CONSOLE_MODE=true to try it without a broker.
The Dockerfile does a multi-stage build (Maven + Temurin 24) and runs on eclipse-temurin:24-jre. docker-compose.yml builds for linux/amd64 and linux/arm64, reads SolarServer.env, and uses host networking so the container can reach the Combox on the local network.
docker compose up -d --buildReleased under the MIT License.
src/main/java/com/fiedlervickery/
├── SolarServer.java entry point, timers, shutdown hook
├── combox/ Modbus client and register definitions
├── brultech/ Dashbox HTTP client
├── analyzer/ SolarAnalyzer – available-solar logic
└── mqtt/ publishers (broker / console), fields and payloads
src/main/resources/Combox.json register map and MQTT topics