Skip to content

Commit 2eb3466

Browse files
springfall2008claudegithub-actions[bot]
authored
feat(car): raise the maximum supported cars from 4 to 8 (#4545)
* feat(car): raise the maximum supported cars from 4 to 8 Some users have more than 4 EVs/chargers registered (e.g. multiple Octopus Intelligent devices), which the previous 4-car ceiling (CONFIG_ITEMS entities, KERNEL_MAX_CARS) rejected outright by clamping num_cars down. Doubling the limit to 8 needs three things kept in lockstep: the shared PREDBAT_MAX_CARS constant, the C++ kernel's PK_MAX_CARS array size (with PK_PARITY_REVISION/ KERNEL_PARITY_REVISION bumped 5 -> 6, on top of the ABI 4 already on main, so a stale 4-car binary is safely rejected rather than corrupting the ctypes struct layout), and the matching ctypes.Structure array sizes on the Python side (previously hardcoded to 4 independently of KERNEL_MAX_CARS - this mismatch caused a segfault before the fix). - const.py: PREDBAT_MAX_CARS 4 -> 8 - prediction_kernel.cpp / .py: PK_MAX_CARS/KERNEL_MAX_CARS -> 8, parity revision bumped 5 -> 6, ctypes array fields now reference KERNEL_MAX_CARS instead of a hardcoded 4 (car_charging_limit, car_charging_soc, car_soc_next) - config.py: added car_charging_rate_4..7 and car_charging_manual_soc(_kwh)_4..7 CONFIG_ITEMS entries so cars 4-7 have HA entities to configure - Regression tests for the new ceiling: kernel parity, 8-car config resolution via the real config_index, MockConfigProvider car rate defaults, and reset_inverter's car_charging_limit/soc arrays sized from PREDBAT_MAX_CARS instead of a hardcoded 4 (Copilot review catch) Kernel binaries are intentionally not included - CI's kernel-binaries job cross-builds and auto-commits all 6 platform binaries once this merges. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: update prediction kernel binaries for all platforms --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 87cb016 commit 2eb3466

13 files changed

Lines changed: 178 additions & 16 deletions

‎apps/predbat/config.py‎

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,58 @@
322322
"enable": "num_cars",
323323
"enable_condition": "num_cars > 3",
324324
},
325+
{
326+
"name": "car_charging_rate_4",
327+
"friendly_name": "Car charging rate (Car 4)",
328+
"type": "input_number",
329+
"min": 1,
330+
"max": 24,
331+
"step": 0.10,
332+
"unit": "kW",
333+
"icon": "mdi:ev-station",
334+
"default": 7.4,
335+
"enable": "num_cars",
336+
"enable_condition": "num_cars > 4",
337+
},
338+
{
339+
"name": "car_charging_rate_5",
340+
"friendly_name": "Car charging rate (Car 5)",
341+
"type": "input_number",
342+
"min": 1,
343+
"max": 24,
344+
"step": 0.10,
345+
"unit": "kW",
346+
"icon": "mdi:ev-station",
347+
"default": 7.4,
348+
"enable": "num_cars",
349+
"enable_condition": "num_cars > 5",
350+
},
351+
{
352+
"name": "car_charging_rate_6",
353+
"friendly_name": "Car charging rate (Car 6)",
354+
"type": "input_number",
355+
"min": 1,
356+
"max": 24,
357+
"step": 0.10,
358+
"unit": "kW",
359+
"icon": "mdi:ev-station",
360+
"default": 7.4,
361+
"enable": "num_cars",
362+
"enable_condition": "num_cars > 6",
363+
},
364+
{
365+
"name": "car_charging_rate_7",
366+
"friendly_name": "Car charging rate (Car 7)",
367+
"type": "input_number",
368+
"min": 1,
369+
"max": 24,
370+
"step": 0.10,
371+
"unit": "kW",
372+
"icon": "mdi:ev-station",
373+
"default": 7.4,
374+
"enable": "num_cars",
375+
"enable_condition": "num_cars > 7",
376+
},
325377
{
326378
"name": "car_charging_loss",
327379
"friendly_name": "Car charging loss",
@@ -741,6 +793,90 @@
741793
"default": 0.0,
742794
"restore": False,
743795
},
796+
{
797+
"name": "car_charging_manual_soc_4",
798+
"friendly_name": "Car charging manual SOC (Car 4)",
799+
"type": "switch",
800+
"default": False,
801+
"enable": "num_cars",
802+
"enable_condition": "num_cars > 4",
803+
},
804+
{
805+
"name": "car_charging_manual_soc_kwh_4",
806+
"friendly_name": "Car manual SOC kWh (Car 4)",
807+
"type": "input_number",
808+
"min": 0,
809+
"max": 100,
810+
"step": 0.01,
811+
"unit": "kWh",
812+
"icon": "mdi:ev-station",
813+
"enable": "car_charging_manual_soc_4",
814+
"default": 0.0,
815+
"restore": False,
816+
},
817+
{
818+
"name": "car_charging_manual_soc_5",
819+
"friendly_name": "Car charging manual SOC (Car 5)",
820+
"type": "switch",
821+
"default": False,
822+
"enable": "num_cars",
823+
"enable_condition": "num_cars > 5",
824+
},
825+
{
826+
"name": "car_charging_manual_soc_kwh_5",
827+
"friendly_name": "Car manual SOC kWh (Car 5)",
828+
"type": "input_number",
829+
"min": 0,
830+
"max": 100,
831+
"step": 0.01,
832+
"unit": "kWh",
833+
"icon": "mdi:ev-station",
834+
"enable": "car_charging_manual_soc_5",
835+
"default": 0.0,
836+
"restore": False,
837+
},
838+
{
839+
"name": "car_charging_manual_soc_6",
840+
"friendly_name": "Car charging manual SOC (Car 6)",
841+
"type": "switch",
842+
"default": False,
843+
"enable": "num_cars",
844+
"enable_condition": "num_cars > 6",
845+
},
846+
{
847+
"name": "car_charging_manual_soc_kwh_6",
848+
"friendly_name": "Car manual SOC kWh (Car 6)",
849+
"type": "input_number",
850+
"min": 0,
851+
"max": 100,
852+
"step": 0.01,
853+
"unit": "kWh",
854+
"icon": "mdi:ev-station",
855+
"enable": "car_charging_manual_soc_6",
856+
"default": 0.0,
857+
"restore": False,
858+
},
859+
{
860+
"name": "car_charging_manual_soc_7",
861+
"friendly_name": "Car charging manual SOC (Car 7)",
862+
"type": "switch",
863+
"default": False,
864+
"enable": "num_cars",
865+
"enable_condition": "num_cars > 7",
866+
},
867+
{
868+
"name": "car_charging_manual_soc_kwh_7",
869+
"friendly_name": "Car manual SOC kWh (Car 7)",
870+
"type": "input_number",
871+
"min": 0,
872+
"max": 100,
873+
"step": 0.01,
874+
"unit": "kWh",
875+
"icon": "mdi:ev-station",
876+
"enable": "car_charging_manual_soc_7",
877+
"default": 0.0,
878+
"restore": False,
879+
},
744880
{
745881
"name": "octopus_intelligent_charging",
746882
"friendly_name": "Octopus Intelligent Charging",

‎apps/predbat/const.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
INVERTER_MAX_RETRY_REST = 5 # Maximum number of retries for inverter REST commands
4141
INVERTER_REST_TIMEOUT = 10 # Seconds to wait for a REST response before giving up (local network call, should be fast)
4242
INVERTER_QUICK_UPDATE_SECONDS = 120 # Minimum seconds between quick inverter data updates
43-
PREDBAT_MAX_CARS = 4 # Matches PK_MAX_CARS in prediction_kernel.cpp and the car_charging_rate_0..3 config items - the hard ceiling on num_cars
43+
PREDBAT_MAX_CARS = 8 # Matches PK_MAX_CARS in prediction_kernel.cpp and the car_charging_rate/_1../_7 config items - the hard ceiling on num_cars
4444

4545
# 240v x 100 amps x 3 phases / 1000 to kW / 60 minutes in an hour is the maximum kWh in a 1 minute period
4646
MAX_INCREMENT = 240 * 100 * 3 / 1000 / 60

‎apps/predbat/prediction_kernel.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
// falling back. Bumping makes the loader reject it and use the Python engine, which is the whole
4444
// point of the check.
4545
#define PK_ABI_VERSION 4
46-
#define PK_PARITY_REVISION 5
47-
#define PK_MAX_CARS 4
46+
#define PK_PARITY_REVISION 6
47+
#define PK_MAX_CARS 8
4848
#define PK_RUN_EVERY 5 // const.py RUN_EVERY
4949

5050
namespace {

‎apps/predbat/prediction_kernel.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
# Expected ABI/parity revisions of the shared library (see prediction_kernel.cpp)
3434
KERNEL_ABI_VERSION = 4
35-
KERNEL_PARITY_REVISION = 5
35+
KERNEL_PARITY_REVISION = 6
3636

3737
# Maximum number of cars supported by the kernel (PK_MAX_CARS in prediction_kernel.cpp)
3838
KERNEL_MAX_CARS = PREDBAT_MAX_CARS
@@ -97,8 +97,8 @@ class PkContext(ctypes.Structure):
9797
("export_today_now", ctypes.c_double),
9898
("iboost_today", ctypes.c_double),
9999
("car_charging_loss", ctypes.c_double),
100-
("car_charging_limit", ctypes.c_double * 4),
101-
("car_charging_soc", ctypes.c_double * 4),
100+
("car_charging_limit", ctypes.c_double * KERNEL_MAX_CARS),
101+
("car_charging_soc", ctypes.c_double * KERNEL_MAX_CARS),
102102
("iboost_max_energy", ctypes.c_double),
103103
("iboost_max_power", ctypes.c_double),
104104
("iboost_min_power", ctypes.c_double),
@@ -169,7 +169,7 @@ class PkResult(ctypes.Structure):
169169
("metric_keep", ctypes.c_double),
170170
("final_iboost", ctypes.c_double),
171171
("final_carbon_g", ctypes.c_double),
172-
("car_soc_next", ctypes.c_double * 4),
172+
("car_soc_next", ctypes.c_double * KERNEL_MAX_CARS),
173173
("iboost_next", ctypes.c_double),
174174
("soc_min_minute", ctypes.c_int32),
175175
("car_soc_next_valid", ctypes.c_int32),
16 Bytes
Binary file not shown.
-32 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
80 Bytes
Binary file not shown.
64 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)