Skip to content

EEBus: add OHPCF heat pump compressor flexibility#30636

Open
andig wants to merge 23 commits into
masterfrom
feat/eebus-ohpcf
Open

EEBus: add OHPCF heat pump compressor flexibility#30636
andig wants to merge 23 commits into
masterfrom
feat/eebus-ohpcf

Conversation

@andig

@andig andig commented Jun 8, 2026

Copy link
Copy Markdown
Member

Stacked on #30633 (eebus-go @dev bump) and based on the upstream OHPCF use case from enbility/eebus-go#223.

OHPCF (Optimization of Self-Consumption by Heat Pump Compressor Flexibility) lets evcc, acting as CEM, observe and control the optional power consumption a remote heat pump compressor announces.

Read (server/eebus):

  • bumps eebus-go to the branch that adds usecases/cem/ohpcf and registers the use case on the CEM entity (reachable via CustomerEnergyManagement).
  • on OHPCF data update events, reads and logs the announced flexibility: availability, requested and maximum power, process state, pausable/stoppable flags, minimal run and pause durations.

Charger (charger/eebus-ohpcf.go):

  • exposes the compressor as a switchable load (api.Charger). Status/Enabled are derived from the power consumption process state: running → charging, available/scheduled/paused → connected, else → disconnected.
  • OHPCF has no power setpoint, so MaxCurrent converts the loadpoint's allotted current to power (using the active phases from loadpoint.Controller) and schedules the optional consumption to start now once the available surplus covers the compressor's requested power, pausing or aborting it otherwise. The schedule/resume/pause/stop decision is centralised in one guarded helper so an unchanged state issues no repeated commands.
  • CurrentPower reports the announced power while running.

The control decision logic (state × surplus → action) and the status/enabled mapping are unit tested. The use-case wiring is exercised by the existing eebus integration handshake test.

go build, go vet, charger + eebus tests, and golangci-lint all pass.

andig added 2 commits June 8, 2026 14:14
The dev branch reworks remote service trust around ship-go's ServiceIdentity
and splits the load control write approval event into separate limit and
device configuration events.

NewConfiguration now takes the device categories as well as an optional SHIP
Pairing config and ring buffer persistence; evcc trusts remote services by
their configured SKI via RegisterRemoteService and does not use SHIP Pairing,
so both are passed as nil.

RegisterRemoteSKI/UnregisterRemoteSKI/RemoteServiceForSKI/CancelPairingWithSKI
are replaced by their ServiceIdentity-based equivalents, and the service
reader callbacks switch from raw SKI strings to ServiceIdentity, gaining the
new SHIP Pairing auto-trust events as no-ops.

Device configuration writes were applied automatically up to v0.7.0 but now
require explicit approval, so the LPC/LPP handlers approve all pending
configuration writes to preserve the previous behaviour.
Bumps eebus-go to the branch adding the cem/ohpcf use case and registers
it on the CEM entity alongside the other use cases.

OHPCF lets evcc, acting as CEM, observe the optional power consumption a
remote heat pump compressor announces. The use case is wired read-only:
its data update events are read and logged (availability, requested and
maximum power, process state, pausable/stoppable flags and the minimal
run and pause durations) and the use case is reachable for on-demand reads
via CustomerEnergyManagement. Scheduling, aborting, pausing and resuming a
consumption process are intentionally left out for now.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@andig andig added the infrastructure Basic functionality label Jun 8, 2026
Adds a charger that exposes a remote heat pump compressor announcing
optional power consumption (OHPCF) as a switchable load.

