etm-powersync-plugins-modbus/v2c/integrationpluginv2c.json
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

188 lines
8.2 KiB
JSON

{
"name": "V2C",
"displayName": "V2C Trydan",
"id": "f0692725-650a-47c3-a673-172f077768f1",
"paramTypes": [],
"vendors": [
{
"id": "56c3e7bb-2db8-4002-b799-9e21530e6fb9",
"name": "v2c",
"displayName": "V2C",
"thingClasses": [
{
"id": "b2a12873-4d11-444e-a4ad-914907491eb4",
"name": "trydan",
"displayName": "Trydan",
"interfaces": [
"evcharger",
"connectable",
"networkdevice"
],
"createMethods": [
"discovery",
"user"
],
"discoveryType": "weak",
"paramTypes": [
{
"id": "2fe17a54-b24b-4868-ae99-36a4627e6827",
"name": "macAddress",
"displayName": "MAC address",
"type": "QString",
"inputType": "MacAddress",
"defaultValue": "",
"readOnly": true
},
{
"id": "c0cdfa64-7054-4486-809e-3b7f257a3aab",
"name": "address",
"displayName": "Host address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": ""
},
{
"id": "bd9987a4-4c45-49c8-ab67-8c80705ec109",
"name": "hostName",
"displayName": "Host name",
"type": "QString",
"inputType": "TextLine",
"defaultValue": ""
},
{
"id": "d096bfe1-5861-456f-915c-f1f2c65b03b3",
"name": "port",
"displayName": "Modbus TCP port",
"type": "uint",
"defaultValue": 502
}
],
"settingsTypes": [
{
"id": "a47f20bd-dfdc-4827-ab7d-05d7819f316e",
"name": "suspendInternalOptimizer",
"displayName": "Suspend internal V2C optimizer when HEMS takes control",
"type": "bool",
"defaultValue": true
}
],
"stateTypes": [
{
"id": "47054399-82fb-4e12-9bb6-9ae3e4fd4f78",
"name": "connected",
"displayName": "Connected",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "f38c313c-47cc-4791-8a8f-64c1af852af6",
"name": "pluggedIn",
"displayName": "Plugged in",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "6d003e09-d30d-4a1c-9e2d-9e838b61dc5d",
"name": "charging",
"displayName": "Charging",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "eb3253a2-7518-4e76-9c06-1bb133d5037d",
"name": "power",
"displayName": "Charging enabled",
"displayNameAction": "Set charging enabled",
"type": "bool",
"defaultValue": true,
"writable": true
},
{
"id": "deb8826d-4f91-42b8-89cf-16feb6425e0e",
"name": "maxChargingCurrent",
"displayName": "Maximum charging current",
"displayNameAction": "Set maximum charging current",
"type": "double",
"unit": "Ampere",
"minValue": 6,
"maxValue": 32,
"stepSize": 1,
"defaultValue": 16,
"writable": true
},
{
"id": "2570c0d7-f9a5-4e24-9242-6d79253004ec",
"name": "currentPower",
"displayName": "Charging power",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "d11a392f-387d-4f10-9050-e25198d2c584",
"name": "housePower",
"displayName": "House power (CT clamp)",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "37e706fb-fa3c-484a-b776-b50aa5de5c61",
"name": "solarPower",
"displayName": "Solar power (PV seen by charger)",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "9a9e6afb-a641-409f-9705-c16776db3a29",
"name": "slaveError",
"displayName": "Charger error code",
"type": "uint",
"defaultValue": 0
},
{
"id": "fe616e45-39cb-4be3-8426-9d27c7a64f06",
"name": "chargeEnergy",
"displayName": "Session energy (diagnostic)",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0
},
{
"id": "71773194-ebd3-4ded-893c-14bfef9b1854",
"name": "intensity",
"displayName": "Current charging current",
"type": "double",
"unit": "Ampere",
"defaultValue": 0,
"cached": false
},
{
"id": "3c86495b-74da-440e-98a5-825678e35c1a",
"name": "conflictDetected",
"displayName": "Internal V2C optimizer active",
"type": "bool",
"defaultValue": false
},
{
"id": "006dd018-9c6a-4a4e-a7b2-eb965d27ca92",
"name": "optimizerSuspended",
"displayName": "Internal optimizer suspended by HEMS",
"type": "bool",
"defaultValue": false
}
],
"actionTypes": []
}
]
}
]
}