29 Commits

Author SHA1 Message Date
Patrick Schurig
d838bce331 fix(v2c): set host address before connectDevice() in setupThing()
Bug: ModbusTcpMaster: device ':502' ConnectionError when adding a trydan
by manual IP (createMethod "user") or after nymead restart before the
NetworkDeviceMonitor completes its ARP scan.

Root cause: TrydanModbusTcpMaster was constructed with
monitor->networkDeviceInfo().address(), which is null at first
registerMonitor() — the monitor only populates its cache after an
asynchronous ARP/mDNS resolution. connectDevice() then ran against
QHostAddress() → ":502".

Fix:
- Read the initial IP from the stored address param (always set at
  discovery or user-entry) and use it as the fallback when the monitor
  has not yet resolved the address.
- Guard the networkDeviceInfoChanged callback: only call setHostAddress()
  when the new address is non-null (the monitor emits transient null info
  during its internal refresh cycles).

Order is now: create master with valid address → wire callbacks →
connectDevice(). The monitor update path remains for DHCP renewals.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 17:13:37 +02:00
Patrick Schurig
2ac00e1330 fix(v2c): correct logging category dcV2C + debian packaging + modbus.pri path
Build fixes found during first local compilation (qmake6 + g++ -Werror):

1. Logging category: nymea-plugininfocompiler generates 'dcV2C' (uppercase C)
   from the JSON vendor name "V2C". All three source files used 'dcV2c'
   (lowercase c) — corrected to 'dcV2C' in integrationpluginv2c.cpp,
   trydanmodbustcpmaster.cpp, v2ctcpdiscovery.cpp.

2. modbus.pri: hardcoded include(/usr/include/nymea-modbus/modbus-tool.pri)
   fails when libnymea-modbus-dev is not system-installed. Changed to a
   conditional: checks $$[QT_INSTALL_PREFIX] first, then NYMEA_MODBUS_PATH
   override, with a graceful fallback (warning + manual C++17 CONFIG flag).
   The v2c plugin has MODBUS_CONNECTIONS empty so the tool loop is a no-op
   either way; the fallback is safe.

3. debian/: add powersync-plugin-v2c package entry (control, .install,
   changelog entry 1.15.0+etm4).

Build verified: libnymea_integrationpluginv2c.so links against libnymea.so.1,
libnymea-modbus.so.1, Qt6Network/SerialBus/Core, 0 compiler warnings
(with -Werror -Wall -Wextra -std=c++17).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:20:11 +02:00
Patrick Schurig
f0be2d00a2 fix(v2c): use qBound for min/maxIntensity clamp in initialize()
qMin(32, m_maxIntensity) gives 0 when firmware returns 0 for MaxIntensity
(aberrant value).  maxIntensity()==0 then causes every setMaxChargingCurrent
call to be clamped to 0, falling into the <6 A path which pauses charging
silently — the user sees the action succeed but the charger stops.

qBound(6, val, 32) is correct in all cases:
  - firmware reports 16 A (mono Trydan) → 16 (real installation limit kept)
  - firmware reports 0 (aberrant)        →  6 (safe floor, does not claim 32 A)
  - firmware reports 65535 (aberrant)    → 32 (IEC absolute ceiling)

The real installation limit still comes from MaxIntensity read on the device;
the bounds [6, 32] are only IEC guardrails, not hardcoded defaults.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 14:22:13 +02:00
Patrick Schurig
18655452d9 feat(v2c): add integration plugin, JSON metadata, and build plumbing
Étape 1 complete: V2C Trydan nymea plugin (Modbus TCP, status Partial).

Plugin (IntegrationPluginV2c):
  - discoverThings: delegates to V2cTcpDiscovery, maps results to
    ThingDescriptors; createMethod "user" bypasses discovery.
  - setupThing: registers a NetworkDeviceMonitor (IP tracking on DHCP
    renewal), creates TrydanModbusTcpMaster, wires reachableChanged →
    initialize() → state update on initializationFinished(), then starts
    periodic polls on a 30s PluginTimer.
  - updateThingStates: ChargeState 0/1/2 → pluggedIn/charging;
    power = (PauseState==0 AND Lock==0) per evcc trydan.go Enabled().
  - handleDynamicConflict: if Dynamic==1 (V2C internal PV optimizer),
    write PauseDynamic=1 to suppress it.  NEVER write Dynamic=0 (silences
    ChargePower telemetry).  Writes only on state transition to minimize
    flash wear.  cf. github.com/evcc-io/evcc/issues/28047.
  - Action "power": writes PauseState then Lock (always in mirror), then
    fire-and-forgets PauseDynamic if Dynamic==1.  Action handlers filter
    writeCompleted by register address to avoid reacting to concurrent
    background writes.
  - Action "maxChargingCurrent": clamps to [minIntensity, maxIntensity],
    writes Intensity.  Below 6A → pause instead of invalid setpoint.