Status, enabled state and control are derived from the compressor's power
consumption process state: running maps to charging, available/scheduled/
paused to connected, everything else to disconnected. OHPCF has no power
setpoint, so MaxCurrent instead converts the loadpoint's allotted current
to power (using the active phases) and schedules the optional consumption
to start now once the available surplus covers the compressor's requested
power, pausing or aborting it otherwise.
@andig andig changed the title EEBus: add read-only OHPCF (heat pump compressor flexibility) support EEBus: OHPCF heat pump compressor flexibility (read + charger control) Jun 8, 2026
CurrentPower now reports the heat pump's actual measured power consumption
through the MPC (Monitoring of Power Consumption) use case instead of the
announced OHPCF estimate, capturing the MPC entity from its support update
and returning api.ErrNotAvailable when no measurement is available, matching
the eebus meter implementation.
Comment thread server/eebus/eebus.go Outdated
andig and others added 4 commits June 8, 2026 20:57
meter/eebus.go inlined the eebusapi.ErrDataNotAvailable to api.ErrNotAvailable
mapping in eebusReadValue and readPhases. Both now use the existing
eebus.WrapError helper so the mapping lives in a single place.
The OHPCF charger now reads and controls the heat pump compressor directly,
so the server-side diagnostic logging is no longer needed. OHPCF use case
events are forwarded straight to registered devices via ucCallback, like the
other use cases.
@andig andig changed the title EEBus: OHPCF heat pump compressor flexibility (read + charger control) EEBus: add OHPCF heat pump compressor flexibility Jun 8, 2026
Base automatically changed from chore/eebus-go-dev to master June 15, 2026 07:55
@github-actions github-actions Bot added the stale Outdated and ready to close label Jun 22, 2026
@andig

andig commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

@github-actions github-actions Bot removed the stale Outdated and ready to close label Jun 23, 2026

Copilot AI commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Done. I merged origin/master into this branch and resolved the conflicts in commit 64412ef.

Copilot finished work on behalf of andig June 23, 2026 09:47
@andig

andig commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Closed due to missing feedback

@andig andig closed this Jun 23, 2026
@TheEragon

Copy link
Copy Markdown

Why close this? This could be useful for Vaillant heat pumps, if there is enough surplus we could heat the hot water to a higher than standard temperature

@andig

andig commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Why close this

In case you've missed it: #30636 (comment)

@CiNcH83

CiNcH83 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Did not realize that this is already in a state worth testing. In fact I am already failing to configure it...

It looks like there is a new charger (OHPCF) for that? Can't find it in the UI (list of heating devices) though. So I tried YAML...

meters:
- name: arotherm_mpc
  type: eebus
  ski: 4bb4ddc3cc35facb2262ac8afde5b3cfea4b0689
  usage: charge

chargers:
- name: arotherm_ohpcf
  type: eebus-ohpcf
  ski: 4bb4ddc3cc35facb2262ac8afde5b3cfea4b0689

loadpoints:
- title: aroTHERM plus
  charger: arotherm_ohpcf
  meter: arotherm_mpc
  mode: pv

Does not seem to work either...
ohpcf_trace.log

Probably can't add EEBUS twice..

@TheEragon

TheEragon commented Jun 24, 2026

Copy link
Copy Markdown

Did not realize that this is already in a state worth testing. In fact I am already failing to configure it...

It looks like there is a new charger (OHPCF) for that? Can't find it in the UI (list of heating devices) though. So I tried YAML...

meters:
- name: arotherm_mpc
  type: eebus
  ski: 4bb4ddc3cc35facb2262ac8afde5b3cfea4b0689
  usage: charge

chargers:
- name: arotherm_ohpcf
  type: eebus-ohpcf
  ski: 4bb4ddc3cc35facb2262ac8afde5b3cfea4b0689

loadpoints:
- title: aroTHERM plus
  charger: arotherm_ohpcf
  meter: arotherm_mpc
  mode: pv

Does not seem to work either... ohpcf_trace.log

I think this might be related to this bug

@CiNcH83

CiNcH83 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

The following config works:

chargers:
- name: arotherm_ohpcf
  type: eebus-ohpcf
  ski: 4bb4ddc3cc35facb2262ac8afde5b3cfea4b0689

loadpoints:
- title: aroTHERM plus
  charger: arotherm_ohpcf
  mode: pv

Including MPC:
image

So how do I test the feature now? DHW has just been loaded. So there is nothing to do for the heat pump right now.

One thing that will sorely be missed opposed to the sensoNET charger is the DHW temperature display. There is however a HVAC use-case (MDT - Monitoring of DHW Temperature) which covers that.

@CiNcH83

CiNcH83 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

I now also enabled EEBUS Energy Management in Vaillant:
image

Guess we have to wait and see what happens now? Does it make sense to remove any DHW time windows to see whether DHW loading starts anyway? Or check whether it overloads to T+5?

@andig

