From db845733db71d5cad83edc807cece71cec584adb Mon Sep 17 00:00:00 2001 From: Fredrik Ahlgren Date: Fri, 30 Oct 2026 08:50:39 +0100 Subject: [PATCH 1/3] test(tesla_wall_connector): idle unit must report 0 W The vitals of an idle Gen 3 (no car, contactor open, 0 V on every leg, 0.8 A and 0.6 A of sensor offset) give 184 W today: the same number a field connection test showed. Adds the idle, car-waiting and charging cases. Fails on 0.1.1. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Fredrik Ahlgren --- .../lua_harness/test_tesla_wall_connector.lua | 116 ++++++++++++++++++ drivers/tests/test_tesla_wall_connector.py | 12 ++ 2 files changed, 128 insertions(+) create mode 100644 drivers/tests/lua_harness/test_tesla_wall_connector.lua create mode 100644 drivers/tests/test_tesla_wall_connector.py diff --git a/drivers/tests/lua_harness/test_tesla_wall_connector.lua b/drivers/tests/lua_harness/test_tesla_wall_connector.lua new file mode 100644 index 0000000..9bbbd69 --- /dev/null +++ b/drivers/tests/lua_harness/test_tesla_wall_connector.lua @@ -0,0 +1,116 @@ +-- Tesla Wall Connector Gen 3: charge power comes only through a closed +-- contactor with a car on the cable, from measured volts and amps. +dofile("drivers/tests/lua_harness/host_mock.lua") + +local BASE = "http://wc.test" + +local function poll(vitals, config) + host.reset() + host._http_responses[BASE .. "/api/1/version"] = + '{"serial_number":"TWC-SN-1","part_number":"1529455-02-J","firmware_version":"24.36.3"}' + host._http_responses[BASE .. "/api/1/vitals"] = vitals + dofile("drivers/lua/tesla_wall_connector.lua") + config = config or {} + config.base_url = BASE + driver_init(config) + assert(driver_poll() == 5000) + local ev = host._emitted.ev + assert(ev and #ev == 1, "one ev reading per poll") + return ev[1] +end + +local function near(got, want, tol, label) + assert(math.abs(got - want) <= tol, + string.format("%s: got %.1f W, want %.1f W", label, got, want)) +end + +-- A real idle unit: no car, contactor open, 0 V on every output leg, and +-- 0.8 A and 0.6 A of sensor offset on legs A and B. The connection test +-- read EV 184 W, which is grid_v × vehicle_current_a (230 V × 0.8 A). +-- The report did not show those two fields; these are the values that +-- give 184 W. +local idle = [[{"contactor_closed":false,"vehicle_connected":false,"session_s":0, +"grid_v":230.0,"grid_hz":50.01,"vehicle_current_a":0.8, +"currentA_a":0.8,"currentB_a":0.6,"currentC_a":0.0,"currentN_a":0.0, +"voltageA_v":0.0,"voltageB_v":0.0,"voltageC_v":0.0, +"relay_coil_v":11.9,"pcba_temp_c":21.3,"handle_temp_c":14.2,"mcu_temp_c":27.5, +"uptime_s":86400,"input_thermopile_uv":-210,"prox_v":0.0, +"pilot_high_v":11.9,"pilot_low_v":11.9,"session_energy_wh":0.0, +"config_status":5,"evse_state":1,"current_alerts":[]}]] +local r = poll(idle) +assert(r.w == 0, "idle unit with open contactor reported " .. tostring(r.w) .. " W") +assert(r.connected == false and r.charging == false) +assert(host._metrics.ev_power_w.value == 0, "ev_power_w must match w") +-- The raw sensor reading stays visible as a diagnostic. +assert(host._metrics.ev_l1_a.value == 0.8 and host._metrics.ev_l1_v.value == 0) + +-- A larger offset sample (the diagram showed 284 W) is still no load. +r = poll((idle:gsub('"vehicle_current_a":0.8', '"vehicle_current_a":1.2') + :gsub('"grid_v":230.0', '"grid_v":236.7'))) +assert(r.w == 0, "offset sample reported " .. tostring(r.w) .. " W") + +-- A reply without contactor_closed: no car means no charge power. +r = poll('{"vehicle_connected":false,"evse_state":1,"grid_v":230,"vehicle_current_a":0.8,' + .. '"currentA_a":0.8,"currentB_a":0.6,"voltageA_v":0,"voltageB_v":0,"voltageC_v":0}') +assert(r.w == 0 and r.charging == false) + +-- Car on the cable, contactor open (waiting or finished): still no load. +r = poll((idle:gsub('"vehicle_connected":false', '"vehicle_connected":true') + :gsub('"evse_state":1', '"evse_state":9'))) +assert(r.w == 0, "open contactor with car reported " .. tostring(r.w) .. " W") +assert(r.connected == true and r.charging == false) + +-- The EVSE state alone does not close the contactor. +r = poll((idle:gsub('"vehicle_connected":false', '"vehicle_connected":true') + :gsub('"evse_state":1', '"evse_state":11'))) +assert(r.w == 0 and r.charging == false, "state 11 with open contactor") + +-- No car on the cable: nothing reaches a car, whatever the contactor says. +r = poll((idle:gsub('"contactor_closed":false', '"contactor_closed":true'))) +assert(r.w == 0 and r.charging == false, "closed contactor without a car") + +-- Split-phase installs use grid_v × vehicle_current_a: the same gate holds. +r = poll(idle, { split_phase = true }) +assert(r.w == 0, "split-phase idle reported " .. tostring(r.w) .. " W") + +-- Three-phase charging: the sum of measured V × A on each leg, positive. +local charging3 = [[{"contactor_closed":true,"vehicle_connected":true,"session_s":1800, +"grid_v":230.0,"grid_hz":50.0,"vehicle_current_a":16.0, +"currentA_a":16.1,"currentB_a":16.0,"currentC_a":16.2,"currentN_a":0.3, +"voltageA_v":230.0,"voltageB_v":229.5,"voltageC_v":230.4, +"session_energy_wh":4200.0,"config_status":5,"evse_state":11,"current_alerts":[]}]] +r = poll(charging3) +-- 230×16.1 + 229.5×16.0 + 230.4×16.2 = 11107.48 +near(r.w, 11107.48, 0.01, "three-phase charging") +assert(r.connected == true and r.charging == true and r.phases == 3) +assert(r.session_wh == 4200) +assert(host._metrics.ev_power_w.value == r.w) + +-- One-phase car: unused legs sit at a few volts and do not count. +r = poll([[{"contactor_closed":true,"vehicle_connected":true,"grid_v":230, +"vehicle_current_a":16,"currentA_a":16,"currentB_a":0.2,"currentC_a":0.1, +"voltageA_v":230,"voltageB_v":2.4,"voltageC_v":1.8,"session_energy_wh":100,"evse_state":11}]]) +near(r.w, 3680, 0.01, "one-phase charging") +assert(r.phases == 1 and r.charging == true) + +-- Contactor closed but no usable leg voltage: the contactor joins the +-- car to the grid, so the measured grid voltage applies. +r = poll([[{"contactor_closed":true,"vehicle_connected":true,"grid_v":230, +"vehicle_current_a":10,"voltageA_v":2,"voltageB_v":1,"voltageC_v":0, +"session_energy_wh":1,"evse_state":11}]]) +near(r.w, 2300, 0.01, "closed contactor, grid voltage") + +-- Split-phase charging: grid_v × vehicle_current_a. +r = poll([[{"contactor_closed":true,"vehicle_connected":true,"grid_v":240, +"vehicle_current_a":32,"currentA_a":40,"currentB_a":40, +"voltageA_v":120,"voltageB_v":120,"session_energy_wh":50,"evse_state":11}]], + { split_phase = true }) +near(r.w, 7680, 0.01, "split-phase charging") + +-- A reply without contactor_closed falls back to the EVSE state. +r = poll([[{"vehicle_connected":true,"evse_state":11,"grid_v":230,"vehicle_current_a":16, +"currentA_a":16,"voltageA_v":230,"voltageB_v":0,"voltageC_v":0}]]) +near(r.w, 3680, 0.01, "no contactor field, charging state") +assert(r.charging == true) + +print("tesla_wall_connector: all cases pass") diff --git a/drivers/tests/test_tesla_wall_connector.py b/drivers/tests/test_tesla_wall_connector.py new file mode 100644 index 0000000..e697309 --- /dev/null +++ b/drivers/tests/test_tesla_wall_connector.py @@ -0,0 +1,12 @@ +"""Tesla Wall Connector reports charge power only through a closed contactor.""" +from pathlib import Path +import subprocess + + +def test_tesla_wall_connector_charge_power(): + root = Path(__file__).resolve().parents[2] + result = subprocess.run( + [str(root / "lua55"), "drivers/tests/lua_harness/test_tesla_wall_connector.lua"], + cwd=root, text=True, capture_output=True, check=False, + ) + assert result.returncode == 0, result.stdout + result.stderr From fe86a7aca040041400e76a7fbca14e1090aaee50 Mon Sep 17 00:00:00 2001 From: Fredrik Ahlgren Date: Fri, 30 Oct 2026 08:50:39 +0100 Subject: [PATCH 2/3] fix(tesla_wall_connector): no charge power through an open contactor (0.1.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the contactor open the output legs read 0 V, the leg sum was 0, and the driver fell back to grid_v × vehicle_current_a. That multiplied the grid-side voltage by the sensor offset of an idle unit and reported about 180-290 W to a car that was not there. Charge power and charging now need a closed contactor and a car on the cable. A reply without contactor_closed falls back to the EVSE charging states. Power through a closed contactor is unchanged. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Fredrik Ahlgren --- CHANGELOG.md | 15 +++++++++++++++ SUPPORT_STATUS.md | 4 ++-- devices.yaml | 2 +- drivers/lua/tesla_wall_connector.lua | 28 ++++++++++++++++++---------- index.yaml | 6 +++--- manifests/tesla_wall_connector.yaml | 6 +++--- support-status.json | 2 +- 7 files changed, 43 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 399f8d4..eb83bc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ ## [Unreleased] +### tesla_wall_connector 0.1.2 + +An idle Wall Connector no longer shows charge power. With no car on the +cable and the contactor open, the unit reads 0 V on its output legs and +about 0.5–1 A of sensor offset. The leg sum was 0, so the driver fell back to +`grid_v × vehicle_current_a`, the voltage on the grid side of the open +contactor times the offset, and reported about 180–290 W to a car that was +not there. Charge power is now 0 W while the contactor is open or +`vehicle_connected` is false. `charging` follows the same rule. A reply +without `contactor_closed` falls back to the EVSE charging states. Power +through a closed contactor is unchanged. + +FTW's bundled copy does not move with this commit. Bump the pin and run +`scripts/sync-bundled-drivers.sh`. + ### sungrow 1.6.1 An inverter that drops off the network no longer loses registers or its diff --git a/SUPPORT_STATUS.md b/SUPPORT_STATUS.md index 2285c99..21ef6ad 100644 --- a/SUPPORT_STATUS.md +++ b/SUPPORT_STATUS.md @@ -164,8 +164,8 @@ Catalog source is not proof that a target can install or run a driver. | tesla_cloud | 0.2.0 | blixt-l1 | not_assessed | — | not_recorded | not_assessed | | tesla_vehicle | 0.2.6 | ftw-core | not_assessed | — | not_recorded | not_assessed | | tesla_vehicle | 0.2.6 | blixt-l1 | not_assessed | — | not_recorded | not_assessed | -| tesla_wall_connector | 0.1.1 | ftw-core | not_assessed | — | not_recorded | not_assessed | -| tesla_wall_connector | 0.1.1 | blixt-l1 | not_assessed | — | not_recorded | not_assessed | +| tesla_wall_connector | 0.1.2 | ftw-core | not_assessed | — | not_recorded | not_assessed | +| tesla_wall_connector | 0.1.2 | blixt-l1 | not_assessed | — | not_recorded | not_assessed | | teslamate_vehicle | 0.2.0 | ftw-core | not_assessed | — | not_recorded | not_assessed | | teslamate_vehicle | 0.2.0 | blixt-l1 | not_assessed | — | not_recorded | not_assessed | | tibber | 1.1.2 | ftw-core | not_assessed | — | not_recorded | not_assessed | diff --git a/devices.yaml b/devices.yaml index 723fd8b..cb0dd99 100644 --- a/devices.yaml +++ b/devices.yaml @@ -1852,7 +1852,7 @@ manufacturers: protocols: - protocol: http driver: "tesla_wall_connector" - version: "0.1.1" + version: "0.1.2" ders: [ev] control: false firmware_versions: "" diff --git a/drivers/lua/tesla_wall_connector.lua b/drivers/lua/tesla_wall_connector.lua index 985895e..f01ba91 100644 --- a/drivers/lua/tesla_wall_connector.lua +++ b/drivers/lua/tesla_wall_connector.lua @@ -26,7 +26,7 @@ DRIVER = { id = "tesla_wall_connector", name = "Tesla Wall Connector", manufacturer = "Tesla", - version = "0.1.1", + version = "0.1.2", protocols = { "http" }, capabilities = { "ev" }, read_only = true, @@ -131,7 +131,18 @@ local function phase_power(v, a) return v * a end -local function charge_power(vitals) +-- The contactor joins the car to the grid. While it is open, or no car is +-- on the cable, no current reaches a car: an idle unit still reads about +-- 0.5–1 A of sensor offset on its legs, and that is not load. A reply +-- without contactor_closed falls back to the EVSE state. +local function contactor_closed(state, vitals) + if vitals.vehicle_connected == false then return false end + if vitals.contactor_closed ~= nil then return vitals.contactor_closed == true end + return state == EVSE_CHARGE_LOW or state == EVSE_CHARGE_FULL +end + +local function charge_power(state, vitals) + if not contactor_closed(state, vitals) then return 0 end if split_phase then local w = (as_number(vitals.grid_v) or 0) * (as_number(vitals.vehicle_current_a) or 0) if w < 0 then w = 0 end @@ -140,6 +151,8 @@ local function charge_power(vitals) local w = phase_power(vitals.voltageA_v, vitals.currentA_a) + phase_power(vitals.voltageB_v, vitals.currentB_a) + phase_power(vitals.voltageC_v, vitals.currentC_a) + -- No usable leg voltage: with the contactor closed the car sees the + -- measured grid voltage. if w <= 0 then local fallback = (as_number(vitals.grid_v) or 0) * (as_number(vitals.vehicle_current_a) or 0) if fallback > 0 then w = fallback end @@ -156,16 +169,11 @@ local function active_phases(vitals) return n end -local function is_charging(state, vitals) - if state == EVSE_CHARGE_LOW or state == EVSE_CHARGE_FULL then return true end - return vitals.contactor_closed == true -end - local function is_connected(state, vitals) if vitals.vehicle_connected == true then return true end return state == EVSE_CONNECTED or state == EVSE_READY or state == EVSE_HANDSHAKE or state == EVSE_FINISHED - or state == EVSE_WAIT_CAR or is_charging(state, vitals) + or state == EVSE_WAIT_CAR or contactor_closed(state, vitals) end function driver_init(config) @@ -209,10 +217,10 @@ function driver_poll() end local state = as_number(vitals.evse_state) or EVSE_IDLE - local power_w = charge_power(vitals) + local power_w = charge_power(state, vitals) local session_wh = as_number(vitals.session_energy_wh) or 0 local connected = is_connected(state, vitals) - local charging = is_charging(state, vitals) + local charging = contactor_closed(state, vitals) local phases = active_phases(vitals) host.emit("ev", { diff --git a/index.yaml b/index.yaml index b701ed6..e8cc24b 100644 --- a/index.yaml +++ b/index.yaml @@ -739,14 +739,14 @@ drivers: size_bytes: 10096 sha256: "d1b8a2cbea20630dcab3cb306df0f6924b0f9ad7f31bcf367fb1d41ca7833350" - name: "tesla_wall_connector" - version: "0.1.1" + version: "0.1.2" tier: community protocol: http connectivity: local ders: [ev] control: false - size_bytes: 8162 - sha256: "66b6313eda4f68a92a7cc55931baa2408cb6cbb249145f7e6005456fb821d284" + size_bytes: 8716 + sha256: "46990348a27e84a0f749f4f54db3ef0201806fc9034efe69571f8e5a65dd877b" - name: "teslamate_vehicle" version: "0.2.0" tier: community diff --git a/manifests/tesla_wall_connector.yaml b/manifests/tesla_wall_connector.yaml index f037d4f..8886b3d 100644 --- a/manifests/tesla_wall_connector.yaml +++ b/manifests/tesla_wall_connector.yaml @@ -1,5 +1,5 @@ name: "tesla_wall_connector" -version: "0.1.1" +version: "0.1.2" tier: community author: "Sourceful Labs AB" protocol: http @@ -15,9 +15,9 @@ tested_devices: notes: "Tesla Wall Connector Gen 3 on the LAN. Observation only; no local current setpoint." min_driver_version: "0.1.0" min_host_version: "2.0.0" -size_bytes: 8162 +size_bytes: 8716 dkb_id: "tesla_wall_connector" -sha256: "66b6313eda4f68a92a7cc55931baa2408cb6cbb249145f7e6005456fb821d284" +sha256: "46990348a27e84a0f749f4f54db3ef0201806fc9034efe69571f8e5a65dd877b" signature: "" bytecode_sha256: "" diff --git a/support-status.json b/support-status.json index 4f61ce4..aa17b7b 100644 --- a/support-status.json +++ b/support-status.json @@ -1682,7 +1682,7 @@ }, { "catalog_source": true, - "catalog_version": "0.1.1", + "catalog_version": "0.1.2", "driver_id": "tesla_wall_connector", "targets": { "blixt-l1": { From 060567a97478ff0333684977c4f7f5fbe296d16e Mon Sep 17 00:00:00 2001 From: Fredrik Ahlgren Date: Fri, 30 Oct 2026 08:56:06 +0100 Subject: [PATCH 3/3] fix(tesla_wall_connector): contactor field first, grid_v only with no leg voltage From review. An explicit contactor_closed now goes before vehicle_connected: the contactor is the physical switch, and a status flag should not cancel volts and amps measured through it. With that order, a closed contactor with every leg at 0 V would still have reached the grid_v fallback, so the fallback now runs only when the reply has no leg voltage at all. A leg that reads 0 V carries no power, as in Home Assistant's own calculation. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Fredrik Ahlgren --- CHANGELOG.md | 18 +++++++---- drivers/lua/tesla_wall_connector.lua | 32 +++++++++---------- .../lua_harness/test_tesla_wall_connector.lua | 26 +++++++++++---- index.yaml | 4 +-- manifests/tesla_wall_connector.yaml | 4 +-- 5 files changed, 51 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb83bc8..f50a0d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,18 @@ An idle Wall Connector no longer shows charge power. With no car on the cable and the contactor open, the unit reads 0 V on its output legs and about 0.5–1 A of sensor offset. The leg sum was 0, so the driver fell back to -`grid_v × vehicle_current_a`, the voltage on the grid side of the open -contactor times the offset, and reported about 180–290 W to a car that was -not there. Charge power is now 0 W while the contactor is open or -`vehicle_connected` is false. `charging` follows the same rule. A reply -without `contactor_closed` falls back to the EVSE charging states. Power -through a closed contactor is unchanged. +`grid_v × vehicle_current_a`: the voltage on the grid side of the open +contactor times the offset. One field report showed 184 W, and 284 W in the +flow diagram, with no car present. That fallback is the only path in 0.1.1 +that gives power with every leg at 0 V. + +- Charge power and `charging` now need a closed contactor. A reply without + `contactor_closed` falls back to `vehicle_connected` and then the EVSE + charging states. +- The `grid_v` fallback now runs only when the reply has no leg voltage at + all. A leg that reads 0 V carries no power. +- Split-phase installs and measured leg power through a closed contactor do + not change. FTW's bundled copy does not move with this commit. Bump the pin and run `scripts/sync-bundled-drivers.sh`. diff --git a/drivers/lua/tesla_wall_connector.lua b/drivers/lua/tesla_wall_connector.lua index f01ba91..85e67b7 100644 --- a/drivers/lua/tesla_wall_connector.lua +++ b/drivers/lua/tesla_wall_connector.lua @@ -131,33 +131,31 @@ local function phase_power(v, a) return v * a end --- The contactor joins the car to the grid. While it is open, or no car is --- on the cable, no current reaches a car: an idle unit still reads about --- 0.5–1 A of sensor offset on its legs, and that is not load. A reply --- without contactor_closed falls back to the EVSE state. +-- The contactor joins the car to the grid and closes only with a car on +-- the cable. While it is open no current reaches a car: an idle unit still +-- reads about 0.5–1 A of sensor offset on its legs, and that is not load. +-- A reply without contactor_closed falls back to vehicle_connected and +-- the EVSE state. local function contactor_closed(state, vitals) - if vitals.vehicle_connected == false then return false end if vitals.contactor_closed ~= nil then return vitals.contactor_closed == true end + if vitals.vehicle_connected == false then return false end return state == EVSE_CHARGE_LOW or state == EVSE_CHARGE_FULL end local function charge_power(state, vitals) if not contactor_closed(state, vitals) then return 0 end - if split_phase then - local w = (as_number(vitals.grid_v) or 0) * (as_number(vitals.vehicle_current_a) or 0) - if w < 0 then w = 0 end - return w + local grid_w = (as_number(vitals.grid_v) or 0) * (as_number(vitals.vehicle_current_a) or 0) + if grid_w < 0 then grid_w = 0 end + if split_phase then return grid_w end + -- The legs are measured on the car side of the contactor. Only a reply + -- with no leg voltage at all falls back to the grid voltage; a leg that + -- reads 0 V carries no power. + if vitals.voltageA_v == nil and vitals.voltageB_v == nil and vitals.voltageC_v == nil then + return grid_w end - local w = phase_power(vitals.voltageA_v, vitals.currentA_a) + return phase_power(vitals.voltageA_v, vitals.currentA_a) + phase_power(vitals.voltageB_v, vitals.currentB_a) + phase_power(vitals.voltageC_v, vitals.currentC_a) - -- No usable leg voltage: with the contactor closed the car sees the - -- measured grid voltage. - if w <= 0 then - local fallback = (as_number(vitals.grid_v) or 0) * (as_number(vitals.vehicle_current_a) or 0) - if fallback > 0 then w = fallback end - end - return w end local function active_phases(vitals) diff --git a/drivers/tests/lua_harness/test_tesla_wall_connector.lua b/drivers/tests/lua_harness/test_tesla_wall_connector.lua index 9bbbd69..1831c39 100644 --- a/drivers/tests/lua_harness/test_tesla_wall_connector.lua +++ b/drivers/tests/lua_harness/test_tesla_wall_connector.lua @@ -44,7 +44,8 @@ assert(host._metrics.ev_power_w.value == 0, "ev_power_w must match w") -- The raw sensor reading stays visible as a diagnostic. assert(host._metrics.ev_l1_a.value == 0.8 and host._metrics.ev_l1_v.value == 0) --- A larger offset sample (the diagram showed 284 W) is still no load. +-- A larger offset sample is still no load. The same report's flow +-- diagram showed 284 W; 1.2 A × 236.7 V is one pair that gives it. r = poll((idle:gsub('"vehicle_current_a":0.8', '"vehicle_current_a":1.2') :gsub('"grid_v":230.0', '"grid_v":236.7'))) assert(r.w == 0, "offset sample reported " .. tostring(r.w) .. " W") @@ -65,9 +66,10 @@ r = poll((idle:gsub('"vehicle_connected":false', '"vehicle_connected":true') :gsub('"evse_state":1', '"evse_state":11'))) assert(r.w == 0 and r.charging == false, "state 11 with open contactor") --- No car on the cable: nothing reaches a car, whatever the contactor says. +-- A contactor that says closed while every leg reads 0 V: the grid +-- voltage must not stand in for the 0 V the charger measured. r = poll((idle:gsub('"contactor_closed":false', '"contactor_closed":true'))) -assert(r.w == 0 and r.charging == false, "closed contactor without a car") +assert(r.w == 0, "closed contactor at 0 V reported " .. tostring(r.w) .. " W") -- Split-phase installs use grid_v × vehicle_current_a: the same gate holds. r = poll(idle, { split_phase = true }) @@ -93,12 +95,24 @@ r = poll([[{"contactor_closed":true,"vehicle_connected":true,"grid_v":230, near(r.w, 3680, 0.01, "one-phase charging") assert(r.phases == 1 and r.charging == true) --- Contactor closed but no usable leg voltage: the contactor joins the --- car to the grid, so the measured grid voltage applies. +-- Power measured through a closed contactor counts even if the cable +-- flag disagrees: a status flag does not cancel measured volts and amps. +r = poll([[{"contactor_closed":true,"vehicle_connected":false,"grid_v":230, +"vehicle_current_a":16,"currentA_a":16,"voltageA_v":230,"voltageB_v":0,"voltageC_v":0, +"session_energy_wh":1,"evse_state":11}]]) +near(r.w, 3680, 0.01, "closed contactor, measured leg") + +-- Contactor closed, legs read about 0 V: no power from grid_v. r = poll([[{"contactor_closed":true,"vehicle_connected":true,"grid_v":230, "vehicle_current_a":10,"voltageA_v":2,"voltageB_v":1,"voltageC_v":0, "session_energy_wh":1,"evse_state":11}]]) -near(r.w, 2300, 0.01, "closed contactor, grid voltage") +assert(r.w == 0, "legs at 0 V reported " .. tostring(r.w) .. " W") + +-- A reply with no leg voltage at all: the car sees the measured grid +-- voltage through the closed contactor. +r = poll([[{"contactor_closed":true,"vehicle_connected":true,"grid_v":230, +"vehicle_current_a":10,"session_energy_wh":1,"evse_state":11}]]) +near(r.w, 2300, 0.01, "no leg voltages, grid voltage") -- Split-phase charging: grid_v × vehicle_current_a. r = poll([[{"contactor_closed":true,"vehicle_connected":true,"grid_v":240, diff --git a/index.yaml b/index.yaml index e8cc24b..e66f04c 100644 --- a/index.yaml +++ b/index.yaml @@ -745,8 +745,8 @@ drivers: connectivity: local ders: [ev] control: false - size_bytes: 8716 - sha256: "46990348a27e84a0f749f4f54db3ef0201806fc9034efe69571f8e5a65dd877b" + size_bytes: 8786 + sha256: "c365aad2a1216fad870b7dadb32e87feed75627714eeb8160f22cec4db651a80" - name: "teslamate_vehicle" version: "0.2.0" tier: community diff --git a/manifests/tesla_wall_connector.yaml b/manifests/tesla_wall_connector.yaml index 8886b3d..a2870a1 100644 --- a/manifests/tesla_wall_connector.yaml +++ b/manifests/tesla_wall_connector.yaml @@ -15,9 +15,9 @@ tested_devices: notes: "Tesla Wall Connector Gen 3 on the LAN. Observation only; no local current setpoint." min_driver_version: "0.1.0" min_host_version: "2.0.0" -size_bytes: 8716 +size_bytes: 8786 dkb_id: "tesla_wall_connector" -sha256: "46990348a27e84a0f749f4f54db3ef0201806fc9034efe69571f8e5a65dd877b" +sha256: "c365aad2a1216fad870b7dadb32e87feed75627714eeb8160f22cec4db651a80" signature: "" bytecode_sha256: ""