JSON (integrationpluginv2c.json):
  - ThingClass "trydan" implementing evcharger + connectable + networkdevice.
  - settingsTypes: suspendInternalOptimizer (bool, default true) — allows
    the user to disable Dynamic conflict management if they want the V2C
    PID to remain active alongside the HEMS.
  - State "chargeEnergy" exposed as diagnostic only (NOT sessionEnergy) —
    firmware reliability on this register is unvalidated; cf. evcc #28047.

Build (v2c.pro):
  - MODBUS_CONNECTIONS intentionally empty: the modbus-tool code generator
    assumes non-overlapping block reads; leaving it empty skips generation
    while still linking nymea-modbus via the PKGCONFIG in modbus.pri.

VendorId 56c3e7bb… is new (no existing V2C vendor in the repo).
PluginId  f0692725… is new (generated for this plugin).

PORTING_STATUS_modbus.md documents register sources, the 4 items that
require validation on real hardware, and the "no beta matrix" constraint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:49:49 +02:00
Patrick Schurig
947da7e1fc feat(v2c): add HTTP LAN discovery (V2cTcpDiscovery)
Architecture choice: Modbus TCP has no network discovery mechanism, so
discovery piggybacks on nymea's NetworkDeviceDiscovery (ARP/mDNS scan)
and then probes each host via GET /RealTimeData (V2C Trydan HTTP REST API).

The presence of a "Paused" field in the JSON response is used as a Trydan
fingerprint — it matches the charger model and is absent on generic devices.
cf. evcc charger/trydan.go realTimeData struct.

Probes run concurrently (one QNetworkReply per host) but the sequence
terminates via two independent paths that could both fire:
  1. The NetworkDeviceDiscoveryReply::finished callback arms a 3-second
     QTimer::singleShot to give outstanding probes time to complete.
  2. Each probe completion path checks (m_scanFinished && pendingProbes==0)
     and calls finishDiscovery() early if all probes are already done.
A m_finished bool guard prevents double-emission of discoveryFinished()
if both paths fire in quick succession (e.g. all probes complete exactly
as the timer fires).

Manual-entry (createMethod "user") skips discovery entirely; setupThing()
is called directly with the user-supplied IP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:49:22 +02:00
Patrick Schurig
14f07cc638 feat(v2c): add Modbus transport abstraction + TCP implementation
Architecture choice: separate the register map / business logic from the
physical transport.  TrydanModbusMaster (abstract QObject) holds:
  - the complete ReadRegister / WriteRegister enums with addresses
  - float32 Big/Big decode helpers (memcpy pattern, matches pymodbus
    BinaryPayloadDecoder byteorder=Endian.Big wordorder=Endian.Big)
  - all last-polled cached values accessible via simple getters

TrydanModbusTcpMaster (concrete, Étape 1) wraps libnymea-modbus
ModbusTcpMaster.  Each register read is a distinct FC3(addr, 2)
transaction via a recursive runReadSequence/doNextRead pattern —
never a block read, because the V2C firmware's register windows
overlap (0x0BC2 and 0x0BC3 each span 2 registers, so a range read
starting at 0x0BC2 for ≥3 registers returns garbage for the second
value).  Writes use FC6 (single uint16, not float).

writeCompleted(quint16 address, bool success) carries the originating
register address so that concurrent background writes (e.g. PauseDynamic
from the conflict manager firing during an action) do not interfere with
action handlers waiting on a specific register's acknowledgement.

0x177E (Dynamic) is intentionally absent from WriteRegister: writing it
to 0 silences ChargePower telemetry even though the charger keeps running.
cf. evcc charger/trydan.go and github.com/evcc-io/evcc/issues/28047.