andig commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

You can add integrateddevice capability, let me check on MDA. We announce flexibility for „now“ (really like FLOA) but its probably bot a good fit.

@andig

andig commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

Why charging never starts (from the trace)

The heat-pump loadpoint (lp-1, mode pv) reports charger status: A for the entire session, so the loadpoint treats the compressor as disconnected (car disconnected) and never calls Enable/MaxCurrent — nothing is ever scheduled, hence DHW never reloads regardless of the 45.5 °C setpoint.

Status() returns StatusA because OHPCF.PowerConsumptionProcessState() has no data: evcc receives exactly one OHPCF event — cem-ohpcf-UseCaseSupportUpdate — and zero data-update events (no RequestedPowerMax, no ConsumptionState).

Root cause: the compressor's SmartEnergyManagementPs feature ([3,1] feature 19) is never subscribed. The log has no outgoing subscription for feature 19, even though evcc does subscribe to the sibling MPC Measurement feature on the very same entity ([3,1] feature 11). Without that subscription loadSmartEnergyManagementPsDataType never runs, so process state / requested power stay empty and Status() falls through to StatusA.

So the gap is upstream in usecases/cem/ohpcf connected() — the SmartEnergyManagementPs Subscribe() isn't taking effect for the compressor entity, and the error there is swallowed at Debug. Adding a log in connected() would show whether it runs and whether Subscribe()/NewSmartEnergyManagementPs errors.

Two evcc-side notes:

  • Mapping "no data" to StatusA makes a subscription/comms gap look like a physically unplugged device. Returning an error (or StatusNone) until the first data arrives would surface this instead of silently idling.
  • Even once data flows, OHPCF can only schedule/resume/pause the optional consumption the heat pump offers — it can't initiate a DHW cycle the heat pump itself hasn't announced as flexibility.

🤖 Generated with Claude Code

@TheEragon

Copy link
Copy Markdown

@andig see my comment here

as a workaround for test we can do this

// customer energy management to EVSE
ohpcfUC := ohpcf.NewOHPCF(localEntity, c.ucCallback)
c.cem = CustomerEnergyManagement{
	EvseCC: evsecc.NewEVSECC(localEntity, c.ucCallback),
	EvCC:   evcc.NewEVCC(c.service, localEntity, c.ucCallback),
	EvCem:  evcem.NewEVCEM(c.service, localEntity, c.ucCallback),
	OpEV:   opev.NewOPEV(localEntity, c.ucCallback),
	OscEV:  oscev.NewOSCEV(localEntity, c.ucCallback),
	EvSoc:  evsoc.NewEVSOC(localEntity, c.ucCallback),
	OHPCF:  ohpcfUC,
}

// eebus-go's ohpcf.NewOHPCF (unlike NewMPC and the other use cases) omits
// subscribing its concrete type to device events, so OHPCF.HandleEvent ->
// connected() never runs and the compressor's SmartEnergyManagementPs
// feature is never subscribed. Subscribe it here until upstream is fixed.
_ = localEntity.Device().Events().Subscribe(ohpcfUC)

