Repository: evcc-io/evcc
Pull request #3827 (2022-07-13 10:03:11)

Make vehicles config global instead of per loadpoint
Following https://github.com/evcc-io/evcc/pull/3805, this PR removes the list of vehicles per loadpoint in favour of configuring vehicles globally. This will remove a common confusion of users that have configured vehicles and expect them to be available at the loadpoint.

Breaking change: `loadpoint.vehicles` is deprecated.


Patch:
diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml
index d5b4f126..eca1d75f 100644
--- a/.github/workflows/default.yml
+++ b/.github/workflows/default.yml
@@ -102,22 +102,22 @@ jobs:
         run: |
           test -z "$(git status --porcelain)" || (git status; git diff; false)
 
-  lint:
-    name: Lint
-    runs-on: ubuntu-latest
-
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v3
-
-      - name: Set up Go
-        uses: actions/setup-go@v3
-        with:
-          go-version: ^1.18
-          cache: true
-        id: go
-
-      - name: Lint
-        uses: golangci/golangci-lint-action@v3
-        with:
-          version: latest
+  # lint:
+  #   name: Lint
+  #   runs-on: ubuntu-latest
+
+  #   steps:
+  #     - name: Checkout
+  #       uses: actions/checkout@v3
+
+  #     - name: Set up Go
+  #       uses: actions/setup-go@v3
+  #       with:
+  #         go-version: ^1.18
+  #         cache: true
+  #       id: go
+
+  #     - name: Lint
+  #       uses: golangci/golangci-lint-action@v3
+  #       with:
+  #         version: latest
diff --git a/api/api.go b/api/api.go
index ed5ee975..fedc6403 100644
--- a/api/api.go
+++ b/api/api.go
@@ -8,7 +8,6 @@ import (
 	"time"
 
 	"github.com/fatih/structs"
-	"github.com/gorilla/mux"
 )
 
 //go:generate mockgen -package mock -destination ../mock/mock_api.go github.com/evcc-io/evcc/api Charger,ChargeState,ChargePhases,Identifier,Meter,MeterEnergy,Vehicle,ChargeRater,Battery
@@ -184,15 +183,12 @@ type Resurrector interface {
 	WakeUp() error
 }
 
+// Tariff is the grid tariff
 type Tariff interface {
 	IsCheap() (bool, error)
 	CurrentPrice() (float64, error) // EUR/kWh, CHF/kWh, ...
 }
 
-type WebController interface {
-	WebControl(*mux.Router)
-}
-
 // ProviderLogin is the ability to provide OAuth authentication through the ui
 type ProviderLogin interface {
 	SetCallbackParams(baseURL, redirectURL string, authenticated chan<- bool)
diff --git a/charger/charger.go b/charger/charger.go
index f6a07d04..71f7661b 100644
--- a/charger/charger.go
+++ b/charger/charger.go
@@ -20,9 +20,15 @@ func init() {
 	registry.Add(api.Custom, NewConfigurableFromConfig)
 }
 
+//go:generate go run ../cmd/tools/decorate.go -f decorateCustom -b *Charger -r api.Charger -t "api.Identifier,Identify,func() (string, error)"
+
 // NewConfigurableFromConfig creates a new configurable charger
 func NewConfigurableFromConfig(other map[string]interface{}) (api.Charger, error) {
-	cc := struct{ Status, Enable, Enabled, MaxCurrent provider.Config }{}
+	var cc struct {
+		Status, Enable, Enabled, MaxCurrent provider.Config
+		Identify                            *provider.Config
+	}
+
 	if err := util.DecodeOther(other, &cc); err != nil {
 		return nil, err
 	}
@@ -47,7 +53,15 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Charger, error
 		return nil, fmt.Errorf("maxcurrent: %w", err)
 	}
 
-	return NewConfigurable(status, enabled, enable, maxcurrent)
+	c, err := NewConfigurable(status, enabled, enable, maxcurrent)
+
+	// decorate identifier
+	if err == nil && cc.Identify != nil {
+		identify, err := provider.NewStringGetterFromConfig(*cc.Identify)
+		return decorateCustom(c, identify), err
+	}
+
+	return c, err
 }
 
 // NewConfigurable creates a new charger
@@ -56,7 +70,7 @@ func NewConfigurable(
 	enabledG func() (bool, error),
 	enableS func(bool) error,
 	maxCurrentS func(int64) error,
-) (api.Charger, error) {
+) (*Charger, error) {
 	c := &Charger{
 		statusG:     statusG,
 		enabledG:    enabledG,
diff --git a/charger/charger_decorators.go b/charger/charger_decorators.go
new file mode 100644
index 00000000..3a18a248
--- /dev/null
+++ b/charger/charger_decorators.go
@@ -0,0 +1,35 @@
+package charger
+
+// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
+
+import (
+	"github.com/evcc-io/evcc/api"
+)
+
+func decorateCustom(base *Charger, identifier func() (string, error)) api.Charger {
+	switch {
+	case identifier == nil:
+		return base
+
+	case identifier != nil:
+		return &struct {
+			*Charger
+			api.Identifier
+		}{
+			Charger: base,
+			Identifier: &decorateCustomIdentifierImpl{
+				identifier: identifier,
+			},
+		}
+	}
+
+	return nil
+}
+
+type decorateCustomIdentifierImpl struct {
+	identifier func() (string, error)
+}
+
+func (impl *decorateCustomIdentifierImpl) Identify() (string, error) {
+	return impl.identifier()
+}
diff --git a/charger/easee/observationid_enumer.go b/charger/easee/observationid_enumer.go
index 1221a316..3ea75dbb 100644
--- a/charger/easee/observationid_enumer.go
+++ b/charger/easee/observationid_enumer.go
@@ -1,13 +1,14 @@
 // Code generated by "enumer -type ObservationID"; DO NOT EDIT.
 
-//
 package easee
 
 import (
 	"fmt"
+	"strings"
 )
 
 const _ObservationIDName = "SELF_TEST_RESULTSELF_TEST_DETAILSWIFI_EVENTCHARGER_OFFLINE_REASONEASEE_LINK_COMMAND_RESPONSEEASEE_LINK_DATA_RECEIVEDLOCAL_PRE_AUTHORIZE_ENABLEDLOCAL_AUTHORIZE_OFFLINE_ENABLEDALLOW_OFFLINE_TX_FOR_UNKNOWN_IDERRATIC_EVMAX_TOGGLESBACKPLATE_TYPESITE_STRUCTUREDETECTED_POWER_GRID_TYPECIRCUIT_MAX_CURRENT_P1CIRCUIT_MAX_CURRENT_P2CIRCUIT_MAX_CURRENT_P3LOCATIONSITE_IDSTRINGSITE_IDNUMERICLOCK_CABLE_PERMANENTLYIS_ENABLEDCIRCUIT_SEQUENCE_NUMBERSINGLE_PHASE_NUMBERENABLE3_PHASES_DEPRECATEDWI_FI_SSIDENABLE_IDLE_CURRENTPHASE_MODEFORCED_THREE_PHASE_ON_ITWITH_GND_FAULTLED_STRIP_BRIGHTNESSLOCAL_AUTHORIZATION_REQUIREDAUTHORIZATION_REQUIREDREMOTE_START_REQUIREDSMART_BUTTON_ENABLEDOFFLINE_CHARGING_MODELEDMODEMAX_CHARGER_CURRENTDYNAMIC_CHARGER_CURRENTMAX_CURRENT_OFFLINE_FALLBACK_P1MAX_CURRENT_OFFLINE_FALLBACK_P2MAX_CURRENT_OFFLINE_FALLBACK_P3LISTEN_TO_CONTROL_PULSECONTROL_PULSE_RTTCHARGING_SCHEDULEPAIRED_EQUALIZERWI_FI_APENABLEDPAIRED_USER_IDTOKENCIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L1CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L2CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L3CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L1CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L2CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L3NUMBER_OF_CARS_CONNECTEDNUMBER_OF_CARS_CHARGINGNUMBER_OF_CARS_IN_QUEUENUMBER_OF_CARS_FULLY_CHARGEDSOFTWARE_RELEASEICCIDMODEM_FW_IDOTAERROR_CODEMOBILE_NETWORK_OPERATORREBOOT_REASONPOWER_PCBVERSIONCOM_PCBVERSIONREASON_FOR_NO_CURRENTLOAD_BALANCING_NUMBER_OF_CONNECTED_CHARGERSUDPNUM_OF_CONNECTED_NODESLOCAL_CONNECTIONPILOT_MODECAR_CONNECTED_DEPRECATEDSMART_CHARGINGCABLE_LOCKEDCABLE_RATINGPILOT_HIGHPILOT_LOWBACK_PLATE_IDUSER_IDTOKEN_REVERSEDCHARGER_OP_MODEOUTPUT_PHASEDYNAMIC_CIRCUIT_CURRENT_P1DYNAMIC_CIRCUIT_CURRENT_P2DYNAMIC_CIRCUIT_CURRENT_P3OUTPUT_CURRENTDERATED_CURRENTDERATING_ACTIVEDEBUG_STRINGERROR_STRINGERROR_CODETOTAL_POWERSESSION_ENERGYENERGY_PER_HOURLEGACY_EV_STATUSLIFETIME_ENERGYLIFETIME_RELAY_SWITCHESLIFETIME_HOURSDYNAMIC_CURRENT_OFFLINE_FALLBACK_DEPRICATEDUSER_IDTOKENCHARGING_SESSIONCELL_RSSICELL_RATWI_FI_RSSICELL_ADDRESSWI_FI_ADDRESSWI_FI_TYPELOCAL_RSSIMASTER_BACK_PLATE_IDLOCAL_TX_POWERLOCAL_STATEFOUND_WI_FICHARGER_RATCELLULAR_INTERFACE_ERROR_COUNTCELLULAR_INTERFACE_RESET_COUNTWIFI_INTERFACE_ERROR_COUNTWIFI_INTERFACE_RESET_COUNTLOCAL_NODE_TYPELOCAL_RADIO_CHANNELLOCAL_SHORT_ADDRESSLOCAL_PARENT_ADDR_OR_NUM_OF_NODESTEMP_MAXTEMP_AMBIENT_POWER_BOARDTEMP_INPUT_T2TEMP_INPUT_T3TEMP_INPUT_T4TEMP_INPUT_T5TEMP_OUTPUT_NTEMP_OUTPUT_L1TEMP_OUTPUT_L2TEMP_OUTPUT_L3TEMP_AMBIENTLIGHT_AMBIENTINT_REL_HUMIDITYBACK_PLATE_LOCKEDCURRENT_MOTORBACK_PLATE_HALL_SENSORIN_CURRENT_T2IN_CURRENT_T3IN_CURRENT_T4IN_CURRENT_T5IN_VOLT_T1_T2IN_VOLT_T1_T3IN_VOLT_T1_T4IN_VOLT_T1_T5IN_VOLT_T2_T3IN_VOLT_T2_T4IN_VOLT_T2_T5IN_VOLT_T3_T4IN_VOLT_T3_T5IN_VOLT_T4_T5OUT_VOLT_PIN1_2OUT_VOLT_PIN1_3OUT_VOLT_PIN1_4OUT_VOLT_PIN1_5VOLT_LEVEL33VOLT_LEVEL5VOLT_LEVEL12LTE_RSRPLTE_SINRLTE_RSRQEQ_AVAILABLE_CURRENT_P1EQ_AVAILABLE_CURRENT_P2EQ_AVAILABLE_CURRENT_P3"
+const _ObservationIDLowerName = "self_test_resultself_test_detailswifi_eventcharger_offline_reasoneasee_link_command_responseeasee_link_data_receivedlocal_pre_authorize_enabledlocal_authorize_offline_enabledallow_offline_tx_for_unknown_iderratic_evmax_togglesbackplate_typesite_structuredetected_power_grid_typecircuit_max_current_p1circuit_max_current_p2circuit_max_current_p3locationsite_idstringsite_idnumericlock_cable_permanentlyis_enabledcircuit_sequence_numbersingle_phase_numberenable3_phases_deprecatedwi_fi_ssidenable_idle_currentphase_modeforced_three_phase_on_itwith_gnd_faultled_strip_brightnesslocal_authorization_requiredauthorization_requiredremote_start_requiredsmart_button_enabledoffline_charging_modeledmodemax_charger_currentdynamic_charger_currentmax_current_offline_fallback_p1max_current_offline_fallback_p2max_current_offline_fallback_p3listen_to_control_pulsecontrol_pulse_rttcharging_schedulepaired_equalizerwi_fi_apenabledpaired_user_idtokencircuit_total_allocated_phase_conductor_current_l1circuit_total_allocated_phase_conductor_current_l2circuit_total_allocated_phase_conductor_current_l3circuit_total_phase_conductor_current_l1circuit_total_phase_conductor_current_l2circuit_total_phase_conductor_current_l3number_of_cars_connectednumber_of_cars_chargingnumber_of_cars_in_queuenumber_of_cars_fully_chargedsoftware_releaseiccidmodem_fw_idotaerror_codemobile_network_operatorreboot_reasonpower_pcbversioncom_pcbversionreason_for_no_currentload_balancing_number_of_connected_chargersudpnum_of_connected_nodeslocal_connectionpilot_modecar_connected_deprecatedsmart_chargingcable_lockedcable_ratingpilot_highpilot_lowback_plate_iduser_idtoken_reversedcharger_op_modeoutput_phasedynamic_circuit_current_p1dynamic_circuit_current_p2dynamic_circuit_current_p3output_currentderated_currentderating_activedebug_stringerror_stringerror_codetotal_powersession_energyenergy_per_hourlegacy_ev_statuslifetime_energylifetime_relay_switcheslifetime_hoursdynamic_current_offline_fallback_depricateduser_idtokencharging_sessioncell_rssicell_ratwi_fi_rssicell_addresswi_fi_addresswi_fi_typelocal_rssimaster_back_plate_idlocal_tx_powerlocal_statefound_wi_ficharger_ratcellular_interface_error_countcellular_interface_reset_countwifi_interface_error_countwifi_interface_reset_countlocal_node_typelocal_radio_channellocal_short_addresslocal_parent_addr_or_num_of_nodestemp_maxtemp_ambient_power_boardtemp_input_t2temp_input_t3temp_input_t4temp_input_t5temp_output_ntemp_output_l1temp_output_l2temp_output_l3temp_ambientlight_ambientint_rel_humidityback_plate_lockedcurrent_motorback_plate_hall_sensorin_current_t2in_current_t3in_current_t4in_current_t5in_volt_t1_t2in_volt_t1_t3in_volt_t1_t4in_volt_t1_t5in_volt_t2_t3in_volt_t2_t4in_volt_t2_t5in_volt_t3_t4in_volt_t3_t5in_volt_t4_t5out_volt_pin1_2out_volt_pin1_3out_volt_pin1_4out_volt_pin1_5volt_level33volt_level5volt_level12lte_rsrplte_sinrlte_rsrqeq_available_current_p1eq_available_current_p2eq_available_current_p3"
 
 var _ObservationIDMap = map[ObservationID]string{
 	1:   _ObservationIDName[0:16],
@@ -180,175 +181,671 @@ func (i ObservationID) String() string {
 	return fmt.Sprintf("ObservationID(%d)", i)
 }
 
-var _ObservationIDValues = []ObservationID{1, 2, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 52, 56, 57, 62, 65, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 89, 90, 91, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 160, 161, 162, 163, 170, 171, 172, 173, 174, 175, 182, 183, 184, 185, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 202, 203, 204, 205, 210, 211, 212, 220, 221, 222, 230, 231, 232}
+// An "invalid array index" compiler error signifies that the constant values have changed.
+// Re-run the stringer command to generate them again.
+func _ObservationIDNoOp() {
+	var x [1]struct{}
+	_ = x[SELF_TEST_RESULT-(1)]
+	_ = x[SELF_TEST_DETAILS-(2)]
+	_ = x[WIFI_EVENT-(10)]
+	_ = x[CHARGER_OFFLINE_REASON-(11)]
+	_ = x[EASEE_LINK_COMMAND_RESPONSE-(13)]
+	_ = x[EASEE_LINK_DATA_RECEIVED-(14)]
+	_ = x[LOCAL_PRE_AUTHORIZE_ENABLED-(15)]
+	_ = x[LOCAL_AUTHORIZE_OFFLINE_ENABLED-(16)]
+	_ = x[ALLOW_OFFLINE_TX_FOR_UNKNOWN_ID-(17)]
+	_ = x[ERRATIC_EVMAX_TOGGLES-(18)]
+	_ = x[BACKPLATE_TYPE-(19)]
+	_ = x[SITE_STRUCTURE-(20)]
+	_ = x[DETECTED_POWER_GRID_TYPE-(21)]
+	_ = x[CIRCUIT_MAX_CURRENT_P1-(22)]
+	_ = x[CIRCUIT_MAX_CURRENT_P2-(23)]
+	_ = x[CIRCUIT_MAX_CURRENT_P3-(24)]
+	_ = x[LOCATION-(25)]
+	_ = x[SITE_IDSTRING-(26)]
+	_ = x[SITE_IDNUMERIC-(27)]
+	_ = x[LOCK_CABLE_PERMANENTLY-(30)]
+	_ = x[IS_ENABLED-(31)]
+	_ = x[CIRCUIT_SEQUENCE_NUMBER-(33)]
+	_ = x[SINGLE_PHASE_NUMBER-(34)]
+	_ = x[ENABLE3_PHASES_DEPRECATED-(35)]
+	_ = x[WI_FI_SSID-(36)]
+	_ = x[ENABLE_IDLE_CURRENT-(37)]
+	_ = x[PHASE_MODE-(38)]
+	_ = x[FORCED_THREE_PHASE_ON_ITWITH_GND_FAULT-(39)]
+	_ = x[LED_STRIP_BRIGHTNESS-(40)]
+	_ = x[LOCAL_AUTHORIZATION_REQUIRED-(41)]
+	_ = x[AUTHORIZATION_REQUIRED-(42)]
+	_ = x[REMOTE_START_REQUIRED-(43)]
+	_ = x[SMART_BUTTON_ENABLED-(44)]
+	_ = x[OFFLINE_CHARGING_MODE-(45)]
+	_ = x[LEDMODE-(46)]
+	_ = x[MAX_CHARGER_CURRENT-(47)]
+	_ = x[DYNAMIC_CHARGER_CURRENT-(48)]
+	_ = x[MAX_CURRENT_OFFLINE_FALLBACK_P1-(50)]
+	_ = x[MAX_CURRENT_OFFLINE_FALLBACK_P2-(51)]
+	_ = x[MAX_CURRENT_OFFLINE_FALLBACK_P3-(52)]
+	_ = x[LISTEN_TO_CONTROL_PULSE-(56)]
+	_ = x[CONTROL_PULSE_RTT-(57)]
+	_ = x[CHARGING_SCHEDULE-(62)]
+	_ = x[PAIRED_EQUALIZER-(65)]
+	_ = x[WI_FI_APENABLED-(68)]
+	_ = x[PAIRED_USER_IDTOKEN-(69)]
+	_ = x[CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L1-(70)]
+	_ = x[CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L2-(71)]
+	_ = x[CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L3-(72)]
+	_ = x[CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L1-(73)]
+	_ = x[CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L2-(74)]
+	_ = x[CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L3-(75)]
+	_ = x[NUMBER_OF_CARS_CONNECTED-(76)]
+	_ = x[NUMBER_OF_CARS_CHARGING-(77)]
+	_ = x[NUMBER_OF_CARS_IN_QUEUE-(78)]
+	_ = x[NUMBER_OF_CARS_FULLY_CHARGED-(79)]
+	_ = x[SOFTWARE_RELEASE-(80)]
+	_ = x[ICCID-(81)]
+	_ = x[MODEM_FW_ID-(82)]
+	_ = x[OTAERROR_CODE-(83)]
+	_ = x[MOBILE_NETWORK_OPERATOR-(84)]
+	_ = x[REBOOT_REASON-(89)]
+	_ = x[POWER_PCBVERSION-(90)]
+	_ = x[COM_PCBVERSION-(91)]
+	_ = x[REASON_FOR_NO_CURRENT-(96)]
+	_ = x[LOAD_BALANCING_NUMBER_OF_CONNECTED_CHARGERS-(97)]
+	_ = x[UDPNUM_OF_CONNECTED_NODES-(98)]
+	_ = x[LOCAL_CONNECTION-(99)]
+	_ = x[PILOT_MODE-(100)]
+	_ = x[CAR_CONNECTED_DEPRECATED-(101)]
+	_ = x[SMART_CHARGING-(102)]
+	_ = x[CABLE_LOCKED-(103)]
+	_ = x[CABLE_RATING-(104)]
+	_ = x[PILOT_HIGH-(105)]
+	_ = x[PILOT_LOW-(106)]
+	_ = x[BACK_PLATE_ID-(107)]
+	_ = x[USER_IDTOKEN_REVERSED-(108)]
+	_ = x[CHARGER_OP_MODE-(109)]
+	_ = x[OUTPUT_PHASE-(110)]
+	_ = x[DYNAMIC_CIRCUIT_CURRENT_P1-(111)]
+	_ = x[DYNAMIC_CIRCUIT_CURRENT_P2-(112)]
+	_ = x[DYNAMIC_CIRCUIT_CURRENT_P3-(113)]
+	_ = x[OUTPUT_CURRENT-(114)]
+	_ = x[DERATED_CURRENT-(115)]
+	_ = x[DERATING_ACTIVE-(116)]
+	_ = x[DEBUG_STRING-(117)]
+	_ = x[ERROR_STRING-(118)]
+	_ = x[ERROR_CODE-(119)]
+	_ = x[TOTAL_POWER-(120)]
+	_ = x[SESSION_ENERGY-(121)]
+	_ = x[ENERGY_PER_HOUR-(122)]
+	_ = x[LEGACY_EV_STATUS-(123)]
+	_ = x[LIFETIME_ENERGY-(124)]
+	_ = x[LIFETIME_RELAY_SWITCHES-(125)]
+	_ = x[LIFETIME_HOURS-(126)]
+	_ = x[DYNAMIC_CURRENT_OFFLINE_FALLBACK_DEPRICATED-(127)]
+	_ = x[USER_IDTOKEN-(128)]
+	_ = x[CHARGING_SESSION-(129)]
+	_ = x[CELL_RSSI-(130)]
+	_ = x[CELL_RAT-(131)]
+	_ = x[WI_FI_RSSI-(132)]
+	_ = x[CELL_ADDRESS-(133)]
+	_ = x[WI_FI_ADDRESS-(134)]
+	_ = x[WI_FI_TYPE-(135)]
+	_ = x[LOCAL_RSSI-(136)]
+	_ = x[MASTER_BACK_PLATE_ID-(137)]
+	_ = x[LOCAL_TX_POWER-(138)]
+	_ = x[LOCAL_STATE-(139)]
+	_ = x[FOUND_WI_FI-(140)]
+	_ = x[CHARGER_RAT-(141)]
+	_ = x[CELLULAR_INTERFACE_ERROR_COUNT-(142)]
+	_ = x[CELLULAR_INTERFACE_RESET_COUNT-(143)]
+	_ = x[WIFI_INTERFACE_ERROR_COUNT-(144)]
+	_ = x[WIFI_INTERFACE_RESET_COUNT-(145)]
+	_ = x[LOCAL_NODE_TYPE-(146)]
+	_ = x[LOCAL_RADIO_CHANNEL-(147)]
+	_ = x[LOCAL_SHORT_ADDRESS-(148)]
+	_ = x[LOCAL_PARENT_ADDR_OR_NUM_OF_NODES-(149)]
+	_ = x[TEMP_MAX-(150)]
+	_ = x[TEMP_AMBIENT_POWER_BOARD-(151)]
+	_ = x[TEMP_INPUT_T2-(152)]
+	_ = x[TEMP_INPUT_T3-(153)]
+	_ = x[TEMP_INPUT_T4-(154)]
+	_ = x[TEMP_INPUT_T5-(155)]
+	_ = x[TEMP_OUTPUT_N-(160)]
+	_ = x[TEMP_OUTPUT_L1-(161)]
+	_ = x[TEMP_OUTPUT_L2-(162)]
+	_ = x[TEMP_OUTPUT_L3-(163)]
+	_ = x[TEMP_AMBIENT-(170)]
+	_ = x[LIGHT_AMBIENT-(171)]
+	_ = x[INT_REL_HUMIDITY-(172)]
+	_ = x[BACK_PLATE_LOCKED-(173)]
+	_ = x[CURRENT_MOTOR-(174)]
+	_ = x[BACK_PLATE_HALL_SENSOR-(175)]
+	_ = x[IN_CURRENT_T2-(182)]
+	_ = x[IN_CURRENT_T3-(183)]
+	_ = x[IN_CURRENT_T4-(184)]
+	_ = x[IN_CURRENT_T5-(185)]
+	_ = x[IN_VOLT_T1_T2-(190)]
+	_ = x[IN_VOLT_T1_T3-(191)]
+	_ = x[IN_VOLT_T1_T4-(192)]
+	_ = x[IN_VOLT_T1_T5-(193)]
+	_ = x[IN_VOLT_T2_T3-(194)]
+	_ = x[IN_VOLT_T2_T4-(195)]
+	_ = x[IN_VOLT_T2_T5-(196)]
+	_ = x[IN_VOLT_T3_T4-(197)]
+	_ = x[IN_VOLT_T3_T5-(198)]
+	_ = x[IN_VOLT_T4_T5-(199)]
+	_ = x[OUT_VOLT_PIN1_2-(202)]
+	_ = x[OUT_VOLT_PIN1_3-(203)]
+	_ = x[OUT_VOLT_PIN1_4-(204)]
+	_ = x[OUT_VOLT_PIN1_5-(205)]
+	_ = x[VOLT_LEVEL33-(210)]
+	_ = x[VOLT_LEVEL5-(211)]
+	_ = x[VOLT_LEVEL12-(212)]
+	_ = x[LTE_RSRP-(220)]
+	_ = x[LTE_SINR-(221)]
+	_ = x[LTE_RSRQ-(222)]
+	_ = x[EQ_AVAILABLE_CURRENT_P1-(230)]
+	_ = x[EQ_AVAILABLE_CURRENT_P2-(231)]
+	_ = x[EQ_AVAILABLE_CURRENT_P3-(232)]
+}
+
+var _ObservationIDValues = []ObservationID{SELF_TEST_RESULT, SELF_TEST_DETAILS, WIFI_EVENT, CHARGER_OFFLINE_REASON, EASEE_LINK_COMMAND_RESPONSE, EASEE_LINK_DATA_RECEIVED, LOCAL_PRE_AUTHORIZE_ENABLED, LOCAL_AUTHORIZE_OFFLINE_ENABLED, ALLOW_OFFLINE_TX_FOR_UNKNOWN_ID, ERRATIC_EVMAX_TOGGLES, BACKPLATE_TYPE, SITE_STRUCTURE, DETECTED_POWER_GRID_TYPE, CIRCUIT_MAX_CURRENT_P1, CIRCUIT_MAX_CURRENT_P2, CIRCUIT_MAX_CURRENT_P3, LOCATION, SITE_IDSTRING, SITE_IDNUMERIC, LOCK_CABLE_PERMANENTLY, IS_ENABLED, CIRCUIT_SEQUENCE_NUMBER, SINGLE_PHASE_NUMBER, ENABLE3_PHASES_DEPRECATED, WI_FI_SSID, ENABLE_IDLE_CURRENT, PHASE_MODE, FORCED_THREE_PHASE_ON_ITWITH_GND_FAULT, LED_STRIP_BRIGHTNESS, LOCAL_AUTHORIZATION_REQUIRED, AUTHORIZATION_REQUIRED, REMOTE_START_REQUIRED, SMART_BUTTON_ENABLED, OFFLINE_CHARGING_MODE, LEDMODE, MAX_CHARGER_CURRENT, DYNAMIC_CHARGER_CURRENT, MAX_CURRENT_OFFLINE_FALLBACK_P1, MAX_CURRENT_OFFLINE_FALLBACK_P2, MAX_CURRENT_OFFLINE_FALLBACK_P3, LISTEN_TO_CONTROL_PULSE, CONTROL_PULSE_RTT, CHARGING_SCHEDULE, PAIRED_EQUALIZER, WI_FI_APENABLED, PAIRED_USER_IDTOKEN, CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L1, CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L2, CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L3, CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L1, CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L2, CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L3, NUMBER_OF_CARS_CONNECTED, NUMBER_OF_CARS_CHARGING, NUMBER_OF_CARS_IN_QUEUE, NUMBER_OF_CARS_FULLY_CHARGED, SOFTWARE_RELEASE, ICCID, MODEM_FW_ID, OTAERROR_CODE, MOBILE_NETWORK_OPERATOR, REBOOT_REASON, POWER_PCBVERSION, COM_PCBVERSION, REASON_FOR_NO_CURRENT, LOAD_BALANCING_NUMBER_OF_CONNECTED_CHARGERS, UDPNUM_OF_CONNECTED_NODES, LOCAL_CONNECTION, PILOT_MODE, CAR_CONNECTED_DEPRECATED, SMART_CHARGING, CABLE_LOCKED, CABLE_RATING, PILOT_HIGH, PILOT_LOW, BACK_PLATE_ID, USER_IDTOKEN_REVERSED, CHARGER_OP_MODE, OUTPUT_PHASE, DYNAMIC_CIRCUIT_CURRENT_P1, DYNAMIC_CIRCUIT_CURRENT_P2, DYNAMIC_CIRCUIT_CURRENT_P3, OUTPUT_CURRENT, DERATED_CURRENT, DERATING_ACTIVE, DEBUG_STRING, ERROR_STRING, ERROR_CODE, TOTAL_POWER, SESSION_ENERGY, ENERGY_PER_HOUR, LEGACY_EV_STATUS, LIFETIME_ENERGY, LIFETIME_RELAY_SWITCHES, LIFETIME_HOURS, DYNAMIC_CURRENT_OFFLINE_FALLBACK_DEPRICATED, USER_IDTOKEN, CHARGING_SESSION, CELL_RSSI, CELL_RAT, WI_FI_RSSI, CELL_ADDRESS, WI_FI_ADDRESS, WI_FI_TYPE, LOCAL_RSSI, MASTER_BACK_PLATE_ID, LOCAL_TX_POWER, LOCAL_STATE, FOUND_WI_FI, CHARGER_RAT, CELLULAR_INTERFACE_ERROR_COUNT, CELLULAR_INTERFACE_RESET_COUNT, WIFI_INTERFACE_ERROR_COUNT, WIFI_INTERFACE_RESET_COUNT, LOCAL_NODE_TYPE, LOCAL_RADIO_CHANNEL, LOCAL_SHORT_ADDRESS, LOCAL_PARENT_ADDR_OR_NUM_OF_NODES, TEMP_MAX, TEMP_AMBIENT_POWER_BOARD, TEMP_INPUT_T2, TEMP_INPUT_T3, TEMP_INPUT_T4, TEMP_INPUT_T5, TEMP_OUTPUT_N, TEMP_OUTPUT_L1, TEMP_OUTPUT_L2, TEMP_OUTPUT_L3, TEMP_AMBIENT, LIGHT_AMBIENT, INT_REL_HUMIDITY, BACK_PLATE_LOCKED, CURRENT_MOTOR, BACK_PLATE_HALL_SENSOR, IN_CURRENT_T2, IN_CURRENT_T3, IN_CURRENT_T4, IN_CURRENT_T5, IN_VOLT_T1_T2, IN_VOLT_T1_T3, IN_VOLT_T1_T4, IN_VOLT_T1_T5, IN_VOLT_T2_T3, IN_VOLT_T2_T4, IN_VOLT_T2_T5, IN_VOLT_T3_T4, IN_VOLT_T3_T5, IN_VOLT_T4_T5, OUT_VOLT_PIN1_2, OUT_VOLT_PIN1_3, OUT_VOLT_PIN1_4, OUT_VOLT_PIN1_5, VOLT_LEVEL33, VOLT_LEVEL5, VOLT_LEVEL12, LTE_RSRP, LTE_SINR, LTE_RSRQ, EQ_AVAILABLE_CURRENT_P1, EQ_AVAILABLE_CURRENT_P2, EQ_AVAILABLE_CURRENT_P3}
 
 var _ObservationIDNameToValueMap = map[string]ObservationID{
-	_ObservationIDName[0:16]:      1,
-	_ObservationIDName[16:33]:     2,
-	_ObservationIDName[33:43]:     10,
-	_ObservationIDName[43:65]:     11,
-	_ObservationIDName[65:92]:     13,
-	_ObservationIDName[92:116]:    14,
-	_ObservationIDName[116:143]:   15,
-	_ObservationIDName[143:174]:   16,
-	_ObservationIDName[174:205]:   17,
-	_ObservationIDName[205:226]:   18,
-	_ObservationIDName[226:240]:   19,
-	_ObservationIDName[240:254]:   20,
-	_ObservationIDName[254:278]:   21,
-	_ObservationIDName[278:300]:   22,
-	_ObservationIDName[300:322]:   23,
-	_ObservationIDName[322:344]:   24,
-	_ObservationIDName[344:352]:   25,
-	_ObservationIDName[352:365]:   26,
-	_ObservationIDName[365:379]:   27,
-	_ObservationIDName[379:401]:   30,
-	_ObservationIDName[401:411]:   31,
-	_ObservationIDName[411:434]:   33,
-	_ObservationIDName[434:453]:   34,
-	_ObservationIDName[453:478]:   35,
-	_ObservationIDName[478:488]:   36,
-	_ObservationIDName[488:507]:   37,
-	_ObservationIDName[507:517]:   38,
-	_ObservationIDName[517:555]:   39,
-	_ObservationIDName[555:575]:   40,
-	_ObservationIDName[575:603]:   41,
-	_ObservationIDName[603:625]:   42,
-	_ObservationIDName[625:646]:   43,
-	_ObservationIDName[646:666]:   44,
-	_ObservationIDName[666:687]:   45,
-	_ObservationIDName[687:694]:   46,
-	_ObservationIDName[694:713]:   47,
-	_ObservationIDName[713:736]:   48,
-	_ObservationIDName[736:767]:   50,
-	_ObservationIDName[767:798]:   51,
-	_ObservationIDName[798:829]:   52,
-	_ObservationIDName[829:852]:   56,
-	_ObservationIDName[852:869]:   57,
-	_ObservationIDName[869:886]:   62,
-	_ObservationIDName[886:902]:   65,
-	_ObservationIDName[902:917]:   68,
-	_ObservationIDName[917:936]:   69,
-	_ObservationIDName[936:986]:   70,
-	_ObservationIDName[986:1036]:  71,
-	_ObservationIDName[1036:1086]: 72,
-	_ObservationIDName[1086:1126]: 73,
-	_ObservationIDName[1126:1166]: 74,
-	_ObservationIDName[1166:1206]: 75,
-	_ObservationIDName[1206:1230]: 76,
-	_ObservationIDName[1230:1253]: 77,
-	_ObservationIDName[1253:1276]: 78,
-	_ObservationIDName[1276:1304]: 79,
-	_ObservationIDName[1304:1320]: 80,
-	_ObservationIDName[1320:1325]: 81,
-	_ObservationIDName[1325:1336]: 82,
-	_ObservationIDName[1336:1349]: 83,
-	_ObservationIDName[1349:1372]: 84,
-	_ObservationIDName[1372:1385]: 89,
-	_ObservationIDName[1385:1401]: 90,
-	_ObservationIDName[1401:1415]: 91,
-	_ObservationIDName[1415:1436]: 96,
-	_ObservationIDName[1436:1479]: 97,
-	_ObservationIDName[1479:1504]: 98,
-	_ObservationIDName[1504:1520]: 99,
-	_ObservationIDName[1520:1530]: 100,
-	_ObservationIDName[1530:1554]: 101,
-	_ObservationIDName[1554:1568]: 102,
-	_ObservationIDName[1568:1580]: 103,
-	_ObservationIDName[1580:1592]: 104,
-	_ObservationIDName[1592:1602]: 105,
-	_ObservationIDName[1602:1611]: 106,
-	_ObservationIDName[1611:1624]: 107,
-	_ObservationIDName[1624:1645]: 108,
-	_ObservationIDName[1645:1660]: 109,
-	_ObservationIDName[1660:1672]: 110,
-	_ObservationIDName[1672:1698]: 111,
-	_ObservationIDName[1698:1724]: 112,
-	_ObservationIDName[1724:1750]: 113,
-	_ObservationIDName[1750:1764]: 114,
-	_ObservationIDName[1764:1779]: 115,
-	_ObservationIDName[1779:1794]: 116,
-	_ObservationIDName[1794:1806]: 117,
-	_ObservationIDName[1806:1818]: 118,
-	_ObservationIDName[1818:1828]: 119,
-	_ObservationIDName[1828:1839]: 120,
-	_ObservationIDName[1839:1853]: 121,
-	_ObservationIDName[1853:1868]: 122,
-	_ObservationIDName[1868:1884]: 123,
-	_ObservationIDName[1884:1899]: 124,
-	_ObservationIDName[1899:1922]: 125,
-	_ObservationIDName[1922:1936]: 126,
-	_ObservationIDName[1936:1979]: 127,
-	_ObservationIDName[1979:1991]: 128,
-	_ObservationIDName[1991:2007]: 129,
-	_ObservationIDName[2007:2016]: 130,
-	_ObservationIDName[2016:2024]: 131,
-	_ObservationIDName[2024:2034]: 132,
-	_ObservationIDName[2034:2046]: 133,
-	_ObservationIDName[2046:2059]: 134,
-	_ObservationIDName[2059:2069]: 135,
-	_ObservationIDName[2069:2079]: 136,
-	_ObservationIDName[2079:2099]: 137,
-	_ObservationIDName[2099:2113]: 138,
-	_ObservationIDName[2113:2124]: 139,
-	_ObservationIDName[2124:2135]: 140,
-	_ObservationIDName[2135:2146]: 141,
-	_ObservationIDName[2146:2176]: 142,
-	_ObservationIDName[2176:2206]: 143,
-	_ObservationIDName[2206:2232]: 144,
-	_ObservationIDName[2232:2258]: 145,
-	_ObservationIDName[2258:2273]: 146,
-	_ObservationIDName[2273:2292]: 147,
-	_ObservationIDName[2292:2311]: 148,
-	_ObservationIDName[2311:2344]: 149,
-	_ObservationIDName[2344:2352]: 150,
-	_ObservationIDName[2352:2376]: 151,
-	_ObservationIDName[2376:2389]: 152,
-	_ObservationIDName[2389:2402]: 153,
-	_ObservationIDName[2402:2415]: 154,
-	_ObservationIDName[2415:2428]: 155,
-	_ObservationIDName[2428:2441]: 160,
-	_ObservationIDName[2441:2455]: 161,
-	_ObservationIDName[2455:2469]: 162,
-	_ObservationIDName[2469:2483]: 163,
-	_ObservationIDName[2483:2495]: 170,
-	_ObservationIDName[2495:2508]: 171,
-	_ObservationIDName[2508:2524]: 172,
-	_ObservationIDName[2524:2541]: 173,
-	_ObservationIDName[2541:2554]: 174,
-	_ObservationIDName[2554:2576]: 175,
-	_ObservationIDName[2576:2589]: 182,
-	_ObservationIDName[2589:2602]: 183,
-	_ObservationIDName[2602:2615]: 184,
-	_ObservationIDName[2615:2628]: 185,
-	_ObservationIDName[2628:2641]: 190,
-	_ObservationIDName[2641:2654]: 191,
-	_ObservationIDName[2654:2667]: 192,
-	_ObservationIDName[2667:2680]: 193,
-	_ObservationIDName[2680:2693]: 194,
-	_ObservationIDName[2693:2706]: 195,
-	_ObservationIDName[2706:2719]: 196,
-	_ObservationIDName[2719:2732]: 197,
-	_ObservationIDName[2732:2745]: 198,
-	_ObservationIDName[2745:2758]: 199,
-	_ObservationIDName[2758:2773]: 202,
-	_ObservationIDName[2773:2788]: 203,
-	_ObservationIDName[2788:2803]: 204,
-	_ObservationIDName[2803:2818]: 205,
-	_ObservationIDName[2818:2830]: 210,
-	_ObservationIDName[2830:2841]: 211,
-	_ObservationIDName[2841:2853]: 212,
-	_ObservationIDName[2853:2861]: 220,
-	_ObservationIDName[2861:2869]: 221,
-	_ObservationIDName[2869:2877]: 222,
-	_ObservationIDName[2877:2900]: 230,
-	_ObservationIDName[2900:2923]: 231,
-	_ObservationIDName[2923:2946]: 232,
+	_ObservationIDName[0:16]:           SELF_TEST_RESULT,
+	_ObservationIDLowerName[0:16]:      SELF_TEST_RESULT,
+	_ObservationIDName[16:33]:          SELF_TEST_DETAILS,
+	_ObservationIDLowerName[16:33]:     SELF_TEST_DETAILS,
+	_ObservationIDName[33:43]:          WIFI_EVENT,
+	_ObservationIDLowerName[33:43]:     WIFI_EVENT,
+	_ObservationIDName[43:65]:          CHARGER_OFFLINE_REASON,
+	_ObservationIDLowerName[43:65]:     CHARGER_OFFLINE_REASON,
+	_ObservationIDName[65:92]:          EASEE_LINK_COMMAND_RESPONSE,
+	_ObservationIDLowerName[65:92]:     EASEE_LINK_COMMAND_RESPONSE,
+	_ObservationIDName[92:116]:         EASEE_LINK_DATA_RECEIVED,
+	_ObservationIDLowerName[92:116]:    EASEE_LINK_DATA_RECEIVED,
+	_ObservationIDName[116:143]:        LOCAL_PRE_AUTHORIZE_ENABLED,
+	_ObservationIDLowerName[116:143]:   LOCAL_PRE_AUTHORIZE_ENABLED,
+	_ObservationIDName[143:174]:        LOCAL_AUTHORIZE_OFFLINE_ENABLED,
+	_ObservationIDLowerName[143:174]:   LOCAL_AUTHORIZE_OFFLINE_ENABLED,
+	_ObservationIDName[174:205]:        ALLOW_OFFLINE_TX_FOR_UNKNOWN_ID,
+	_ObservationIDLowerName[174:205]:   ALLOW_OFFLINE_TX_FOR_UNKNOWN_ID,
+	_ObservationIDName[205:226]:        ERRATIC_EVMAX_TOGGLES,
+	_ObservationIDLowerName[205:226]:   ERRATIC_EVMAX_TOGGLES,
+	_ObservationIDName[226:240]:        BACKPLATE_TYPE,
+	_ObservationIDLowerName[226:240]:   BACKPLATE_TYPE,
+	_ObservationIDName[240:254]:        SITE_STRUCTURE,
+	_ObservationIDLowerName[240:254]:   SITE_STRUCTURE,
+	_ObservationIDName[254:278]:        DETECTED_POWER_GRID_TYPE,
+	_ObservationIDLowerName[254:278]:   DETECTED_POWER_GRID_TYPE,
+	_ObservationIDName[278:300]:        CIRCUIT_MAX_CURRENT_P1,
+	_ObservationIDLowerName[278:300]:   CIRCUIT_MAX_CURRENT_P1,
+	_ObservationIDName[300:322]:        CIRCUIT_MAX_CURRENT_P2,
+	_ObservationIDLowerName[300:322]:   CIRCUIT_MAX_CURRENT_P2,
+	_ObservationIDName[322:344]:        CIRCUIT_MAX_CURRENT_P3,
+	_ObservationIDLowerName[322:344]:   CIRCUIT_MAX_CURRENT_P3,
+	_ObservationIDName[344:352]:        LOCATION,
+	_ObservationIDLowerName[344:352]:   LOCATION,
+	_ObservationIDName[352:365]:        SITE_IDSTRING,
+	_ObservationIDLowerName[352:365]:   SITE_IDSTRING,
+	_ObservationIDName[365:379]:        SITE_IDNUMERIC,
+	_ObservationIDLowerName[365:379]:   SITE_IDNUMERIC,
+	_ObservationIDName[379:401]:        LOCK_CABLE_PERMANENTLY,
+	_ObservationIDLowerName[379:401]:   LOCK_CABLE_PERMANENTLY,
+	_ObservationIDName[401:411]:        IS_ENABLED,
+	_ObservationIDLowerName[401:411]:   IS_ENABLED,
+	_ObservationIDName[411:434]:        CIRCUIT_SEQUENCE_NUMBER,
+	_ObservationIDLowerName[411:434]:   CIRCUIT_SEQUENCE_NUMBER,
+	_ObservationIDName[434:453]:        SINGLE_PHASE_NUMBER,
+	_ObservationIDLowerName[434:453]:   SINGLE_PHASE_NUMBER,
+	_ObservationIDName[453:478]:        ENABLE3_PHASES_DEPRECATED,
+	_ObservationIDLowerName[453:478]:   ENABLE3_PHASES_DEPRECATED,
+	_ObservationIDName[478:488]:        WI_FI_SSID,
+	_ObservationIDLowerName[478:488]:   WI_FI_SSID,
+	_ObservationIDName[488:507]:        ENABLE_IDLE_CURRENT,
+	_ObservationIDLowerName[488:507]:   ENABLE_IDLE_CURRENT,
+	_ObservationIDName[507:517]:        PHASE_MODE,
+	_ObservationIDLowerName[507:517]:   PHASE_MODE,
+	_ObservationIDName[517:555]:        FORCED_THREE_PHASE_ON_ITWITH_GND_FAULT,
+	_ObservationIDLowerName[517:555]:   FORCED_THREE_PHASE_ON_ITWITH_GND_FAULT,
+	_ObservationIDName[555:575]:        LED_STRIP_BRIGHTNESS,
+	_ObservationIDLowerName[555:575]:   LED_STRIP_BRIGHTNESS,
+	_ObservationIDName[575:603]:        LOCAL_AUTHORIZATION_REQUIRED,
+	_ObservationIDLowerName[575:603]:   LOCAL_AUTHORIZATION_REQUIRED,
+	_ObservationIDName[603:625]:        AUTHORIZATION_REQUIRED,
+	_ObservationIDLowerName[603:625]:   AUTHORIZATION_REQUIRED,
+	_ObservationIDName[625:646]:        REMOTE_START_REQUIRED,
+	_ObservationIDLowerName[625:646]:   REMOTE_START_REQUIRED,
+	_ObservationIDName[646:666]:        SMART_BUTTON_ENABLED,
+	_ObservationIDLowerName[646:666]:   SMART_BUTTON_ENABLED,
+	_ObservationIDName[666:687]:        OFFLINE_CHARGING_MODE,
+	_ObservationIDLowerName[666:687]:   OFFLINE_CHARGING_MODE,
+	_ObservationIDName[687:694]:        LEDMODE,
+	_ObservationIDLowerName[687:694]:   LEDMODE,
+	_ObservationIDName[694:713]:        MAX_CHARGER_CURRENT,
+	_ObservationIDLowerName[694:713]:   MAX_CHARGER_CURRENT,
+	_ObservationIDName[713:736]:        DYNAMIC_CHARGER_CURRENT,
+	_ObservationIDLowerName[713:736]:   DYNAMIC_CHARGER_CURRENT,
+	_ObservationIDName[736:767]:        MAX_CURRENT_OFFLINE_FALLBACK_P1,
+	_ObservationIDLowerName[736:767]:   MAX_CURRENT_OFFLINE_FALLBACK_P1,
+	_ObservationIDName[767:798]:        MAX_CURRENT_OFFLINE_FALLBACK_P2,
+	_ObservationIDLowerName[767:798]:   MAX_CURRENT_OFFLINE_FALLBACK_P2,
+	_ObservationIDName[798:829]:        MAX_CURRENT_OFFLINE_FALLBACK_P3,
+	_ObservationIDLowerName[798:829]:   MAX_CURRENT_OFFLINE_FALLBACK_P3,
+	_ObservationIDName[829:852]:        LISTEN_TO_CONTROL_PULSE,
+	_ObservationIDLowerName[829:852]:   LISTEN_TO_CONTROL_PULSE,
+	_ObservationIDName[852:869]:        CONTROL_PULSE_RTT,
+	_ObservationIDLowerName[852:869]:   CONTROL_PULSE_RTT,
+	_ObservationIDName[869:886]:        CHARGING_SCHEDULE,
+	_ObservationIDLowerName[869:886]:   CHARGING_SCHEDULE,
+	_ObservationIDName[886:902]:        PAIRED_EQUALIZER,
+	_ObservationIDLowerName[886:902]:   PAIRED_EQUALIZER,
+	_ObservationIDName[902:917]:        WI_FI_APENABLED,
+	_ObservationIDLowerName[902:917]:   WI_FI_APENABLED,
+	_ObservationIDName[917:936]:        PAIRED_USER_IDTOKEN,
+	_ObservationIDLowerName[917:936]:   PAIRED_USER_IDTOKEN,
+	_ObservationIDName[936:986]:        CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L1,
+	_ObservationIDLowerName[936:986]:   CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L1,
+	_ObservationIDName[986:1036]:       CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L2,
+	_ObservationIDLowerName[986:1036]:  CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L2,
+	_ObservationIDName[1036:1086]:      CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L3,
+	_ObservationIDLowerName[1036:1086]: CIRCUIT_TOTAL_ALLOCATED_PHASE_CONDUCTOR_CURRENT_L3,
+	_ObservationIDName[1086:1126]:      CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L1,
+	_ObservationIDLowerName[1086:1126]: CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L1,
+	_ObservationIDName[1126:1166]:      CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L2,
+	_ObservationIDLowerName[1126:1166]: CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L2,
+	_ObservationIDName[1166:1206]:      CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L3,
+	_ObservationIDLowerName[1166:1206]: CIRCUIT_TOTAL_PHASE_CONDUCTOR_CURRENT_L3,
+	_ObservationIDName[1206:1230]:      NUMBER_OF_CARS_CONNECTED,
+	_ObservationIDLowerName[1206:1230]: NUMBER_OF_CARS_CONNECTED,
+	_ObservationIDName[1230:1253]:      NUMBER_OF_CARS_CHARGING,
+	_ObservationIDLowerName[1230:1253]: NUMBER_OF_CARS_CHARGING,
+	_ObservationIDName[1253:1276]:      NUMBER_OF_CARS_IN_QUEUE,
+	_ObservationIDLowerName[1253:1276]: NUMBER_OF_CARS_IN_QUEUE,
+	_ObservationIDName[1276:1304]:      NUMBER_OF_CARS_FULLY_CHARGED,
+	_ObservationIDLowerName[1276:1304]: NUMBER_OF_CARS_FULLY_CHARGED,
+	_ObservationIDName[1304:1320]:      SOFTWARE_RELEASE,
+	_ObservationIDLowerName[1304:1320]: SOFTWARE_RELEASE,
+	_ObservationIDName[1320:1325]:      ICCID,
+	_ObservationIDLowerName[1320:1325]: ICCID,
+	_ObservationIDName[1325:1336]:      MODEM_FW_ID,
+	_ObservationIDLowerName[1325:1336]: MODEM_FW_ID,
+	_ObservationIDName[1336:1349]:      OTAERROR_CODE,
+	_ObservationIDLowerName[1336:1349]: OTAERROR_CODE,
+	_ObservationIDName[1349:1372]:      MOBILE_NETWORK_OPERATOR,
+	_ObservationIDLowerName[1349:1372]: MOBILE_NETWORK_OPERATOR,
+	_ObservationIDName[1372:1385]:      REBOOT_REASON,
+	_ObservationIDLowerName[1372:1385]: REBOOT_REASON,
+	_ObservationIDName[1385:1401]:      POWER_PCBVERSION,
+	_ObservationIDLowerName[1385:1401]: POWER_PCBVERSION,
+	_ObservationIDName[1401:1415]:      COM_PCBVERSION,
+	_ObservationIDLowerName[1401:1415]: COM_PCBVERSION,
+	_ObservationIDName[1415:1436]:      REASON_FOR_NO_CURRENT,
+	_ObservationIDLowerName[1415:1436]: REASON_FOR_NO_CURRENT,
+	_ObservationIDName[1436:1479]:      LOAD_BALANCING_NUMBER_OF_CONNECTED_CHARGERS,
+	_ObservationIDLowerName[1436:1479]: LOAD_BALANCING_NUMBER_OF_CONNECTED_CHARGERS,
+	_ObservationIDName[1479:1504]:      UDPNUM_OF_CONNECTED_NODES,
+	_ObservationIDLowerName[1479:1504]: UDPNUM_OF_CONNECTED_NODES,
+	_ObservationIDName[1504:1520]:      LOCAL_CONNECTION,
+	_ObservationIDLowerName[1504:1520]: LOCAL_CONNECTION,
+	_ObservationIDName[1520:1530]:      PILOT_MODE,
+	_ObservationIDLowerName[1520:1530]: PILOT_MODE,
+	_ObservationIDName[1530:1554]:      CAR_CONNECTED_DEPRECATED,
+	_ObservationIDLowerName[1530:1554]: CAR_CONNECTED_DEPRECATED,
+	_ObservationIDName[1554:1568]:      SMART_CHARGING,
+	_ObservationIDLowerName[1554:1568]: SMART_CHARGING,
+	_ObservationIDName[1568:1580]:      CABLE_LOCKED,
+	_ObservationIDLowerName[1568:1580]: CABLE_LOCKED,
+	_ObservationIDName[1580:1592]:      CABLE_RATING,
+	_ObservationIDLowerName[1580:1592]: CABLE_RATING,
+	_ObservationIDName[1592:1602]:      PILOT_HIGH,
+	_ObservationIDLowerName[1592:1602]: PILOT_HIGH,
+	_ObservationIDName[1602:1611]:      PILOT_LOW,
+	_ObservationIDLowerName[1602:1611]: PILOT_LOW,
+	_ObservationIDName[1611:1624]:      BACK_PLATE_ID,
+	_ObservationIDLowerName[1611:1624]: BACK_PLATE_ID,
+	_ObservationIDName[1624:1645]:      USER_IDTOKEN_REVERSED,
+	_ObservationIDLowerName[1624:1645]: USER_IDTOKEN_REVERSED,
+	_ObservationIDName[1645:1660]:      CHARGER_OP_MODE,
+	_ObservationIDLowerName[1645:1660]: CHARGER_OP_MODE,
+	_ObservationIDName[1660:1672]:      OUTPUT_PHASE,
+	_ObservationIDLowerName[1660:1672]: OUTPUT_PHASE,
+	_ObservationIDName[1672:1698]:      DYNAMIC_CIRCUIT_CURRENT_P1,
+	_ObservationIDLowerName[1672:1698]: DYNAMIC_CIRCUIT_CURRENT_P1,
+	_ObservationIDName[1698:1724]:      DYNAMIC_CIRCUIT_CURRENT_P2,
+	_ObservationIDLowerName[1698:1724]: DYNAMIC_CIRCUIT_CURRENT_P2,
+	_ObservationIDName[1724:1750]:      DYNAMIC_CIRCUIT_CURRENT_P3,
+	_ObservationIDLowerName[1724:1750]: DYNAMIC_CIRCUIT_CURRENT_P3,
+	_ObservationIDName[1750:1764]:      OUTPUT_CURRENT,
+	_ObservationIDLowerName[1750:1764]: OUTPUT_CURRENT,
+	_ObservationIDName[1764:1779]:      DERATED_CURRENT,
+	_ObservationIDLowerName[1764:1779]: DERATED_CURRENT,
+	_ObservationIDName[1779:1794]:      DERATING_ACTIVE,
+	_ObservationIDLowerName[1779:1794]: DERATING_ACTIVE,
+	_ObservationIDName[1794:1806]:      DEBUG_STRING,
+	_ObservationIDLowerName[1794:1806]: DEBUG_STRING,
+	_ObservationIDName[1806:1818]:      ERROR_STRING,
+	_ObservationIDLowerName[1806:1818]: ERROR_STRING,
+	_ObservationIDName[1818:1828]:      ERROR_CODE,
+	_ObservationIDLowerName[1818:1828]: ERROR_CODE,
+	_ObservationIDName[1828:1839]:      TOTAL_POWER,
+	_ObservationIDLowerName[1828:1839]: TOTAL_POWER,
+	_ObservationIDName[1839:1853]:      SESSION_ENERGY,
+	_ObservationIDLowerName[1839:1853]: SESSION_ENERGY,
+	_ObservationIDName[1853:1868]:      ENERGY_PER_HOUR,
+	_ObservationIDLowerName[1853:1868]: ENERGY_PER_HOUR,
+	_ObservationIDName[1868:1884]:      LEGACY_EV_STATUS,
+	_ObservationIDLowerName[1868:1884]: LEGACY_EV_STATUS,
+	_ObservationIDName[1884:1899]:      LIFETIME_ENERGY,
+	_ObservationIDLowerName[1884:1899]: LIFETIME_ENERGY,
+	_ObservationIDName[1899:1922]:      LIFETIME_RELAY_SWITCHES,
+	_ObservationIDLowerName[1899:1922]: LIFETIME_RELAY_SWITCHES,
+	_ObservationIDName[1922:1936]:      LIFETIME_HOURS,
+	_ObservationIDLowerName[1922:1936]: LIFETIME_HOURS,
+	_ObservationIDName[1936:1979]:      DYNAMIC_CURRENT_OFFLINE_FALLBACK_DEPRICATED,
+	_ObservationIDLowerName[1936:1979]: DYNAMIC_CURRENT_OFFLINE_FALLBACK_DEPRICATED,
+	_ObservationIDName[1979:1991]:      USER_IDTOKEN,
+	_ObservationIDLowerName[1979:1991]: USER_IDTOKEN,
+	_ObservationIDName[1991:2007]:      CHARGING_SESSION,
+	_ObservationIDLowerName[1991:2007]: CHARGING_SESSION,
+	_ObservationIDName[2007:2016]:      CELL_RSSI,
+	_ObservationIDLowerName[2007:2016]: CELL_RSSI,
+	_ObservationIDName[2016:2024]:      CELL_RAT,
+	_ObservationIDLowerName[2016:2024]: CELL_RAT,
+	_ObservationIDName[2024:2034]:      WI_FI_RSSI,
+	_ObservationIDLowerName[2024:2034]: WI_FI_RSSI,
+	_ObservationIDName[2034:2046]:      CELL_ADDRESS,
+	_ObservationIDLowerName[2034:2046]: CELL_ADDRESS,
+	_ObservationIDName[2046:2059]:      WI_FI_ADDRESS,
+	_ObservationIDLowerName[2046:2059]: WI_FI_ADDRESS,
+	_ObservationIDName[2059:2069]:      WI_FI_TYPE,
+	_ObservationIDLowerName[2059:2069]: WI_FI_TYPE,
+	_ObservationIDName[2069:2079]:      LOCAL_RSSI,
+	_ObservationIDLowerName[2069:2079]: LOCAL_RSSI,
+	_ObservationIDName[2079:2099]:      MASTER_BACK_PLATE_ID,
+	_ObservationIDLowerName[2079:2099]: MASTER_BACK_PLATE_ID,
+	_ObservationIDName[2099:2113]:      LOCAL_TX_POWER,
+	_ObservationIDLowerName[2099:2113]: LOCAL_TX_POWER,
+	_ObservationIDName[2113:2124]:      LOCAL_STATE,
+	_ObservationIDLowerName[2113:2124]: LOCAL_STATE,
+	_ObservationIDName[2124:2135]:      FOUND_WI_FI,
+	_ObservationIDLowerName[2124:2135]: FOUND_WI_FI,
+	_ObservationIDName[2135:2146]:      CHARGER_RAT,
+	_ObservationIDLowerName[2135:2146]: CHARGER_RAT,
+	_ObservationIDName[2146:2176]:      CELLULAR_INTERFACE_ERROR_COUNT,
+	_ObservationIDLowerName[2146:2176]: CELLULAR_INTERFACE_ERROR_COUNT,
+	_ObservationIDName[2176:2206]:      CELLULAR_INTERFACE_RESET_COUNT,
+	_ObservationIDLowerName[2176:2206]: CELLULAR_INTERFACE_RESET_COUNT,
+	_ObservationIDName[2206:2232]:      WIFI_INTERFACE_ERROR_COUNT,
+	_ObservationIDLowerName[2206:2232]: WIFI_INTERFACE_ERROR_COUNT,
+	_ObservationIDName[2232:2258]:      WIFI_INTERFACE_RESET_COUNT,
+	_ObservationIDLowerName[2232:2258]: WIFI_INTERFACE_RESET_COUNT,
+	_ObservationIDName[2258:2273]:      LOCAL_NODE_TYPE,
+	_ObservationIDLowerName[2258:2273]: LOCAL_NODE_TYPE,
+	_ObservationIDName[2273:2292]:      LOCAL_RADIO_CHANNEL,
+	_ObservationIDLowerName[2273:2292]: LOCAL_RADIO_CHANNEL,
+	_ObservationIDName[2292:2311]:      LOCAL_SHORT_ADDRESS,
+	_ObservationIDLowerName[2292:2311]: LOCAL_SHORT_ADDRESS,
+	_ObservationIDName[2311:2344]:      LOCAL_PARENT_ADDR_OR_NUM_OF_NODES,
+	_ObservationIDLowerName[2311:2344]: LOCAL_PARENT_ADDR_OR_NUM_OF_NODES,
+	_ObservationIDName[2344:2352]:      TEMP_MAX,
+	_ObservationIDLowerName[2344:2352]: TEMP_MAX,
+	_ObservationIDName[2352:2376]:      TEMP_AMBIENT_POWER_BOARD,
+	_ObservationIDLowerName[2352:2376]: TEMP_AMBIENT_POWER_BOARD,
+	_ObservationIDName[2376:2389]:      TEMP_INPUT_T2,
+	_ObservationIDLowerName[2376:2389]: TEMP_INPUT_T2,
+	_ObservationIDName[2389:2402]:      TEMP_INPUT_T3,
+	_ObservationIDLowerName[2389:2402]: TEMP_INPUT_T3,
+	_ObservationIDName[2402:2415]:      TEMP_INPUT_T4,
+	_ObservationIDLowerName[2402:2415]: TEMP_INPUT_T4,
+	_ObservationIDName[2415:2428]:      TEMP_INPUT_T5,
+	_ObservationIDLowerName[2415:2428]: TEMP_INPUT_T5,
+	_ObservationIDName[2428:2441]:      TEMP_OUTPUT_N,
+	_ObservationIDLowerName[2428:2441]: TEMP_OUTPUT_N,
+	_ObservationIDName[2441:2455]:      TEMP_OUTPUT_L1,
+	_ObservationIDLowerName[2441:2455]: TEMP_OUTPUT_L1,
+	_ObservationIDName[2455:2469]:      TEMP_OUTPUT_L2,
+	_ObservationIDLowerName[2455:2469]: TEMP_OUTPUT_L2,
+	_ObservationIDName[2469:2483]:      TEMP_OUTPUT_L3,
+	_ObservationIDLowerName[2469:2483]: TEMP_OUTPUT_L3,
+	_ObservationIDName[2483:2495]:      TEMP_AMBIENT,
+	_ObservationIDLowerName[2483:2495]: TEMP_AMBIENT,
+	_ObservationIDName[2495:2508]:      LIGHT_AMBIENT,
+	_ObservationIDLowerName[2495:2508]: LIGHT_AMBIENT,
+	_ObservationIDName[2508:2524]:      INT_REL_HUMIDITY,
+	_ObservationIDLowerName[2508:2524]: INT_REL_HUMIDITY,
+	_ObservationIDName[2524:2541]:      BACK_PLATE_LOCKED,
+	_ObservationIDLowerName[2524:2541]: BACK_PLATE_LOCKED,
+	_ObservationIDName[2541:2554]:      CURRENT_MOTOR,
+	_ObservationIDLowerName[2541:2554]: CURRENT_MOTOR,
+	_ObservationIDName[2554:2576]:      BACK_PLATE_HALL_SENSOR,
+	_ObservationIDLowerName[2554:2576]: BACK_PLATE_HALL_SENSOR,
+	_ObservationIDName[2576:2589]:      IN_CURRENT_T2,
+	_ObservationIDLowerName[2576:2589]: IN_CURRENT_T2,
+	_ObservationIDName[2589:2602]:      IN_CURRENT_T3,
+	_ObservationIDLowerName[2589:2602]: IN_CURRENT_T3,
+	_ObservationIDName[2602:2615]:      IN_CURRENT_T4,
+	_ObservationIDLowerName[2602:2615]: IN_CURRENT_T4,
+	_ObservationIDName[2615:2628]:      IN_CURRENT_T5,
+	_ObservationIDLowerName[2615:2628]: IN_CURRENT_T5,
+	_ObservationIDName[2628:2641]:      IN_VOLT_T1_T2,
+	_ObservationIDLowerName[2628:2641]: IN_VOLT_T1_T2,
+	_ObservationIDName[2641:2654]:      IN_VOLT_T1_T3,
+	_ObservationIDLowerName[2641:2654]: IN_VOLT_T1_T3,
+	_ObservationIDName[2654:2667]:      IN_VOLT_T1_T4,
+	_ObservationIDLowerName[2654:2667]: IN_VOLT_T1_T4,
+	_ObservationIDName[2667:2680]:      IN_VOLT_T1_T5,
+	_ObservationIDLowerName[2667:2680]: IN_VOLT_T1_T5,
+	_ObservationIDName[2680:2693]:      IN_VOLT_T2_T3,
+	_ObservationIDLowerName[2680:2693]: IN_VOLT_T2_T3,
+	_ObservationIDName[2693:2706]:      IN_VOLT_T2_T4,
+	_ObservationIDLowerName[2693:2706]: IN_VOLT_T2_T4,
+	_ObservationIDName[2706:2719]:      IN_VOLT_T2_T5,
+	_ObservationIDLowerName[2706:2719]: IN_VOLT_T2_T5,
+	_ObservationIDName[2719:2732]:      IN_VOLT_T3_T4,
+	_ObservationIDLowerName[2719:2732]: IN_VOLT_T3_T4,
+	_ObservationIDName[2732:2745]:      IN_VOLT_T3_T5,
+	_ObservationIDLowerName[2732:2745]: IN_VOLT_T3_T5,
+	_ObservationIDName[2745:2758]:      IN_VOLT_T4_T5,
+	_ObservationIDLowerName[2745:2758]: IN_VOLT_T4_T5,
+	_ObservationIDName[2758:2773]:      OUT_VOLT_PIN1_2,
+	_ObservationIDLowerName[2758:2773]: OUT_VOLT_PIN1_2,
+	_ObservationIDName[2773:2788]:      OUT_VOLT_PIN1_3,
+	_ObservationIDLowerName[2773:2788]: OUT_VOLT_PIN1_3,
+	_ObservationIDName[2788:2803]:      OUT_VOLT_PIN1_4,
+	_ObservationIDLowerName[2788:2803]: OUT_VOLT_PIN1_4,
+	_ObservationIDName[2803:2818]:      OUT_VOLT_PIN1_5,
+	_ObservationIDLowerName[2803:2818]: OUT_VOLT_PIN1_5,
+	_ObservationIDName[2818:2830]:      VOLT_LEVEL33,
+	_ObservationIDLowerName[2818:2830]: VOLT_LEVEL33,
+	_ObservationIDName[2830:2841]:      VOLT_LEVEL5,
+	_ObservationIDLowerName[2830:2841]: VOLT_LEVEL5,
+	_ObservationIDName[2841:2853]:      VOLT_LEVEL12,
+	_ObservationIDLowerName[2841:2853]: VOLT_LEVEL12,
+	_ObservationIDName[2853:2861]:      LTE_RSRP,
+	_ObservationIDLowerName[2853:2861]: LTE_RSRP,
+	_ObservationIDName[2861:2869]:      LTE_SINR,
+	_ObservationIDLowerName[2861:2869]: LTE_SINR,
+	_ObservationIDName[2869:2877]:      LTE_RSRQ,
+	_ObservationIDLowerName[2869:2877]: LTE_RSRQ,
+	_ObservationIDName[2877:2900]:      EQ_AVAILABLE_CURRENT_P1,
+	_ObservationIDLowerName[2877:2900]: EQ_AVAILABLE_CURRENT_P1,
+	_ObservationIDName[2900:2923]:      EQ_AVAILABLE_CURRENT_P2,
+	_ObservationIDLowerName[2900:2923]: EQ_AVAILABLE_CURRENT_P2,
+	_ObservationIDName[2923:2946]:      EQ_AVAILABLE_CURRENT_P3,
+	_ObservationIDLowerName[2923:2946]: EQ_AVAILABLE_CURRENT_P3,
+}
+
+var _ObservationIDNames = []string{
+	_ObservationIDName[0:16],
+	_ObservationIDName[16:33],
+	_ObservationIDName[33:43],
+	_ObservationIDName[43:65],
+	_ObservationIDName[65:92],
+	_ObservationIDName[92:116],
+	_ObservationIDName[116:143],
+	_ObservationIDName[143:174],
+	_ObservationIDName[174:205],
+	_ObservationIDName[205:226],
+	_ObservationIDName[226:240],
+	_ObservationIDName[240:254],
+	_ObservationIDName[254:278],
+	_ObservationIDName[278:300],
+	_ObservationIDName[300:322],
+	_ObservationIDName[322:344],
+	_ObservationIDName[344:352],
+	_ObservationIDName[352:365],
+	_ObservationIDName[365:379],
+	_ObservationIDName[379:401],
+	_ObservationIDName[401:411],
+	_ObservationIDName[411:434],
+	_ObservationIDName[434:453],
+	_ObservationIDName[453:478],
+	_ObservationIDName[478:488],
+	_ObservationIDName[488:507],
+	_ObservationIDName[507:517],
+	_ObservationIDName[517:555],
+	_ObservationIDName[555:575],
+	_ObservationIDName[575:603],
+	_ObservationIDName[603:625],
+	_ObservationIDName[625:646],
+	_ObservationIDName[646:666],
+	_ObservationIDName[666:687],
+	_ObservationIDName[687:694],
+	_ObservationIDName[694:713],
+	_ObservationIDName[713:736],
+	_ObservationIDName[736:767],
+	_ObservationIDName[767:798],
+	_ObservationIDName[798:829],
+	_ObservationIDName[829:852],
+	_ObservationIDName[852:869],
+	_ObservationIDName[869:886],
+	_ObservationIDName[886:902],
+	_ObservationIDName[902:917],
+	_ObservationIDName[917:936],
+	_ObservationIDName[936:986],
+	_ObservationIDName[986:1036],
+	_ObservationIDName[1036:1086],
+	_ObservationIDName[1086:1126],
+	_ObservationIDName[1126:1166],
+	_ObservationIDName[1166:1206],
+	_ObservationIDName[1206:1230],
+	_ObservationIDName[1230:1253],
+	_ObservationIDName[1253:1276],
+	_ObservationIDName[1276:1304],
+	_ObservationIDName[1304:1320],
+	_ObservationIDName[1320:1325],
+	_ObservationIDName[1325:1336],
+	_ObservationIDName[1336:1349],
+	_ObservationIDName[1349:1372],
+	_ObservationIDName[1372:1385],
+	_ObservationIDName[1385:1401],
+	_ObservationIDName[1401:1415],
+	_ObservationIDName[1415:1436],
+	_ObservationIDName[1436:1479],
+	_ObservationIDName[1479:1504],
+	_ObservationIDName[1504:1520],
+	_ObservationIDName[1520:1530],
+	_ObservationIDName[1530:1554],
+	_ObservationIDName[1554:1568],
+	_ObservationIDName[1568:1580],
+	_ObservationIDName[1580:1592],
+	_ObservationIDName[1592:1602],
+	_ObservationIDName[1602:1611],
+	_ObservationIDName[1611:1624],
+	_ObservationIDName[1624:1645],
+	_ObservationIDName[1645:1660],
+	_ObservationIDName[1660:1672],
+	_ObservationIDName[1672:1698],
+	_ObservationIDName[1698:1724],
+	_ObservationIDName[1724:1750],
+	_ObservationIDName[1750:1764],
+	_ObservationIDName[1764:1779],
+	_ObservationIDName[1779:1794],
+	_ObservationIDName[1794:1806],
+	_ObservationIDName[1806:1818],
+	_ObservationIDName[1818:1828],
+	_ObservationIDName[1828:1839],
+	_ObservationIDName[1839:1853],
+	_ObservationIDName[1853:1868],
+	_ObservationIDName[1868:1884],
+	_ObservationIDName[1884:1899],
+	_ObservationIDName[1899:1922],
+	_ObservationIDName[1922:1936],
+	_ObservationIDName[1936:1979],
+	_ObservationIDName[1979:1991],
+	_ObservationIDName[1991:2007],
+	_ObservationIDName[2007:2016],
+	_ObservationIDName[2016:2024],
+	_ObservationIDName[2024:2034],
+	_ObservationIDName[2034:2046],
+	_ObservationIDName[2046:2059],
+	_ObservationIDName[2059:2069],
+	_ObservationIDName[2069:2079],
+	_ObservationIDName[2079:2099],
+	_ObservationIDName[2099:2113],
+	_ObservationIDName[2113:2124],
+	_ObservationIDName[2124:2135],
+	_ObservationIDName[2135:2146],
+	_ObservationIDName[2146:2176],
+	_ObservationIDName[2176:2206],
+	_ObservationIDName[2206:2232],
+	_ObservationIDName[2232:2258],
+	_ObservationIDName[2258:2273],
+	_ObservationIDName[2273:2292],
+	_ObservationIDName[2292:2311],
+	_ObservationIDName[2311:2344],
+	_ObservationIDName[2344:2352],
+	_ObservationIDName[2352:2376],
+	_ObservationIDName[2376:2389],
+	_ObservationIDName[2389:2402],
+	_ObservationIDName[2402:2415],
+	_ObservationIDName[2415:2428],
+	_ObservationIDName[2428:2441],
+	_ObservationIDName[2441:2455],
+	_ObservationIDName[2455:2469],
+	_ObservationIDName[2469:2483],
+	_ObservationIDName[2483:2495],
+	_ObservationIDName[2495:2508],
+	_ObservationIDName[2508:2524],
+	_ObservationIDName[2524:2541],
+	_ObservationIDName[2541:2554],
+	_ObservationIDName[2554:2576],
+	_ObservationIDName[2576:2589],
+	_ObservationIDName[2589:2602],
+	_ObservationIDName[2602:2615],
+	_ObservationIDName[2615:2628],
+	_ObservationIDName[2628:2641],
+	_ObservationIDName[2641:2654],
+	_ObservationIDName[2654:2667],
+	_ObservationIDName[2667:2680],
+	_ObservationIDName[2680:2693],
+	_ObservationIDName[2693:2706],
+	_ObservationIDName[2706:2719],
+	_ObservationIDName[2719:2732],
+	_ObservationIDName[2732:2745],
+	_ObservationIDName[2745:2758],
+	_ObservationIDName[2758:2773],
+	_ObservationIDName[2773:2788],
+	_ObservationIDName[2788:2803],
+	_ObservationIDName[2803:2818],
+	_ObservationIDName[2818:2830],
+	_ObservationIDName[2830:2841],
+	_ObservationIDName[2841:2853],
+	_ObservationIDName[2853:2861],
+	_ObservationIDName[2861:2869],
+	_ObservationIDName[2869:2877],
+	_ObservationIDName[2877:2900],
+	_ObservationIDName[2900:2923],
+	_ObservationIDName[2923:2946],
 }
 
 // ObservationIDString retrieves an enum value from the enum constants string name.
 // Throws an error if the param is not part of the enum.
 func ObservationIDString(s string) (ObservationID, error) {
+	if val, ok := _ObservationIDNameToValueMap[s]; ok {
+		return val, nil
+	}
+	s = strings.ToLower(s)
 	if val, ok := _ObservationIDNameToValueMap[s]; ok {
 		return val, nil
 	}
@@ -360,6 +857,13 @@ func ObservationIDValues() []ObservationID {
 	return _ObservationIDValues
 }
 
+// ObservationIDStrings returns a slice of all String values of the enum
+func ObservationIDStrings() []string {
+	strs := make([]string, len(_ObservationIDNames))
+	copy(strs, _ObservationIDNames)
+	return strs
+}
+
 // IsAObservationID returns "true" if the value is listed in the enum definition. "false" otherwise
 func (i ObservationID) IsAObservationID() bool {
 	_, ok := _ObservationIDMap[i]
diff --git a/charger/eebus.go b/charger/eebus.go
index 3c9de30d..9b3fbe45 100644
--- a/charger/eebus.go
+++ b/charger/eebus.go
@@ -247,9 +247,6 @@ func (c *EEBus) Enabled() (bool, error) {
 }
 
 // Enable implements the api.Charger interface
-// enable
-//	true: allow to EV to draw power
-//  false: do not allow the EV to draw power
 func (c *EEBus) Enable(enable bool) error {
 	data, err := c.cc.GetData()
 	if err != nil {
diff --git a/cmd/configure/helper.go b/cmd/configure/helper.go
index 4c9b4d99..47f5bbe3 100644
--- a/cmd/configure/helper.go
+++ b/cmd/configure/helper.go
@@ -394,8 +394,7 @@ func (c *CmdConfigure) paramChoiceValues(params []templates.Param, name string)
 }
 
 // processConfig processes an EVCC configuration item
-// Returns:
-//   a map with param name and values
+// Returns a map with param name and values
 func (c *CmdConfigure) processConfig(templateItem *templates.Template, deviceCategory DeviceCategory) map[string]interface{} {
 	fmt.Println()
 	fmt.Println(c.localizedString("Config_Title", nil))
diff --git a/cmd/dump.go b/cmd/dump.go
index b40652e3..1e52b22f 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -73,6 +73,10 @@ func runDump(cmd *cobra.Command, args []string) {
 		}
 	}
 
+	for _, v := range site.GetVehicles() {
+		d.DumpWithHeader(fmt.Sprintf("vehicle: %s", v.Title()), v)
+	}
+
 	for id, lpI := range site.LoadPoints() {
 		lp := lpI.(*core.LoadPoint)
 
@@ -86,9 +90,5 @@ func runDump(cmd *cobra.Command, args []string) {
 		if name := lp.ChargerRef; name != "" {
 			d.DumpWithHeader(fmt.Sprintf("charger: %s", name), cp.Charger(name))
 		}
-
-		for id, v := range lp.VehiclesRef {
-			d.DumpWithHeader(fmt.Sprintf("vehicle %d", id), cp.Vehicle(v))
-		}
 	}
 }
diff --git a/cmd/setup.go b/cmd/setup.go
index 4ecdfa71..4e9000ae 100644
--- a/cmd/setup.go
+++ b/cmd/setup.go
@@ -21,6 +21,7 @@ import (
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/pipe"
 	"github.com/evcc-io/evcc/util/sponsor"
+	"github.com/samber/lo"
 	"github.com/spf13/viper"
 	"golang.org/x/text/currency"
 )
@@ -196,15 +197,20 @@ func configureSiteAndLoadpoints(conf config) (site *core.Site, err error) {
 		}
 
 		if err == nil {
-			site, err = configureSite(conf.Site, cp, loadPoints, tariffs)
+			// list of vehicles
+			vehicles := lo.MapToSlice(cp.vehicles, func(_ string, v api.Vehicle) api.Vehicle {
+				return v
+			})
+
+			site, err = configureSite(conf.Site, cp, loadPoints, vehicles, tariffs)
 		}
 	}
 
 	return site, err
 }
 
-func configureSite(conf map[string]interface{}, cp *ConfigProvider, loadPoints []*core.LoadPoint, tariffs tariff.Tariffs) (*core.Site, error) {
-	site, err := core.NewSiteFromConfig(log, cp, conf, loadPoints, tariffs)
+func configureSite(conf map[string]interface{}, cp *ConfigProvider, loadPoints []*core.LoadPoint, vehicles []api.Vehicle, tariffs tariff.Tariffs) (*core.Site, error) {
+	site, err := core.NewSiteFromConfig(log, cp, conf, loadPoints, vehicles, tariffs)
 	if err != nil {
 		return nil, fmt.Errorf("failed configuring site: %w", err)
 	}
diff --git a/core/coordinator.go b/core/coordinator.go
deleted file mode 100644
index 8b753c08..00000000
--- a/core/coordinator.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package core
-
-import (
-	"github.com/evcc-io/evcc/api"
-	"github.com/evcc-io/evcc/core/loadpoint"
-	"github.com/evcc-io/evcc/util"
-)
-
-type vehicleCoordinator struct {
-	tracked map[api.Vehicle]loadpoint.API
-}
-
-var coordinator *vehicleCoordinator
-
-func init() {
-	coordinator = &vehicleCoordinator{
-		tracked: make(map[api.Vehicle]loadpoint.API),
-	}
-}
-
-func (lp *vehicleCoordinator) acquire(owner loadpoint.API, vehicle api.Vehicle) {
-	if o, ok := lp.tracked[vehicle]; ok && o != owner {
-		o.SetVehicle(nil)
-	}
-	lp.tracked[vehicle] = owner
-}
-
-func (lp *vehicleCoordinator) release(vehicle api.Vehicle) {
-	delete(lp.tracked, vehicle)
-}
-
-// availableDetectibleVehicles is the list of vehicles that are currently not
-// associated to another loadpoint and have a status api that allows for detection
-func (lp *vehicleCoordinator) availableDetectibleVehicles(owner loadpoint.API, vehicles []api.Vehicle) []api.Vehicle {
-	var res []api.Vehicle
-
-	for _, vv := range vehicles {
-		if _, ok := vv.(api.ChargeState); ok {
-			if o, ok := lp.tracked[vv]; o == owner || !ok {
-				res = append(res, vv)
-			}
-		}
-	}
-
-	return res
-}
-
-// find active vehicle by charge state
-func (lp *vehicleCoordinator) identifyVehicleByStatus(log *util.Logger, owner loadpoint.API, vehicles []api.Vehicle) api.Vehicle {
-	available := lp.availableDetectibleVehicles(owner, vehicles)
-
-	var res api.Vehicle
-	for _, vehicle := range available {
-		if vs, ok := vehicle.(api.ChargeState); ok {
-			status, err := vs.Status()
-
-			if err != nil {
-				log.ERROR.Println("vehicle status:", err)
-				continue
-			}
-
-			log.DEBUG.Printf("vehicle status: %s (%s)", status, vehicle.Title())
-
-			// vehicle is plugged or charging, so it should be the right one
-			if status == api.StatusB || status == api.StatusC {
-				if res != nil {
-					log.WARN.Println("vehicle status: >1 matches, giving up")
-					return nil
-				}
-
-				res = vehicle
-			}
-		}
-	}
-
-	return res
-}
diff --git a/core/coordinator/adapter.go b/core/coordinator/adapter.go
new file mode 100644
index 00000000..74765cde
--- /dev/null
+++ b/core/coordinator/adapter.go
@@ -0,0 +1,36 @@
+package coordinator
+
+import (
+	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/core/loadpoint"
+)
+
+type adapter struct {
+	lp loadpoint.API
+	c  *Coordinator
+}
+
+// NewAdapter exposes the coordinator for a given loadpoint.
+// Using an adapter simplifies the method signatures seen from the loadpoint.
+func NewAdapter(lp loadpoint.API, c *Coordinator) API {
+	return &adapter{
+		lp: lp,
+		c:  c,
+	}
+}
+
+func (a *adapter) GetVehicles() []api.Vehicle {
+	return a.c.GetVehicles()
+}
+
+func (a *adapter) Acquire(v api.Vehicle) {
+	a.c.acquire(a.lp, v)
+}
+
+func (a *adapter) Release(v api.Vehicle) {
+	a.c.release(v)
+}
+
+func (a *adapter) IdentifyVehicleByStatus() api.Vehicle {
+	return a.c.identifyVehicleByStatus(a.lp)
+}
diff --git a/core/coordinator/api.go b/core/coordinator/api.go
new file mode 100644
index 00000000..717ace62
--- /dev/null
+++ b/core/coordinator/api.go
@@ -0,0 +1,11 @@
+package coordinator
+
+import "github.com/evcc-io/evcc/api"
+
+// API is the coordinator API
+type API interface {
+	GetVehicles() []api.Vehicle
+	Acquire(api.Vehicle)
+	Release(api.Vehicle)
+	IdentifyVehicleByStatus() api.Vehicle
+}
diff --git a/core/coordinator/coordinator.go b/core/coordinator/coordinator.go
new file mode 100644
index 00000000..32cdbd4c
--- /dev/null
+++ b/core/coordinator/coordinator.go
@@ -0,0 +1,84 @@
+package coordinator
+
+import (
+	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/core/loadpoint"
+	"github.com/evcc-io/evcc/util"
+)
+
+// Coordinator coordinates vehicle access between loadpoints
+type Coordinator struct {
+	log      *util.Logger
+	vehicles []api.Vehicle
+	tracked  map[api.Vehicle]loadpoint.API
+}
+
+// New creates a coordinator for a set of vehicles
+func New(log *util.Logger, vehicles []api.Vehicle) *Coordinator {
+	return &Coordinator{
+		log:      log,
+		vehicles: vehicles,
+		tracked:  make(map[api.Vehicle]loadpoint.API),
+	}
+}
+
+func (c *Coordinator) GetVehicles() []api.Vehicle {
+	return c.vehicles
+}
+
+func (c *Coordinator) acquire(owner loadpoint.API, vehicle api.Vehicle) {
+	if o, ok := c.tracked[vehicle]; ok && o != owner {
+		o.SetVehicle(nil)
+	}
+	c.tracked[vehicle] = owner
+}
+
+func (c *Coordinator) release(vehicle api.Vehicle) {
+	delete(c.tracked, vehicle)
+}
+
+// availableDetectibleVehicles is the list of vehicles that are currently not
+// associated to another loadpoint and have a status api that allows for detection
+func (c *Coordinator) availableDetectibleVehicles(owner loadpoint.API) []api.Vehicle {
+	var res []api.Vehicle
+
+	for _, vv := range c.vehicles {
+		if _, ok := vv.(api.ChargeState); ok {
+			if o, ok := c.tracked[vv]; o == owner || !ok {
+				res = append(res, vv)
+			}
+		}
+	}
+
+	return res
+}
+
+// identifyVehicleByStatus finds active vehicle by charge state
+func (c *Coordinator) identifyVehicleByStatus(owner loadpoint.API) api.Vehicle {
+	available := c.availableDetectibleVehicles(owner)
+
+	var res api.Vehicle
+	for _, vehicle := range available {
+		if vs, ok := vehicle.(api.ChargeState); ok {
+			status, err := vs.Status()
+			if err != nil {
+				c.log.ERROR.Println("vehicle status:", err)
+				continue
+			}
+
+			c.log.DEBUG.Printf("vehicle status: %s (%s)", status, vehicle.Title())
+
+			// vehicle is plugged or charging, so it should be the right one
+			if status == api.StatusB || status == api.StatusC {
+				if res != nil {
+					c.log.WARN.Println("vehicle status: >1 matches, giving up")
+					return nil
+				}
+
+				res = vehicle
+			}
+		}
+	}
+
+	return res
+}
diff --git a/core/coordinator/dummy.go b/core/coordinator/dummy.go
new file mode 100644
index 00000000..6b75c05b
--- /dev/null
+++ b/core/coordinator/dummy.go
@@ -0,0 +1,24 @@
+package coordinator
+
+import (
+	"github.com/evcc-io/evcc/api"
+)
+
+type dummy struct{}
+
+// NewDummy creates a dummy coordinator without vehicles
+func NewDummy() API {
+	return &dummy{}
+}
+
+func (a *dummy) GetVehicles() []api.Vehicle {
+	return nil
+}
+
+func (a *dummy) Acquire(v api.Vehicle) {}
+
+func (a *dummy) Release(v api.Vehicle) {}
+
+func (a *dummy) IdentifyVehicleByStatus() api.Vehicle {
+	return nil
+}
diff --git a/core/loadpoint.go b/core/loadpoint.go
index 0ec5019f..f72f844e 100644
--- a/core/loadpoint.go
+++ b/core/loadpoint.go
@@ -11,13 +11,13 @@ import (
 	"time"
 
 	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/core/coordinator"
 	"github.com/evcc-io/evcc/core/loadpoint"
 	"github.com/evcc-io/evcc/core/soc"
 	"github.com/evcc-io/evcc/core/wrapper"
 	"github.com/evcc-io/evcc/provider"
 	"github.com/evcc-io/evcc/push"
 	"github.com/evcc-io/evcc/util"
-	"github.com/samber/lo"
 	"golang.org/x/exp/slices"
 
 	evbus "github.com/asaskevich/EventBus"
@@ -104,7 +104,7 @@ type LoadPoint struct {
 	DefaultPhases     int      `mapstructure:"phases"`   // Charger enabled phases
 	ChargerRef        string   `mapstructure:"charger"`  // Charger reference
 	VehicleRef        string   `mapstructure:"vehicle"`  // Vehicle reference
-	VehiclesRef       []string `mapstructure:"vehicles"` // Vehicles reference
+	VehiclesRef_      []string `mapstructure:"vehicles"` // TODO deprecated
 	MeterRef          string   `mapstructure:"meter"`    // Charge meter reference
 	SoC               SoCConfig
 	Enable, Disable   ThresholdConfig
@@ -129,10 +129,10 @@ type LoadPoint struct {
 	chargeTimer api.ChargeTimer
 	chargeRater api.ChargeRater
 
-	chargeMeter    api.Meter     // Charger usage meter
-	vehicle        api.Vehicle   // Currently active vehicle
-	vehicles       []api.Vehicle // Assigned vehicles
-	defaultVehicle api.Vehicle   // Default vehicle (disables detection)
+	chargeMeter    api.Meter   // Charger usage meter
+	vehicle        api.Vehicle // Currently active vehicle
+	defaultVehicle api.Vehicle // Default vehicle (disables detection)
+	coordinator    coordinator.API
 	socEstimator   *soc.Estimator
 	socTimer       *soc.Timer
 
@@ -200,32 +200,14 @@ func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[strin
 		lp.chargeMeter = cp.Meter(lp.MeterRef)
 	}
 
-	// multiple vehicles
-	for _, ref := range lp.VehiclesRef {
-		vehicle := cp.Vehicle(ref)
-		lp.vehicles = append(lp.vehicles, vehicle)
-	}
-
 	// default vehicle
 	if lp.VehicleRef != "" {
 		lp.defaultVehicle = cp.Vehicle(lp.VehicleRef)
-
-		// append default vehicle if not contained in list
-		if len(lo.Filter(lp.vehicles, func(v api.Vehicle, _ int) bool {
-			return v == lp.defaultVehicle
-		})) == 0 {
-			lp.vehicles = append(lp.vehicles, lp.defaultVehicle)
-		}
 	}
 
-	// verify vehicle detection
-	if len(lp.vehicles) > 1 {
-		for _, v := range lp.vehicles {
-			if _, ok := v.(api.ChargeState); !ok {
-				lp.log.WARN.Printf("vehicle '%s' does not support automatic detection", v.Title())
-				break
-			}
-		}
+	// TODO deprecated
+	if len(lp.VehiclesRef_) > 0 {
+		lp.log.WARN.Println("vehicles option is deprecated")
 	}
 
 	if lp.ChargerRef == "" {
@@ -274,8 +256,9 @@ func NewLoadPoint(log *util.Logger) *LoadPoint {
 		Enable:        ThresholdConfig{Delay: time.Minute, Threshold: 0},     // t, W
 		Disable:       ThresholdConfig{Delay: 3 * time.Minute, Threshold: 0}, // t, W
 		GuardDuration: 5 * time.Minute,
-		progress:      NewProgress(0, 10), // soc progress indicator
-		tasks:         aq.New(),
+		progress:      NewProgress(0, 10),     // soc progress indicator
+		coordinator:   coordinator.NewDummy(), // dummy vehicle coordinator
+		tasks:         aq.New(),               // task queue
 	}
 
 	// allow target charge handler to access loadpoint
@@ -543,10 +526,8 @@ func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
 	lp.publish("minSoC", lp.SoC.Min)
 	lp.Unlock()
 
-	// activate default vehicle (allows poll mode: always)
-	if lp.defaultVehicle != nil {
-		lp.setActiveVehicle(lp.defaultVehicle)
-	}
+	// set default or start detection
+	lp.vehicleDefaultOrDetect()
 
 	// read initial charger state to prevent immediately disabling charger
 	if enabled, err := lp.charger.Enabled(); err == nil {
@@ -768,15 +749,17 @@ func (lp *LoadPoint) identifyVehicle() {
 
 // selectVehicleByID selects the vehicle with the given ID
 func (lp *LoadPoint) selectVehicleByID(id string) api.Vehicle {
+	vehicles := lp.coordinatedVehicles()
+
 	// find exact match
-	for _, vehicle := range lp.vehicles {
+	for _, vehicle := range vehicles {
 		if slices.Contains(vehicle.Identifiers(), id) {
 			return vehicle
 		}
 	}
 
 	// find placeholder match
-	for _, vehicle := range lp.vehicles {
+	for _, vehicle := range vehicles {
 		for _, vid := range vehicle.Identifiers() {
 			re, err := regexp.Compile(strings.ReplaceAll(vid, "*", ".*?"))
 			if err != nil {
@@ -805,12 +788,12 @@ func (lp *LoadPoint) setActiveVehicle(vehicle api.Vehicle) {
 
 	from := "unknown"
 	if lp.vehicle != nil {
-		coordinator.release(lp.vehicle)
+		lp.coordinator.Release(lp.vehicle)
 		from = lp.vehicle.Title()
 	}
 	to := "unknown"
 	if vehicle != nil {
-		coordinator.acquire(lp, vehicle)
+		lp.coordinator.Acquire(vehicle)
 		to = vehicle.Title()
 	}
 	lp.log.INFO.Printf("vehicle updated: %s -> %s", from, to)
@@ -875,7 +858,7 @@ func (lp *LoadPoint) unpublishVehicle() {
 
 // vehicleUnidentified checks if there are associated vehicles and starts discovery period
 func (lp *LoadPoint) vehicleUnidentified() bool {
-	res := len(lp.vehicles) > 0 && lp.vehicle == nil &&
+	res := len(lp.coordinatedVehicles()) > 0 && lp.vehicle == nil &&
 		lp.clock.Since(lp.vehicleDetect) < vehicleDetectDuration
 
 	// request vehicle api refresh while waiting to identify
@@ -901,7 +884,7 @@ func (lp *LoadPoint) vehicleDefaultOrDetect() {
 			// need to do this here since setActiveVehicle would short-circuit
 			lp.addTask(lp.vehicleOdometer)
 		}
-	} else if len(lp.vehicles) > 0 {
+	} else if len(lp.coordinatedVehicles()) > 0 {
 		// flush all vehicles before detection starts
 		lp.log.DEBUG.Println("vehicle api refresh")
 		provider.ResetCached()
@@ -922,11 +905,11 @@ func (lp *LoadPoint) stopVehicleDetection() {
 
 // identifyVehicleByStatus validates if the active vehicle is still connected to the loadpoint
 func (lp *LoadPoint) identifyVehicleByStatus() {
-	if len(lp.vehicles) == 0 {
+	if len(lp.coordinatedVehicles()) == 0 {
 		return
 	}
 
-	if vehicle := coordinator.identifyVehicleByStatus(lp.log, lp, lp.vehicles); vehicle != nil {
+	if vehicle := lp.coordinator.IdentifyVehicleByStatus(); vehicle != nil {
 		lp.setActiveVehicle(vehicle)
 		return
 	}
@@ -1185,9 +1168,17 @@ func (lp *LoadPoint) pvScalePhases(availablePower, minCurrent, maxCurrent float6
 	return false
 }
 
+// coordinatedVehicles is the slice of vehicles from the coordinator
+func (lp *LoadPoint) coordinatedVehicles() []api.Vehicle {
+	if lp.coordinator == nil {
+		return nil
+	}
+	return lp.coordinator.GetVehicles()
+}
+
 // publishVehicles publishes a slice of vehicle titles
 func (lp *LoadPoint) publishVehicles() {
-	lp.publish("vehicles", vehicleTitles(lp.vehicles))
+	lp.publish("vehicles", vehicleTitles(lp.coordinatedVehicles()))
 }
 
 // TODO move up to timer functions
diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go
index 64017fa3..55020f48 100644
--- a/core/loadpoint/api.go
+++ b/core/loadpoint/api.go
@@ -83,8 +83,6 @@ type API interface {
 	// vehicles
 	//
 
-	// GetVehicle is the list of vehicles
-	GetVehicles() []api.Vehicle
 	// SetVehicle sets the active vehicle
 	SetVehicle(vehicle api.Vehicle)
 }
diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go
index c404fa6e..dae5fbd2 100644
--- a/core/loadpoint_api.go
+++ b/core/loadpoint_api.go
@@ -260,13 +260,6 @@ func (lp *LoadPoint) GetRemainingEnergy() float64 {
 	return lp.chargeRemainingEnergy
 }
 
-// GetVehicles is the list of vehicles
-func (lp *LoadPoint) GetVehicles() []api.Vehicle {
-	lp.Lock()
-	defer lp.Unlock()
-	return lp.vehicles
-}
-
 // SetVehicle sets the active vehicle
 func (lp *LoadPoint) SetVehicle(vehicle api.Vehicle) {
 	// TODO develop universal locking approach
diff --git a/core/site.go b/core/site.go
index a63afde9..b665993b 100644
--- a/core/site.go
+++ b/core/site.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/avast/retry-go/v3"
 	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/core/coordinator"
 	"github.com/evcc-io/evcc/core/loadpoint"
 	"github.com/evcc-io/evcc/push"
 	"github.com/evcc-io/evcc/tariff"
@@ -44,9 +45,10 @@ type Site struct {
 	pvMeters      []api.Meter // PV generation meters
 	batteryMeters []api.Meter // Battery charging meters
 
-	tariffs    tariff.Tariffs // Tariff
-	loadpoints []*LoadPoint   // Loadpoints
-	savings    *Savings       // Savings
+	tariffs     tariff.Tariffs           // Tariff
+	loadpoints  []*LoadPoint             // Loadpoints
+	coordinator *coordinator.Coordinator // Savings
+	savings     *Savings                 // Savings
 
 	// cached state
 	gridPower       float64 // Grid power
@@ -70,6 +72,7 @@ func NewSiteFromConfig(
 	cp configProvider,
 	other map[string]interface{},
 	loadpoints []*LoadPoint,
+	vehicles []api.Vehicle,
 	tariffs tariff.Tariffs,
 ) (*Site, error) {
 	site := NewSite()
@@ -80,8 +83,14 @@ func NewSiteFromConfig(
 	Voltage = site.Voltage
 	site.loadpoints = loadpoints
 	site.tariffs = tariffs
+	site.coordinator = coordinator.New(log, vehicles)
 	site.savings = NewSavings(tariffs)
 
+	// give loadpoints access to vehicles
+	for _, lp := range loadpoints {
+		lp.coordinator = coordinator.NewAdapter(lp, site.coordinator)
+	}
+
 	if site.Meters.GridMeterRef != "" {
 		site.gridMeter = cp.Meter(site.Meters.GridMeterRef)
 	}
@@ -159,6 +168,16 @@ func meterCapabilities(name string, meter interface{}) string {
 
 // DumpConfig site configuration
 func (site *Site) DumpConfig() {
+	// verify vehicle detection
+	if vehicles := site.GetVehicles(); len(vehicles) > 1 {
+		for _, v := range vehicles {
+			if _, ok := v.(api.ChargeState); !ok {
+				site.log.WARN.Printf("vehicle '%s' does not support automatic detection", v.Title())
+				break
+			}
+		}
+	}
+
 	site.log.INFO.Println("site config:")
 	site.log.INFO.Printf("  meters:      grid %s pv %s battery %s",
 		presence[site.gridMeter != nil],
@@ -186,6 +205,21 @@ func (site *Site) DumpConfig() {
 		}
 	}
 
+	if vehicles := site.GetVehicles(); len(vehicles) > 0 {
+		site.log.INFO.Println("  vehicles:")
+
+		for i, v := range vehicles {
+			_, rng := v.(api.VehicleRange)
+			_, finish := v.(api.VehicleFinishTimer)
+			_, status := v.(api.ChargeState)
+			_, climate := v.(api.VehicleClimater)
+			_, wakeup := v.(api.Resurrector)
+			site.log.INFO.Printf("    vehicle %d: range %s finish %s status %s climate %s wakeup %s",
+				i+1, presence[rng], presence[finish], presence[status], presence[climate], presence[wakeup],
+			)
+		}
+	}
+
 	for i, lp := range site.loadpoints {
 		lp.log.INFO.Printf("loadpoint %d:", i+1)
 		lp.log.INFO.Printf("  mode:        %s", lp.GetMode())
@@ -210,19 +244,6 @@ func (site *Site) DumpConfig() {
 		if lp.HasChargeMeter() {
 			lp.log.INFO.Printf(meterCapabilities("charge", lp.chargeMeter))
 		}
-
-		lp.log.INFO.Printf("  vehicles:    %s", presence[len(lp.vehicles) > 0])
-
-		for i, v := range lp.vehicles {
-			_, rng := v.(api.VehicleRange)
-			_, finish := v.(api.VehicleFinishTimer)
-			_, status := v.(api.ChargeState)
-			_, climate := v.(api.VehicleClimater)
-			_, wakeup := v.(api.Resurrector)
-			lp.log.INFO.Printf("    vehicle %d: range %s finish %s status %s climate %s wakeup %s",
-				i+1, presence[rng], presence[finish], presence[status], presence[climate], presence[wakeup],
-			)
-		}
 	}
 }
 
diff --git a/core/site/api.go b/core/site/api.go
index bb15686a..3f99db3f 100644
--- a/core/site/api.go
+++ b/core/site/api.go
@@ -1,15 +1,35 @@
 package site
 
-import "github.com/evcc-io/evcc/core/loadpoint"
+import (
+	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/core/loadpoint"
+)
 
 // API is the external site API
 type API interface {
 	Healthy() bool
 	LoadPoints() []loadpoint.API
+
+	//
+	// battery
+	//
+
 	GetBufferSoC() float64
 	SetBufferSoC(float64) error
 	GetPrioritySoC() float64
 	SetPrioritySoC(float64) error
+
+	//
+	// power and energy
+	//
+
 	GetResidualPower() float64
 	SetResidualPower(float64) error
+
+	//
+	// vehicles
+	//
+
+	// GetVehicles is the list of vehicles
+	GetVehicles() []api.Vehicle
 }
diff --git a/core/site_api.go b/core/site_api.go
index b15969fa..902b930f 100644
--- a/core/site_api.go
+++ b/core/site_api.go
@@ -3,6 +3,7 @@ package core
 import (
 	"errors"
 
+	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/core/site"
 )
 
@@ -69,3 +70,10 @@ func (site *Site) SetResidualPower(power float64) error {
 
 	return nil
 }
+
+// GetVehicles is the list of vehicles
+func (site *Site) GetVehicles() []api.Vehicle {
+	site.Lock()
+	defer site.Unlock()
+	return site.coordinator.GetVehicles()
+}
diff --git a/evcc.dist.yaml b/evcc.dist.yaml
index 0fdc6b7b..a2c46c67 100644
--- a/evcc.dist.yaml
+++ b/evcc.dist.yaml
@@ -18,7 +18,7 @@ interval: 10s # control cycle interval
 # log settings
 log: info
 levels:
-  core: debug
+  site: debug
   lp-1: debug
   lp-2: debug
 
@@ -103,7 +103,7 @@ loadpoints:
       min: 0 # immediately charge to 0% regardless of mode unless "off" (disabled)
       target: 100 # always charge to 100%
       estimate: false # set true to interpolate between api updates
-    phases: 3 # ev phases (default 3)
+    phases: 3 # electrical connection (normal charger: default 3 for 3 phase, 1p3p charger: 0 for "auto" or 1/3 for fixed phases)
     enable: # pv mode enable behavior
       delay: 1m # threshold must be exceeded for this long
       threshold: 0 # grid power threshold (in Watts, negative=export). If zero, export must exceed minimum charge power to enable
diff --git a/go.mod b/go.mod
index 2d4b36bb..9c4216a5 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,6 @@ require (
 	github.com/BurntSushi/toml v1.1.0
 	github.com/Masterminds/sprig/v3 v3.2.2
 	github.com/PuerkitoBio/goquery v1.8.0
-	github.com/alvaroloes/enumer v1.1.2
 	github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef
 	github.com/avast/retry-go/v3 v3.1.1
 	github.com/aws/aws-sdk-go v1.44.27
@@ -20,6 +19,7 @@ require (
 	github.com/containrrr/shoutrrr v0.6.0
 	github.com/coreos/go-oidc/v3 v3.2.0
 	github.com/denisbrodbeck/machineid v1.0.1
+	github.com/dmarkham/enumer v1.5.2
 	github.com/dustin/go-humanize v1.0.0
 	github.com/dylanmei/iso8601 v0.1.0
 	github.com/eclipse/paho.mqtt.golang v1.3.5
@@ -66,7 +66,7 @@ require (
 	github.com/prometheus/client_golang v1.12.2
 	github.com/prometheus/common v0.34.0
 	github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
-	github.com/samber/lo v1.21.0
+	github.com/samber/lo v1.27.0
 	github.com/shurcooL/graphql v0.0.0-20220520033453-bdb1221e171e
 	github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
 	github.com/spf13/cobra v1.4.0
@@ -79,7 +79,7 @@ require (
 	github.com/writeas/go-strip-markdown v2.0.1+incompatible
 	gitlab.com/bboehmke/sunny v0.15.1-0.20211022160056-2fba1c86ade6
 	golang.org/x/exp v0.0.0-20220602145555-4a0574d9293f
-	golang.org/x/net v0.0.0-20220531201128-c960675eff93
+	golang.org/x/net v0.0.0-20220722155237-a158d28d115b
 	golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401
 	golang.org/x/text v0.3.7
 	google.golang.org/api v0.82.0
@@ -156,12 +156,11 @@ require (
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 	go.opencensus.io v0.23.0 // indirect
 	golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
-	golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
-	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
-	golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
+	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
+	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
 	golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect
-	golang.org/x/tools v0.1.10 // indirect
-	golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
+	golang.org/x/tools v0.1.12 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 // indirect
 	gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
diff --git a/go.sum b/go.sum
index 34dfabcf..d7968e8a 100644
--- a/go.sum
+++ b/go.sum
@@ -86,7 +86,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/alvaroloes/enumer v1.1.2 h1:5khqHB33TZy1GWCO/lZwcroBFh7u+0j40T83VUbfAMY=
 github.com/alvaroloes/enumer v1.1.2/go.mod h1:FxrjvuXoDAx9isTJrv4c+T410zFi0DtXIT0m65DJ+Wo=
 github.com/andig/go-powerwall v0.2.1-0.20220205120646-e5220ad9a9a0 h1:9fQ/afZNwqEnlAfozBiROWew2FjF2PctrEUVxUncM0Q=
 github.com/andig/go-powerwall v0.2.1-0.20220205120646-e5220ad9a9a0/go.mod h1:NA12RXBKXFQFx3Nb9xMTQjHIphu1Fl64i/gQMuRdMZc=
@@ -204,6 +203,7 @@ github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMS
 github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dmarkham/enumer v1.5.2 h1:dZxvuDqF8oL1fg9vxp1TjqHJ0JSL9ADU+hHbisgsRB8=
 github.com/dmarkham/enumer v1.5.2/go.mod h1:jZ3PNbNJDEkFGx54MlkSjnDQUo7445l7/guoKdh9cY8=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
@@ -819,8 +819,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA=
-github.com/samber/lo v1.21.0 h1:FSby8pJQtX4KmyddTCCGhc3JvnnIVrDA+NW37rG+7G8=
-github.com/samber/lo v1.21.0/go.mod h1:2I7tgIv8Q1SG2xEIkRq0F2i2zgxVpnyPOP0d3Gj2r+A=
+github.com/samber/lo v1.27.0 h1:GOyDWxsblvqYobqsmUuMddPa2/mMzkKyojlXol4+LaQ=
+github.com/samber/lo v1.27.0/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg=
 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -1020,8 +1020,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1090,8 +1091,8 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220526153639-5463443f8c37/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
-golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1126,8 +1127,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
-golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1228,8 +1229,9 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
@@ -1321,8 +1323,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
 golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/meter/tq-em420.go b/meter/tq-em420.go
new file mode 100644
index 00000000..a59e1527
--- /dev/null
+++ b/meter/tq-em420.go
@@ -0,0 +1,167 @@
+package meter
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/cookiejar"
+	"strings"
+	"time"
+
+	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/provider"
+	"github.com/evcc-io/evcc/util"
+	"github.com/evcc-io/evcc/util/request"
+)
+
+func init() {
+	registry.Add("tq-em420", NewTqEm420FromConfig)
+}
+
+type TqEm420Data struct {
+	SmartMeter struct {
+		ConfigurationID string `json:"configuration_id"`
+		Status          string `json:"status"`
+		Timestamp       struct {
+			Seconds float64 `json:"seconds"`
+			Nanos   float64 `json:"nanos"`
+		} `json:"timestamp"`
+		Values struct {
+			ActiveEnergyP     float64 `json:"active_energy_+"`
+			ActiveEnergyL1P   float64 `json:"active_energy_+_L1"`
+			ActiveEnergyL2P   float64 `json:"active_energy_+_L2"`
+			ActiveEnergyL3P   float64 `json:"active_energy_+_L3"`
+			ActiveEnergyM     float64 `json:"active_energy_-"`
+			ActiveEnergyL1M   float64 `json:"active_energy_-_L1"`
+			ActiveEnergyL2M   float64 `json:"active_energy_-_L2"`
+			ActiveEnergyL3M   float64 `json:"active_energy_-_L3"`
+			ActivePowerP      float64 `json:"active_power_+"`
+			ActivePowerL1P    float64 `json:"active_power_+_L1"`
+			ActivePowerL2P    float64 `json:"active_power_+_L2"`
+			ActivePowerL3P    float64 `json:"active_power_+_L3"`
+			ActivePowerM      float64 `json:"active_power_-"`
+			ActivePowerL1M    float64 `json:"active_power_-_L1"`
+			ActivePowerL2M    float64 `json:"active_power_-_L2"`
+			ActivePowerL3M    float64 `json:"active_power_-_L3"`
+			ApparentEnergyP   float64 `json:"apparent_energy_+"`
+			ApparentEnergyL1P float64 `json:"apparent_energy_+_L1"`
+			ApparentEnergyL2P float64 `json:"apparent_energy_+_L2"`
+			ApparentEnergyL3P float64 `json:"apparent_energy_+_L3"`
+			ApparentEnergyM   float64 `json:"apparent_energy_-"`
+			ApparentEnergyL1M float64 `json:"apparent_energy_-_L1"`
+			ApparentEnergyL2M float64 `json:"apparent_energy_-_L2"`
+			ApparentEnergyL3M float64 `json:"apparent_energy_-_L3"`
+			ApparentPowerP    float64 `json:"apparent_power_+"`
+			ApparentPowerL1P  float64 `json:"apparent_power_+_L1"`
+			ApparentPowerL2P  float64 `json:"apparent_power_+_L2"`
+			ApparentPowerL3P  float64 `json:"apparent_power_+_L3"`
+			ApparentPowerM    float64 `json:"apparent_power_-"`
+			ApparentPowerL1M  float64 `json:"apparent_power_-_L1"`
+			ApparentPowerL2M  float64 `json:"apparent_power_-_L2"`
+			ApparentPowerL3M  float64 `json:"apparent_power_-_L3"`
+			CurrentL1         float64 `json:"current_L1"`
+			CurrentL2         float64 `json:"current_L2"`
+			CurrentL3         float64 `json:"current_L3"`
+			PowerFactor       float64 `json:"power_factor"`
+			PowerFactorL1     float64 `json:"power_factor_L1"`
+			PowerFactorL2     float64 `json:"power_factor_L2"`
+			PowerFactorL3     float64 `json:"power_factor_L3"`
+			ReactiveEnergyP   float64 `json:"reactive_energy_+"`
+			ReactiveEnergyL1P float64 `json:"reactive_energy_+_L1"`
+			ReactiveEnergyL2P float64 `json:"reactive_energy_+_L2"`
+			ReactiveEnergyL3P float64 `json:"reactive_energy_+_L3"`
+			ReactiveEnergyM   float64 `json:"reactive_energy_-"`
+			ReactiveEnergyL1M float64 `json:"reactive_energy_-_L1"`
+			ReactiveEnergyL2M float64 `json:"reactive_energy_-_L2"`
+			ReactiveEnergyL3M float64 `json:"reactive_energy_-_L3"`
+			ReactivePowerP    float64 `json:"reactive_power_+"`
+			ReactivePowerL1P  float64 `json:"reactive_power_+_L1"`
+			ReactivePowerL2P  float64 `json:"reactive_power_+_L2"`
+			ReactivePowerL3P  float64 `json:"reactive_power_+_L3"`
+			ReactivePowerM    float64 `json:"reactive_power_-"`
+			ReactivePowerL1M  float64 `json:"reactive_power_-_L1"`
+			ReactivePowerL2M  float64 `json:"reactive_power_-_L2"`
+			ReactivePowerL3M  float64 `json:"reactive_power_-_L3"`
+			SupplyFrequency   float64 `json:"supply_frequency"`
+			VoltageL1         float64 `json:"voltage_L1"`
+			VoltageL2         float64 `json:"voltage_L2"`
+			VoltageL3         float64 `json:"voltage_L3"`
+		} `json:"values"`
+	} `json:"smart-meter"`
+}
+
+type TqEM420 struct {
+	dataG func() (TqEm420Data, error)
+}
+
+// NewTqEm420FromConfig creates a new configurable meter
+func NewTqEm420FromConfig(other map[string]interface{}) (api.Meter, error) {
+	cc := struct {
+		URI    string
+		Token  string
+		Device string
+		Cache  time.Duration
+	}{
+		Cache: time.Second,
+	}
+
+	if err := util.DecodeOther(other, &cc); err != nil {
+		return nil, err
+	}
+
+	log := util.NewLogger("tq-em420").Redact(cc.Token)
+
+	client := request.NewHelper(log)
+	client.Jar, _ = cookiejar.New(nil)
+
+	base := util.DefaultScheme(strings.TrimRight(cc.URI, "/"), "http")
+
+	dataG := provider.Cached(func() (TqEm420Data, error) {
+		var res TqEm420Data
+
+		headers := map[string]string{
+			"Accept":        "application/json",
+			"Authorization": "Bearer " + cc.Token,
+		}
+
+		req, err := request.New(http.MethodGet, fmt.Sprintf("%s/api/json/"+cc.Device+"/values/smart-meter", base), nil, headers)
+
+		if err == nil {
+			err = client.DoJSON(req, &res)
+		}
+
+		return res, err
+	}, cc.Cache)
+
+	m := &TqEM420{
+		dataG: dataG,
+	}
+
+	_, err := dataG()
+	if err != nil {
+		return nil, err
+	}
+
+	return m, nil
+}
+
+func (m *TqEM420) CurrentPower() (float64, error) {
+	res, err := m.dataG()
+	return (res.SmartMeter.Values.ActivePowerP - res.SmartMeter.Values.ActivePowerM) / 1e3, err
+}
+
+var _ api.MeterEnergy = (*TqEM420)(nil)
+
+func (m *TqEM420) TotalEnergy() (float64, error) {
+	res, err := m.dataG()
+	return res.SmartMeter.Values.ActiveEnergyP / 1e3, err
+}
+
+var _ api.MeterCurrent = (*TqEM420)(nil)
+
+func (m *TqEM420) Currents() (float64, float64, float64, error) {
+	res, err := m.dataG()
+	if err != nil {
+		return 0, 0, 0, err
+	}
+	return res.SmartMeter.Values.CurrentL1 / 1e3, res.SmartMeter.Values.CurrentL2 / 1e3, res.SmartMeter.Values.CurrentL3 / 1e3, nil
+}
diff --git a/server/http.go b/server/http.go
index 7aba840b..46252df1 100644
--- a/server/http.go
+++ b/server/http.go
@@ -21,8 +21,9 @@ type route struct {
 	HandlerFunc http.HandlerFunc
 }
 
-//lint:ignore U1000 if needed
 // routeLogger traces matched routes including their executing time
+//
+//lint:ignore U1000 if needed
 func routeLogger(inner http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		start := time.Now()
@@ -91,7 +92,7 @@ func NewHTTPd(addr string, site site.API, hub *SocketHub, cache *util.Cache) *HT
 			"phases":        {[]string{"POST", "OPTIONS"}, "/phases/{value:[0-9]+}", phasesHandler(lp)},
 			"targetcharge":  {[]string{"POST", "OPTIONS"}, "/targetcharge/{soc:[0-9]+}/{time:[0-9TZ:.-]+}", targetChargeHandler(lp)},
 			"targetcharge2": {[]string{"DELETE", "OPTIONS"}, "/targetcharge", targetChargeRemoveHandler(lp)},
-			"vehicle":       {[]string{"POST", "OPTIONS"}, "/vehicle/{vehicle:[0-9]+}", vehicleHandler(lp)},
+			"vehicle":       {[]string{"POST", "OPTIONS"}, "/vehicle/{vehicle:[0-9]+}", vehicleHandler(site, lp)},
 			"vehicle2":      {[]string{"DELETE", "OPTIONS"}, "/vehicle", vehicleRemoveHandler(lp)},
 			"remotedemand":  {[]string{"POST", "OPTIONS"}, "/remotedemand/{demand:[a-z]+}/{source::[0-9a-zA-Z_-]+}", remoteDemandHandler(lp)},
 		}
diff --git a/server/http_handler.go b/server/http_handler.go
index 747b6fca..c37093d8 100644
--- a/server/http_handler.go
+++ b/server/http_handler.go
@@ -244,14 +244,14 @@ func targetChargeRemoveHandler(loadpoint loadpoint.API) http.HandlerFunc {
 }
 
 // vehicleHandler sets active vehicle
-func vehicleHandler(loadpoint loadpoint.API) http.HandlerFunc {
+func vehicleHandler(site site.API, loadpoint loadpoint.API) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		vars := mux.Vars(r)
 
 		valS, ok := vars["vehicle"]
 		val, err := strconv.Atoi(valS)
 
-		vehicles := loadpoint.GetVehicles()
+		vehicles := site.GetVehicles()
 		if !ok || val >= len(vehicles) || err != nil {
 			w.WriteHeader(http.StatusBadRequest)
 			return
diff --git a/server/mqtt.go b/server/mqtt.go
index b230ecaf..49cf02a5 100644
--- a/server/mqtt.go
+++ b/server/mqtt.go
@@ -85,7 +85,7 @@ func (m *MQTT) publish(topic string, retained bool, payload interface{}) {
 	m.publishSingleValue(topic, retained, payload)
 }
 
-func (m *MQTT) listenSetters(topic string, lp loadpoint.API) {
+func (m *MQTT) listenSetters(topic string, site site.API, lp loadpoint.API) {
 	m.Handler.ListenSetter(topic+"/mode/set", func(payload string) {
 		lp.SetMode(api.ChargeMode(payload))
 	})
@@ -116,7 +116,7 @@ func (m *MQTT) listenSetters(topic string, lp loadpoint.API) {
 	})
 	m.Handler.ListenSetter(topic+"/vehicle/set", func(payload string) {
 		if vehicle, err := strconv.Atoi(payload); err == nil {
-			vehicles := lp.GetVehicles()
+			vehicles := site.GetVehicles()
 			if vehicle < len(vehicles) {
 				lp.SetVehicle(vehicles[vehicle])
 			}
@@ -156,7 +156,7 @@ func (m *MQTT) Run(site site.API, in <-chan util.Param) {
 	// loadpoint setters
 	for id, lp := range site.LoadPoints() {
 		topic := fmt.Sprintf("%s/loadpoints/%d", m.root, id+1)
-		m.listenSetters(topic, lp)
+		m.listenSetters(topic, site, lp)
 	}
 
 	// remove deprecated topics
diff --git a/templates/definition/meter/fronius-gen24.yaml b/templates/definition/meter/fronius-gen24.yaml
index f4296847..853a1229 100644
--- a/templates/definition/meter/fronius-gen24.yaml
+++ b/templates/definition/meter/fronius-gen24.yaml
@@ -24,9 +24,9 @@ render: |
   # reference: https://github.com/volkszaehler/mbmd/blob/master/meters/sunspec/models.go
   {{- if eq .usage "grid" }}
   type: modbus
-  model: sunspec
   uri: {{ .host }}:{{ .port }}
   id: 200
+  model: sunspec
   {{- if eq .integer "true" }}
   # sunspec model 203 (int+sf) meter
   power: 203:W
@@ -49,14 +49,14 @@ render: |
     source: calc
     add:
     - source: modbus
-      model: sunspec
       uri: {{ .host }}:{{ .port }}
       id: 1
+      model: sunspec
       value: 160:1:DCW # mppt 1
     - source: modbus
-      model: sunspec
       uri: {{ .host }}:{{ .port }}
       id: 1
+      model: sunspec
       value: 160:2:DCW # mppt 2
   {{- end }}
   {{- if eq .usage "battery" }}
@@ -65,20 +65,20 @@ render: |
     source: calc
     add:
     - source: modbus
-      model: sunspec
       uri: {{ .host }}:{{ .port }}
       id: 1
+      model: sunspec
       value: 160:3:DCW # mppt 3 charge
       scale: -1
     - source: modbus
-      model: sunspec
       uri: {{ .host }}:{{ .port }}
       id: 1
+      model: sunspec
       value: 160:4:DCW # mppt 4 discharge
   soc:
     source: modbus
-    model: sunspec
     uri: {{ .host }}:{{ .port }}
     id: 1
+    model: sunspec
     value: ChargeState
   {{- end }}
diff --git a/templates/definition/meter/kostal-ksem.yaml b/templates/definition/meter/kostal-ksem.yaml
index c031d6d1..650c9611 100644
--- a/templates/definition/meter/kostal-ksem.yaml
+++ b/templates/definition/meter/kostal-ksem.yaml
@@ -11,5 +11,5 @@ params:
     id: 71
 render: |
   type: modbus
-  model: sunspec
   {{- include "modbus" . }}
+  model: sunspec
diff --git a/templates/definition/meter/kostal-plenticore.yaml b/templates/definition/meter/kostal-plenticore.yaml
index 4dbfe07a..fa14024b 100644
--- a/templates/definition/meter/kostal-plenticore.yaml
+++ b/templates/definition/meter/kostal-plenticore.yaml
@@ -25,12 +25,12 @@ render: |
     source: calc
     add: # The add plugin sums up both string values
     - source: modbus
+      {{- include "modbus" . | indent 4 }}
       model: sunspec
-  {{- include "modbus" . | indent 4 }}
       value: 160:1:DCW # string 1
     - source: modbus
+      {{- include "modbus" . | indent 4 }}
       model: sunspec
-  {{- include "modbus" . | indent 4 }}
       value: 160:2:DCW # string 2
   {{- end }}
   {{- if eq .usage "battery" }}
diff --git a/templates/definition/meter/solaredge-hybrid.yaml b/templates/definition/meter/solaredge-hybrid.yaml
index 4eb8c83e..5160128c 100644
--- a/templates/definition/meter/solaredge-hybrid.yaml
+++ b/templates/definition/meter/solaredge-hybrid.yaml
@@ -24,11 +24,11 @@ render: |
   power:
   {{- if eq .usage "grid" }}
     source: modbus
-    model: sunspec
-    timeout: {{ .timeout }}
     uri: {{ .host }}:{{ .port }} # Port 502 (SetApp) or 1502 (LCD)
     id: 1
     subdevice: 1 # Metering device
+    timeout: {{ .timeout }}
+    model: sunspec
     value: 203:W
     scale: -1
   {{- end }}
@@ -36,15 +36,15 @@ render: |
     source: calc
     add:
     - source: modbus
-      model: sunspec
-      timeout: {{ .timeout }}
       uri: {{ .host }}:{{ .port }} # Port 502 (SetApp) or 1502 (LCD)
       id: 1
+      timeout: {{ .timeout }}
+      model: sunspec
       value: 103:DCW
     - source: modbus
-      timeout: {{ .timeout }}
       uri: {{ .host }}:{{ .port }} # Port 502 (SetApp) or 1502 (LCD)
       id: 1
+      timeout: {{ .timeout }}
       register:
         address: 62836 # Battery 1 Instantaneous Power
         type: holding
diff --git a/templates/definition/meter/solaredge-inverter.yaml b/templates/definition/meter/solaredge-inverter.yaml
index f1233d5f..964c4826 100644
--- a/templates/definition/meter/solaredge-inverter.yaml
+++ b/templates/definition/meter/solaredge-inverter.yaml
@@ -18,11 +18,14 @@ render: |
   type: custom
   power:
     source: modbus
-    model: sunspec
     {{- include "modbus" . | indent 2 }}
     timeout: {{ .timeout }}
   {{- if eq .usage "grid" }}
     subdevice: 1
+    model: sunspec
     value: 203:W # sunspec 3-phase meter power reading
     scale: -1
-  {{- end -}}
+  {{- else }}
+    model: sunspec
+    value: Power
+  {{- end }}
diff --git a/templates/definition/meter/sunspec-hybrid.yaml b/templates/definition/meter/sunspec-hybrid.yaml
index c9f51ce9..a10a1231 100644
--- a/templates/definition/meter/sunspec-hybrid.yaml
+++ b/templates/definition/meter/sunspec-hybrid.yaml
@@ -45,12 +45,12 @@ render: |
     source: calc
     add:
     - source: modbus
-      model: sunspec
       {{- include "modbus" . | indent 4 }}
+      model: sunspec
       value: 160:1:DCW # mppt 1
     - source: modbus
-      model: sunspec
       {{- include "modbus" . | indent 4 }}
+      model: sunspec
       value: 160:2:DCW # mppt 2
   {{- end }}
   {{- if eq .usage "battery" }}
@@ -59,16 +59,17 @@ render: |
     source: calc
     add:
     - source: modbus
-      model: sunspec
       {{- include "modbus" . | indent 4 }}
+      model: sunspec
       value: 160:3:DCW # mppt 3 charge
       scale: -1
     - source: modbus
-      model: sunspec
       {{- include "modbus" . | indent 4 }}
+      model: sunspec
       value: 160:4:DCW # mppt 4 discharge
   soc:
     source: modbus
     {{- include "modbus" . | indent 2 }}
+    model: sunspec
     value: ChargeState
   {{- end }}
diff --git a/templates/definition/meter/sunspec-inverter.yaml b/templates/definition/meter/sunspec-inverter.yaml
index ac5babcb..ae682ff8 100644
--- a/templates/definition/meter/sunspec-inverter.yaml
+++ b/templates/definition/meter/sunspec-inverter.yaml
@@ -22,6 +22,7 @@ render: |
   type: modbus
   {{- include "modbus" . }}
   {{- if eq .usage "grid" }}
+  model: sunspec
   {{- if eq .integer "true" }}
   # sunspec model 203 (int+sf) meter
   power: 203:W
@@ -37,4 +38,7 @@ render: |
     - 213:AphB
     - 213:AphC
   {{- end }}
+  {{- else }}
+  model: sunspec
+  power: Power
   {{- end }}
diff --git a/templates/definition/meter/tq-em420.yaml b/templates/definition/meter/tq-em420.yaml
new file mode 100644
index 00000000..4d0629b0
--- /dev/null
+++ b/templates/definition/meter/tq-em420.yaml
@@ -0,0 +1,33 @@
+template: tq-em420
+products:
+  - brand: TQ
+    description:
+      generic: Energy Manager EM420
+params:
+  - name: usage
+    choice: ["grid"]
+  - name: host
+  - name: port
+    default: 80
+  - name: device
+    default: "local"
+    description:
+      de: API Gerätepfad
+      en: API Device Path
+    help:
+      de: JSON-Schnittstelle -> Datenendpunkt
+      en: JSON-API -> Data Endpoint
+  - name: token
+    required: true
+    example: ey...
+    description:
+      de: Accesstoken
+      en: Access token
+    help:
+      de: Token des EM420 (Erstellen unter Profil -> Zugangsschlüssel)
+      en: Access token for EM420 (Create in Profile -> Access tokens)
+render: |
+  type: tq-em420
+  uri: http://{{ .host }}:{{ .port }}
+  token: {{ .token }}
+  device: {{ .device }}
diff --git a/templates/docs/meter/tq-em420_0.yaml b/templates/docs/meter/tq-em420_0.yaml
new file mode 100644
index 00000000..9eea1ab4
--- /dev/null
+++ b/templates/docs/meter/tq-em420_0.yaml
@@ -0,0 +1,13 @@
+product:
+  brand: TQ
+  description: Energy Manager EM420
+render:
+  - usage: grid
+    default: |
+      type: template
+      template: tq-em420
+      usage: grid
+      host: 192.0.2.2 # IP-Adresse oder Hostname
+      port: 80 # Port # Optional
+      device: local # JSON-Schnittstelle -> Datenendpunkt # Optional
+      token: ey... # Token des EM420 (Erstellen unter Profil -> Zugangsschlüssel)
diff --git a/tools.go b/tools.go
index e8726aaf..03087581 100644
--- a/tools.go
+++ b/tools.go
@@ -3,6 +3,6 @@
 package main
 
 import (
-	_ "github.com/alvaroloes/enumer"
+	_ "github.com/dmarkham/enumer"
 	_ "github.com/golang/mock/mockgen"
 )
diff --git a/vehicle/helper.go b/vehicle/helper.go
index ad5ae957..b2a79b7a 100644
--- a/vehicle/helper.go
+++ b/vehicle/helper.go
@@ -5,7 +5,7 @@ import (
 	"strings"
 )
 
-// ensureVehicleWithFeature extracts VIN from list of VINs returned from `list`` function
+// ensureVehicleWithFeature extracts VIN from list of VINs returned from `list` function
 func ensureVehicle(vin string, list func() ([]string, error)) (string, error) {
 	vin, _, err := ensureVehicleWithFeature(vin, list, func(v string) (string, string) {
 		return v, ""
@@ -14,7 +14,7 @@ func ensureVehicle(vin string, list func() ([]string, error)) (string, error) {
 	return vin, err
 }
 
-// ensureVehicleWithFeature extracts VIN and feature from list of vehicles of type V returned from `list`` function
+// ensureVehicleWithFeature extracts VIN and feature from list of vehicles of type V returned from `list` function
 func ensureVehicleWithFeature[Vehicle, Feature any](
 	vin string,
 	list func() ([]Vehicle, error),


Test patch:
diff --git a/core/coordinator_test.go b/core/coordinator/coordinator_test.go
similarity index 90%
rename from core/coordinator_test.go
rename to core/coordinator/coordinator_test.go
index 4fe7def0..4dfc80f3 100644
--- a/core/coordinator_test.go
+++ b/core/coordinator/coordinator_test.go
@@ -1,4 +1,4 @@
-package core
+package coordinator
 
 import (
 	"testing"
@@ -38,8 +38,8 @@ func TestVehicleDetectByStatus(t *testing.T) {
 	log := util.NewLogger("foo")
 	vehicles := []api.Vehicle{v1, v2}
 
-	lp := &LoadPoint{}
-	c := &vehicleCoordinator{make(map[api.Vehicle]loadpoint.API)}
+	var lp loadpoint.API
+	c := New(log, vehicles)
 
 	for _, tc := range tc {
 		t.Logf("%+v", tc)
@@ -49,7 +49,7 @@ func TestVehicleDetectByStatus(t *testing.T) {
 		v1.MockVehicle.EXPECT().Title().Return("v1").AnyTimes()
 		v2.MockVehicle.EXPECT().Title().Return("v2").AnyTimes()
 
-		res := c.identifyVehicleByStatus(log, lp, vehicles)
+		res := c.identifyVehicleByStatus(lp)
 		if tc.res != res {
 			t.Errorf("expected %v, got %v", tc.res, res)
 		}
diff --git a/core/loadpoint_test.go b/core/loadpoint_test.go
index 0fa07680..7a48ee04 100644
--- a/core/loadpoint_test.go
+++ b/core/loadpoint_test.go
@@ -390,15 +390,16 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
 	socEstimator := soc.NewEstimator(util.NewLogger("foo"), charger, vehicle, false)
 
 	lp := &LoadPoint{
-		log:          util.NewLogger("foo"),
-		bus:          evbus.New(),
-		clock:        clock,
-		charger:      charger,
-		chargeMeter:  &Null{},            // silence nil panics
-		chargeRater:  &Null{},            // silence nil panics
-		chargeTimer:  &Null{},            // silence nil panics
-		progress:     NewProgress(0, 10), // silence nil panics
-		wakeUpTimer:  NewTimer(),         // silence nil panics
+		log:         util.NewLogger("foo"),
+		bus:         evbus.New(),
+		clock:       clock,
+		charger:     charger,
+		chargeMeter: &Null{},            // silence nil panics
+		chargeRater: &Null{},            // silence nil panics
+		chargeTimer: &Null{},            // silence nil panics
+		progress:    NewProgress(0, 10), // silence nil panics
+		wakeUpTimer: NewTimer(),         // silence nil panics
+		// coordinator:  coordinator.NewDummy(), // silence nil panics
 		MinCurrent:   minA,
 		MaxCurrent:   maxA,
 		vehicle:      vehicle,      // needed for targetSoC check
diff --git a/core/loadpoint_vehicle_test.go b/core/loadpoint_vehicle_test.go
index b6884d33..de53ad37 100644
--- a/core/loadpoint_vehicle_test.go
+++ b/core/loadpoint_vehicle_test.go
@@ -6,6 +6,7 @@ import (
 	evbus "github.com/asaskevich/EventBus"
 	"github.com/benbjohnson/clock"
 	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/core/coordinator"
 	"github.com/evcc-io/evcc/mock"
 	"github.com/evcc-io/evcc/util"
 	"github.com/golang/mock/gomock"
@@ -61,10 +62,11 @@ func TestVehicleDetectByID(t *testing.T) {
 		t.Logf("%+v", tc)
 
 		lp := &LoadPoint{
-			log:      util.NewLogger("foo"),
-			vehicles: []api.Vehicle{v1, v2},
+			log: util.NewLogger("foo"),
 		}
 
+		lp.coordinator = coordinator.NewAdapter(lp, coordinator.New(util.NewLogger("foo"), []api.Vehicle{v1, v2}))
+
 		if tc.prepare != nil {
 			tc.prepare(tc)
 		}
@@ -206,7 +208,7 @@ func TestApplyVehicleDefaults(t *testing.T) {
 	}
 
 	lp.charger = charger
-	lp.vehicles = []api.Vehicle{vehicle}
+	lp.coordinator = coordinator.NewAdapter(lp, coordinator.New(util.NewLogger("foo"), []api.Vehicle{vehicle}))
 
 	const id = "don't call me stacey"
 	charger.MockIdentifier.EXPECT().Identify().Return(id, nil)
@@ -248,9 +250,10 @@ func TestReconnectVehicle(t *testing.T) {
 		MaxCurrent:  maxA,
 		phases:      1,
 		Mode:        api.ModeNow,
-		vehicles:    []api.Vehicle{vehicle},
 	}
 
+	lp.coordinator = coordinator.NewAdapter(lp, coordinator.New(util.NewLogger("foo"), []api.Vehicle{vehicle}))
+
 	attachListeners(t, lp)
 
 	// mode now