Étape 2 (RTU) will add TrydanModbusRtuMaster on the same interface;
no changes to plugin or register logic will be required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:49:11 +02:00
8f017dbb91 Initial commit V2C Trydan 2026-06-07 07:50:35 +02:00
368fc174bf Revert "Reapply "remove empty placeholder json (plugin en cours)""
This reverts commit d09db4fa1643fbd609517e4d7ad86c624728c800.
2026-06-02 15:22:42 +02:00
d09db4fa16 Reapply "remove empty placeholder json (plugin en cours)"
This reverts commit 1699e56f55fafc46bc84258ead447ffdace75190.
2026-06-02 15:17:48 +02:00
1699e56f55 Revert "remove empty placeholder json (plugin en cours)"
This reverts commit 67836a883770a723c4fb5ef7eb5822d2d5485461.
2026-06-02 15:16:49 +02:00
67836a8837 remove empty placeholder json (plugin en cours) 2026-06-02 15:05:40 +02:00
Patrick Schurig
c82b00ddfa abbterra: communicationTimeoutReadback en 1er niveau (update) pour generer la methode reclamee par applyTimeoutSetting 2026-06-01 09:58:37 +02:00
Patrick Schurig
e0582d1fff abbterra: restaure le .h original (declaration applyTimeoutSetting manquait, .cpp et .h desynchronises) 2026-06-01 09:45:31 +02:00
Patrick Schurig
8086e80f20 abbterra: WO -> RW sur les 4 registres de commande (generateur 1.15 ne gere pas WO pur en 1er niveau) 2026-06-01 09:42:29 +02:00
Patrick Schurig
653418ce30 abbterra: abbterra-registers.json) 2026-06-01 09:39:24 +02:00
Patrick Schurig
95177a417a abbterra: readSchedule:write sur les WO (cle presente, valeur neutre -> ni KeyError ni updateXxxCommand) 2026-06-01 09:16:06 +02:00
Patrick Schurig
09dd698cf6 abbterra: restaure pilotage (cpp+json upstream) + registers.json sans readSchedule vide (compat generateur 1.15) 2026-06-01 09:09:22 +02:00
Patrick Schurig
47257a6a29 abbb2x: registers.json avec cle registers au 1er niveau (vrai contenu, 11 registres) 2026-06-01 08:52:12 +02:00
Patrick Schurig
14a2ae89c1 abbb2x: registers.json - cle 'registers' au 1er niveau (fix KeyError generateur) 2026-06-01 08:48:50 +02:00
Patrick Schurig
3cbce56454 abbterra: retire les registres WO pour build sur generateur 1.15 (lecture seule)
- abbterra-registers.json : suppression des 4 registres WO
  (chargingCurrentLimitCommand, socketLockCommand, startStopChargingSession,
  communicationTimeoutCommand) incompatibles avec le generateur libnymea-modbus 1.15
- integrationpluginabbterra.cpp/.h : suppression de executeAction + applyTimeoutSetting
- integrationpluginabbterra.json : power + maxChargingCurrent passes en lecture seule
  (writable/displayNameAction retires)
La borne sera reconnue et lira les mesures ; le pilotage sera rebranche
quand le generateur 1.16 sera disponible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 08:29:36 +02:00
Patrick Schurig
c26b0336bd Ajout plugin ABB Terra AC (vendoring upstream experimental-silo)
- abbterra/ copié depuis nymea-plugins-modbus experimental-silo (a652793)
- paquet nymea-plugin-abbterra (nom upstream, vendoring tracé dans VENDORED.md)
- 3 paquets binaires => .install par paquet
- abbterra ajouté à FORKED_PLUGINS du mirror (exclusion upstream)
- changelog 1.15.0+etm3
2026-06-01 07:38:51 +02:00
da2ce5253d Initial commit ABB meter B21/B23/B24 2026-06-01 07:05:07 +02:00
Patrick Schurig
79d11acbea debian: retire le .install (qmake6 installe deja la .so, cas paquet unique comme openmeteo) 2026-05-31 18:09:34 +02:00
Patrick Schurig
52008bf8a0 debian: packaging powersync-plugin-eastron (fix nom .install, format changelog/rules) 2026-05-31 18:07:28 +02:00
Patrick Schurig
1b1c47c231 modbus: retire les .depends vers les sous-projets libnymea-modbus/sunspec (lib systeme) 2026-05-31 17:49:31 +02:00
Patrick Schurig
19d982c2e7 modbus: retire le SUBDIRS local (dossiers elagues), gitignore artefacts 2026-05-31 17:47:26 +02:00
Patrick Schurig
228a30e585 modbus: build eastron via paquets systeme (pkgconfig nymea-modbus + modbus-tool.pri), nettoyage .pro 2026-05-31 17:43:28 +02:00
Patrick Schurig
b1da668036 Initial init etm-powersync 2026-05-31 11:50:55 +02:00
0e440ca25b Initial commit 2026-05-31 11:30:06 +02:00