Point eebus-go at andig/eebus-go fix branch (enbility/eebus-go#228) which
registers the OHPCF use case event handler, so the compressor's
SmartEnergyManagementPs feature is actually subscribed and process-state
updates arrive. Revert to an upstream pseudo-version once #228 lands.
@andig

andig commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@CiNcH83 upstream bug enbility/eebus-go#228 fixed here, please give it another try.

@andig

andig commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@TheEragon are you on Slack?

andig added 3 commits June 25, 2026 11:05
Status returned StatusA when no compressor entity was present, which made a
missing/disconnected compressor indistinguishable from an idle device. Return
a "not connected" error instead so the loadpoint surfaces the condition.
Enabled, Enable and MaxCurrent silently returned a benign value when no
compressor entity was present, hiding a disconnected device. Return the
shared errNotConnected from every method that accesses the compressor.
The device template renders features: [integrateddevice], but the config
struct rejected unknown keys, failing TestTemplates. Embed the shared embed
struct so icon/features decode and the charger reports integrateddevice.
@CiNcH83

CiNcH83 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Still no DHW production even though there should be demand (T=43,5 °C). I had a boost running over an hour ago but stopped it to keep the demand for testing. I configured a blocking time for DHW production of 1h which is already over by now though. I am attaching the log but keep it running. Maybe something is still going to happen...

ohpcf_trace.log

@TheEragon

TheEragon commented Jun 25, 2026

Copy link
Copy Markdown

@CiNcH83 can you share your full config please?

I connected my Vaillant to evcc but I don't see the energy management menu

Screenshot_20260625_114716.jpg

And I can see that connection is working in logs, no idea what could be the problem.

@CiNcH83

CiNcH83 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Energymanagement is configured in a different place:
image

Configuration (YAML)

chargers:
- name: arotherm_ohpcf
  type: eebus-ohpcf
  ski: *****
  features:
    - heating
    - integrateddevice
loadpoints:
- title: aroTHERM plus
  charger: arotherm_ohpcf
  mode: pv

Configuration (UI)

{
  "charger": [
    {"id":7,"name":"db:7","type":"template","config":{"template":"tplink","host":"192.168.1.15","icon":"bike","standbypower":"15"},"deviceProduct":"TP-Link H-Series Smart Plug"}
  ],
  "circuits": "- name: main
  title: Hauptstromkreis
  maxCurrent: 35
  maxPower: 24150
  meter: db:1",
  "experimental": true,
  "hems": "type: eebus
ski: *****",
  "influx": {"database":"","insecure":false,"org":"","password":"","token":"","url":"","user":""},
  "interval": 15,
  "loadpoints": [
    {"id":8,"name":"db:8","batteryBoostLimit":100,"charger":"db:7","defaultMode":"off","limitEnergy":0,"limitSoc":0,"maxCurrent":1,"minCurrent":0.5,"phasesConfigured":1,"planEnergy":0,"planPrecondition":0,"planStrategy":{"continuous":false,"precondition":0},"planTime":"0001-01-01T00:00:00Z","priority":0,"smartCostLimit":null,"smartFeedInPriorityLimit":null,"soc":{"estimate":true,"poll":{"interval":3600000000000,"mode":"charging"}},"thresholds":{"disable":{"delay":180000000000,"threshold":0},"enable":{"delay":60000000000,"threshold":0}},"title":"eBike Charger","ui":{"maxTemp":0,"minTemp":0},"vehicle":"db:9"}
  ],
  "meter": [
    {"id":16,"name":"db:16","type":"template","config":{"template":"iammeter","host":"192.168.1.13","usage":"grid"},"deviceIcon":"meter","deviceProduct":"IAMMETER WEM3080T/WEM3046T/WEM3050T","deviceTitle":"Cascaded"},
    {"id":2,"name":"db:2","type":"template","config":{"id":1,"template":"huawei-sun2000","baudrate":9600,"comset":"8N1","forceaccharging":"false","host":"192.168.1.12","maxacpower":8800,"maxchargepower":"10000","modbus":"tcpip","port":502,"storageunit":"1","timeout":5000000000,"usage":"pv"},"deviceProduct":"Huawei SUN2000","deviceTitle":"SUN2000"},
    {"id":1,"name":"db:1","type":"template","config":{"id":1,"template":"huawei-sun2000","baudrate":9600,"comset":"8N1","forceaccharging":"false","host":"192.168.1.12","maxacpower":"0","maxchargepower":"10000","modbus":"tcpip","port":502,"storageunit":1,"timeout":5000000000,"usage":"grid"},"deviceProduct":"Huawei SUN2000"},
    {"id":3,"name":"db:3","type":"template","config":{"id":1,"template":"huawei-sun2000","baudrate":9600,"capacity":10,"comset":"8N1","forceaccharging":true,"host":"192.168.1.12","maxacpower":"0","maxchargepower":5000,"maxdischargepower":5000,"maxsoc":100,"minsoc":4,"modbus":"tcpip","port":502,"storageunit":"1","timeout":5000000000,"usage":"battery"},"deviceProduct":"Huawei SUN2000","deviceTitle":"LUNA2000"}
  ],
  "modbusproxy": [{"port":5021,"readonly":"false","settings":{"rtu":false,"uri":"192.168.1.12:502"}}],
  "mqtt": {"broker":"","caCert":"","clientCert":"","clientID":"","clientKey":"","insecure":false,"password":"","topic":"evcc","user":""},
  "residualPower": 100,
  "shm": {"deviceId":"","deviceSerial":"","vendorId":""},
  "site": {"aux":null,"battery":["db:3"],"consumers":null,"ext":["db:16"],"grid":"db:1","pv":["db:2"],"title":"HEMS"},
  "tariff": [
    {"id":12,"name":"db:12","type":"template","config":{"template":"grünstromindex","zip":"***"},"deviceProduct":"Grünstromindex"},
    {"id":10,"name":"db:10","type":"template","config":{"template":"fixed","price":0.3},"deviceProduct":"Fixed Price"},
    {"id":11,"name":"db:11","type":"template","config":{"template":"fixed","price":0.0643},"deviceProduct":"Fixed Price"},
    {"id":15,"name":"db:15","type":"template","config":{"template":"open-meteo","ac":"1000","alphatemp":"-0.004","az":-10,"de":"0","dec":6,"dm":"0","efficiency":80,"interval":"1h","kwp":3.24,"lat":"***","lon":"***","rossmodel":"0.0342"},"deviceProduct":"Open-Meteo","deviceTitle":"PV-String 2 (Flachdach)"},
    {"id":13,"name":"db:13","type":"template","config":{"template":"open-meteo","ac":1000,"alphatemp":"-0.004","az":-10,"de":"0","dec":25,"dm":"0","efficiency":80,"interval":"1h","kwp":6.075,"lat":"***","lon":"***","rossmodel":"0.0342"},"deviceProduct":"Open-Meteo","deviceTitle":"PV-String 1 (Schrägdach)"}
  ],
  "tariffRefs": {"co2":"db:12","feedIn":"db:11","grid":"db:10","planner":"","solar":["db:13","db:15"]},
  "vehicle": [
    {"id":9,"name":"db:9","type":"template","config":{"template":"offline","capacity":0.75,"icon":"bike","title":"CUBE Kathmandu"},"deviceProduct":"Generisches Fahrzeug (ohne API)"}
  ]
}

This is just a minimal configuration for testing.

@TheEragon

Copy link
Copy Markdown

Energymanagement is configured in a different place

I know, but I don't see it in myVaillant app after I connect to evcc. I tried different EMS and with some it showed and with other didn't. I always thought it's because of the OHPCF but I guess it's something else :(

@CiNcH83

CiNcH83 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Those settings are indeed weird. I feel like the cloud is sometimes out of sync with the real configuration. So if you just paired evcc it may very well be that Energymanagement only appears tomorrow. I also had it missing at times. Probably reappeared after enabling EnergyPLUS? Dunno. Vaillant hasn't been a super smooth experience for me so far...

Comment thread templates/definition/charger/eebus-ohpcf.yaml Outdated
andig added 2 commits June 25, 2026 12:48
It appeared as a wallbox in the UI. Move the template to the heating group
and add the heating feature so the loadpoint renders it as a heat pump.
Pick up enbility/eebus-go#228 follow-up that reads the OHPCF data on connect
so the compressor's process state is populated instead of staying empty.
@CiNcH83

CiNcH83 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Looks like a heat pump now with latest commit:
image

DHW charging not started though. Heat pump status always goes back to Standby and restarts the 1m countdown. Continuously getting:

aroTHERM plus: charger out of sync: expected enabled, got disabled

ohpcf_trace.log

@andig

andig commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

Both upstream fixes are working — the OHPCF data now flows (all DataUpdate* events fire, status B, renders as a heat pump). The remaining issue is a control-model mismatch, not a comms gap.

Why it still doesn't start / loops:

The heat pump offers a fixed, all-or-nothing 3850 W optional consumption (powerMax: 3850, isPausable:false, isStoppable:true, activeDurationMin: 3min, state: inactive).

On enable, the loadpoint offers its minimum current first:

set charge current limit: 8A      → 8A × 230V = 1840 W   (min power 1.8 kW)
charger enable

MaxCurrent(8) computes available = 1840 W < requested = 3850 W, so the schedule condition (available >= requested) is never met → SchedulePowerConsumptionProcess is never sent (0 writes to feature 19 in the trace, only the initial read). The compressor stays inactive, so Enabled() (true only for Scheduled/Running) stays false, and after the 60 s sync grace the loadpoint gives up:

13:02:30 enable → 13:03:31 out of sync → disable → 13:04:31 enable → …

That's the Standby + 1 m countdown loop.

Root cause: OHPCF is a fixed-power on/off device, but it's modelled as a ramping charger:

  1. The charger re-gates on available >= requested using the loadpoint's ramped current (starts at min, never reaches 3850 W). Surplus enablement is already the loadpoint's job (enable threshold).
  2. Enable(true) defers to MaxCurrent and Enabled() returns the live compressor state instead of the commanded intent, so the loadpoint never sees its enable take effect → out-of-sync.

Fix (charger-side): make it a true switch — Enable(true) schedules/resumes directly, Enable(false) pauses/stops, Enabled() returns the commanded intent, and drop the available >= requested gate in MaxCurrent.

Workaround to test right now, @CiNcH83: set the loadpoint min power ≥ 3850 W (the announced powerMax). Then the first offered current is ≥16.7 A, available >= requested holds, and the schedule fires.

🤖 Generated with Claude Code

@CiNcH83

CiNcH83 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Set it to:
image

Looks like wife is running some washing and dishwasher machines... so not enoug surplus for that right now...

The compressor offers a fixed, non-modulatable consumption, but it was driven
like a ramping charger: enabling deferred to MaxCurrent, which only scheduled
once the loadpoint's offered power exceeded the request. With the loadpoint
starting at minimum current that never held, so nothing was scheduled and
Enabled stayed false, looping the loadpoint via repeated out-of-sync.

Drive it on/off instead: Enable schedules/resumes or pauses/stops, Enabled
reports the commanded intent, MaxCurrent ignores the offered current, and the
surplus decision is left to the loadpoint. Drop the now-unused phase handling
and loadpoint controller.
@CiNcH83

CiNcH83 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Still does not want to charge despite enough surplus...
image

ohpcf_trace.log

@andig

andig commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

Good news: the control path is now correct end-to-end. In the latest trace evcc does send the schedule, there's no out-of-sync loop, and status holds at B. The remaining blocker is that the heat pump rejects the write:

[3,1] feature 19  →  "resultData":[{"errorNumber":1},{"description":"Write failed"}]

for what evcc writes:

alternatives:[[{ powerSequence:[[
  { description:[{sequenceId:0}] },
  { schedule:[{startTime:"2026-06-25T12:27:58Z"}] }
]] }]]

So the compressor stays inactive and never starts. This is now a write-encoding question in eebus-go's SchedulePowerConsumptionProcess, not the evcc charger.

errorNumber:1 "Write failed" is generic, so I can't pin the exact cause from the log. Leading candidates:

  1. Absolute vs relative startTime — evcc writes an absolute ISO time, but your compressor expresses its scheduleConstraints as relative durations (earliestStartTime: -PT3H47M, latestEndTime: PT20H12M). Vaillant may only accept a relative startTime (e.g. PT0S = now).
  2. State not set — moving inactive → scheduled may require writing the sequence State.State, not just a schedule start time.
  3. Different write shape expected for the partial write.

@CiNcH83 — could you check what Vaillant expects here? Specifically, from the Cross-manufacturer communication via EEBUS doc (or via Vaillant support):

  • For OHPCF, does scheduling the optional consumption expect the startTime as relative (PT…) or absolute time?
  • Is a separate State write required to start it, or is writing the schedule sufficient?
  • Any example of the exact smartEnergyManagementPsData payload they accept to start the compressor?

That would let us fix the write encoding upstream.

🤖 Generated with Claude Code

@CiNcH83

CiNcH83 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Forwarded.

The startTime does not seem right though? More like 14:30. Another DST/time zone issue?

@andig

andig commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

Z is UTC

Control writes passed a nil result callback, so a rejected schedule/resume/
pause/abort was silently dropped while the loadpoint assumed success. Wait for
the write result with a timeout: log and return the error on rejection, and
return a timeout error if no result arrives.
@CiNcH83

CiNcH83 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

A log with the most recent change...

ohpcf_trace.log

@andig

andig commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

Thank you, those logs are immensely helpful .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

infrastructure Basic functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants