Repository: evcc-io/evcc
Pull request #10111 (2023-09-29 16:08:04)

OpenEVSE: refactor and simplify
Replace https://github.com/evcc-io/evcc/pull/10110 for modifications


Patch:
diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml
index 1c418150..25fce64a 100644
--- a/.github/workflows/default.yml
+++ b/.github/workflows/default.yml
@@ -16,7 +16,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       # - uses: actions/setup-go@v4
       - uses: erezrokah/setup-go@feat/add_cache_prefix
@@ -45,7 +45,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       - uses: erezrokah/setup-go@feat/add_cache_prefix
         with:
@@ -68,7 +68,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       # - uses: actions/setup-go@v4
       - uses: erezrokah/setup-go@feat/add_cache_prefix
@@ -85,7 +85,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       - uses: actions/setup-go@v4
         with:
@@ -106,7 +106,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       - uses: actions/setup-node@v3
         with:
@@ -141,7 +141,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       - uses: erezrokah/setup-go@feat/add_cache_prefix
         with:
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index 9b6aa8be..8d88aad2 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       - uses: actions/setup-go@v4
         with:
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 2c2b6458..cbd57f51 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -15,7 +15,7 @@ jobs:
     outputs:
       should_run: ${{ steps.should_run.outputs.should_run }}
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - name: print latest_commit
         run: echo ${{ github.sha }}
 
@@ -41,7 +41,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           ref: refs/heads/master # force master
           fetch-depth: 0
@@ -54,13 +54,13 @@ jobs:
           key: ${{ runner.os }}-${{ github.sha }}-dist
 
       - name: Login
-        uses: docker/login-action@v2
+        uses: docker/login-action@v3
         with:
           username: ${{ secrets.DOCKER_USER }}
           password: ${{ secrets.DOCKER_PASS }}
 
       - name: Setup Buildx
-        uses: docker/setup-buildx-action@v2
+        uses: docker/setup-buildx-action@v3
 
       - name: Publish
         uses: docker/build-push-action@v4
@@ -78,7 +78,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           fetch-depth: 0
 
@@ -101,7 +101,7 @@ jobs:
           key: ${{ runner.os }}-${{ github.sha }}-dist
 
       - name: Create nightly build
-        uses: goreleaser/goreleaser-action@v4
+        uses: goreleaser/goreleaser-action@v5
         with:
           version: latest
           args: --snapshot -f .goreleaser-nightly.yml --clean
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c50b37cc..8dd4dd5b 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,22 +20,22 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           fetch-depth: 0
 
       - name: Login
-        uses: docker/login-action@v2
+        uses: docker/login-action@v3
         with:
           username: ${{ secrets.DOCKER_USER }}
           password: ${{ secrets.DOCKER_PASS }}
 
       - name: Setup Buildx
-        uses: docker/setup-buildx-action@v2
+        uses: docker/setup-buildx-action@v3
 
       - name: Meta
         id: meta
-        uses: docker/metadata-action@v4
+        uses: docker/metadata-action@v5
         with:
           images: |
             evcc/evcc
@@ -56,7 +56,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           fetch-depth: 0
 
@@ -94,7 +94,7 @@ jobs:
       #   run: make image-rootfs
 
       - name: Create Github Release
-        uses: goreleaser/goreleaser-action@v4
+        uses: goreleaser/goreleaser-action@v5
         with:
           version: latest
           args: release --clean
@@ -115,7 +115,7 @@ jobs:
     env:
       FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - uses: superfly/flyctl-actions/setup-flyctl@master
       - run: flyctl deploy --local-only --config packaging/fly.toml
 
diff --git a/.github/workflows/schema.yml b/.github/workflows/schema.yml
index 1ed057e8..89e45949 100644
--- a/.github/workflows/schema.yml
+++ b/.github/workflows/schema.yml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - uses: nwisbeta/validate-yaml-schema@v2.0.0
         with:
           yamlSchemasJson: |
diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
index b535b37a..c23d1757 100644
--- a/.github/workflows/website.yml
+++ b/.github/workflows/website.yml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       - uses: actions/setup-go@v4
         with:
diff --git a/README.md b/README.md
index c142fbb5..0e63f59f 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ evcc is an extensible EV Charge Controller with PV integration implemented in [G
 
 ## Getting Started
 
-You'll find everything you need in our [documentation](https://docs.evcc.io/) (German).
+You'll find everything you need in our [documentation](https://docs.evcc.io/).
 
 ## Contribute
 
diff --git a/assets/js/api.js b/assets/js/api.js
index 3ca9d5ca..211755ec 100644
--- a/assets/js/api.js
+++ b/assets/js/api.js
@@ -20,7 +20,7 @@ api.interceptors.response.use(
       const url = error.config.baseURL + error.config.url;
       message += `: API request failed ${url}`;
     }
-    window.app.error({ message });
+    window.app.raise({ message });
     return Promise.reject(error);
   }
 );
diff --git a/assets/js/app.js b/assets/js/app.js
index d86ec263..cb668f95 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -27,10 +27,10 @@ const app = createApp({
   },
   methods: {
     raise: function (msg) {
-      console[msg.type](msg);
+      if (!msg.level) msg.level = "error";
       const now = new Date();
       const latestMsg = this.notifications[0];
-      if (latestMsg && latestMsg.message === msg.message) {
+      if (latestMsg && latestMsg.message === msg.message && latestMsg.lp === msg.lp) {
         latestMsg.count++;
         latestMsg.time = now;
       } else {
@@ -47,20 +47,12 @@ const app = createApp({
     clear: function () {
       this.notifications = [];
     },
-    error: function (msg) {
-      msg.type = "error";
-      this.raise(msg);
-    },
     setOnline: function () {
       this.offline = false;
     },
     setOffline: function () {
       this.offline = true;
     },
-    warn: function (msg) {
-      msg.type = "warn";
-      this.raise(msg);
-    },
   },
   render: function () {
     return h(App, { notifications: this.notifications, offline: this.offline });
diff --git a/assets/js/baseapi.js b/assets/js/baseapi.js
index 380b546d..da1299ad 100644
--- a/assets/js/baseapi.js
+++ b/assets/js/baseapi.js
@@ -12,7 +12,7 @@ baseAPI.interceptors.response.use(
   (error) => {
     const url = error.config.baseURL + error.config.url;
     const message = `${error.message}: API request failed ${url}`;
-    window.app.error({ message });
+    window.app.raise({ message });
   }
 );
 
diff --git a/assets/js/communityApi.js b/assets/js/communityApi.js
index 292789f6..140ac651 100644
--- a/assets/js/communityApi.js
+++ b/assets/js/communityApi.js
@@ -13,7 +13,7 @@ api.interceptors.response.use(
   (error) => {
     const url = error.config.baseURL + error.config.url;
     const message = `${error.message}: API request failed ${url}`;
-    window.app.error({ message });
+    window.app.raise({ message });
     return Promise.reject(error);
   }
 );
diff --git a/assets/js/components/Loadpoint.vue b/assets/js/components/Loadpoint.vue
index 5b380b21..04b869af 100644
--- a/assets/js/components/Loadpoint.vue
+++ b/assets/js/components/Loadpoint.vue
@@ -9,7 +9,7 @@
 						class="me-2 flex-shrink-0"
 					/>
 					<div class="text-truncate">
-						{{ title || $t("main.loadpoint.fallbackName") }}
+						{{ loadpointTitle }}
 					</div>
 				</h3>
 				<LoadpointSettingsButton
@@ -210,6 +210,9 @@ export default {
 		};
 	},
 	computed: {
+		loadpointTitle: function () {
+			return this.title || this.$t("main.loadpoint.fallbackName");
+		},
 		integratedDevice: function () {
 			return this.chargerFeatureIntegratedDevice;
 		},
diff --git a/assets/js/components/Notifications.story.vue b/assets/js/components/Notifications.story.vue
index 350af2d1..ee63e4f1 100644
--- a/assets/js/components/Notifications.story.vue
+++ b/assets/js/components/Notifications.story.vue
@@ -14,46 +14,46 @@ const state = reactive({
 	notifications: [
 		{
 			message: "Server unavailable",
-			type: "error",
+			level: "error",
 			time: timeAgo(),
 			count: 1,
 		},
 		{
 			message: "charger out of sync: expected disabled, got enabled",
-			type: "warn",
+			level: "warn",
 			count: 4,
 			time: timeAgo(0, 0, 42),
 		},
 		{
 			message: "Sponsortoken: x509: certificate has expired",
-			type: "error",
+			level: "error",
 			count: 1,
 			time: timeAgo(1, 12, 44),
 		},
 		{
 			message:
 				"Block device full: can not write to /tmp/evcc/foobarloremawefhwuiehfwuiehfwiauhefjkajowaeigjwalvmoweivwail",
-			type: "error",
+			level: "error",
 			count: 1,
 			time: timeAgo(1, 12, 44),
 		},
 		{
 			message: "charger out of sync: expected disabled, got enabled",
-			type: "warn",
+			level: "warn",
 			count: 4,
 			time: timeAgo(1, 22, 0),
 		},
 		{
 			message:
 				"vehicle remote charge start: invalid character '<' looking for beginning of value",
-			type: "warn",
+			level: "warn",
 			count: 3,
 			time: timeAgo(4, 2, 0),
 		},
 		{
 			message:
 				"Amet irure quis incididunt voluptate esse. Commodo ea sunt est ipsum tempor nisi laboris voluptate labore elit laborum. Ex irure commodo reprehenderit consequat consequat do ad tempor aliquip deserunt eu. Laboris minim nostrud quis nisi. Dolor occaecat reprehenderit velit dolore exercitation cupidatat et voluptate. Nulla pariatur deserunt esse minim nisi nisi nulla. Sit eiusmod do incididunt sint minim pariatur aute.",
-			type: "warn",
+			level: "warn",
 			count: 1,
 			time: timeAgo(5, 2, 44),
 		},
diff --git a/assets/js/components/Notifications.vue b/assets/js/components/Notifications.vue
index 20507b39..4643d275 100644
--- a/assets/js/components/Notifications.vue
+++ b/assets/js/components/Notifications.vue
@@ -45,13 +45,13 @@
 							<p class="d-flex align-items-baseline">
 								<shopicon-regular-exclamationtriangle
 									:class="{
-										'text-danger': msg.type === 'error',
-										'text-warning': msg.type === 'warn',
+										'text-danger': msg.level === 'error',
+										'text-warning': msg.level === 'warn',
 									}"
 									class="flex-grow-0 flex-shrink-0 d-block"
 								></shopicon-regular-exclamationtriangle>
 								<span class="flex-grow-1 px-2 py-1 text-break">
-									{{ msg.message }}
+									{{ message(msg) }}
 								</span>
 								<span v-if="msg.count > 1" class="badge rounded-pill bg-secondary">
 									{{ msg.count }}
@@ -85,13 +85,14 @@ export default {
 	mixins: [formatter],
 	props: {
 		notifications: Array,
+		loadpointTitles: Array,
 	},
 	computed: {
 		iconVisible: function () {
 			return this.notifications.length > 0;
 		},
 		iconClass: function () {
-			return this.notifications.find((m) => m.type === "error")
+			return this.notifications.find((m) => m.level === "error")
 				? "text-danger"
 				: "text-warning";
 		},
@@ -105,6 +106,13 @@ export default {
 		clearTimeout(this.interval);
 	},
 	methods: {
+		message({ message, lp }) {
+			let context = "";
+			if (lp) {
+				context = `${this.loadpointTitles[lp - 1] || lp}: `;
+			}
+			return `${context}${message}`;
+		},
 		clear: function () {
 			window.app && window.app.clear();
 		},
diff --git a/assets/js/components/Site.vue b/assets/js/components/Site.vue
index 41f4aad2..ed332cd0 100644
--- a/assets/js/components/Site.vue
+++ b/assets/js/components/Site.vue
@@ -6,7 +6,11 @@
 					{{ siteTitle || "evcc" }}
 				</h1>
 				<div class="d-flex">
-					<Notifications :notifications="notifications" class="me-2" />
+					<Notifications
+						:notifications="notifications"
+						:loadpointTitles="loadpointTitles"
+						class="me-2"
+					/>
 					<TopNavigation v-bind="topNavigation" />
 				</div>
 			</div>
@@ -104,6 +108,9 @@ export default {
 		energyflow: function () {
 			return this.collectProps(Energyflow);
 		},
+		loadpointTitles: function () {
+			return this.loadpoints.map((lp) => lp.title);
+		},
 		loadpointsCompact: function () {
 			return this.loadpoints.map((lp) => {
 				const icon = lp.chargerIcon || lp.vehicleIcon || "car";
diff --git a/assets/js/store.js b/assets/js/store.js
index d91ba15c..1841ec00 100644
--- a/assets/js/store.js
+++ b/assets/js/store.js
@@ -26,8 +26,8 @@ const store = {
   state,
   update: function (msg) {
     Object.keys(msg).forEach(function (k) {
-      if (typeof window.app[k] === "function") {
-        window.app[k]({ message: msg[k] });
+      if (k === "log") {
+        window.app.raise(msg[k]);
       } else {
         setProperty(state, k.split("."), msg[k]);
       }
diff --git a/assets/js/views/App.vue b/assets/js/views/App.vue
index c6290c22..b13a0b64 100644
--- a/assets/js/views/App.vue
+++ b/assets/js/views/App.vue
@@ -59,7 +59,7 @@ export default {
 			console.log("websocket connect");
 			const supportsWebSockets = "WebSocket" in window;
 			if (!supportsWebSockets) {
-				window.app.error({
+				window.app.raise({
 					message: "Web sockets not supported. Please upgrade your browser.",
 				});
 				return;
@@ -99,7 +99,7 @@ export default {
 					var msg = JSON.parse(evt.data);
 					store.update(msg);
 				} catch (error) {
-					window.app.error({
+					window.app.raise({
 						message: `Failed to parse web socket data: ${error.message} [${evt.data}]`,
 					});
 				}
diff --git a/charger/daheimladen.go b/charger/daheimladen.go
index 4146c9d1..91867125 100644
--- a/charger/daheimladen.go
+++ b/charger/daheimladen.go
@@ -22,8 +22,8 @@ type DaheimLaden struct {
 	idTag         string
 	token         string
 	transactionID int32
-	statusCache   provider.Cacheable[daheimladen.GetLatestStatus]
-	meterCache    provider.Cacheable[daheimladen.GetLatestMeterValueResponse]
+	statusG       provider.Cacheable[daheimladen.GetLatestStatus]
+	meterG        provider.Cacheable[daheimladen.GetLatestMeterValueResponse]
 	cache         time.Duration
 }
 
@@ -67,13 +67,13 @@ func NewDaheimLaden(token, stationID string, cache time.Duration) (*DaheimLaden,
 		Base: c.Client.Transport,
 	}
 
-	c.statusCache = provider.ResettableCached(func() (daheimladen.GetLatestStatus, error) {
+	c.statusG = provider.ResettableCached(func() (daheimladen.GetLatestStatus, error) {
 		var res daheimladen.GetLatestStatus
 		err := c.GetJSON(fmt.Sprintf("%s/cs/%s/status", daheimladen.BASE_URL, c.stationID), &res)
 		return res, err
 	}, c.cache)
 
-	c.meterCache = provider.ResettableCached(func() (daheimladen.GetLatestMeterValueResponse, error) {
+	c.meterG = provider.ResettableCached(func() (daheimladen.GetLatestMeterValueResponse, error) {
 		var res daheimladen.GetLatestMeterValueResponse
 		err := c.GetJSON(fmt.Sprintf("%s/cs/%s/metervalue", daheimladen.BASE_URL, c.stationID), &res)
 		return res, err
@@ -84,13 +84,13 @@ func NewDaheimLaden(token, stationID string, cache time.Duration) (*DaheimLaden,
 
 // reset cache
 func (c *DaheimLaden) reset() {
-	c.statusCache.Reset()
-	c.meterCache.Reset()
+	c.statusG.Reset()
+	c.meterG.Reset()
 }
 
 // Status implements the api.Charger interface
 func (c *DaheimLaden) Status() (api.ChargeStatus, error) {
-	res, err := c.statusCache.Get()
+	res, err := c.statusG.Get()
 	if err != nil {
 		return api.StatusNone, err
 	}
@@ -112,7 +112,7 @@ func (c *DaheimLaden) Status() (api.ChargeStatus, error) {
 
 // Enabled implements the api.Charger interface
 func (c *DaheimLaden) Enabled() (bool, error) {
-	res, err := c.statusCache.Get()
+	res, err := c.statusG.Get()
 	return res.Status == string(daheimladen.CHARGING), err
 }
 
@@ -193,7 +193,7 @@ var _ api.Meter = (*DaheimLaden)(nil)
 
 // CurrentPower implements the api.Meter interface
 func (c *DaheimLaden) CurrentPower() (float64, error) {
-	res, err := c.meterCache.Get()
+	res, err := c.meterG.Get()
 	return float64(res.ActivePowerImport * 1e3), err
 }
 
@@ -201,7 +201,7 @@ var _ api.MeterEnergy = (*DaheimLaden)(nil)
 
 // TotalEnergy implements the api.MeterMeterEnergy interface
 func (c *DaheimLaden) TotalEnergy() (float64, error) {
-	res, err := c.meterCache.Get()
+	res, err := c.meterG.Get()
 	return float64(res.EnergyActiveImportRegister), err
 }
 
@@ -209,6 +209,6 @@ var _ api.PhaseCurrents = (*DaheimLaden)(nil)
 
 // Currents implements the api.PhaseCurrents interface
 func (c *DaheimLaden) Currents() (float64, float64, float64, error) {
-	res, err := c.meterCache.Get()
+	res, err := c.meterG.Get()
 	return float64(res.CurrentImportPhaseL1), float64(res.CurrentImportPhaseL2), float64(res.CurrentImportPhaseL3), err
 }
diff --git a/charger/ocpp/cp_core.go b/charger/ocpp/cp_core.go
index 3af1f9b9..0f8bb99f 100644
--- a/charger/ocpp/cp_core.go
+++ b/charger/ocpp/cp_core.go
@@ -89,6 +89,11 @@ func (cp *CP) MeterValues(request *core.MeterValuesRequest) (*core.MeterValuesCo
 		cp.mu.Lock()
 		defer cp.mu.Unlock()
 
+		if request.TransactionId != nil && cp.txnId == 0 {
+			cp.log.DEBUG.Printf("hijacking transaction: %d", *request.TransactionId)
+			cp.txnId = *request.TransactionId
+		}
+
 		for _, meterValue := range request.MeterValue {
 			// ignore old meter value requests
 			if meterValue.Timestamp.Time.After(cp.meterUpdated) {
diff --git a/charger/openevse.go b/charger/openevse.go
index 56f713a5..c56a93dd 100644
--- a/charger/openevse.go
+++ b/charger/openevse.go
@@ -1,33 +1,35 @@
 package charger
 
 import (
-	"context"
 	"errors"
 	"fmt"
+	"net/http"
 	"net/url"
 	"strings"
 	"time"
 
-	"github.com/deepmap/oapi-codegen/pkg/securityprovider"
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/charger/openevse"
+	"github.com/evcc-io/evcc/provider"
 	"github.com/evcc-io/evcc/util"
 	"github.com/evcc-io/evcc/util/request"
+	"github.com/evcc-io/evcc/util/transport"
 )
 
 // OpenEVSE charger implementation
 type OpenEVSE struct {
+	*request.Helper
 	uri     string
-	api     *openevse.ClientWithResponses
-	helper  *request.Helper
-	timeout time.Duration
+	statusG provider.Cacheable[openevse.Status]
+	current int
+	enabled bool
 }
 
 func init() {
 	registry.Add("openevse", NewOpenEVSEFromConfig)
 }
 
-// go:generate go run ../cmd/tools/decorate.go -f decorateOpenEVSE -b "*OpenEVSE" -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) error"
+// go:generate go run ../cmd/tools/decorate.go -f decorateOpenEVSE -b ""*OpenEVSE" -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) error"
 
 // NewOpenEVSEFromConfig creates an OpenEVSE charger from generic config
 func NewOpenEVSEFromConfig(other map[string]interface{}) (api.Charger, error) {
@@ -35,9 +37,9 @@ func NewOpenEVSEFromConfig(other map[string]interface{}) (api.Charger, error) {
 		URI      string
 		User     string
 		Password string
-		Timeout  time.Duration
+		Cache    time.Duration
 	}{
-		Timeout: request.Timeout,
+		Cache: time.Second,
 	}
 
 	if err := util.DecodeOther(other, &cc); err != nil {
@@ -48,34 +50,31 @@ func NewOpenEVSEFromConfig(other map[string]interface{}) (api.Charger, error) {
 		return nil, errors.New("missing uri")
 	}
 
-	return NewOpenEVSE(cc.URI, cc.User, cc.Password, cc.Timeout)
+	return NewOpenEVSE(cc.URI, cc.User, cc.Password, cc.Cache)
 }
 
 // NewOpenEVSE creates OpenEVSE charger
-func NewOpenEVSE(uri, user, password string, timeout time.Duration) (api.Charger, error) {
-	log := util.NewLogger("openevse").Redact(user, password)
+func NewOpenEVSE(uri, user, password string, cache time.Duration) (api.Charger, error) {
+	basicAuth := transport.BasicAuthHeader(user, password)
+	log := util.NewLogger("openevse").Redact(user, password, basicAuth)
+
 	c := &OpenEVSE{
-		helper:  request.NewHelper(log),
-		uri:     uri,
-		timeout: timeout,
+		Helper: request.NewHelper(log),
+		uri:    util.DefaultScheme(strings.TrimSuffix(uri, "/"), "http"),
 	}
 
-	options := []openevse.ClientOption{openevse.WithHTTPClient(c.helper.Client)}
-
 	if user != "" && password != "" {
-		basicAuthProvider, err := securityprovider.NewSecurityProviderBasicAuth(user, password)
-		if err != nil {
-			return c, err
-		}
-
-		options = append(options, openevse.WithRequestEditorFn(basicAuthProvider.Intercept))
+		c.Client.Transport = transport.BasicAuth(user, password, c.Client.Transport)
 	}
 
-	var err error
-	c.api, err = openevse.NewClientWithResponses(uri, options...)
-	if err != nil {
-		return c, err
-	}
+	c.statusG = provider.ResettableCached(func() (openevse.Status, error) {
+		var res openevse.Status
+
+		uri := fmt.Sprintf("%s/status", c.uri)
+		err := c.GetJSON(uri, &res)
+
+		return res, err
+	}, cache)
 
 	var phases1p3p func(int) error
 	if err := c.hasPhaseSwitchCapabilities(); err == nil {
@@ -87,16 +86,30 @@ func NewOpenEVSE(uri, user, password string, timeout time.Duration) (api.Charger
 		}
 	}
 
-	return decorateOpenEVSE(c, phases1p3p), err
+	return decorateOpenEVSE(c, phases1p3p), nil
 }
 
-func (c *OpenEVSE) requestContextWithTimeout() (context.Context, context.CancelFunc) {
-	ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
-	return ctx, cancel
-}
+func (c *OpenEVSE) setOverride() error {
+	var data openevse.Override
+	uri := fmt.Sprintf("%s/override", c.uri)
 
-func (c *OpenEVSE) hasPhaseSwitchCapabilities() error {
-	return c.rapiCommand("$G7")
+	if err := c.GetJSON(uri, &data); err != nil {
+		return err
+	}
+
+	state := openevse.Disabled
+	if c.enabled {
+		state = openevse.Enabled
+	}
+	data.State = &state
+	data.MaxCurrent = &c.current
+
+	req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
+	if err == nil {
+		_, err = c.DoBody(req)
+	}
+
+	return err
 }
 
 func (c *OpenEVSE) rapiCommand(command string) error {
@@ -106,7 +119,7 @@ func (c *OpenEVSE) rapiCommand(command string) error {
 
 	uri := fmt.Sprintf("%s/r?json=1&rapi=%s", c.uri, url.QueryEscape(command))
 
-	err := c.helper.GetJSON(uri, &res)
+	err := c.GetJSON(uri, &res)
 	if err == nil && !strings.HasPrefix(res.Ret, "$OK") {
 		err = fmt.Errorf("rapi command failed: %s", res.Ret)
 	}
@@ -114,32 +127,13 @@ func (c *OpenEVSE) rapiCommand(command string) error {
 	return err
 }
 
-func (c *OpenEVSE) SetManualOverride(enable bool) error {
-	state := "disabled"
-	if enable {
-		state = "active"
-	}
-
-	data := openevse.SetManualOverrideJSONRequestBody{
-		State: &state,
-	}
-
-	ctx, cancel := c.requestContextWithTimeout()
-	defer cancel()
-	_, err := c.api.SetManualOverrideWithResponse(ctx, data)
-
-	return err
+func (c *OpenEVSE) hasPhaseSwitchCapabilities() error {
+	return c.rapiCommand("$G7")
 }
 
+// Status implements the api.Charger interface
 func (c *OpenEVSE) Status() (api.ChargeStatus, error) {
-	ctx, cancel := c.requestContextWithTimeout()
-	defer cancel()
-
-	res, err := c.api.GetStatusWithResponse(ctx)
-	if err != nil {
-		return api.StatusNone, err
-	}
-
+	res, err := c.statusG.Get()
 	/*
 		0: "unknown",
 		1: "not connected",
@@ -157,20 +151,20 @@ func (c *OpenEVSE) Status() (api.ChargeStatus, error) {
 		255: "disabled"
 	*/
 
-	switch state := *res.JSON200.State; state {
+	switch state := *res.State; state {
 	case 1:
-		return api.StatusA, nil
+		return api.StatusA, err
 	case 2, 254, 255:
-		if connected := *res.JSON200.Vehicle != 0; connected {
-			return api.StatusB, nil
+		if *res.Vehicle == 1 {
+			return api.StatusB, err
 		}
-		return api.StatusA, nil
+		return api.StatusA, err
 	case 3:
-		return api.StatusC, nil
+		return api.StatusC, err
 	case 4:
-		return api.StatusD, nil
+		return api.StatusD, err
 	case 5, 6, 7, 8, 9, 10, 11:
-		return api.StatusF, nil
+		return api.StatusF, err
 	default:
 		return api.StatusNone, fmt.Errorf("unknown EVSE state: %d", state)
 	}
@@ -178,79 +172,64 @@ func (c *OpenEVSE) Status() (api.ChargeStatus, error) {
 
 // Enabled implements the api.Charger interface
 func (c *OpenEVSE) Enabled() (bool, error) {
-	ctx, cancel := c.requestContextWithTimeout()
-	defer cancel()
-	overrideResp, err := c.api.GetStatusWithResponse(ctx)
-
-	if err != nil {
-		return false, err
-	}
-
-	if overrideResp.JSON200 != nil && overrideResp.JSON200.State != nil {
-		status := *overrideResp.JSON200.Status
-
-		switch status {
-		case "disabled":
-			return false, nil
-		case "enabled", "active":
-			return true, nil
-		default:
-			return false, fmt.Errorf("unknown EVSE status: %s", status)
-		}
-	}
-
-	// no override:
-	return false, fmt.Errorf("invalid EVSE status")
+	res, err := c.statusG.Get()
+	return *res.Status == openevse.Enabled, err
 }
 
 // Enable implements the api.Charger interface
 func (c *OpenEVSE) Enable(enable bool) error {
-	return c.SetManualOverride(enable)
+	c.enabled = enable
+	return c.setOverride()
 }
 
 // MaxCurrent implements the api.Charger interface
 func (c *OpenEVSE) MaxCurrent(current int64) error {
-	cur := int(current)
-	data := openevse.SetManualOverrideJSONRequestBody{
-		ChargeCurrent: &cur,
-	}
-
-	ctx, cancel := c.requestContextWithTimeout()
-	defer cancel()
-
-	_, err := c.api.SetManualOverrideWithResponse(ctx, data)
-
-	return err
+	c.current = int(current)
+	return c.setOverride()
 }
 
 var _ api.ChargeRater = (*OpenEVSE)(nil)
 
 // ChargedEnergy implements the api.ChargeRater interface
 func (c *OpenEVSE) ChargedEnergy() (float64, error) {
-	ctx, cancel := c.requestContextWithTimeout()
-	defer cancel()
-
-	resp, err := c.api.GetStatusWithResponse(ctx)
+	res, err := c.statusG.Get()
 	if err != nil {
 		return 0, err
 	}
 
-	return float64(*resp.JSON200.Wattsec) / 3600 / 1e3, nil
+	return *res.SessionEnergy / 1e3, err
+}
+
+var _ api.ChargeTimer = (*OpenEVSE)(nil)
+
+func (c *OpenEVSE) ChargingTime() (time.Duration, error) {
+	res, err := c.statusG.Get()
+	if err != nil {
+		return 0, err
+	}
+	return time.Duration(*res.SessionElapsed) * time.Second, err
 }
 
 var _ api.MeterEnergy = (*OpenEVSE)(nil)
 
 // TotalEnergy implements the api.MeterEnergy interface
 func (c *OpenEVSE) TotalEnergy() (float64, error) {
-	ctx, cancel := c.requestContextWithTimeout()
-	defer cancel()
-
-	resp, err := c.api.GetStatusWithResponse(ctx)
+	res, err := c.statusG.Get()
 	if err != nil {
 		return 0, err
 	}
 
-	return float64(*resp.JSON200.Watthour) / 1e3, nil
+	return *res.TotalEnergy, err
+}
+
+var _ api.Meter = (*OpenEVSE)(nil)
+
+func (c *OpenEVSE) CurrentPower() (float64, error) {
+	res, err := c.statusG.Get()
+	if err != nil {
+		return 0, err
+	}
+	return *res.Power, err
 }
 
 // phases1p3p implements the api.ChargePhases interface
diff --git a/charger/openevse/api.go b/charger/openevse/api.go
deleted file mode 100644
index 08414cf2..00000000
--- a/charger/openevse/api.go
+++ /dev/null
@@ -1,3496 +0,0 @@
-// Package openevse provides primitives to interact with the openapi HTTP API.
-//
-// Code generated by github.com/deepmap/oapi-codegen version v1.11.0 DO NOT EDIT.
-package openevse
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"strings"
-	"time"
-
-	"github.com/deepmap/oapi-codegen/pkg/runtime"
-)
-
-// Defines values for LogEventType.
-const (
-	Information  LogEventType = "information"
-	Notification LogEventType = "notification"
-	Warning      LogEventType = "warning"
-)
-
-// Defines values for ScheduleEventDays.
-const (
-	Friday    ScheduleEventDays = "friday"
-	Monday    ScheduleEventDays = "monday"
-	Saturday  ScheduleEventDays = "saturday"
-	Sunday    ScheduleEventDays = "sunday"
-	Thursday  ScheduleEventDays = "thursday"
-	Tuesday   ScheduleEventDays = "tuesday"
-	Wednesday ScheduleEventDays = "wednesday"
-)
-
-// LogEvent defines model for LogEvent.
-type LogEvent struct {
-	DivertMode *int32   `json:"divertMode,omitempty"`
-	Elapsed    *int32   `json:"elapsed,omitempty"`
-	Energy     *float64 `json:"energy,omitempty"`
-	EvseFlags  *int32   `json:"evseFlags,omitempty"`
-	EvseState  *int     `json:"evseState,omitempty"`
-
-	// Either enable charging (`active`) or block charging (`disabled`)
-	ManagerState   *string       `json:"managerState,omitempty"`
-	Pilot          *int32        `json:"pilot,omitempty"`
-	Temperature    *float64      `json:"temperature,omitempty"`
-	TempuratureMax *float64      `json:"tempuratureMax,omitempty"`
-	Time           *time.Time    `json:"time,omitempty"`
-	Type           *LogEventType `json:"type,omitempty"`
-}
-
-// LogEventType defines model for LogEvent.Type.
-type LogEventType string
-
-// Message defines model for Message.
-type Message struct {
-	// The message
-	Msg *string `json:"msg,omitempty"`
-}
-
-// ScheduleEvent defines model for ScheduleEvent.
-type ScheduleEvent struct {
-	Days []ScheduleEventDays `json:"days"`
-	Id   *int                `json:"id,omitempty"`
-
-	// Either enable charging (`active`) or block charging (`disabled`)
-	State string `json:"state"`
-	Time  string `json:"time"`
-}
-
-// ScheduleEventDays defines model for ScheduleEvent.Days.
-type ScheduleEventDays string
-
-// BadRequest defines model for BadRequest.
-type BadRequest = Message
-
-// NotFound defines model for NotFound.
-type NotFound = Message
-
-// UpdateSuccessful defines model for UpdateSuccessful.
-type UpdateSuccessful = Message
-
-// SetClaimJSONBody defines parameters for SetClaim.
-type SetClaimJSONBody struct {
-	// `true` if the manual override is auto-released when the vehicle is
-	// disconnected, `false` if manual override will persist after vehicle
-	// disconnection.
-	AutoRelease *bool `json:"auto_release,omitempty"`
-
-	// Specify the active charge current in Amps
-	ChargeCurrent *int `json:"charge_current,omitempty"`
-
-	// Stop the charge after `energy_limit` Wh of energy have been transfered.
-	//
-	// > Note:  Not currently implemented
-	EnergyLimit *int32 `json:"energy_limit,omitempty"`
-
-	// Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-	MaxCurrent *int `json:"max_current,omitempty"`
-
-	// Either enable charging (`active`) or block charging (`disabled`)
-	State *string `json:"state,omitempty"`
-
-	// Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-	//
-	// > Note:  Not currently implemented
-	TimeLimit *int32 `json:"time_limit,omitempty"`
-}
-
-// UpdateConfigJSONBody defines parameters for UpdateConfig.
-type UpdateConfigJSONBody struct {
-	Buildenv                    *string   `json:"buildenv,omitempty"`
-	ChargeMode                  *string   `json:"charge_mode,omitempty"`
-	DiodeCheck                  *bool     `json:"diode_check,omitempty"`
-	DivertPVRatio               *float32  `json:"divert_PV_ratio,omitempty"`
-	DivertAttackSmoothingFactor *float32  `json:"divert_attack_smoothing_factor,omitempty"`
-	DivertDecaySmoothingFactor  *float32  `json:"divert_decay_smoothing_factor,omitempty"`
-	DivertEnabled               *bool     `json:"divert_enabled,omitempty"`
-	DivertMinChargeTime         *float32  `json:"divert_min_charge_time,omitempty"`
-	EmoncmsApikey               *string   `json:"emoncms_apikey,omitempty"`
-	EmoncmsEnabled              *bool     `json:"emoncms_enabled,omitempty"`
-	EmoncmsFingerprint          *string   `json:"emoncms_fingerprint,omitempty"`
-	EmoncmsNode                 *string   `json:"emoncms_node,omitempty"`
-	EmoncmsServer               *string   `json:"emoncms_server,omitempty"`
-	Espflash                    *float32  `json:"espflash,omitempty"`
-	Espinfo                     *string   `json:"espinfo,omitempty"`
-	Firmware                    *string   `json:"firmware,omitempty"`
-	Flags                       *float32  `json:"flags,omitempty"`
-	GfciCheck                   *bool     `json:"gfci_check,omitempty"`
-	GroundCheck                 *bool     `json:"ground_check,omitempty"`
-	Hostname                    *string   `json:"hostname,omitempty"`
-	HttpSupportedProtocols      *[]string `json:"http_supported_protocols,omitempty"`
-	LedBrightness               *float32  `json:"led_brightness,omitempty"`
-	MaxCurrentHard              *int      `json:"max_current_hard,omitempty"`
-	MaxCurrentSoft              *int      `json:"max_current_soft,omitempty"`
-	MinCurrentHard              *int      `json:"min_current_hard,omitempty"`
-	MqttAnnounceTopic           *string   `json:"mqtt_announce_topic,omitempty"`
-	MqttEnabled                 *bool     `json:"mqtt_enabled,omitempty"`
-	MqttGridIe                  *string   `json:"mqtt_grid_ie,omitempty"`
-	MqttPass                    *string   `json:"mqtt_pass,omitempty"`
-	MqttPort                    *int      `json:"mqtt_port,omitempty"`
-	MqttProtocol                *string   `json:"mqtt_protocol,omitempty"`
-	MqttRejectUnauthorized      *bool     `json:"mqtt_reject_unauthorized,omitempty"`
-	MqttServer                  *string   `json:"mqtt_server,omitempty"`
-	MqttSolar                   *string   `json:"mqtt_solar,omitempty"`
-	MqttSupportedProtocols      *[]string `json:"mqtt_supported_protocols,omitempty"`
-	MqttTopic                   *string   `json:"mqtt_topic,omitempty"`
-	MqttUser                    *string   `json:"mqtt_user,omitempty"`
-	MqttVehicleEta              *string   `json:"mqtt_vehicle_eta,omitempty"`
-	MqttVehicleRange            *string   `json:"mqtt_vehicle_range,omitempty"`
-	MqttVehicleRangeMiles       *bool     `json:"mqtt_vehicle_range_miles,omitempty"`
-	MqttVehicleSoc              *string   `json:"mqtt_vehicle_soc,omitempty"`
-	MqttVrms                    *string   `json:"mqtt_vrms,omitempty"`
-	OcppChargeBoxId             *string   `json:"ocpp_chargeBoxId,omitempty"`
-	OcppEnabled                 *bool     `json:"ocpp_enabled,omitempty"`
-	OcppEnergizePlug            *bool     `json:"ocpp_energize_plug,omitempty"`
-	OcppIdTag                   *string   `json:"ocpp_idTag,omitempty"`
-	OcppServer                  *string   `json:"ocpp_server,omitempty"`
-	OcppSuspendEvse             *bool     `json:"ocpp_suspend_evse,omitempty"`
-	Offset                      *int      `json:"offset,omitempty"`
-	Ohm                         *string   `json:"ohm,omitempty"`
-	OhmEnabled                  *bool     `json:"ohm_enabled,omitempty"`
-	Pass                        *string   `json:"pass,omitempty"`
-	PauseUsesDisabled           *bool     `json:"pause_uses_disabled,omitempty"`
-	Protocol                    *string   `json:"protocol,omitempty"`
-	RelayCheck                  *bool     `json:"relay_check,omitempty"`
-	Scale                       *int      `json:"scale,omitempty"`
-	Service                     *int      `json:"service,omitempty"`
-	SntpEnabled                 *bool     `json:"sntp_enabled,omitempty"`
-	SntpHostname                *string   `json:"sntp_hostname,omitempty"`
-	Ssid                        *string   `json:"ssid,omitempty"`
-	TempCheck                   *bool     `json:"temp_check,omitempty"`
-	TeslaAccessToken            *string   `json:"tesla_access_token,omitempty"`
-	TeslaCreatedAt              *float32  `json:"tesla_created_at,omitempty"`
-	TeslaEnabled                *bool     `json:"tesla_enabled,omitempty"`
-	TeslaExpiresIn              *float32  `json:"tesla_expires_in,omitempty"`
-	TeslaRefreshToken           *string   `json:"tesla_refresh_token,omitempty"`
-	TeslaVehicleId              *string   `json:"tesla_vehicle_id,omitempty"`
-	TimeZone                    *string   `json:"time_zone,omitempty"`
-	TxStartPoint                *string   `json:"tx_start_point,omitempty"`
-	VentCheck                   *bool     `json:"vent_check,omitempty"`
-	Version                     *string   `json:"version,omitempty"`
-	WwwPassword                 *string   `json:"www_password,omitempty"`
-	WwwUsername                 *string   `json:"www_username,omitempty"`
-}
-
-// SetManualOverrideJSONBody defines parameters for SetManualOverride.
-type SetManualOverrideJSONBody struct {
-	// `true` if the manual override is auto-released when the vehicle is
-	// disconnected, `false` if manual override will persist after vehicle
-	// disconnection.
-	AutoRelease *bool `json:"auto_release,omitempty"`
-
-	// Specify the active charge current in Amps
-	ChargeCurrent *int `json:"charge_current,omitempty"`
-
-	// Stop the charge after `energy_limit` Wh of energy have been transfered.
-	//
-	// > Note:  Not currently implemented
-	EnergyLimit *int32 `json:"energy_limit,omitempty"`
-
-	// Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-	MaxCurrent *int `json:"max_current,omitempty"`
-
-	// Either enable charging (`active`) or block charging (`disabled`)
-	State *string `json:"state,omitempty"`
-
-	// Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-	//
-	// > Note:  Not currently implemented
-	TimeLimit *int32 `json:"time_limit,omitempty"`
-}
-
-// UpdateScheduleJSONBody defines parameters for UpdateSchedule.
-type UpdateScheduleJSONBody = []ScheduleEvent
-
-// SetScheduleEventJSONBody defines parameters for SetScheduleEvent.
-type SetScheduleEventJSONBody = string
-
-// SetClaimJSONRequestBody defines body for SetClaim for application/json ContentType.
-type SetClaimJSONRequestBody SetClaimJSONBody
-
-// UpdateConfigJSONRequestBody defines body for UpdateConfig for application/json ContentType.
-type UpdateConfigJSONRequestBody UpdateConfigJSONBody
-
-// SetManualOverrideJSONRequestBody defines body for SetManualOverride for application/json ContentType.
-type SetManualOverrideJSONRequestBody SetManualOverrideJSONBody
-
-// UpdateScheduleJSONRequestBody defines body for UpdateSchedule for application/json ContentType.
-type UpdateScheduleJSONRequestBody = UpdateScheduleJSONBody
-
-// SetScheduleEventJSONRequestBody defines body for SetScheduleEvent for application/json ContentType.
-type SetScheduleEventJSONRequestBody = SetScheduleEventJSONBody
-
-// RequestEditorFn  is the function signature for the RequestEditor callback function
-type RequestEditorFn func(ctx context.Context, req *http.Request) error
-
-// Doer performs HTTP requests.
-//
-// The standard http.Client implements this interface.
-type HttpRequestDoer interface {
-	Do(req *http.Request) (*http.Response, error)
-}
-
-// Client which conforms to the OpenAPI3 specification for this service.
-type Client struct {
-	// The endpoint of the server conforming to this interface, with scheme,
-	// https://api.deepmap.com for example. This can contain a path relative
-	// to the server, such as https://api.deepmap.com/dev-test, and all the
-	// paths in the swagger spec will be appended to the server.
-	Server string
-
-	// Doer for performing requests, typically a *http.Client with any
-	// customized settings, such as certificate chains.
-	Client HttpRequestDoer
-
-	// A list of callbacks for modifying requests which are generated before sending over
-	// the network.
-	RequestEditors []RequestEditorFn
-}
-
-// ClientOption allows setting custom parameters during construction
-type ClientOption func(*Client) error
-
-// Creates a new Client, with reasonable defaults
-func NewClient(server string, opts ...ClientOption) (*Client, error) {
-	// create a client with sane default values
-	client := Client{
-		Server: server,
-	}
-	// mutate client and add all optional params
-	for _, o := range opts {
-		if err := o(&client); err != nil {
-			return nil, err
-		}
-	}
-	// ensure the server URL always has a trailing slash
-	if !strings.HasSuffix(client.Server, "/") {
-		client.Server += "/"
-	}
-	// create httpClient, if not already present
-	if client.Client == nil {
-		client.Client = &http.Client{}
-	}
-	return &client, nil
-}
-
-// WithHTTPClient allows overriding the default Doer, which is
-// automatically created using http.Client. This is useful for tests.
-func WithHTTPClient(doer HttpRequestDoer) ClientOption {
-	return func(c *Client) error {
-		c.Client = doer
-		return nil
-	}
-}
-
-// WithRequestEditorFn allows setting up a callback function, which will be
-// called right before sending the request. This can be used to mutate the request.
-func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
-	return func(c *Client) error {
-		c.RequestEditors = append(c.RequestEditors, fn)
-		return nil
-	}
-}
-
-// The interface specification for the client above.
-type ClientInterface interface {
-	// ListClaims request
-	ListClaims(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// ReleaseClaim request
-	ReleaseClaim(ctx context.Context, client int, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// GetClaim request
-	GetClaim(ctx context.Context, client int, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// SetClaim request with any body
-	SetClaimWithBody(ctx context.Context, client int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	SetClaim(ctx context.Context, client int, body SetClaimJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// GetConfig request
-	GetConfig(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// UpdateConfig request with any body
-	UpdateConfigWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	UpdateConfig(ctx context.Context, body UpdateConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// GetEventBlockInfo request
-	GetEventBlockInfo(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// GetEventBlock request
-	GetEventBlock(ctx context.Context, index int, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// ClearManualOverride request
-	ClearManualOverride(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// GetManualOverride request
-	GetManualOverride(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// ToggleManualOverride request
-	ToggleManualOverride(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// SetManualOverride request with any body
-	SetManualOverrideWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	SetManualOverride(ctx context.Context, body SetManualOverrideJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// ListSchedule request
-	ListSchedule(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// UpdateSchedule request with any body
-	UpdateScheduleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	UpdateSchedule(ctx context.Context, body UpdateScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// RemoveScheduleEvent request
-	RemoveScheduleEvent(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// GetScheduleEvent request
-	GetScheduleEvent(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// SetScheduleEvent request with any body
-	SetScheduleEventWithBody(ctx context.Context, id int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	SetScheduleEvent(ctx context.Context, id int, body SetScheduleEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// GetStatus request
-	GetStatus(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// GetTeslaVehicles request
-	GetTeslaVehicles(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// StatusUpdates request
-	StatusUpdates(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-}
-
-func (c *Client) ListClaims(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewListClaimsRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) ReleaseClaim(ctx context.Context, client int, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewReleaseClaimRequest(c.Server, client)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) GetClaim(ctx context.Context, client int, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewGetClaimRequest(c.Server, client)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) SetClaimWithBody(ctx context.Context, client int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewSetClaimRequestWithBody(c.Server, client, contentType, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) SetClaim(ctx context.Context, client int, body SetClaimJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewSetClaimRequest(c.Server, client, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) GetConfig(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewGetConfigRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) UpdateConfigWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewUpdateConfigRequestWithBody(c.Server, contentType, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) UpdateConfig(ctx context.Context, body UpdateConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewUpdateConfigRequest(c.Server, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) GetEventBlockInfo(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewGetEventBlockInfoRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) GetEventBlock(ctx context.Context, index int, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewGetEventBlockRequest(c.Server, index)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) ClearManualOverride(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewClearManualOverrideRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) GetManualOverride(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewGetManualOverrideRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) ToggleManualOverride(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewToggleManualOverrideRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) SetManualOverrideWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewSetManualOverrideRequestWithBody(c.Server, contentType, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) SetManualOverride(ctx context.Context, body SetManualOverrideJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewSetManualOverrideRequest(c.Server, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) ListSchedule(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewListScheduleRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) UpdateScheduleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewUpdateScheduleRequestWithBody(c.Server, contentType, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) UpdateSchedule(ctx context.Context, body UpdateScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewUpdateScheduleRequest(c.Server, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) RemoveScheduleEvent(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewRemoveScheduleEventRequest(c.Server, id)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) GetScheduleEvent(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewGetScheduleEventRequest(c.Server, id)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) SetScheduleEventWithBody(ctx context.Context, id int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewSetScheduleEventRequestWithBody(c.Server, id, contentType, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) SetScheduleEvent(ctx context.Context, id int, body SetScheduleEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewSetScheduleEventRequest(c.Server, id, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) GetStatus(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewGetStatusRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) GetTeslaVehicles(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewGetTeslaVehiclesRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) StatusUpdates(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewStatusUpdatesRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-// NewListClaimsRequest generates requests for ListClaims
-func NewListClaimsRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/claims")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewReleaseClaimRequest generates requests for ReleaseClaim
-func NewReleaseClaimRequest(server string, client int) (*http.Request, error) {
-	var err error
-
-	var pathParam0 string
-
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "client", runtime.ParamLocationPath, client)
-	if err != nil {
-		return nil, err
-	}
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/claims/%s", pathParam0)
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("DELETE", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewGetClaimRequest generates requests for GetClaim
-func NewGetClaimRequest(server string, client int) (*http.Request, error) {
-	var err error
-
-	var pathParam0 string
-
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "client", runtime.ParamLocationPath, client)
-	if err != nil {
-		return nil, err
-	}
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/claims/%s", pathParam0)
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewSetClaimRequest calls the generic SetClaim builder with application/json body
-func NewSetClaimRequest(server string, client int, body SetClaimJSONRequestBody) (*http.Request, error) {
-	var bodyReader io.Reader
-	buf, err := json.Marshal(body)
-	if err != nil {
-		return nil, err
-	}
-	bodyReader = bytes.NewReader(buf)
-	return NewSetClaimRequestWithBody(server, client, "application/json", bodyReader)
-}
-
-// NewSetClaimRequestWithBody generates requests for SetClaim with any type of body
-func NewSetClaimRequestWithBody(server string, client int, contentType string, body io.Reader) (*http.Request, error) {
-	var err error
-
-	var pathParam0 string
-
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "client", runtime.ParamLocationPath, client)
-	if err != nil {
-		return nil, err
-	}
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/claims/%s", pathParam0)
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("POST", queryURL.String(), body)
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Add("Content-Type", contentType)
-
-	return req, nil
-}
-
-// NewGetConfigRequest generates requests for GetConfig
-func NewGetConfigRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/config")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewUpdateConfigRequest calls the generic UpdateConfig builder with application/json body
-func NewUpdateConfigRequest(server string, body UpdateConfigJSONRequestBody) (*http.Request, error) {
-	var bodyReader io.Reader
-	buf, err := json.Marshal(body)
-	if err != nil {
-		return nil, err
-	}
-	bodyReader = bytes.NewReader(buf)
-	return NewUpdateConfigRequestWithBody(server, "application/json", bodyReader)
-}
-
-// NewUpdateConfigRequestWithBody generates requests for UpdateConfig with any type of body
-func NewUpdateConfigRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/config")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("POST", queryURL.String(), body)
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Add("Content-Type", contentType)
-
-	return req, nil
-}
-
-// NewGetEventBlockInfoRequest generates requests for GetEventBlockInfo
-func NewGetEventBlockInfoRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/logs")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewGetEventBlockRequest generates requests for GetEventBlock
-func NewGetEventBlockRequest(server string, index int) (*http.Request, error) {
-	var err error
-
-	var pathParam0 string
-
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "index", runtime.ParamLocationPath, index)
-	if err != nil {
-		return nil, err
-	}
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/logs/%s", pathParam0)
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewClearManualOverrideRequest generates requests for ClearManualOverride
-func NewClearManualOverrideRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/override")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("DELETE", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewGetManualOverrideRequest generates requests for GetManualOverride
-func NewGetManualOverrideRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/override")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewToggleManualOverrideRequest generates requests for ToggleManualOverride
-func NewToggleManualOverrideRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/override")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("PATCH", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewSetManualOverrideRequest calls the generic SetManualOverride builder with application/json body
-func NewSetManualOverrideRequest(server string, body SetManualOverrideJSONRequestBody) (*http.Request, error) {
-	var bodyReader io.Reader
-	buf, err := json.Marshal(body)
-	if err != nil {
-		return nil, err
-	}
-	bodyReader = bytes.NewReader(buf)
-	return NewSetManualOverrideRequestWithBody(server, "application/json", bodyReader)
-}
-
-// NewSetManualOverrideRequestWithBody generates requests for SetManualOverride with any type of body
-func NewSetManualOverrideRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/override")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("POST", queryURL.String(), body)
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Add("Content-Type", contentType)
-
-	return req, nil
-}
-
-// NewListScheduleRequest generates requests for ListSchedule
-func NewListScheduleRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/schedule")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewUpdateScheduleRequest calls the generic UpdateSchedule builder with application/json body
-func NewUpdateScheduleRequest(server string, body UpdateScheduleJSONRequestBody) (*http.Request, error) {
-	var bodyReader io.Reader
-	buf, err := json.Marshal(body)
-	if err != nil {
-		return nil, err
-	}
-	bodyReader = bytes.NewReader(buf)
-	return NewUpdateScheduleRequestWithBody(server, "application/json", bodyReader)
-}
-
-// NewUpdateScheduleRequestWithBody generates requests for UpdateSchedule with any type of body
-func NewUpdateScheduleRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/schedule")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("POST", queryURL.String(), body)
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Add("Content-Type", contentType)
-
-	return req, nil
-}
-
-// NewRemoveScheduleEventRequest generates requests for RemoveScheduleEvent
-func NewRemoveScheduleEventRequest(server string, id int) (*http.Request, error) {
-	var err error
-
-	var pathParam0 string
-
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id)
-	if err != nil {
-		return nil, err
-	}
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/schedule/%s", pathParam0)
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("DELETE", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewGetScheduleEventRequest generates requests for GetScheduleEvent
-func NewGetScheduleEventRequest(server string, id int) (*http.Request, error) {
-	var err error
-
-	var pathParam0 string
-
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id)
-	if err != nil {
-		return nil, err
-	}
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/schedule/%s", pathParam0)
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewSetScheduleEventRequest calls the generic SetScheduleEvent builder with application/json body
-func NewSetScheduleEventRequest(server string, id int, body SetScheduleEventJSONRequestBody) (*http.Request, error) {
-	var bodyReader io.Reader
-	buf, err := json.Marshal(body)
-	if err != nil {
-		return nil, err
-	}
-	bodyReader = bytes.NewReader(buf)
-	return NewSetScheduleEventRequestWithBody(server, id, "application/json", bodyReader)
-}
-
-// NewSetScheduleEventRequestWithBody generates requests for SetScheduleEvent with any type of body
-func NewSetScheduleEventRequestWithBody(server string, id int, contentType string, body io.Reader) (*http.Request, error) {
-	var err error
-
-	var pathParam0 string
-
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id)
-	if err != nil {
-		return nil, err
-	}
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/schedule/%s", pathParam0)
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("POST", queryURL.String(), body)
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Add("Content-Type", contentType)
-
-	return req, nil
-}
-
-// NewGetStatusRequest generates requests for GetStatus
-func NewGetStatusRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/status")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewGetTeslaVehiclesRequest generates requests for GetTeslaVehicles
-func NewGetTeslaVehiclesRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/tesla/vehicles")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewStatusUpdatesRequest generates requests for StatusUpdates
-func NewStatusUpdatesRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/ws")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
-	for _, r := range c.RequestEditors {
-		if err := r(ctx, req); err != nil {
-			return err
-		}
-	}
-	for _, r := range additionalEditors {
-		if err := r(ctx, req); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// ClientWithResponses builds on ClientInterface to offer response payloads
-type ClientWithResponses struct {
-	ClientInterface
-}
-
-// NewClientWithResponses creates a new ClientWithResponses, which wraps
-// Client with return type handling
-func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
-	client, err := NewClient(server, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return &ClientWithResponses{client}, nil
-}
-
-// WithBaseURL overrides the baseURL.
-func WithBaseURL(baseURL string) ClientOption {
-	return func(c *Client) error {
-		newBaseURL, err := url.Parse(baseURL)
-		if err != nil {
-			return err
-		}
-		c.Server = newBaseURL.String()
-		return nil
-	}
-}
-
-// ClientWithResponsesInterface is the interface specification for the client with responses above.
-type ClientWithResponsesInterface interface {
-	// ListClaims request
-	ListClaimsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListClaimsResponse, error)
-
-	// ReleaseClaim request
-	ReleaseClaimWithResponse(ctx context.Context, client int, reqEditors ...RequestEditorFn) (*ReleaseClaimResponse, error)
-
-	// GetClaim request
-	GetClaimWithResponse(ctx context.Context, client int, reqEditors ...RequestEditorFn) (*GetClaimResponse, error)
-
-	// SetClaim request with any body
-	SetClaimWithBodyWithResponse(ctx context.Context, client int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetClaimResponse, error)
-
-	SetClaimWithResponse(ctx context.Context, client int, body SetClaimJSONRequestBody, reqEditors ...RequestEditorFn) (*SetClaimResponse, error)
-
-	// GetConfig request
-	GetConfigWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetConfigResponse, error)
-
-	// UpdateConfig request with any body
-	UpdateConfigWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateConfigResponse, error)
-
-	UpdateConfigWithResponse(ctx context.Context, body UpdateConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateConfigResponse, error)
-
-	// GetEventBlockInfo request
-	GetEventBlockInfoWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetEventBlockInfoResponse, error)
-
-	// GetEventBlock request
-	GetEventBlockWithResponse(ctx context.Context, index int, reqEditors ...RequestEditorFn) (*GetEventBlockResponse, error)
-
-	// ClearManualOverride request
-	ClearManualOverrideWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ClearManualOverrideResponse, error)
-
-	// GetManualOverride request
-	GetManualOverrideWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetManualOverrideResponse, error)
-
-	// ToggleManualOverride request
-	ToggleManualOverrideWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ToggleManualOverrideResponse, error)
-
-	// SetManualOverride request with any body
-	SetManualOverrideWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetManualOverrideResponse, error)
-
-	SetManualOverrideWithResponse(ctx context.Context, body SetManualOverrideJSONRequestBody, reqEditors ...RequestEditorFn) (*SetManualOverrideResponse, error)
-
-	// ListSchedule request
-	ListScheduleWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListScheduleResponse, error)
-
-	// UpdateSchedule request with any body
-	UpdateScheduleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateScheduleResponse, error)
-
-	UpdateScheduleWithResponse(ctx context.Context, body UpdateScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateScheduleResponse, error)
-
-	// RemoveScheduleEvent request
-	RemoveScheduleEventWithResponse(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*RemoveScheduleEventResponse, error)
-
-	// GetScheduleEvent request
-	GetScheduleEventWithResponse(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*GetScheduleEventResponse, error)
-
-	// SetScheduleEvent request with any body
-	SetScheduleEventWithBodyWithResponse(ctx context.Context, id int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetScheduleEventResponse, error)
-
-	SetScheduleEventWithResponse(ctx context.Context, id int, body SetScheduleEventJSONRequestBody, reqEditors ...RequestEditorFn) (*SetScheduleEventResponse, error)
-
-	// GetStatus request
-	GetStatusWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetStatusResponse, error)
-
-	// GetTeslaVehicles request
-	GetTeslaVehiclesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetTeslaVehiclesResponse, error)
-
-	// StatusUpdates request
-	StatusUpdatesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*StatusUpdatesResponse, error)
-}
-
-type ListClaimsResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *[]struct {
-		// `true` if the manual override is auto-released when the vehicle is
-		// disconnected, `false` if manual override will persist after vehicle
-		// disconnection.
-		AutoRelease *bool `json:"auto_release,omitempty"`
-
-		// Specify the active charge current in Amps
-		ChargeCurrent *int `json:"charge_current,omitempty"`
-
-		// The client ID, defined by [OpenEVSE](src/evse_man.h#L25)
-		Client *int `json:"client,omitempty"`
-
-		// Stop the charge after `energy_limit` Wh of energy have been transfered.
-		//
-		// > Note:  Not currently implemented
-		EnergyLimit *int32 `json:"energy_limit,omitempty"`
-
-		// Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-		MaxCurrent *int `json:"max_current,omitempty"`
-
-		// The priority of the claim, the higher the number the higher the priority
-		Priority *int `json:"priority,omitempty"`
-
-		// Either enable charging (`active`) or block charging (`disabled`)
-		State *string `json:"state,omitempty"`
-
-		// Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-		//
-		// > Note:  Not currently implemented
-		TimeLimit *int32 `json:"time_limit,omitempty"`
-	}
-}
-
-// Status returns HTTPResponse.Status
-func (r ListClaimsResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ListClaimsResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type ReleaseClaimResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *Message
-	JSON404      *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r ReleaseClaimResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ReleaseClaimResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type GetClaimResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *struct {
-		// `true` if the manual override is auto-released when the vehicle is
-		// disconnected, `false` if manual override will persist after vehicle
-		// disconnection.
-		AutoRelease *bool `json:"auto_release,omitempty"`
-
-		// Specify the active charge current in Amps
-		ChargeCurrent *int `json:"charge_current,omitempty"`
-
-		// Stop the charge after `energy_limit` Wh of energy have been transfered.
-		//
-		// > Note:  Not currently implemented
-		EnergyLimit *int32 `json:"energy_limit,omitempty"`
-
-		// Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-		MaxCurrent *int `json:"max_current,omitempty"`
-
-		// Either enable charging (`active`) or block charging (`disabled`)
-		State *string `json:"state,omitempty"`
-
-		// Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-		//
-		// > Note:  Not currently implemented
-		TimeLimit *int32 `json:"time_limit,omitempty"`
-	}
-	JSON404 *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r GetClaimResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r GetClaimResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type SetClaimResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *Message
-	JSON400      *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r SetClaimResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r SetClaimResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type GetConfigResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *struct {
-		Buildenv                    *string   `json:"buildenv,omitempty"`
-		ChargeMode                  *string   `json:"charge_mode,omitempty"`
-		DiodeCheck                  *bool     `json:"diode_check,omitempty"`
-		DivertPVRatio               *float32  `json:"divert_PV_ratio,omitempty"`
-		DivertAttackSmoothingFactor *float32  `json:"divert_attack_smoothing_factor,omitempty"`
-		DivertDecaySmoothingFactor  *float32  `json:"divert_decay_smoothing_factor,omitempty"`
-		DivertEnabled               *bool     `json:"divert_enabled,omitempty"`
-		DivertMinChargeTime         *float32  `json:"divert_min_charge_time,omitempty"`
-		EmoncmsApikey               *string   `json:"emoncms_apikey,omitempty"`
-		EmoncmsEnabled              *bool     `json:"emoncms_enabled,omitempty"`
-		EmoncmsFingerprint          *string   `json:"emoncms_fingerprint,omitempty"`
-		EmoncmsNode                 *string   `json:"emoncms_node,omitempty"`
-		EmoncmsServer               *string   `json:"emoncms_server,omitempty"`
-		Espflash                    *float32  `json:"espflash,omitempty"`
-		Espinfo                     *string   `json:"espinfo,omitempty"`
-		Firmware                    *string   `json:"firmware,omitempty"`
-		Flags                       *float32  `json:"flags,omitempty"`
-		GfciCheck                   *bool     `json:"gfci_check,omitempty"`
-		GroundCheck                 *bool     `json:"ground_check,omitempty"`
-		Hostname                    *string   `json:"hostname,omitempty"`
-		HttpSupportedProtocols      *[]string `json:"http_supported_protocols,omitempty"`
-		LedBrightness               *float32  `json:"led_brightness,omitempty"`
-		MaxCurrentHard              *int      `json:"max_current_hard,omitempty"`
-		MaxCurrentSoft              *int      `json:"max_current_soft,omitempty"`
-		MinCurrentHard              *int      `json:"min_current_hard,omitempty"`
-		MqttAnnounceTopic           *string   `json:"mqtt_announce_topic,omitempty"`
-		MqttEnabled                 *bool     `json:"mqtt_enabled,omitempty"`
-		MqttGridIe                  *string   `json:"mqtt_grid_ie,omitempty"`
-		MqttPass                    *string   `json:"mqtt_pass,omitempty"`
-		MqttPort                    *int      `json:"mqtt_port,omitempty"`
-		MqttProtocol                *string   `json:"mqtt_protocol,omitempty"`
-		MqttRejectUnauthorized      *bool     `json:"mqtt_reject_unauthorized,omitempty"`
-		MqttServer                  *string   `json:"mqtt_server,omitempty"`
-		MqttSolar                   *string   `json:"mqtt_solar,omitempty"`
-		MqttSupportedProtocols      *[]string `json:"mqtt_supported_protocols,omitempty"`
-		MqttTopic                   *string   `json:"mqtt_topic,omitempty"`
-		MqttUser                    *string   `json:"mqtt_user,omitempty"`
-		MqttVehicleEta              *string   `json:"mqtt_vehicle_eta,omitempty"`
-		MqttVehicleRange            *string   `json:"mqtt_vehicle_range,omitempty"`
-		MqttVehicleRangeMiles       *bool     `json:"mqtt_vehicle_range_miles,omitempty"`
-		MqttVehicleSoc              *string   `json:"mqtt_vehicle_soc,omitempty"`
-		MqttVrms                    *string   `json:"mqtt_vrms,omitempty"`
-		OcppChargeBoxId             *string   `json:"ocpp_chargeBoxId,omitempty"`
-		OcppEnabled                 *bool     `json:"ocpp_enabled,omitempty"`
-		OcppEnergizePlug            *bool     `json:"ocpp_energize_plug,omitempty"`
-		OcppIdTag                   *string   `json:"ocpp_idTag,omitempty"`
-		OcppServer                  *string   `json:"ocpp_server,omitempty"`
-		OcppSuspendEvse             *bool     `json:"ocpp_suspend_evse,omitempty"`
-		Offset                      *int      `json:"offset,omitempty"`
-		Ohm                         *string   `json:"ohm,omitempty"`
-		OhmEnabled                  *bool     `json:"ohm_enabled,omitempty"`
-		Pass                        *string   `json:"pass,omitempty"`
-		PauseUsesDisabled           *bool     `json:"pause_uses_disabled,omitempty"`
-		Protocol                    *string   `json:"protocol,omitempty"`
-		RelayCheck                  *bool     `json:"relay_check,omitempty"`
-		Scale                       *int      `json:"scale,omitempty"`
-		Service                     *int      `json:"service,omitempty"`
-		SntpEnabled                 *bool     `json:"sntp_enabled,omitempty"`
-		SntpHostname                *string   `json:"sntp_hostname,omitempty"`
-		Ssid                        *string   `json:"ssid,omitempty"`
-		TempCheck                   *bool     `json:"temp_check,omitempty"`
-		TeslaAccessToken            *string   `json:"tesla_access_token,omitempty"`
-		TeslaCreatedAt              *float32  `json:"tesla_created_at,omitempty"`
-		TeslaEnabled                *bool     `json:"tesla_enabled,omitempty"`
-		TeslaExpiresIn              *float32  `json:"tesla_expires_in,omitempty"`
-		TeslaRefreshToken           *string   `json:"tesla_refresh_token,omitempty"`
-		TeslaVehicleId              *string   `json:"tesla_vehicle_id,omitempty"`
-		TimeZone                    *string   `json:"time_zone,omitempty"`
-		TxStartPoint                *string   `json:"tx_start_point,omitempty"`
-		VentCheck                   *bool     `json:"vent_check,omitempty"`
-		Version                     *string   `json:"version,omitempty"`
-		WwwPassword                 *string   `json:"www_password,omitempty"`
-		WwwUsername                 *string   `json:"www_username,omitempty"`
-	}
-}
-
-// Status returns HTTPResponse.Status
-func (r GetConfigResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r GetConfigResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type UpdateConfigResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *Message
-	JSON400      *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r UpdateConfigResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r UpdateConfigResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type GetEventBlockInfoResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *struct {
-		Max *int `json:"max,omitempty"`
-		Min *int `json:"min,omitempty"`
-	}
-}
-
-// Status returns HTTPResponse.Status
-func (r GetEventBlockInfoResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r GetEventBlockInfoResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type GetEventBlockResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *[]LogEvent
-	JSON404      *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r GetEventBlockResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r GetEventBlockResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type ClearManualOverrideResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-}
-
-// Status returns HTTPResponse.Status
-func (r ClearManualOverrideResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ClearManualOverrideResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type GetManualOverrideResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *struct {
-		// `true` if the manual override is auto-released when the vehicle is
-		// disconnected, `false` if manual override will persist after vehicle
-		// disconnection.
-		AutoRelease *bool `json:"auto_release,omitempty"`
-
-		// Specify the active charge current in Amps
-		ChargeCurrent *int `json:"charge_current,omitempty"`
-
-		// Stop the charge after `energy_limit` Wh of energy have been transfered.
-		//
-		// > Note:  Not currently implemented
-		EnergyLimit *int32 `json:"energy_limit,omitempty"`
-
-		// Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-		MaxCurrent *int `json:"max_current,omitempty"`
-
-		// Either enable charging (`active`) or block charging (`disabled`)
-		State *string `json:"state,omitempty"`
-
-		// Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-		//
-		// > Note:  Not currently implemented
-		TimeLimit *int32 `json:"time_limit,omitempty"`
-	}
-	JSON404 *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r GetManualOverrideResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r GetManualOverrideResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type ToggleManualOverrideResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-}
-
-// Status returns HTTPResponse.Status
-func (r ToggleManualOverrideResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ToggleManualOverrideResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type SetManualOverrideResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r SetManualOverrideResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r SetManualOverrideResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type ListScheduleResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *[]ScheduleEvent
-	JSON400      *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r ListScheduleResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ListScheduleResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type UpdateScheduleResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-}
-
-// Status returns HTTPResponse.Status
-func (r UpdateScheduleResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r UpdateScheduleResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type RemoveScheduleEventResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r RemoveScheduleEventResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r RemoveScheduleEventResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type GetScheduleEventResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *string
-}
-
-// Status returns HTTPResponse.Status
-func (r GetScheduleEventResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r GetScheduleEventResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type SetScheduleEventResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *Message
-}
-
-// Status returns HTTPResponse.Status
-func (r SetScheduleEventResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r SetScheduleEventResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type GetStatusResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *struct {
-		// the value of the charge current in mA
-		Amp *float32 `json:"amp,omitempty"`
-
-		// The computed avalible current for divert
-		AvailableCurrent *int `json:"available_current,omitempty"`
-
-		// The divert charge rate
-		ChargeRate *int `json:"charge_rate,omitempty"`
-
-		// The LCD colour
-		Colour *int `json:"colour,omitempty"`
-
-		// Number of RAPI messages sent
-		CommSent *int `json:"comm_sent,omitempty"`
-
-		// Number of successful RAPI messages
-		CommSuccess *int `json:"comm_success,omitempty"`
-
-		// `true` if divert is active, `false` if not
-		DivertActive *bool `json:"divert_active,omitempty"`
-
-		// The time since the last update of the divert feed
-		DivertUpdate *float32 `json:"divert_update,omitempty"`
-
-		// The PV divert mode, `1` normal, `2` Eco
-		Divertmode *int `json:"divertmode,omitempty"`
-
-		// The duration of the current charge session, in seconds
-		Elapsed *float32 `json:"elapsed,omitempty"`
-
-		// `1`, if connected to an EmonCMS server, `0` not connected
-		EmoncmsConnected *int `json:"emoncms_connected,omitempty"`
-
-		// Last response message from the EmonCMS server
-		EmoncmsMessage *string `json:"emoncms_message,omitempty"`
-
-		// `1`, Wired ethernet is connected, `0` no wired ethernet connection
-		EthConnected *int `json:"eth_connected,omitempty"`
-
-		// `1`, if the WiFi module has negociated a connection to the OpenEVSE module, `0` not connected
-		EvseConnected *int `json:"evse_connected,omitempty"`
-
-		// Number of bytes free in the ESP memory heap
-		FreeHeap *int `json:"free_heap,omitempty"`
-
-		// Number of GFCI errors
-		Gfcicount *int `json:"gfcicount,omitempty"`
-
-		// The latest grid +I/-E feed value received
-		GridIe *int `json:"grid_ie,omitempty"`
-
-		// The current IP address of the WiFi module
-		Ipaddress *string `json:"ipaddress,omitempty"`
-
-		// `1`, if the manual override is active, `0` default behaviour
-		ManualOverride *int `json:"manual_override,omitempty"`
-
-		// The current mode of the EVSE
-		Mode *string `json:"mode,omitempty"`
-
-		// `1`, if connected to an EmonCMS server, `0` not connected
-		MqttConnected *int `json:"mqtt_connected,omitempty"`
-
-		// `1`, network is connected, either wired or WiFi, `0` no network connection
-		NetConnected *int `json:"net_connected,omitempty"`
-
-		// Number of no ground errors
-		Nogndcount *int `json:"nogndcount,omitempty"`
-
-		// The current timezone
-		Offset *string `json:"offset,omitempty"`
-
-		// Last status message from the OhmHour connection
-		OhmHour *string `json:"ohm_hour,omitempty"`
-
-		// `1`, if there is an OTA update active, `0` if normal operation
-		OtaUpdate *int `json:"ota_update,omitempty"`
-
-		// The number of posts sent to the EmonCMS server
-		PacketsSent *int `json:"packets_sent,omitempty"`
-
-		// The number of successful posts to the EmonCMS server
-		PacketsSuccess *int `json:"packets_success,omitempty"`
-
-		// the pilot value, in amps
-		Pilot *int `json:"pilot,omitempty"`
-
-		// `1`, if RAPI messages have successfully been sent to the OpenEVSE module, `0` not connected
-		RapiConnected *int `json:"rapi_connected,omitempty"`
-
-		// The current service level
-		ServiceLevel *int `json:"service_level,omitempty"`
-
-		// The computed smoothed available current for divert
-		SmoothedAvailableCurrent *int `json:"smoothed_available_current,omitempty"`
-
-		// The latest solar feed value received
-		Solar *int `json:"solar,omitempty"`
-
-		// WiFi RSSI signal strength
-		Srssi *int `json:"srssi,omitempty"`
-
-		// The EVSE state
-		State *int `json:"state,omitempty"`
-
-		// The EVSE status
-		Status *string `json:"status,omitempty"`
-
-		// Number of stuck relay errors
-		Stuckcount *int `json:"stuckcount,omitempty"`
-
-		// the derived temperature, in 1/10th °C
-		Temp *float32 `json:"temp,omitempty"`
-
-		// The current time on the WiFi module
-		Time *string `json:"time,omitempty"`
-
-		// `1`, if a vehicle id connected to the EVSE, `0` not connected
-		Vehicle *int `json:"vehicle,omitempty"`
-
-		// the charge voltage, read form the EVSE if supported, or supplied via MQTT or other external source
-		Voltage *float32 `json:"voltage,omitempty"`
-
-		// The total amount of energy transfered, in W/hour
-		Watthour *float32 `json:"watthour,omitempty"`
-
-		// The amount of energy transfered in this charge session, in W/sec
-		Wattsec *float32 `json:"wattsec,omitempty"`
-
-		// `1`, WiFi is connected, `0` no WiFi connection
-		WifiClientConnected *int `json:"wifi_client_connected,omitempty"`
-	}
-}
-
-// Status returns HTTPResponse.Status
-func (r GetStatusResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r GetStatusResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type GetTeslaVehiclesResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *struct {
-		Count    float32 `json:"count"`
-		Vehicles []struct {
-			Id   string `json:"id"`
-			Name string `json:"name"`
-		} `json:"vehicles"`
-	}
-}
-
-// Status returns HTTPResponse.Status
-func (r GetTeslaVehiclesResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r GetTeslaVehiclesResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type StatusUpdatesResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *struct {
-		// the value of the charge current in mA
-		Amp *float32 `json:"amp,omitempty"`
-
-		// The computed avalible current for divert
-		AvailableCurrent *int `json:"available_current,omitempty"`
-
-		// The divert charge rate
-		ChargeRate *int `json:"charge_rate,omitempty"`
-
-		// The LCD colour
-		Colour *int `json:"colour,omitempty"`
-
-		// Number of RAPI messages sent
-		CommSent *int `json:"comm_sent,omitempty"`
-
-		// Number of successful RAPI messages
-		CommSuccess *int `json:"comm_success,omitempty"`
-
-		// `true` if divert is active, `false` if not
-		DivertActive *bool `json:"divert_active,omitempty"`
-
-		// The time since the last update of the divert feed
-		DivertUpdate *float32 `json:"divert_update,omitempty"`
-
-		// The PV divert mode, `1` normal, `2` Eco
-		Divertmode *int `json:"divertmode,omitempty"`
-
-		// The duration of the current charge session, in seconds
-		Elapsed *float32 `json:"elapsed,omitempty"`
-
-		// `1`, if connected to an EmonCMS server, `0` not connected
-		EmoncmsConnected *int `json:"emoncms_connected,omitempty"`
-
-		// Last response message from the EmonCMS server
-		EmoncmsMessage *string `json:"emoncms_message,omitempty"`
-
-		// `1`, Wired ethernet is connected, `0` no wired ethernet connection
-		EthConnected *int `json:"eth_connected,omitempty"`
-
-		// `1`, if the WiFi module has negociated a connection to the OpenEVSE module, `0` not connected
-		EvseConnected *int `json:"evse_connected,omitempty"`
-
-		// Number of bytes free in the ESP memory heap
-		FreeHeap *int `json:"free_heap,omitempty"`
-
-		// Number of GFCI errors
-		Gfcicount *int `json:"gfcicount,omitempty"`
-
-		// The latest grid +I/-E feed value received
-		GridIe *int `json:"grid_ie,omitempty"`
-
-		// The current IP address of the WiFi module
-		Ipaddress *string `json:"ipaddress,omitempty"`
-
-		// `1`, if the manual override is active, `0` default behaviour
-		ManualOverride *int `json:"manual_override,omitempty"`
-
-		// The current mode of the EVSE
-		Mode *string `json:"mode,omitempty"`
-
-		// `1`, if connected to an EmonCMS server, `0` not connected
-		MqttConnected *int `json:"mqtt_connected,omitempty"`
-
-		// `1`, network is connected, either wired or WiFi, `0` no network connection
-		NetConnected *int `json:"net_connected,omitempty"`
-
-		// Number of no ground errors
-		Nogndcount *int `json:"nogndcount,omitempty"`
-
-		// The current timezone
-		Offset *string `json:"offset,omitempty"`
-
-		// Last status message from the OhmHour connection
-		OhmHour *string `json:"ohm_hour,omitempty"`
-
-		// `1`, if there is an OTA update active, `0` if normal operation
-		OtaUpdate *int `json:"ota_update,omitempty"`
-
-		// The number of posts sent to the EmonCMS server
-		PacketsSent *int `json:"packets_sent,omitempty"`
-
-		// The number of successful posts to the EmonCMS server
-		PacketsSuccess *int `json:"packets_success,omitempty"`
-
-		// the pilot value, in amps
-		Pilot *int `json:"pilot,omitempty"`
-
-		// `1`, if RAPI messages have successfully been sent to the OpenEVSE module, `0` not connected
-		RapiConnected *int `json:"rapi_connected,omitempty"`
-
-		// The current service level
-		ServiceLevel *int `json:"service_level,omitempty"`
-
-		// The computed smoothed available current for divert
-		SmoothedAvailableCurrent *int `json:"smoothed_available_current,omitempty"`
-
-		// The latest solar feed value received
-		Solar *int `json:"solar,omitempty"`
-
-		// WiFi RSSI signal strength
-		Srssi *int `json:"srssi,omitempty"`
-
-		// The EVSE status
-		State *int `json:"state,omitempty"`
-
-		// Number of stuck relay errors
-		Stuckcount *int `json:"stuckcount,omitempty"`
-
-		// the derived temperature, in 1/10th °C
-		Temp *float32 `json:"temp,omitempty"`
-
-		// The current time on the WiFi module
-		Time *string `json:"time,omitempty"`
-
-		// `1`, if a vehicle id connected to the EVSE, `0` not connected
-		Vehicle *int `json:"vehicle,omitempty"`
-
-		// the charge voltage, read form the EVSE if supported, or supplied via MQTT or other external source
-		Voltage *float32 `json:"voltage,omitempty"`
-
-		// The total amount of energy transfered, in W/hour
-		Watthour *float32 `json:"watthour,omitempty"`
-
-		// The amount of energy transfered in this charge session, in W/sec
-		Wattsec *float32 `json:"wattsec,omitempty"`
-
-		// `1`, WiFi is connected, `0` no WiFi connection
-		WifiClientConnected *int `json:"wifi_client_connected,omitempty"`
-	}
-}
-
-// Status returns HTTPResponse.Status
-func (r StatusUpdatesResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r StatusUpdatesResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-// ListClaimsWithResponse request returning *ListClaimsResponse
-func (c *ClientWithResponses) ListClaimsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListClaimsResponse, error) {
-	rsp, err := c.ListClaims(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseListClaimsResponse(rsp)
-}
-
-// ReleaseClaimWithResponse request returning *ReleaseClaimResponse
-func (c *ClientWithResponses) ReleaseClaimWithResponse(ctx context.Context, client int, reqEditors ...RequestEditorFn) (*ReleaseClaimResponse, error) {
-	rsp, err := c.ReleaseClaim(ctx, client, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseReleaseClaimResponse(rsp)
-}
-
-// GetClaimWithResponse request returning *GetClaimResponse
-func (c *ClientWithResponses) GetClaimWithResponse(ctx context.Context, client int, reqEditors ...RequestEditorFn) (*GetClaimResponse, error) {
-	rsp, err := c.GetClaim(ctx, client, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseGetClaimResponse(rsp)
-}
-
-// SetClaimWithBodyWithResponse request with arbitrary body returning *SetClaimResponse
-func (c *ClientWithResponses) SetClaimWithBodyWithResponse(ctx context.Context, client int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetClaimResponse, error) {
-	rsp, err := c.SetClaimWithBody(ctx, client, contentType, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseSetClaimResponse(rsp)
-}
-
-func (c *ClientWithResponses) SetClaimWithResponse(ctx context.Context, client int, body SetClaimJSONRequestBody, reqEditors ...RequestEditorFn) (*SetClaimResponse, error) {
-	rsp, err := c.SetClaim(ctx, client, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseSetClaimResponse(rsp)
-}
-
-// GetConfigWithResponse request returning *GetConfigResponse
-func (c *ClientWithResponses) GetConfigWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetConfigResponse, error) {
-	rsp, err := c.GetConfig(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseGetConfigResponse(rsp)
-}
-
-// UpdateConfigWithBodyWithResponse request with arbitrary body returning *UpdateConfigResponse
-func (c *ClientWithResponses) UpdateConfigWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateConfigResponse, error) {
-	rsp, err := c.UpdateConfigWithBody(ctx, contentType, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseUpdateConfigResponse(rsp)
-}
-
-func (c *ClientWithResponses) UpdateConfigWithResponse(ctx context.Context, body UpdateConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateConfigResponse, error) {
-	rsp, err := c.UpdateConfig(ctx, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseUpdateConfigResponse(rsp)
-}
-
-// GetEventBlockInfoWithResponse request returning *GetEventBlockInfoResponse
-func (c *ClientWithResponses) GetEventBlockInfoWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetEventBlockInfoResponse, error) {
-	rsp, err := c.GetEventBlockInfo(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseGetEventBlockInfoResponse(rsp)
-}
-
-// GetEventBlockWithResponse request returning *GetEventBlockResponse
-func (c *ClientWithResponses) GetEventBlockWithResponse(ctx context.Context, index int, reqEditors ...RequestEditorFn) (*GetEventBlockResponse, error) {
-	rsp, err := c.GetEventBlock(ctx, index, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseGetEventBlockResponse(rsp)
-}
-
-// ClearManualOverrideWithResponse request returning *ClearManualOverrideResponse
-func (c *ClientWithResponses) ClearManualOverrideWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ClearManualOverrideResponse, error) {
-	rsp, err := c.ClearManualOverride(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseClearManualOverrideResponse(rsp)
-}
-
-// GetManualOverrideWithResponse request returning *GetManualOverrideResponse
-func (c *ClientWithResponses) GetManualOverrideWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetManualOverrideResponse, error) {
-	rsp, err := c.GetManualOverride(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseGetManualOverrideResponse(rsp)
-}
-
-// ToggleManualOverrideWithResponse request returning *ToggleManualOverrideResponse
-func (c *ClientWithResponses) ToggleManualOverrideWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ToggleManualOverrideResponse, error) {
-	rsp, err := c.ToggleManualOverride(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseToggleManualOverrideResponse(rsp)
-}
-
-// SetManualOverrideWithBodyWithResponse request with arbitrary body returning *SetManualOverrideResponse
-func (c *ClientWithResponses) SetManualOverrideWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetManualOverrideResponse, error) {
-	rsp, err := c.SetManualOverrideWithBody(ctx, contentType, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseSetManualOverrideResponse(rsp)
-}
-
-func (c *ClientWithResponses) SetManualOverrideWithResponse(ctx context.Context, body SetManualOverrideJSONRequestBody, reqEditors ...RequestEditorFn) (*SetManualOverrideResponse, error) {
-	rsp, err := c.SetManualOverride(ctx, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseSetManualOverrideResponse(rsp)
-}
-
-// ListScheduleWithResponse request returning *ListScheduleResponse
-func (c *ClientWithResponses) ListScheduleWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListScheduleResponse, error) {
-	rsp, err := c.ListSchedule(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseListScheduleResponse(rsp)
-}
-
-// UpdateScheduleWithBodyWithResponse request with arbitrary body returning *UpdateScheduleResponse
-func (c *ClientWithResponses) UpdateScheduleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateScheduleResponse, error) {
-	rsp, err := c.UpdateScheduleWithBody(ctx, contentType, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseUpdateScheduleResponse(rsp)
-}
-
-func (c *ClientWithResponses) UpdateScheduleWithResponse(ctx context.Context, body UpdateScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateScheduleResponse, error) {
-	rsp, err := c.UpdateSchedule(ctx, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseUpdateScheduleResponse(rsp)
-}
-
-// RemoveScheduleEventWithResponse request returning *RemoveScheduleEventResponse
-func (c *ClientWithResponses) RemoveScheduleEventWithResponse(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*RemoveScheduleEventResponse, error) {
-	rsp, err := c.RemoveScheduleEvent(ctx, id, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseRemoveScheduleEventResponse(rsp)
-}
-
-// GetScheduleEventWithResponse request returning *GetScheduleEventResponse
-func (c *ClientWithResponses) GetScheduleEventWithResponse(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*GetScheduleEventResponse, error) {
-	rsp, err := c.GetScheduleEvent(ctx, id, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseGetScheduleEventResponse(rsp)
-}
-
-// SetScheduleEventWithBodyWithResponse request with arbitrary body returning *SetScheduleEventResponse
-func (c *ClientWithResponses) SetScheduleEventWithBodyWithResponse(ctx context.Context, id int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetScheduleEventResponse, error) {
-	rsp, err := c.SetScheduleEventWithBody(ctx, id, contentType, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseSetScheduleEventResponse(rsp)
-}
-
-func (c *ClientWithResponses) SetScheduleEventWithResponse(ctx context.Context, id int, body SetScheduleEventJSONRequestBody, reqEditors ...RequestEditorFn) (*SetScheduleEventResponse, error) {
-	rsp, err := c.SetScheduleEvent(ctx, id, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseSetScheduleEventResponse(rsp)
-}
-
-// GetStatusWithResponse request returning *GetStatusResponse
-func (c *ClientWithResponses) GetStatusWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetStatusResponse, error) {
-	rsp, err := c.GetStatus(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseGetStatusResponse(rsp)
-}
-
-// GetTeslaVehiclesWithResponse request returning *GetTeslaVehiclesResponse
-func (c *ClientWithResponses) GetTeslaVehiclesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetTeslaVehiclesResponse, error) {
-	rsp, err := c.GetTeslaVehicles(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseGetTeslaVehiclesResponse(rsp)
-}
-
-// StatusUpdatesWithResponse request returning *StatusUpdatesResponse
-func (c *ClientWithResponses) StatusUpdatesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*StatusUpdatesResponse, error) {
-	rsp, err := c.StatusUpdates(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseStatusUpdatesResponse(rsp)
-}
-
-// ParseListClaimsResponse parses an HTTP response from a ListClaimsWithResponse call
-func ParseListClaimsResponse(rsp *http.Response) (*ListClaimsResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &ListClaimsResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest []struct {
-			// `true` if the manual override is auto-released when the vehicle is
-			// disconnected, `false` if manual override will persist after vehicle
-			// disconnection.
-			AutoRelease *bool `json:"auto_release,omitempty"`
-
-			// Specify the active charge current in Amps
-			ChargeCurrent *int `json:"charge_current,omitempty"`
-
-			// The client ID, defined by [OpenEVSE](src/evse_man.h#L25)
-			Client *int `json:"client,omitempty"`
-
-			// Stop the charge after `energy_limit` Wh of energy have been transfered.
-			//
-			// > Note:  Not currently implemented
-			EnergyLimit *int32 `json:"energy_limit,omitempty"`
-
-			// Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-			MaxCurrent *int `json:"max_current,omitempty"`
-
-			// The priority of the claim, the higher the number the higher the priority
-			Priority *int `json:"priority,omitempty"`
-
-			// Either enable charging (`active`) or block charging (`disabled`)
-			State *string `json:"state,omitempty"`
-
-			// Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-			//
-			// > Note:  Not currently implemented
-			TimeLimit *int32 `json:"time_limit,omitempty"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseReleaseClaimResponse parses an HTTP response from a ReleaseClaimWithResponse call
-func ParseReleaseClaimResponse(rsp *http.Response) (*ReleaseClaimResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &ReleaseClaimResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON404 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseGetClaimResponse parses an HTTP response from a GetClaimWithResponse call
-func ParseGetClaimResponse(rsp *http.Response) (*GetClaimResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &GetClaimResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest struct {
-			// `true` if the manual override is auto-released when the vehicle is
-			// disconnected, `false` if manual override will persist after vehicle
-			// disconnection.
-			AutoRelease *bool `json:"auto_release,omitempty"`
-
-			// Specify the active charge current in Amps
-			ChargeCurrent *int `json:"charge_current,omitempty"`
-
-			// Stop the charge after `energy_limit` Wh of energy have been transfered.
-			//
-			// > Note:  Not currently implemented
-			EnergyLimit *int32 `json:"energy_limit,omitempty"`
-
-			// Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-			MaxCurrent *int `json:"max_current,omitempty"`
-
-			// Either enable charging (`active`) or block charging (`disabled`)
-			State *string `json:"state,omitempty"`
-
-			// Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-			//
-			// > Note:  Not currently implemented
-			TimeLimit *int32 `json:"time_limit,omitempty"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON404 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseSetClaimResponse parses an HTTP response from a SetClaimWithResponse call
-func ParseSetClaimResponse(rsp *http.Response) (*SetClaimResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &SetClaimResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON400 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseGetConfigResponse parses an HTTP response from a GetConfigWithResponse call
-func ParseGetConfigResponse(rsp *http.Response) (*GetConfigResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &GetConfigResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest struct {
-			Buildenv                    *string   `json:"buildenv,omitempty"`
-			ChargeMode                  *string   `json:"charge_mode,omitempty"`
-			DiodeCheck                  *bool     `json:"diode_check,omitempty"`
-			DivertPVRatio               *float32  `json:"divert_PV_ratio,omitempty"`
-			DivertAttackSmoothingFactor *float32  `json:"divert_attack_smoothing_factor,omitempty"`
-			DivertDecaySmoothingFactor  *float32  `json:"divert_decay_smoothing_factor,omitempty"`
-			DivertEnabled               *bool     `json:"divert_enabled,omitempty"`
-			DivertMinChargeTime         *float32  `json:"divert_min_charge_time,omitempty"`
-			EmoncmsApikey               *string   `json:"emoncms_apikey,omitempty"`
-			EmoncmsEnabled              *bool     `json:"emoncms_enabled,omitempty"`
-			EmoncmsFingerprint          *string   `json:"emoncms_fingerprint,omitempty"`
-			EmoncmsNode                 *string   `json:"emoncms_node,omitempty"`
-			EmoncmsServer               *string   `json:"emoncms_server,omitempty"`
-			Espflash                    *float32  `json:"espflash,omitempty"`
-			Espinfo                     *string   `json:"espinfo,omitempty"`
-			Firmware                    *string   `json:"firmware,omitempty"`
-			Flags                       *float32  `json:"flags,omitempty"`
-			GfciCheck                   *bool     `json:"gfci_check,omitempty"`
-			GroundCheck                 *bool     `json:"ground_check,omitempty"`
-			Hostname                    *string   `json:"hostname,omitempty"`
-			HttpSupportedProtocols      *[]string `json:"http_supported_protocols,omitempty"`
-			LedBrightness               *float32  `json:"led_brightness,omitempty"`
-			MaxCurrentHard              *int      `json:"max_current_hard,omitempty"`
-			MaxCurrentSoft              *int      `json:"max_current_soft,omitempty"`
-			MinCurrentHard              *int      `json:"min_current_hard,omitempty"`
-			MqttAnnounceTopic           *string   `json:"mqtt_announce_topic,omitempty"`
-			MqttEnabled                 *bool     `json:"mqtt_enabled,omitempty"`
-			MqttGridIe                  *string   `json:"mqtt_grid_ie,omitempty"`
-			MqttPass                    *string   `json:"mqtt_pass,omitempty"`
-			MqttPort                    *int      `json:"mqtt_port,omitempty"`
-			MqttProtocol                *string   `json:"mqtt_protocol,omitempty"`
-			MqttRejectUnauthorized      *bool     `json:"mqtt_reject_unauthorized,omitempty"`
-			MqttServer                  *string   `json:"mqtt_server,omitempty"`
-			MqttSolar                   *string   `json:"mqtt_solar,omitempty"`
-			MqttSupportedProtocols      *[]string `json:"mqtt_supported_protocols,omitempty"`
-			MqttTopic                   *string   `json:"mqtt_topic,omitempty"`
-			MqttUser                    *string   `json:"mqtt_user,omitempty"`
-			MqttVehicleEta              *string   `json:"mqtt_vehicle_eta,omitempty"`
-			MqttVehicleRange            *string   `json:"mqtt_vehicle_range,omitempty"`
-			MqttVehicleRangeMiles       *bool     `json:"mqtt_vehicle_range_miles,omitempty"`
-			MqttVehicleSoc              *string   `json:"mqtt_vehicle_soc,omitempty"`
-			MqttVrms                    *string   `json:"mqtt_vrms,omitempty"`
-			OcppChargeBoxId             *string   `json:"ocpp_chargeBoxId,omitempty"`
-			OcppEnabled                 *bool     `json:"ocpp_enabled,omitempty"`
-			OcppEnergizePlug            *bool     `json:"ocpp_energize_plug,omitempty"`
-			OcppIdTag                   *string   `json:"ocpp_idTag,omitempty"`
-			OcppServer                  *string   `json:"ocpp_server,omitempty"`
-			OcppSuspendEvse             *bool     `json:"ocpp_suspend_evse,omitempty"`
-			Offset                      *int      `json:"offset,omitempty"`
-			Ohm                         *string   `json:"ohm,omitempty"`
-			OhmEnabled                  *bool     `json:"ohm_enabled,omitempty"`
-			Pass                        *string   `json:"pass,omitempty"`
-			PauseUsesDisabled           *bool     `json:"pause_uses_disabled,omitempty"`
-			Protocol                    *string   `json:"protocol,omitempty"`
-			RelayCheck                  *bool     `json:"relay_check,omitempty"`
-			Scale                       *int      `json:"scale,omitempty"`
-			Service                     *int      `json:"service,omitempty"`
-			SntpEnabled                 *bool     `json:"sntp_enabled,omitempty"`
-			SntpHostname                *string   `json:"sntp_hostname,omitempty"`
-			Ssid                        *string   `json:"ssid,omitempty"`
-			TempCheck                   *bool     `json:"temp_check,omitempty"`
-			TeslaAccessToken            *string   `json:"tesla_access_token,omitempty"`
-			TeslaCreatedAt              *float32  `json:"tesla_created_at,omitempty"`
-			TeslaEnabled                *bool     `json:"tesla_enabled,omitempty"`
-			TeslaExpiresIn              *float32  `json:"tesla_expires_in,omitempty"`
-			TeslaRefreshToken           *string   `json:"tesla_refresh_token,omitempty"`
-			TeslaVehicleId              *string   `json:"tesla_vehicle_id,omitempty"`
-			TimeZone                    *string   `json:"time_zone,omitempty"`
-			TxStartPoint                *string   `json:"tx_start_point,omitempty"`
-			VentCheck                   *bool     `json:"vent_check,omitempty"`
-			Version                     *string   `json:"version,omitempty"`
-			WwwPassword                 *string   `json:"www_password,omitempty"`
-			WwwUsername                 *string   `json:"www_username,omitempty"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseUpdateConfigResponse parses an HTTP response from a UpdateConfigWithResponse call
-func ParseUpdateConfigResponse(rsp *http.Response) (*UpdateConfigResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &UpdateConfigResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON400 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseGetEventBlockInfoResponse parses an HTTP response from a GetEventBlockInfoWithResponse call
-func ParseGetEventBlockInfoResponse(rsp *http.Response) (*GetEventBlockInfoResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &GetEventBlockInfoResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest struct {
-			Max *int `json:"max,omitempty"`
-			Min *int `json:"min,omitempty"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseGetEventBlockResponse parses an HTTP response from a GetEventBlockWithResponse call
-func ParseGetEventBlockResponse(rsp *http.Response) (*GetEventBlockResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &GetEventBlockResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest []LogEvent
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON404 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseClearManualOverrideResponse parses an HTTP response from a ClearManualOverrideWithResponse call
-func ParseClearManualOverrideResponse(rsp *http.Response) (*ClearManualOverrideResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &ClearManualOverrideResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	return response, nil
-}
-
-// ParseGetManualOverrideResponse parses an HTTP response from a GetManualOverrideWithResponse call
-func ParseGetManualOverrideResponse(rsp *http.Response) (*GetManualOverrideResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &GetManualOverrideResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest struct {
-			// `true` if the manual override is auto-released when the vehicle is
-			// disconnected, `false` if manual override will persist after vehicle
-			// disconnection.
-			AutoRelease *bool `json:"auto_release,omitempty"`
-
-			// Specify the active charge current in Amps
-			ChargeCurrent *int `json:"charge_current,omitempty"`
-
-			// Stop the charge after `energy_limit` Wh of energy have been transfered.
-			//
-			// > Note:  Not currently implemented
-			EnergyLimit *int32 `json:"energy_limit,omitempty"`
-
-			// Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-			MaxCurrent *int `json:"max_current,omitempty"`
-
-			// Either enable charging (`active`) or block charging (`disabled`)
-			State *string `json:"state,omitempty"`
-
-			// Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-			//
-			// > Note:  Not currently implemented
-			TimeLimit *int32 `json:"time_limit,omitempty"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON404 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseToggleManualOverrideResponse parses an HTTP response from a ToggleManualOverrideWithResponse call
-func ParseToggleManualOverrideResponse(rsp *http.Response) (*ToggleManualOverrideResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &ToggleManualOverrideResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	return response, nil
-}
-
-// ParseSetManualOverrideResponse parses an HTTP response from a SetManualOverrideWithResponse call
-func ParseSetManualOverrideResponse(rsp *http.Response) (*SetManualOverrideResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &SetManualOverrideResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseListScheduleResponse parses an HTTP response from a ListScheduleWithResponse call
-func ParseListScheduleResponse(rsp *http.Response) (*ListScheduleResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &ListScheduleResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest []ScheduleEvent
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON400 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseUpdateScheduleResponse parses an HTTP response from a UpdateScheduleWithResponse call
-func ParseUpdateScheduleResponse(rsp *http.Response) (*UpdateScheduleResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &UpdateScheduleResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	return response, nil
-}
-
-// ParseRemoveScheduleEventResponse parses an HTTP response from a RemoveScheduleEventWithResponse call
-func ParseRemoveScheduleEventResponse(rsp *http.Response) (*RemoveScheduleEventResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &RemoveScheduleEventResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseGetScheduleEventResponse parses an HTTP response from a GetScheduleEventWithResponse call
-func ParseGetScheduleEventResponse(rsp *http.Response) (*GetScheduleEventResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &GetScheduleEventResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest string
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseSetScheduleEventResponse parses an HTTP response from a SetScheduleEventWithResponse call
-func ParseSetScheduleEventResponse(rsp *http.Response) (*SetScheduleEventResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &SetScheduleEventResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest Message
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseGetStatusResponse parses an HTTP response from a GetStatusWithResponse call
-func ParseGetStatusResponse(rsp *http.Response) (*GetStatusResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &GetStatusResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest struct {
-			// the value of the charge current in mA
-			Amp *float32 `json:"amp,omitempty"`
-
-			// The computed avalible current for divert
-			AvailableCurrent *int `json:"available_current,omitempty"`
-
-			// The divert charge rate
-			ChargeRate *int `json:"charge_rate,omitempty"`
-
-			// The LCD colour
-			Colour *int `json:"colour,omitempty"`
-
-			// Number of RAPI messages sent
-			CommSent *int `json:"comm_sent,omitempty"`
-
-			// Number of successful RAPI messages
-			CommSuccess *int `json:"comm_success,omitempty"`
-
-			// `true` if divert is active, `false` if not
-			DivertActive *bool `json:"divert_active,omitempty"`
-
-			// The time since the last update of the divert feed
-			DivertUpdate *float32 `json:"divert_update,omitempty"`
-
-			// The PV divert mode, `1` normal, `2` Eco
-			Divertmode *int `json:"divertmode,omitempty"`
-
-			// The duration of the current charge session, in seconds
-			Elapsed *float32 `json:"elapsed,omitempty"`
-
-			// `1`, if connected to an EmonCMS server, `0` not connected
-			EmoncmsConnected *int `json:"emoncms_connected,omitempty"`
-
-			// Last response message from the EmonCMS server
-			EmoncmsMessage *string `json:"emoncms_message,omitempty"`
-
-			// `1`, Wired ethernet is connected, `0` no wired ethernet connection
-			EthConnected *int `json:"eth_connected,omitempty"`
-
-			// `1`, if the WiFi module has negociated a connection to the OpenEVSE module, `0` not connected
-			EvseConnected *int `json:"evse_connected,omitempty"`
-
-			// Number of bytes free in the ESP memory heap
-			FreeHeap *int `json:"free_heap,omitempty"`
-
-			// Number of GFCI errors
-			Gfcicount *int `json:"gfcicount,omitempty"`
-
-			// The latest grid +I/-E feed value received
-			GridIe *int `json:"grid_ie,omitempty"`
-
-			// The current IP address of the WiFi module
-			Ipaddress *string `json:"ipaddress,omitempty"`
-
-			// `1`, if the manual override is active, `0` default behaviour
-			ManualOverride *int `json:"manual_override,omitempty"`
-
-			// The current mode of the EVSE
-			Mode *string `json:"mode,omitempty"`
-
-			// `1`, if connected to an EmonCMS server, `0` not connected
-			MqttConnected *int `json:"mqtt_connected,omitempty"`
-
-			// `1`, network is connected, either wired or WiFi, `0` no network connection
-			NetConnected *int `json:"net_connected,omitempty"`
-
-			// Number of no ground errors
-			Nogndcount *int `json:"nogndcount,omitempty"`
-
-			// The current timezone
-			Offset *string `json:"offset,omitempty"`
-
-			// Last status message from the OhmHour connection
-			OhmHour *string `json:"ohm_hour,omitempty"`
-
-			// `1`, if there is an OTA update active, `0` if normal operation
-			OtaUpdate *int `json:"ota_update,omitempty"`
-
-			// The number of posts sent to the EmonCMS server
-			PacketsSent *int `json:"packets_sent,omitempty"`
-
-			// The number of successful posts to the EmonCMS server
-			PacketsSuccess *int `json:"packets_success,omitempty"`
-
-			// the pilot value, in amps
-			Pilot *int `json:"pilot,omitempty"`
-
-			// `1`, if RAPI messages have successfully been sent to the OpenEVSE module, `0` not connected
-			RapiConnected *int `json:"rapi_connected,omitempty"`
-
-			// The current service level
-			ServiceLevel *int `json:"service_level,omitempty"`
-
-			// The computed smoothed available current for divert
-			SmoothedAvailableCurrent *int `json:"smoothed_available_current,omitempty"`
-
-			// The latest solar feed value received
-			Solar *int `json:"solar,omitempty"`
-
-			// WiFi RSSI signal strength
-			Srssi *int `json:"srssi,omitempty"`
-
-			// The EVSE state
-			State *int `json:"state,omitempty"`
-
-			// The EVSE status
-			Status *string `json:"status,omitempty"`
-
-			// Number of stuck relay errors
-			Stuckcount *int `json:"stuckcount,omitempty"`
-
-			// the derived temperature, in 1/10th °C
-			Temp *float32 `json:"temp,omitempty"`
-
-			// The current time on the WiFi module
-			Time *string `json:"time,omitempty"`
-
-			// `1`, if a vehicle id connected to the EVSE, `0` not connected
-			Vehicle *int `json:"vehicle,omitempty"`
-
-			// the charge voltage, read form the EVSE if supported, or supplied via MQTT or other external source
-			Voltage *float32 `json:"voltage,omitempty"`
-
-			// The total amount of energy transfered, in W/hour
-			Watthour *float32 `json:"watthour,omitempty"`
-
-			// The amount of energy transfered in this charge session, in W/sec
-			Wattsec *float32 `json:"wattsec,omitempty"`
-
-			// `1`, WiFi is connected, `0` no WiFi connection
-			WifiClientConnected *int `json:"wifi_client_connected,omitempty"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseGetTeslaVehiclesResponse parses an HTTP response from a GetTeslaVehiclesWithResponse call
-func ParseGetTeslaVehiclesResponse(rsp *http.Response) (*GetTeslaVehiclesResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &GetTeslaVehiclesResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest struct {
-			Count    float32 `json:"count"`
-			Vehicles []struct {
-				Id   string `json:"id"`
-				Name string `json:"name"`
-			} `json:"vehicles"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseStatusUpdatesResponse parses an HTTP response from a StatusUpdatesWithResponse call
-func ParseStatusUpdatesResponse(rsp *http.Response) (*StatusUpdatesResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &StatusUpdatesResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest struct {
-			// the value of the charge current in mA
-			Amp *float32 `json:"amp,omitempty"`
-
-			// The computed avalible current for divert
-			AvailableCurrent *int `json:"available_current,omitempty"`
-
-			// The divert charge rate
-			ChargeRate *int `json:"charge_rate,omitempty"`
-
-			// The LCD colour
-			Colour *int `json:"colour,omitempty"`
-
-			// Number of RAPI messages sent
-			CommSent *int `json:"comm_sent,omitempty"`
-
-			// Number of successful RAPI messages
-			CommSuccess *int `json:"comm_success,omitempty"`
-
-			// `true` if divert is active, `false` if not
-			DivertActive *bool `json:"divert_active,omitempty"`
-
-			// The time since the last update of the divert feed
-			DivertUpdate *float32 `json:"divert_update,omitempty"`
-
-			// The PV divert mode, `1` normal, `2` Eco
-			Divertmode *int `json:"divertmode,omitempty"`
-
-			// The duration of the current charge session, in seconds
-			Elapsed *float32 `json:"elapsed,omitempty"`
-
-			// `1`, if connected to an EmonCMS server, `0` not connected
-			EmoncmsConnected *int `json:"emoncms_connected,omitempty"`
-
-			// Last response message from the EmonCMS server
-			EmoncmsMessage *string `json:"emoncms_message,omitempty"`
-
-			// `1`, Wired ethernet is connected, `0` no wired ethernet connection
-			EthConnected *int `json:"eth_connected,omitempty"`
-
-			// `1`, if the WiFi module has negociated a connection to the OpenEVSE module, `0` not connected
-			EvseConnected *int `json:"evse_connected,omitempty"`
-
-			// Number of bytes free in the ESP memory heap
-			FreeHeap *int `json:"free_heap,omitempty"`
-
-			// Number of GFCI errors
-			Gfcicount *int `json:"gfcicount,omitempty"`
-
-			// The latest grid +I/-E feed value received
-			GridIe *int `json:"grid_ie,omitempty"`
-
-			// The current IP address of the WiFi module
-			Ipaddress *string `json:"ipaddress,omitempty"`
-
-			// `1`, if the manual override is active, `0` default behaviour
-			ManualOverride *int `json:"manual_override,omitempty"`
-
-			// The current mode of the EVSE
-			Mode *string `json:"mode,omitempty"`
-
-			// `1`, if connected to an EmonCMS server, `0` not connected
-			MqttConnected *int `json:"mqtt_connected,omitempty"`
-
-			// `1`, network is connected, either wired or WiFi, `0` no network connection
-			NetConnected *int `json:"net_connected,omitempty"`
-
-			// Number of no ground errors
-			Nogndcount *int `json:"nogndcount,omitempty"`
-
-			// The current timezone
-			Offset *string `json:"offset,omitempty"`
-
-			// Last status message from the OhmHour connection
-			OhmHour *string `json:"ohm_hour,omitempty"`
-
-			// `1`, if there is an OTA update active, `0` if normal operation
-			OtaUpdate *int `json:"ota_update,omitempty"`
-
-			// The number of posts sent to the EmonCMS server
-			PacketsSent *int `json:"packets_sent,omitempty"`
-
-			// The number of successful posts to the EmonCMS server
-			PacketsSuccess *int `json:"packets_success,omitempty"`
-
-			// the pilot value, in amps
-			Pilot *int `json:"pilot,omitempty"`
-
-			// `1`, if RAPI messages have successfully been sent to the OpenEVSE module, `0` not connected
-			RapiConnected *int `json:"rapi_connected,omitempty"`
-
-			// The current service level
-			ServiceLevel *int `json:"service_level,omitempty"`
-
-			// The computed smoothed available current for divert
-			SmoothedAvailableCurrent *int `json:"smoothed_available_current,omitempty"`
-
-			// The latest solar feed value received
-			Solar *int `json:"solar,omitempty"`
-
-			// WiFi RSSI signal strength
-			Srssi *int `json:"srssi,omitempty"`
-
-			// The EVSE status
-			State *int `json:"state,omitempty"`
-
-			// Number of stuck relay errors
-			Stuckcount *int `json:"stuckcount,omitempty"`
-
-			// the derived temperature, in 1/10th °C
-			Temp *float32 `json:"temp,omitempty"`
-
-			// The current time on the WiFi module
-			Time *string `json:"time,omitempty"`
-
-			// `1`, if a vehicle id connected to the EVSE, `0` not connected
-			Vehicle *int `json:"vehicle,omitempty"`
-
-			// the charge voltage, read form the EVSE if supported, or supplied via MQTT or other external source
-			Voltage *float32 `json:"voltage,omitempty"`
-
-			// The total amount of energy transfered, in W/hour
-			Watthour *float32 `json:"watthour,omitempty"`
-
-			// The amount of energy transfered in this charge session, in W/sec
-			Wattsec *float32 `json:"wattsec,omitempty"`
-
-			// `1`, WiFi is connected, `0` no WiFi connection
-			WifiClientConnected *int `json:"wifi_client_connected,omitempty"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	}
-
-	return response, nil
-}
diff --git a/charger/openevse/api.yaml b/charger/openevse/api.yaml
deleted file mode 100644
index 5a069984..00000000
--- a/charger/openevse/api.yaml
+++ /dev/null
@@ -1,644 +0,0 @@
-openapi: 3.0.2
-info:
-  title: OpenEVSE WiFi API
-  description: |
-    The API for the OpenEVSE WiFi module
-  version: "1.0"
-  contact:
-    email: jeremy@bigjungle.net
-    name: Jeremy Poulter
-  license:
-    name: Apache 2.0
-    url: "http://www.apache.org/licenses/LICENSE-2.0.html"
-servers:
-  - url: "{server}"
-    variables:
-      server:
-        default: "http://openevse.local"
-  - url: "http://localhost:8080"
-paths:
-  /status:
-    get:
-      operationId: getStatus
-      summary: Get the EVSE status
-      description: |
-        The status endpoint can be called to get the status of the EVSE.
-
-        While it is posible to poll this endpoint, the recomendatoin is to retrieve the initial
-        state then use the [/ws](#statusUpdates)
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: ./models/Status.yaml
-              examples:
-                EVSE Status:
-                  value:
-                    mode: STA
-                    wifi_client_connected: 1
-                    eth_connected: 0
-                    net_connected: 1
-                    srssi: -73
-                    ipaddress: 192.168.1.43
-                    emoncms_connected: 1
-                    packets_sent: 22307
-                    packets_success: 22290
-                    mqtt_connected: 1
-                    ohm_hour: NotConnected
-                    free_heap: 203268
-                    comm_sent: 335139
-                    comm_success: 335139
-                    rapi_connected: 1
-                    amp: 0
-                    pilot: 32
-                    temp1: 282
-                    temp2: -2560
-                    temp3: -2560
-                    state: 254
-                    elapsed: 3473
-                    wattsec: 22493407
-                    watthour: 51536
-                    gfcicount: 0
-                    nogndcount: 0
-                    stuckcount: 0
-                    divertmode: 1
-                    solar: 390
-                    grid_ie: 0
-                    charge_rate: 7
-                    divert_update: 0
-                    ota_update: 0
-                    time: "2020-05-12T17:53:48Z"
-                    offset: "+0000"
-      tags:
-        - Status
-  /ws:
-    get:
-      operationId: statusUpdates
-      summary: EVSE status updates
-      description: |
-        As the state changes on the EVSE a websocket frame will be sent over the socket.
-        The frame will contain a JSON document conntaining only the updated state.
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: ./models/Status.yaml
-      tags:
-        - Status
-  /config:
-    get:
-      operationId: getConfig
-      summary: Get the EVSE configuration
-      description: ""
-      tags:
-        - Config
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: ./models/Config.yaml
-              examples: {}
-    post:
-      operationId: updateConfig
-      responses:
-        "200":
-          $ref: "#/components/responses/UpdateSuccessful"
-        "400":
-          $ref: "#/components/responses/BadRequest"
-      requestBody:
-        content:
-          application/json:
-            schema:
-              $ref: ./models/Config.yaml
-            examples:
-              Update EmonCMS setting:
-                value:
-                  emoncms_enabled: true
-                  emoncms_server: "http://emoncms.org"
-                  emoncms_node: openevse-a7d4
-                  emoncms_apikey: emoncmskey
-                  emoncms_fingerprint: ""
-              Configure Solar divert:
-                value:
-                  divert_enabled: true
-                  divert_attack_smoothing_factor: 0.4
-                  divert_decay_smoothing_factor: 0.05
-                  divert_min_charge_time: 60
-              Set normal charging mode:
-                value:
-                  charge_mode: fast
-              Set Eco mode:
-                value:
-                  charge_mode: eco
-              Configure Tesla credentials:
-                value:
-                  tesla_enabled: true
-                  tesla_access_token: qts-1234.......
-                  tesla_refresh_token: abcd.......
-                  tesla_created_at: 1623358948
-                  tesla_expires_in: 3888000
-              Select Tesla vehicle:
-                value:
-                  tesla_vehicle_id: "2238974987235987"
-      summary: Update the EVSE configuration
-      tags:
-        - Config
-  /override:
-    description: Manual override can be used to override a charging timer or to immediately start a charge if the EVSE is in sleeping state.
-    get:
-      responses:
-        "200":
-          description: Manual overide is active
-          content:
-            application/json:
-              schema:
-                $ref: ./models/Properties.yaml
-        "404":
-          $ref: "#/components/responses/NotFound"
-      operationId: getManualOverride
-      tags:
-        - Manual Override
-      summary: Get the Manual Override status
-      description: Returns the current state of the manual override.
-    post:
-      requestBody:
-        content:
-          application/json:
-            schema:
-              $ref: ./models/Properties.yaml
-            examples:
-              Force stop of charging:
-                value:
-                  state: disabled
-              example-1:
-                value:
-                  state: active
-                  charge_current: 0
-                  max_current: 0
-                  energy_limit: 0
-                  time_limit: 0
-                  auto_release: true
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Message"
-      operationId: setManualOverride
-      tags:
-        - Manual Override
-      summary: Set the manual override
-    patch:
-      description: Toggle the manual override
-      responses:
-        "200":
-          description: OK
-      operationId: toggleManualOverride
-      tags:
-        - Manual Override
-      summary: Toggle the manual Override
-    delete:
-      description: Clears the manual override
-      responses:
-        "200":
-          description: OK
-      operationId: clearManualOverride
-      tags:
-        - Manual Override
-      summary: Clear the manual override
-  /claims:
-    get:
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                type: array
-                items:
-                  $ref: ./models/Claim.yaml
-      operationId: listClaims
-      tags:
-        - Claims
-      summary: List EVSE claims
-  "/claims/{client}":
-    get:
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: ./models/Properties.yaml
-        "404":
-          $ref: "#/components/responses/NotFound"
-      parameters: []
-      operationId: getClaim
-      tags:
-        - Claims
-      summary: Get EVSE claim information
-    post:
-      description: Make/Update a claim
-      requestBody:
-        content:
-          application/json:
-            schema:
-              $ref: ./models/Properties.yaml
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Message"
-        "400":
-          $ref: "#/components/responses/BadRequest"
-      operationId: setClaim
-      summary: Make/Update an EVSE claim
-      tags:
-        - Claims
-    delete:
-      description: Release a claim
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Message"
-        "404":
-          $ref: "#/components/responses/NotFound"
-      operationId: releaseClaim
-      summary: Release an EVSE claim
-      tags:
-        - Claims
-    parameters:
-      - schema:
-          type: integer
-        name: client
-        in: path
-        required: true
-        description: "The client ID, defined by [OpenEVSE](https://github.com/OpenEVSE/ESP32_WiFi_V4.x/blob/master/src/evse_man.h#L25)"
-  /schedule:
-    get:
-      summary: List schedule events
-      description: This will get a complete list of events in the schedule
-      responses:
-        "200":
-          description: List of the schedule events
-          content:
-            application/json:
-              schema:
-                type: array
-                items:
-                  $ref: "#/components/schemas/ScheduleEvent"
-        "400":
-          $ref: "#/components/responses/BadRequest"
-      operationId: listSchedule
-      tags:
-        - Schedule
-    post:
-      operationId: updateSchedule
-      description: Perform multiple event updates
-      responses:
-        "201":
-          description: item created
-        "400":
-          description: "invalid input, object invalid"
-        "409":
-          description: an existing item already exists
-      requestBody:
-        content:
-          application/json:
-            schema:
-              type: array
-              items:
-                $ref: "#/components/schemas/ScheduleEvent"
-            examples:
-              Single event:
-                value:
-                  - id: 1
-                    state: active
-                    time: "14:15:22Z"
-                    days:
-                      - monday
-              Daily Timer:
-                value:
-                  - id: 1
-                    state: active
-                    days:
-                      - monday
-                      - tuesday
-                      - wednesday
-                      - thursday
-                      - friday
-                      - saturday
-                      - sunday
-                    time: "07:00:00"
-                  - id: 2
-                    state: disabled
-                    days:
-                      - monday
-                      - tuesday
-                      - wednesday
-                      - thursday
-                      - friday
-                      - saturday
-                      - sunday
-                    time: "10:00:00"
-        description: Inventory item to add
-      tags:
-        - Schedule
-      summary: Batch update schedule
-  "/schedule/{id}":
-    parameters:
-      - in: path
-        name: id
-        required: true
-        schema:
-          type: integer
-        description: Numeric ID of the event
-    get:
-      summary: Get event details
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: ./models/EvseState.yaml
-      operationId: getScheduleEvent
-      tags:
-        - Schedule
-    post:
-      responses:
-        "200":
-          $ref: "#/components/responses/UpdateSuccessful"
-      requestBody:
-        content:
-          application/json:
-            schema:
-              $ref: ./models/EvseState.yaml
-        description: ""
-      operationId: setScheduleEvent
-      tags:
-        - Schedule
-      summary: Update event details
-    delete:
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Message"
-              examples: {}
-      operationId: removeScheduleEvent
-      tags:
-        - Schedule
-      summary: Remove event
-  /logs:
-    get:
-      summary: Get event block information
-      tags:
-        - Event Log
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                type: object
-                properties:
-                  min:
-                    type: integer
-                  max:
-                    type: integer
-      operationId: getEventBlockInfo
-      description: "Retrieve the start and end indexes of the log event blocks. Each log event block contains "
-      parameters: []
-    parameters: []
-  "/logs/{index}":
-    get:
-      summary: Get log block events
-      tags:
-        - Event Log
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                type: array
-                items:
-                  $ref: "#/components/schemas/LogEvent"
-        "404":
-          $ref: "#/components/responses/NotFound"
-      operationId: getEventBlock
-      description: ""
-    parameters:
-      - schema:
-          type: integer
-        name: index
-        in: path
-        required: true
-  /tesla/vehicles:
-    get:
-      summary: Get Tesla vehicle list
-      tags:
-        - Tesla
-      responses:
-        "200":
-          description: OK
-          content:
-            application/json:
-              schema:
-                description: ""
-                type: object
-                x-examples:
-                  example-1:
-                    count: 3
-                    vehicles:
-                      - id: "1492932278133304"
-                        name: AwesomeSAUCE
-                      - id: "1492931610165415"
-                        name: Herbie
-                      - id: "1492930973960452"
-                        name: "Life, the Universe, and Everything"
-                properties:
-                  count:
-                    type: number
-                  vehicles:
-                    type: array
-                    uniqueItems: true
-                    items:
-                      type: object
-                      additionalProperties: false
-                      properties:
-                        id:
-                          type: string
-                          minLength: 1
-                        name:
-                          type: string
-                          minLength: 1
-                      required:
-                        - id
-                        - name
-                required:
-                  - count
-                  - vehicles
-              examples:
-                example-1:
-                  value:
-                    count: 1
-                    vehicles:
-                      - id: "2238974987235987"
-                        name: My Tesla
-      operationId: get-tesla-vehicles
-      description: Once the Tesla account credentials have been configured via the `/config` API this endpoint will return a list of vehicles associated with the account. The ID of the vehicle to use for Soc/Range can then be set using `tesla_vehicle_id` on the `/config` endpoint.
-components:
-  schemas:
-    Message:
-      title: Informational message
-      type: object
-      properties:
-        msg:
-          type: string
-          description: The message
-    ScheduleEvent:
-      type: object
-      properties:
-        id:
-          type: integer
-          example: 1
-        state:
-          $ref: ./models/EvseState.yaml
-        time:
-          type: string
-          format: time
-        days:
-          type: array
-          uniqueItems: true
-          items:
-            type: string
-            enum:
-              - monday
-              - tuesday
-              - wednesday
-              - thursday
-              - friday
-              - saturday
-              - sunday
-      required:
-        - state
-        - time
-        - days
-      title: ""
-    LogEvent:
-      title: LogEvent
-      type: object
-      x-examples:
-        example-1:
-          value:
-            time: "2021-08-24T14:15:22Z"
-            type: information
-            managerState: active
-            evseState: 1
-            evseFlags: 123
-            pilot: 32
-            energy: 1234.5
-            elapsed: 123456
-            temperature: 35.4
-            tempuratureMax: 50.4
-            divertMode: 1
-      properties:
-        time:
-          type: string
-          format: date-time
-          readOnly: true
-        type:
-          type: string
-          enum:
-            - information
-            - notification
-            - warning
-        managerState:
-          $ref: ./models/EvseState.yaml
-        evseState:
-          type: integer
-          minimum: 0
-          maximum: 255
-        evseFlags:
-          type: integer
-          format: int32
-        pilot:
-          type: integer
-          format: int32
-          minimum: 0
-        energy:
-          type: number
-          format: double
-          minimum: 0
-        elapsed:
-          type: integer
-          format: int32
-          minimum: 0
-        temperature:
-          type: number
-          format: double
-        tempuratureMax:
-          type: number
-          format: double
-        divertMode:
-          type: integer
-          format: int32
-          minimum: 1
-          maximum: 2
-          exclusiveMaximum: false
-  responses:
-    UpdateSuccessful:
-      description: Update successful
-      content:
-        application/json:
-          schema:
-            $ref: "#/components/schemas/Message"
-          examples:
-            Success message:
-              value:
-                msg: done
-    NotFound:
-      description: Example response
-      content:
-        application/json:
-          schema:
-            $ref: "#/components/schemas/Message"
-          examples:
-            Not found:
-              value:
-                msg: Not found
-    BadRequest:
-      description: Example response
-      content:
-        application/json:
-          schema:
-            $ref: "#/components/schemas/Message"
-          examples:
-            Bad JSON:
-              value:
-                msg: Could not parse JSON
-  parameters: {}
-tags:
-  - name: Claims
-  - name: Config
-  - name: Event Log
-  - name: Manual Override
-  - name: Schedule
-  - name: Status
-  - name: Tesla
diff --git a/charger/openevse/generate.go b/charger/openevse/generate.go
deleted file mode 100644
index 4bc5b399..00000000
--- a/charger/openevse/generate.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package openevse
-
-// go:generate sh -c "oapi-codegen -package openevse -old-config-style -generate types,client api.yaml > api.go"
diff --git a/charger/openevse/models/Claim.yaml b/charger/openevse/models/Claim.yaml
deleted file mode 100644
index 2c987237..00000000
--- a/charger/openevse/models/Claim.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-title: EVSE claim
-allOf:
-  - type: object
-    properties:
-      client:
-        type: integer
-        description: "The client ID, defined by [OpenEVSE](src/evse_man.h#L25)"
-      priority:
-        type: integer
-        description: "The priority of the claim, the higher the number the higher the priority"
-  - $ref: ./Properties.yaml
-x-tags:
-  - Claims
diff --git a/charger/openevse/models/Config.yaml b/charger/openevse/models/Config.yaml
deleted file mode 100644
index 5d9ad1cd..00000000
--- a/charger/openevse/models/Config.yaml
+++ /dev/null
@@ -1,352 +0,0 @@
-description: ""
-type: object
-x-examples:
-  example-1:
-    firmware: D7.1.2
-    protocol: "-"
-    espflash: 4194304
-    espinfo: ESP32r1 2 core WiFi BLE BT
-    buildenv: openevse_esp-wrover-kit
-    version: 4.1.0.dev
-    diode_check: true
-    gfci_check: true
-    ground_check: true
-    relay_check: true
-    vent_check: true
-    temp_check: true
-    service: 0
-    scale: 220
-    offset: 0
-    max_current_soft: 32
-    min_current_hard: 6
-    max_current_hard: 32
-    mqtt_supported_protocols:
-      - mqtt
-      - mqtts
-    http_supported_protocols:
-      - http
-      - https
-    ssid: wibble_ext
-    pass: _DUMMY_PASSWORD
-    www_username: ""
-    www_password: ""
-    hostname: openevse-a7d4
-    sntp_hostname: pool.ntp.org
-    time_zone: "Europe/London|GMT0BST,M3.5.0/1,M10.5.0"
-    emoncms_server: "https://emoncms.org"
-    emoncms_node: openevse-a7d4
-    emoncms_apikey: _DUMMY_PASSWORD
-    emoncms_fingerprint: ""
-    mqtt_server: homeassistant.lan
-    mqtt_port: 1883
-    mqtt_topic: openevse
-    mqtt_user: emonpi
-    mqtt_pass: _DUMMY_PASSWORD
-    mqtt_solar: ""
-    mqtt_grid_ie: emon/test/grid_ie
-    mqtt_vrms: emon/rightbar/voltage
-    mqtt_vehicle_soc: ""
-    mqtt_vehicle_range: ""
-    mqtt_vehicle_eta: ""
-    mqtt_announce_topic: openevse/announce/a7d4
-    ocpp_server: ""
-    ocpp_chargeBoxId: ""
-    ocpp_idTag: ""
-    tx_start_point: tx_pending
-    ohm: ""
-    divert_PV_ratio: 1.1
-    divert_attack_smoothing_factor: 0.4
-    divert_decay_smoothing_factor: 0.05
-    divert_min_charge_time: 10
-    tesla_access_token: _DUMMY_PASSWORD
-    tesla_refresh_token: _DUMMY_PASSWORD
-    tesla_created_at: 2479357952
-    tesla_expires_in: 3888000
-    tesla_vehicle_id: "142960157278"
-    led_brightness: 51
-    flags: 115467
-    emoncms_enabled: true
-    mqtt_enabled: true
-    mqtt_reject_unauthorized: true
-    ohm_enabled: false
-    sntp_enabled: true
-    tesla_enabled: true
-    divert_enabled: true
-    pause_uses_disabled: false
-    mqtt_vehicle_range_miles: false
-    ocpp_enabled: true
-    ocpp_suspend_evse: true
-    ocpp_energize_plug: true
-    mqtt_protocol: mqtt
-    charge_mode: fast
-examples:
-  - firmware: D7.1.2
-    protocol: "-"
-    espflash: 4194304
-    espinfo: ESP32r1 2 core WiFi BLE BT
-    buildenv: openevse_esp-wrover-kit
-    version: 4.1.0.dev
-    diode_check: true
-    gfci_check: true
-    ground_check: true
-    relay_check: true
-    vent_check: true
-    temp_check: true
-    service: 0
-    scale: 220
-    offset: 0
-    max_current_soft: 32
-    min_current_hard: 6
-    max_current_hard: 32
-    mqtt_supported_protocols:
-      - mqtt
-      - mqtts
-    http_supported_protocols:
-      - http
-      - https
-    ssid: wibble_ext
-    pass: _DUMMY_PASSWORD
-    www_username: ""
-    www_password: ""
-    hostname: openevse-a7d4
-    sntp_hostname: pool.ntp.org
-    time_zone: "Europe/London|GMT0BST,M3.5.0/1,M10.5.0"
-    emoncms_server: "https://emoncms.org"
-    emoncms_node: openevse-a7d4
-    emoncms_apikey: _DUMMY_PASSWORD
-    emoncms_fingerprint: ""
-    mqtt_server: homeassistant.lan
-    mqtt_port: 1883
-    mqtt_topic: openevse
-    mqtt_user: emonpi
-    mqtt_pass: _DUMMY_PASSWORD
-    mqtt_solar: ""
-    mqtt_grid_ie: emon/test/grid_ie
-    mqtt_vrms: emon/rightbar/voltage
-    mqtt_vehicle_soc: ""
-    mqtt_vehicle_range: ""
-    mqtt_vehicle_eta: ""
-    mqtt_announce_topic: openevse/announce/a7d4
-    ocpp_server: ""
-    ocpp_chargeBoxId: ""
-    ocpp_idTag: ""
-    tx_start_point: tx_pending
-    ohm: ""
-    divert_PV_ratio: 1.1
-    divert_attack_smoothing_factor: 0.4
-    divert_decay_smoothing_factor: 0.05
-    divert_min_charge_time: 10
-    tesla_access_token: _DUMMY_PASSWORD
-    tesla_refresh_token: _DUMMY_PASSWORD
-    tesla_created_at: 2479357952
-    tesla_expires_in: 3888000
-    tesla_vehicle_id: "142960157278"
-    led_brightness: 51
-    flags: 115467
-    emoncms_enabled: true
-    mqtt_enabled: true
-    mqtt_reject_unauthorized: true
-    ohm_enabled: false
-    sntp_enabled: true
-    tesla_enabled: true
-    divert_enabled: true
-    pause_uses_disabled: false
-    mqtt_vehicle_range_miles: false
-    ocpp_enabled: true
-    ocpp_suspend_evse: true
-    ocpp_energize_plug: true
-    mqtt_protocol: mqtt
-    charge_mode: fast
-x-tags:
-  - Config
-properties:
-  firmware:
-    type: string
-    minLength: 1
-    readOnly: true
-  protocol:
-    type: string
-    minLength: 1
-    readOnly: true
-  espflash:
-    type: number
-    readOnly: true
-  espinfo:
-    type: string
-    minLength: 1
-    readOnly: true
-  buildenv:
-    type: string
-    minLength: 1
-    readOnly: true
-  version:
-    type: string
-    minLength: 1
-    readOnly: true
-  diode_check:
-    type: boolean
-  gfci_check:
-    type: boolean
-  ground_check:
-    type: boolean
-  relay_check:
-    type: boolean
-  vent_check:
-    type: boolean
-  temp_check:
-    type: boolean
-  service:
-    type: integer
-    minimum: 0
-    maximum: 2
-  scale:
-    type: integer
-  offset:
-    type: integer
-  max_current_soft:
-    type: integer
-  min_current_hard:
-    type: integer
-    readOnly: true
-  max_current_hard:
-    type: integer
-    readOnly: true
-  mqtt_supported_protocols:
-    type: array
-    uniqueItems: true
-    items:
-      type: string
-    readOnly: true
-  http_supported_protocols:
-    type: array
-    uniqueItems: true
-    items:
-      type: string
-    readOnly: true
-  ssid:
-    type: string
-    minLength: 1
-  pass:
-    type: string
-    minLength: 1
-  www_username:
-    type: string
-  www_password:
-    type: string
-  hostname:
-    type: string
-    minLength: 1
-  sntp_hostname:
-    type: string
-    minLength: 1
-  time_zone:
-    type: string
-    minLength: 1
-  emoncms_server:
-    type: string
-    minLength: 1
-  emoncms_node:
-    type: string
-    minLength: 1
-  emoncms_apikey:
-    type: string
-    minLength: 1
-  emoncms_fingerprint:
-    type: string
-  mqtt_server:
-    type: string
-    minLength: 1
-  mqtt_port:
-    type: integer
-  mqtt_topic:
-    type: string
-    minLength: 1
-  mqtt_user:
-    type: string
-    minLength: 1
-  mqtt_pass:
-    type: string
-    minLength: 1
-  mqtt_solar:
-    type: string
-  mqtt_grid_ie:
-    type: string
-    minLength: 1
-  mqtt_vrms:
-    type: string
-    minLength: 1
-  mqtt_vehicle_soc:
-    type: string
-  mqtt_vehicle_range:
-    type: string
-  mqtt_vehicle_eta:
-    type: string
-  mqtt_announce_topic:
-    type: string
-    minLength: 1
-  ocpp_server:
-    type: string
-  ocpp_chargeBoxId:
-    type: string
-  ocpp_idTag:
-    type: string
-  tx_start_point:
-    type: string
-    minLength: 1
-  ohm:
-    type: string
-  divert_PV_ratio:
-    type: number
-  divert_attack_smoothing_factor:
-    type: number
-  divert_decay_smoothing_factor:
-    type: number
-  divert_min_charge_time:
-    type: number
-  tesla_access_token:
-    type: string
-    minLength: 1
-  tesla_refresh_token:
-    type: string
-    minLength: 1
-  tesla_created_at:
-    type: number
-  tesla_expires_in:
-    type: number
-  tesla_vehicle_id:
-    type: string
-    minLength: 1
-  led_brightness:
-    type: number
-  flags:
-    type: number
-  emoncms_enabled:
-    type: boolean
-  mqtt_enabled:
-    type: boolean
-  mqtt_reject_unauthorized:
-    type: boolean
-  ohm_enabled:
-    type: boolean
-  sntp_enabled:
-    type: boolean
-  tesla_enabled:
-    type: boolean
-  divert_enabled:
-    type: boolean
-  pause_uses_disabled:
-    type: boolean
-  mqtt_vehicle_range_miles:
-    type: boolean
-  ocpp_enabled:
-    type: boolean
-  ocpp_suspend_evse:
-    type: boolean
-  ocpp_energize_plug:
-    type: boolean
-  mqtt_protocol:
-    type: string
-    minLength: 1
-  charge_mode:
-    type: string
-    minLength: 1
diff --git a/charger/openevse/models/EvseState.yaml b/charger/openevse/models/EvseState.yaml
deleted file mode 100644
index e5c2ebc5..00000000
--- a/charger/openevse/models/EvseState.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-title: EvseState
-type: string
-x-tags:
-  - Claims
-  - Status
-description: Either enable charging (`active`) or block charging (`disabled`)
diff --git a/charger/openevse/models/Properties.yaml b/charger/openevse/models/Properties.yaml
deleted file mode 100644
index 77e24e13..00000000
--- a/charger/openevse/models/Properties.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-title: Properties
-type: object
-x-tags:
-  - Claims
-description: ""
-examples: []
-properties:
-  state:
-    $ref: ./EvseState.yaml
-  charge_current:
-    type: integer
-    minimum: 0
-    description: Specify the active charge current in Amps
-  max_current:
-    type: integer
-    minimum: 0
-    description: Dynamically alter the max current while still allowing other claims to very the current via `charge_current` that can not exceede this value. This is primerialy designed for load sharing situations while still allowing things like PV divert to work so the charge current is the minimum of `charge_current` <= `max_current` <= configured soft limit <= hard coded limit
-  energy_limit:
-    type: integer
-    minimum: 0
-    format: int32
-    description: |-
-      Stop the charge after `energy_limit` Wh of energy have been transfered.
-
-      > Note:  Not currently implemented
-  time_limit:
-    type: integer
-    minimum: 0
-    format: int32
-    description: |-
-      Stop the charge after the duration of the charging session has exceeded `time_limit` seconds
-
-      > Note:  Not currently implemented
-  auto_release:
-    type: boolean
-    description: |
-      `true` if the manual override is auto-released when the vehicle is
-      disconnected, `false` if manual override will persist after vehicle
-      disconnection.
diff --git a/charger/openevse/models/Status.yaml b/charger/openevse/models/Status.yaml
deleted file mode 100644
index 53418293..00000000
--- a/charger/openevse/models/Status.yaml
+++ /dev/null
@@ -1,136 +0,0 @@
-title: EVSE status
-type: object
-x-tags:
-  - Status
-properties:
-  mode:
-    type: string
-    description: The current mode of the EVSE
-  wifi_client_connected:
-    type: integer
-    description: "`1`, WiFi is connected, `0` no WiFi connection"
-  eth_connected:
-    type: integer
-    description: "`1`, Wired ethernet is connected, `0` no wired ethernet connection"
-  net_connected:
-    type: integer
-    description: "`1`, network is connected, either wired or WiFi, `0` no network connection"
-  srssi:
-    type: integer
-    description: WiFi RSSI signal strength
-  ipaddress:
-    type: string
-    description: The current IP address of the WiFi module
-  packets_sent:
-    type: integer
-    description: The number of posts sent to the EmonCMS server
-  packets_success:
-    type: integer
-    description: The number of successful posts to the EmonCMS server
-  emoncms_connected:
-    type: integer
-    description: "`1`, if connected to an EmonCMS server, `0` not connected"
-  emoncms_message:
-    type: string
-    description: Last response message from the EmonCMS server
-  mqtt_connected:
-    type: integer
-    description: "`1`, if connected to an EmonCMS server, `0` not connected"
-  ohm_hour:
-    type: string
-    description: Last status message from the OhmHour connection
-  free_heap:
-    type: integer
-    description: Number of bytes free in the ESP memory heap
-  comm_sent:
-    type: integer
-    description: Number of RAPI messages sent
-  comm_success:
-    type: integer
-    description: Number of successful RAPI messages
-  rapi_connected:
-    type: integer
-    description: "`1`, if RAPI messages have successfully been sent to the OpenEVSE module, `0` not connected"
-  evse_connected:
-    type: integer
-    description: "`1`, if the WiFi module has negociated a connection to the OpenEVSE module, `0` not connected"
-  amp:
-    type: number
-    description: the value of the charge current in mA
-  voltage:
-    type: number
-    description: "the charge voltage, read form the EVSE if supported, or supplied via MQTT or other external source"
-  pilot:
-    type: integer
-    description: "the pilot value, in amps"
-  temp:
-    type: number
-    description: "the derived temperature, in 1/10th °C"
-  state:
-    type: integer
-    description: The EVSE status
-  vehicle:
-    type: integer
-    description: "`1`, if a vehicle id connected to the EVSE, `0` not connected"
-  colour:
-    type: integer
-    description: The LCD colour
-  manual_override:
-    type: integer
-    description: "`1`, if the manual override is active, `0` default behaviour"
-  elapsed:
-    type: number
-    description: "The duration of the current charge session, in seconds"
-  wattsec:
-    type: number
-    description: "The amount of energy transfered in this charge session, in W/sec"
-  watthour:
-    type: integer
-    description: "The total amount of energy transfered, in W/hour"
-  gfcicount:
-    type: integer
-    description: Number of GFCI errors
-  nogndcount:
-    type: integer
-    description: Number of no ground errors
-  stuckcount:
-    type: integer
-    description: Number of stuck relay errors
-  divertmode:
-    type: integer
-    description: "The PV divert mode, `1` normal, `2` Eco"
-  service_level:
-    type: integer
-    description: The current service level
-    minimum: 1
-    maximum: 2
-  solar:
-    type: integer
-    description: The latest solar feed value received
-  grid_ie:
-    type: integer
-    description: The latest grid +I/-E feed value received
-  charge_rate:
-    type: integer
-    description: The divert charge rate
-  available_current:
-    type: integer
-    description: The computed avalible current for divert
-  smoothed_available_current:
-    type: integer
-    description: The computed smoothed available current for divert
-  divert_update:
-    type: number
-    description: The time since the last update of the divert feed
-  divert_active:
-    type: boolean
-    description: "`true` if divert is active, `false` if not"
-  ota_update:
-    type: integer
-    description: "`1`, if there is an OTA update active, `0` if normal operation"
-  time:
-    type: string
-    description: The current time on the WiFi module
-  offset:
-    type: string
-    description: The current timezone
diff --git a/charger/openevse/types.go b/charger/openevse/types.go
index 238157e9..11a3492f 100644
--- a/charger/openevse/types.go
+++ b/charger/openevse/types.go
@@ -1,6 +1,30 @@
 package openevse
 
-type RAPIResponse struct {
-	Cmd string `json:"cmd"`
-	Ret string `json:"ret"`
+const (
+	Enabled  = "active"
+	Disabled = "disabled"
+)
+
+// Only keep the interesting properties from the status endpoint
+type Status struct {
+	Amp            *float64 `json:"amp,omitempty"`             // the value of the charge current in mA
+	Elapsed        *float64 `json:"elapsed,omitempty"`         // current session duration in seconds
+	ManualOverride *int     `json:"manual_override,omitempty"` // 1 = active, 0 = default
+	Mode           *string  `json:"mode,omitempty"`            // The current mode of the EVSE
+	Pilot          *int     `json:"pilot,omitempty"`           // the pilot value, in amps
+	Power          *float64 `json:"power,omitempty"`           // apparent power in watts
+	SessionEnergy  *float64 `json:"session_energy"`            // The total amount of energy accumulated for current session (in wh)
+	SessionElapsed *int     `json:"session_elapsed"`           // duration of this charging session in seconds
+	State          *int     `json:"state,omitempty"`           // evse state 1=A 2=B 3=C 4=D 5-11=F 254=sleeping 255=disabled
+	Status         *string  `json:"status,omitempty"`          // active, disabled, none, unknown
+	TotalEnergy    *float64 `json:"total_energy"`              // The total amount of energy accumulated (in kwh)
+	Vehicle        *int     `json:"vehicle,omitempty"`         // 0=not connected, 1=connected
+	Voltage        *float64 `json:"voltage,omitempty"`         // supplied via MQTT/Tesla/HTTP or assume a default
+}
+
+type Override struct {
+	State         *string `json:"state,omitempty"`          // Either enable charging (active) or block charging (disabled)
+	ChargeCurrent *int    `json:"charge_current,omitempty"` // Specify the active charge current in Amps >= 0
+	MaxCurrent    *int    `json:"max_current,omitempty"`    // Maximum current, primarily for load sharing situations
+	AutoRelease   *bool   `json:"auto_release,omitempty"`
 }
diff --git a/charger/openwb-pro.go b/charger/openwb-pro.go
index 5f6db894..342e594b 100644
--- a/charger/openwb-pro.go
+++ b/charger/openwb-pro.go
@@ -21,9 +21,9 @@ func init() {
 // OpenWBPro charger implementation
 type OpenWBPro struct {
 	*request.Helper
-	uri         string
-	current     float64
-	statusCache provider.Cacheable[pro.Status]
+	uri     string
+	current float64
+	statusG provider.Cacheable[pro.Status]
 }
 
 // NewOpenWBProFromConfig creates a OpenWBPro charger from generic config
@@ -52,7 +52,7 @@ func NewOpenWBPro(uri string, cache time.Duration) (*OpenWBPro, error) {
 		current: 6, // 6A defined value
 	}
 
-	wb.statusCache = provider.ResettableCached(func() (pro.Status, error) {
+	wb.statusG = provider.ResettableCached(func() (pro.Status, error) {
 		var res pro.Status
 		uri := fmt.Sprintf("%s/%s", wb.uri, "connect.php")
 		err := wb.GetJSON(uri, &res)
@@ -66,7 +66,7 @@ func NewOpenWBPro(uri string, cache time.Duration) (*OpenWBPro, error) {
 
 func (wb *OpenWBPro) heartbeat(log *util.Logger) {
 	for range time.Tick(30 * time.Second) {
-		if _, err := wb.statusCache.Get(); err != nil {
+		if _, err := wb.statusG.Get(); err != nil {
 			log.ERROR.Printf("heartbeat: %v", err)
 		}
 	}
@@ -77,7 +77,7 @@ func (wb *OpenWBPro) set(payload string) error {
 	resp, err := wb.Post(uri, "application/x-www-form-urlencoded", strings.NewReader(payload))
 	if err == nil {
 		resp.Body.Close()
-		wb.statusCache.Reset()
+		wb.statusG.Reset()
 	}
 
 	return err
@@ -85,7 +85,7 @@ func (wb *OpenWBPro) set(payload string) error {
 
 // Status implements the api.Charger interface
 func (wb *OpenWBPro) Status() (api.ChargeStatus, error) {
-	resp, err := wb.statusCache.Get()
+	resp, err := wb.statusG.Get()
 	if err != nil {
 		return api.StatusNone, err
 	}
@@ -103,7 +103,7 @@ func (wb *OpenWBPro) Status() (api.ChargeStatus, error) {
 
 // Enabled implements the api.Charger interface
 func (wb *OpenWBPro) Enabled() (bool, error) {
-	res, err := wb.statusCache.Get()
+	res, err := wb.statusG.Get()
 	return res.OfferedCurrent > 0, err
 }
 
@@ -137,7 +137,7 @@ var _ api.Meter = (*OpenWBPro)(nil)
 
 // CurrentPower implements the api.Meter interface
 func (wb *OpenWBPro) CurrentPower() (float64, error) {
-	res, err := wb.statusCache.Get()
+	res, err := wb.statusG.Get()
 	return res.PowerAll, err
 }
 
@@ -145,7 +145,7 @@ var _ api.MeterEnergy = (*OpenWBPro)(nil)
 
 // TotalEnergy implements the api.MeterEnergy interface
 func (wb *OpenWBPro) TotalEnergy() (float64, error) {
-	res, err := wb.statusCache.Get()
+	res, err := wb.statusG.Get()
 	return res.Imported / 1e3, err
 }
 
@@ -153,7 +153,7 @@ var _ api.PhaseCurrents = (*OpenWBPro)(nil)
 
 // Currents implements the api.PhaseCurrents interface
 func (wb *OpenWBPro) Currents() (float64, float64, float64, error) {
-	res, err := wb.statusCache.Get()
+	res, err := wb.statusG.Get()
 	if err != nil {
 		return 0, 0, 0, err
 	}
@@ -169,7 +169,7 @@ var _ api.Battery = (*OpenWBPro)(nil)
 
 // Soc implements the api.Battery interface
 func (wb *OpenWBPro) Soc() (float64, error) {
-	res, err := wb.statusCache.Get()
+	res, err := wb.statusG.Get()
 	if err != nil {
 		return 0, err
 	}
@@ -192,7 +192,7 @@ var _ api.Identifier = (*OpenWBPro)(nil)
 
 // Identify implements the api.Identifier interface
 func (wb *OpenWBPro) Identify() (string, error) {
-	res, err := wb.statusCache.Get()
+	res, err := wb.statusG.Get()
 	if err != nil || res.VehicleID == "--" {
 		return "", err
 	}
diff --git a/charger/smaevcharger.go b/charger/smaevcharger.go
index 50d6f58f..37502204 100644
--- a/charger/smaevcharger.go
+++ b/charger/smaevcharger.go
@@ -37,12 +37,12 @@ import (
 // smaevchager charger implementation
 type Smaevcharger struct {
 	*request.Helper
-	log              *util.Logger
-	uri              string // 192.168.XXX.XXX
-	cache            time.Duration
-	oldstate         float64
-	measurementCache provider.Cacheable[[]smaevcharger.Measurements]
-	parameterCache   provider.Cacheable[[]smaevcharger.Parameters]
+	log          *util.Logger
+	uri          string // 192.168.XXX.XXX
+	cache        time.Duration
+	oldstate     float64
+	measurementG provider.Cacheable[[]smaevcharger.Measurements]
+	parameterG   provider.Cacheable[[]smaevcharger.Parameters]
 }
 
 func init() {
@@ -95,8 +95,8 @@ func NewSmaevcharger(uri, user, password string, cache time.Duration) (api.Charg
 	}
 
 	// setup cached values
-	wb.measurementCache = provider.ResettableCached(wb._measurementData, wb.cache)
-	wb.parameterCache = provider.ResettableCached(wb._parameterData, wb.cache)
+	wb.measurementG = provider.ResettableCached(wb._measurementData, wb.cache)
+	wb.parameterG = provider.ResettableCached(wb._parameterData, wb.cache)
 
 	ts, err := smaevcharger.TokenSource(log, wb.uri, user, password)
 	if err != nil {
@@ -268,8 +268,8 @@ func (wb *Smaevcharger) Currents() (float64, float64, float64, error) {
 
 // reset cache
 func (wb *Smaevcharger) reset() {
-	wb.measurementCache.Reset()
-	wb.parameterCache.Reset()
+	wb.measurementG.Reset()
+	wb.parameterG.Reset()
 }
 
 func (wb *Smaevcharger) _measurementData() ([]smaevcharger.Measurements, error) {
@@ -299,7 +299,7 @@ func (wb *Smaevcharger) _parameterData() ([]smaevcharger.Parameters, error) {
 }
 
 func (wb *Smaevcharger) getMeasurement(id string) (float64, error) {
-	res, err := wb.measurementCache.Get()
+	res, err := wb.measurementG.Get()
 	if err != nil {
 		return 0, err
 	}
@@ -314,7 +314,7 @@ func (wb *Smaevcharger) getMeasurement(id string) (float64, error) {
 }
 
 func (wb *Smaevcharger) getParameter(id string) (string, error) {
-	res, err := wb.parameterCache.Get()
+	res, err := wb.parameterG.Get()
 	if err != nil {
 		return "", err
 	}
diff --git a/charger/zaptec.go b/charger/zaptec.go
index 5354b977..fddcf0b4 100644
--- a/charger/zaptec.go
+++ b/charger/zaptec.go
@@ -42,11 +42,11 @@ import (
 // Zaptec charger implementation
 type Zaptec struct {
 	*request.Helper
-	log         *util.Logger
-	statusCache provider.Cacheable[zaptec.StateResponse]
-	id          string
-	enabled     bool
-	priority    bool
+	log      *util.Logger
+	statusG  provider.Cacheable[zaptec.StateResponse]
+	id       string
+	enabled  bool
+	priority bool
 }
 
 func init() {
@@ -91,7 +91,7 @@ func NewZaptec(user, password, id string, priority bool, cache time.Duration) (a
 	}
 
 	// setup cached values
-	c.statusCache = provider.ResettableCached(func() (zaptec.StateResponse, error) {
+	c.statusG = provider.ResettableCached(func() (zaptec.StateResponse, error) {
 		var res zaptec.StateResponse
 
 		uri := fmt.Sprintf("%s/api/chargers/%s/state", zaptec.ApiURL, c.id)
@@ -150,7 +150,7 @@ func (c *Zaptec) chargers() ([]string, error) {
 
 // Status implements the api.Charger interface
 func (c *Zaptec) Status() (api.ChargeStatus, error) {
-	res, err := c.statusCache.Get()
+	res, err := c.statusG.Get()
 	if err != nil {
 		return api.StatusA, err
 	}
@@ -172,7 +172,7 @@ func (c *Zaptec) Status() (api.ChargeStatus, error) {
 
 // Enabled implements the api.Charger interface
 func (c *Zaptec) Enabled() (bool, error) {
-	res, err := c.statusCache.Get()
+	res, err := c.statusG.Get()
 	return c.enabled && !res.ObservationByID(zaptec.FinalStopActive).Bool(), err
 }
 
@@ -189,7 +189,7 @@ func (c *Zaptec) Enable(enable bool) error {
 	if err == nil {
 		_, err = c.DoBody(req)
 		c.enabled = enable
-		c.statusCache.Reset()
+		c.statusG.Reset()
 	}
 
 	return err
@@ -201,7 +201,7 @@ func (c *Zaptec) chargerUpdate(data zaptec.Update) error {
 	req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
 	if err == nil {
 		_, err = c.DoBody(req)
-		c.statusCache.Reset()
+		c.statusG.Reset()
 	}
 
 	return err
@@ -213,7 +213,7 @@ func (c *Zaptec) sessionPriority(session string, data zaptec.SessionPriority) er
 	req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
 	if err == nil {
 		_, err = c.DoBody(req)
-		c.statusCache.Reset()
+		c.statusG.Reset()
 	}
 
 	return err
@@ -233,7 +233,7 @@ var _ api.Meter = (*Zaptec)(nil)
 
 // CurrentPower implements the api.Meter interface
 func (c *Zaptec) CurrentPower() (float64, error) {
-	res, err := c.statusCache.Get()
+	res, err := c.statusG.Get()
 	if err != nil {
 		return 0, err
 	}
@@ -245,7 +245,7 @@ var _ api.ChargeRater = (*Zaptec)(nil)
 
 // ChargedEnergy implements the api.ChargeRater interface
 func (c *Zaptec) ChargedEnergy() (float64, error) {
-	res, err := c.statusCache.Get()
+	res, err := c.statusG.Get()
 	if err != nil {
 		return 0, err
 	}
@@ -257,7 +257,7 @@ var _ api.PhaseCurrents = (*Zaptec)(nil)
 
 // Currents implements the api.PhaseCurrents interface
 func (c *Zaptec) Currents() (float64, float64, float64, error) {
-	res, err := c.statusCache.Get()
+	res, err := c.statusG.Get()
 	if err != nil {
 		return 0, 0, 0, err
 	}
@@ -276,7 +276,7 @@ var _ api.PhaseSwitcher = (*Zaptec)(nil)
 
 // Phases1p3p implements the api.ChargePhases interface
 func (c *Zaptec) Phases1p3p(phases int) error {
-	res, err := c.statusCache.Get()
+	res, err := c.statusG.Get()
 
 	if err == nil {
 		data := zaptec.Update{
@@ -305,7 +305,7 @@ var _ api.Diagnosis = (*Zaptec)(nil)
 
 // Diagnosis implements the api.ChargePhases interface
 func (c *Zaptec) Diagnose() {
-	res, _ := c.statusCache.Get()
+	res, _ := c.statusG.Get()
 
 	// sort for printing
 	sort.Slice(res, func(i, j int) bool {
diff --git a/cmd/configure/localization/en.toml b/cmd/configure/localization/en.toml
index 80f8d1d7..c4effe76 100644
--- a/cmd/configure/localization/en.toml
+++ b/cmd/configure/localization/en.toml
@@ -57,9 +57,9 @@ TestingDevice_AddFailedUsage = "Testing of the {{ .Usage }} configuration of {{
 TestingMQTTFailed = "Testing the MQTT configuration failed. Do you want to repeat its configuration?"
 Requirements_Title = "The device has the following requirements:"
 Requirements_More = "Additional information:"
-Requirements_Sponsorship_Title = "This device requires evcc sponsorship. Check the following link for what this is and how it works: https://docs.evcc.io/docs/sponsorship"
-Requirements_Sponsorship_Optional_Title = "The development of evcc can be supported via sponsoring. Check the following link for what this is and how it works: https://docs.evcc.io/docs/sponsorship"
-Requirements_Sponsorship_Feature_Title = "To use this feature evcc sponsorship is required. Check the following link for what this is and how it works: https://docs.evcc.io/docs/sponsorship"
+Requirements_Sponsorship_Title = "This device requires evcc sponsorship. Check the following link for what this is and how it works: https://docs.evcc.io/en/docs/sponsorship"
+Requirements_Sponsorship_Optional_Title = "The development of evcc can be supported via sponsoring. Check the following link for what this is and how it works: https://docs.evcc.io/en/docs/sponsorship"
+Requirements_Sponsorship_Feature_Title = "To use this feature evcc sponsorship is required. Check the following link for what this is and how it works: https://docs.evcc.io/en/docs/sponsorship"
 Requirements_Sponsorship_Token = "Are you already a sponsor?"
 Requirements_Sponsorship_Token_Input = "Please enter the sponsorship token"
 Requirements_EEBUS_Cert_Error = "Error: The EEBUS certificate couldn't be created"
@@ -69,7 +69,7 @@ Config_ModbusInterface = "Choose the ModBus interface"
 Config_AddAnotherValue = "Do you want to add another value?"
 Config_Yes = "Yes"
 Config_No = "No"
-Cancel = "The configuration was cancelled.\n\nIf this guided configuration process doesn't work for you, please try the manual configuration. You can find more details about that on our website: https://docs.evcc.io/docs/\n"
+Cancel = "The configuration was cancelled.\n\nIf this guided configuration process doesn't work for you, please try the manual configuration. You can find more details about that on our website: https://docs.evcc.io/en/docs/\n"
 InputError = "An input error occurred:"
 Value_Choice = "Choose:"
 Value_Help = "Help:"
diff --git a/cmd/root.go b/cmd/root.go
index 29702234..41f2071b 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -38,9 +38,9 @@ var (
 	log     = util.NewLogger("main")
 	cfgFile string
 
-	ignoreEmpty  = ""                               // ignore empty keys
-	ignoreErrors = []string{"warn", "error"}        // ignore errors
-	ignoreMqtt   = []string{"auth", "releaseNotes"} // excessive size may crash certain brokers
+	ignoreEmpty = ""                               // ignore empty keys
+	ignoreLogs  = []string{"log"}                  // ignore log messages, including warn/error
+	ignoreMqtt  = []string{"auth", "releaseNotes"} // excessive size may crash certain brokers
 )
 
 // rootCmd represents the base command when called without any subcommands
@@ -137,7 +137,7 @@ func runRoot(cmd *cobra.Command, args []string) {
 
 	// value cache
 	cache := util.NewCache()
-	go cache.Run(pipe.NewDropper(ignoreErrors...).Pipe(tee.Attach()))
+	go cache.Run(pipe.NewDropper(ignoreLogs...).Pipe(tee.Attach()))
 
 	// create web server
 	socketHub := server.NewSocketHub()
@@ -193,7 +193,7 @@ func runRoot(cmd *cobra.Command, args []string) {
 
 	// setup database
 	if err == nil && conf.Influx.URL != "" {
-		configureInflux(conf.Influx, site, pipe.NewDropper(append(ignoreErrors, ignoreEmpty)...).Pipe(tee.Attach()))
+		configureInflux(conf.Influx, site, pipe.NewDropper(append(ignoreLogs, ignoreEmpty)...).Pipe(tee.Attach()))
 	}
 
 	// setup mqtt publisher
diff --git a/cmd/setup.go b/cmd/setup.go
index 77462ba9..134b4344 100644
--- a/cmd/setup.go
+++ b/cmd/setup.go
@@ -657,7 +657,7 @@ func configureLoadpoints(conf globalConfig) (loadpoints []*core.Loadpoint, err e
 			return nil, fmt.Errorf("failed decoding loadpoint configuration: %w", err)
 		}
 
-		log := util.NewLogger("lp-" + strconv.Itoa(id+1))
+		log := util.NewLoggerWithLoadpoint("lp-"+strconv.Itoa(id+1), id+1)
 		lp, err := core.NewLoadpointFromConfig(log, lpc)
 		if err != nil {
 			return nil, fmt.Errorf("failed configuring loadpoint: %w", err)
diff --git a/core/loadpoint.go b/core/loadpoint.go
index a493cc68..5c14738e 100644
--- a/core/loadpoint.go
+++ b/core/loadpoint.go
@@ -1045,6 +1045,10 @@ func (lp *Loadpoint) pvScalePhases(availablePower, minCurrent, maxCurrent float6
 	if targetCurrent := powerToCurrent(availablePower, activePhases); targetCurrent < minCurrent && activePhases > 1 && lp.ConfiguredPhases < 3 {
 		lp.log.DEBUG.Printf("available power %.0fW < %.0fW min %dp threshold", availablePower, float64(activePhases)*Voltage*minCurrent, activePhases)
 
+		if !lp.charging() { // scale immediately if not charging
+			lp.phaseTimer = elapsed
+		}
+
 		if lp.phaseTimer.IsZero() {
 			lp.log.DEBUG.Printf("start phase %s timer", phaseScale1p)
 			lp.phaseTimer = lp.clock.Now()
@@ -1070,6 +1074,10 @@ func (lp *Loadpoint) pvScalePhases(availablePower, minCurrent, maxCurrent float6
 	if targetCurrent := powerToCurrent(availablePower, maxPhases); targetCurrent >= minCurrent && scalable {
 		lp.log.DEBUG.Printf("available power %.0fW > %.0fW min %dp threshold", availablePower, 3*Voltage*minCurrent, maxPhases)
 
+		if !lp.charging() { // scale immediately if not charging
+			lp.phaseTimer = elapsed
+		}
+
 		if lp.phaseTimer.IsZero() {
 			lp.log.DEBUG.Printf("start phase %s timer", phaseScale3p)
 			lp.phaseTimer = lp.clock.Now()
diff --git a/core/site.go b/core/site.go
index 82949653..26deef6d 100644
--- a/core/site.go
+++ b/core/site.go
@@ -186,7 +186,7 @@ func NewSiteFromConfig(
 	}
 
 	if len(site.batteryMeters) > 0 && site.ResidualPower <= 0 {
-		site.log.WARN.Println("battery configured but residualPower is missing or <= 0 (add residualPower: 100 to site), see https://https://docs.evcc.io/docs/reference/configuration/site#residualpower")
+		site.log.WARN.Println("battery configured but residualPower is missing or <= 0 (add residualPower: 100 to site), see https://docs.evcc.io/en/docs/reference/configuration/site#residualpower")
 	}
 
 	// auxiliary meters
diff --git a/go.mod b/go.mod
index dd0ca806..4dcb0ad0 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,6 @@ require (
 	github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
 	github.com/containrrr/shoutrrr v0.8.0
 	github.com/coreos/go-oidc/v3 v3.6.0
-	github.com/deepmap/oapi-codegen v1.15.0
 	github.com/denisbrodbeck/machineid v1.0.1
 	github.com/dmarkham/enumer v1.5.8
 	github.com/dylanmei/iso8601 v0.1.0
@@ -106,17 +105,11 @@ require (
 )
 
 require (
-	github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
-	github.com/CloudyKit/jet/v6 v6.2.0 // indirect
-	github.com/Joker/jade v1.1.3 // indirect
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/semver/v3 v3.2.1 // indirect
-	github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
 	github.com/ahmetb/go-linq/v3 v3.2.0 // indirect
-	github.com/andybalholm/brotli v1.0.5 // indirect
 	github.com/andybalholm/cascadia v1.3.2 // indirect
 	github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
-	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bitly/go-simplejson v0.5.0 // indirect
 	github.com/bytedance/sonic v1.10.1 // indirect
@@ -125,10 +118,10 @@ require (
 	github.com/chenzhuoyu/iasm v0.9.0 // indirect
 	github.com/chzyer/readline v1.5.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/deepmap/oapi-codegen v1.13.0 // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/fatih/color v1.15.0 // indirect
 	github.com/felixge/httpsnoop v1.0.3 // indirect
-	github.com/flosch/pongo2/v4 v4.0.2 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
@@ -146,10 +139,7 @@ require (
 	github.com/gobwas/ws v1.3.0 // indirect
 	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
-	github.com/golang/snappy v0.0.4 // indirect
-	github.com/gomarkdown/markdown v0.0.0-20230916125811-7478c230c7cd // indirect
 	github.com/google/go-querystring v1.1.0 // indirect
-	github.com/gorilla/css v1.0.0 // indirect
 	github.com/gorilla/websocket v1.5.0 // indirect
 	github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
@@ -157,19 +147,11 @@ require (
 	github.com/huandu/xstrings v1.4.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
-	github.com/iris-contrib/schema v0.0.6 // indirect
 	github.com/itchyny/timefmt-go v0.1.5 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
-	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/kataras/blocks v0.0.7 // indirect
-	github.com/kataras/golog v0.1.9 // indirect
-	github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 // indirect
-	github.com/kataras/pio v0.0.12 // indirect
-	github.com/kataras/sitemap v0.0.6 // indirect
-	github.com/kataras/tunnel v0.0.4 // indirect
 	github.com/klauspost/compress v1.16.7 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.5 // indirect
 	github.com/kr/text v0.2.0 // indirect
@@ -177,14 +159,11 @@ require (
 	github.com/labstack/gommon v0.4.0 // indirect
 	github.com/leodido/go-urn v1.2.4 // indirect
 	github.com/magiconair/properties v1.8.7 // indirect
-	github.com/mailgun/raymond/v2 v2.0.48 // indirect
-	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.19 // indirect
 	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
-	github.com/microcosm-cc/bluemonday v1.0.25 // indirect
 	github.com/miekg/dns v1.1.56 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -202,14 +181,10 @@ require (
 	github.com/rickb777/plural v1.4.1 // indirect
 	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/rogpeppe/go-internal v1.11.0 // indirect
-	github.com/russross/blackfriday/v2 v2.1.0 // indirect
-	github.com/schollz/closestmatch v2.1.0+incompatible // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/spf13/afero v1.9.5 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
-	github.com/tdewolff/minify/v2 v2.12.9 // indirect
-	github.com/tdewolff/parse/v2 v2.6.8 // indirect
 	github.com/teivah/onecontext v1.3.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.11 // indirect
@@ -217,14 +192,12 @@ require (
 	github.com/valyala/fasttemplate v1.2.2 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	github.com/yosssi/ace v0.0.5 // indirect
 	gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect
 	golang.org/x/arch v0.5.0 // indirect
 	golang.org/x/crypto v0.13.0 // indirect
 	golang.org/x/mod v0.12.0 // indirect
 	golang.org/x/sys v0.12.0 // indirect
 	golang.org/x/term v0.12.0 // indirect
-	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/tools v0.13.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb // indirect
diff --git a/go.sum b/go.sum
index a02c950a..d5871611 100644
--- a/go.sum
+++ b/go.sum
@@ -43,14 +43,6 @@ github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
 github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
-github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
-github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
-github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
-github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
-github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
-github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
-github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
 github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
@@ -64,8 +56,6 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe
 github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
 github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
 github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
-github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
-github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
@@ -73,8 +63,6 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/
 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
 github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE=
 github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U=
-github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
-github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -86,8 +74,6 @@ github.com/andig/gosunspec v0.0.0-20211108155140-af2e73b86e71 h1:tnjVNZjuz+CK6fd
 github.com/andig/gosunspec v0.0.0-20211108155140-af2e73b86e71/go.mod h1:c6P6szcR+ROkqZruOR4f6qbDKFjZX6OitPpj+yJ/r8k=
 github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e h1:m/NTP3JWpR7M0ljLxiQU4fzR25jjhe1LDtxLMNcoNJQ=
 github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e/go.mod h1:4VtYzTm//oUipwvO3yh0g/udTE7pYJM+U/kyAuFDsgM=
-github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
-github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
 github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
 github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
@@ -108,8 +94,6 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
 github.com/aws/aws-sdk-go v1.45.11 h1:8qiSrA12+NRr+2MVpMApi3JxtiFFjDVU1NeWe+80bYg=
 github.com/aws/aws-sdk-go v1.45.11/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
-github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
-github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
 github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw=
 github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw=
 github.com/basvdlei/gotsmart v0.0.3 h1:7hrI6btBc8dmFzzHRDkr9Xl87PvrrOT43DzbsTFqMk8=
@@ -182,8 +166,8 @@ github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/deepmap/oapi-codegen v1.15.0 h1:SQqViaeb4k2vMul8gx12oDOIadEtoRqTdLkxjzqtQ90=
-github.com/deepmap/oapi-codegen v1.15.0/go.mod h1:a6KoHV7lMRwsPoEg2C6NDHiXYV3EQfiFocOlJ8dgJQE=
+github.com/deepmap/oapi-codegen v1.13.0 h1:cnFHelhsRQbYvanCUAbRSn/ZpkUb1HPRlQcu8YqSORQ=
+github.com/deepmap/oapi-codegen v1.13.0/go.mod h1:Amy7tbubKY9qkZOXqymI3Z6xSbndmu+atMJheLdyg44=
 github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
 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=
@@ -223,8 +207,6 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
 github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
-github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
@@ -291,8 +273,6 @@ github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGi
 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
 github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA=
 github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA=
-github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
-github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
 github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
 github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
@@ -349,10 +329,6 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
 github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
 github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
-github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/gomarkdown/markdown v0.0.0-20230916125811-7478c230c7cd h1:laCEzrtkKEkT2424vMTGl6N1m0xN8kq371hksD5Be+8=
-github.com/gomarkdown/markdown v0.0.0-20230916125811-7478c230c7cd/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -402,8 +378,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
 github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
 github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
-github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
 github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@@ -466,8 +440,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:
 github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
 github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
-github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
-github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@@ -476,10 +448,6 @@ github.com/influxdata/influxdb-client-go/v2 v2.12.3/go.mod h1:IrrLUbCjjfkmRuaCiG
 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
 github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU=
 github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
-github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=
-github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=
-github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
-github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
 github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU=
 github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4=
 github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
@@ -504,8 +472,6 @@ github.com/joeshaw/carwings v0.0.0-20230425210048-195f7e10e966/go.mod h1:tB0Olpi
 github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
 github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/jpfielding/go-http-digest v0.0.0-20211006141426-fbc93758452e h1:iLmt7czLE9ophoU4xZcckVNCXp+auTvVSQ9MGTWbgBY=
 github.com/jpfielding/go-http-digest v0.0.0-20211006141426-fbc93758452e/go.mod h1:oLt6zF2euTyCg2Cxz01B3VC5SJcPpKpYnOAqvO+480s=
 github.com/jpfielding/gowirelog v0.0.0-20200123170752-df8f8dccb721/go.mod h1:R8b6Hefiy6MSxbJ8dbF9Gkaa/LwFh9meFV0i9hkR5i8=
@@ -525,18 +491,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
 github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
-github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4=
-github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I=
-github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk=
-github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY=
-github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 h1:Vx8kDVhO2qepK8w44lBtp+RzN3ld743i+LYPzODJSpQ=
-github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9/go.mod h1:ldkoR3iXABBeqlTibQ3MYaviA1oSlPvim6f55biwBh4=
-github.com/kataras/pio v0.0.12 h1:o52SfVYauS3J5X08fNjlGS5arXHjW/ItLkyLcKjoH6w=
-github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY=
-github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=
-github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
-github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
-github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -584,10 +538,6 @@ github.com/mabunixda/wattpilot v1.6.2 h1:xZE/elFntUJgaWTK2V7CJVQRcE+WvkT8g6dCk/o
 github.com/mabunixda/wattpilot v1.6.2/go.mod h1:2tcdLRoqycO6bWYAjZMP9g1giMgw4t2MnbN3VxaCN1Q=
 github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
-github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
-github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
 github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@@ -615,8 +565,6 @@ github.com/mergermarket/go-pkcs7 v0.0.0-20170926155232-153b18ea13c9/go.mod h1:GH
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
-github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
 github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
@@ -629,8 +577,6 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
-github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
 github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -775,19 +721,12 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f
 github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
 github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 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/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
 github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
-github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
-github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
-github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
-github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
 github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -799,7 +738,6 @@ github.com/simonvetter/modbus v1.6.0/go.mod h1:hh90ZaTaPLcK2REj6/fpTbiV0J6S7GWmd
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
@@ -852,12 +790,6 @@ github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQE
 github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
-github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RUB4zA=
-github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU=
-github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA=
-github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM=
-github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
-github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
 github.com/teivah/onecontext v1.3.0 h1:tbikMhAlo6VhAuEGCvhc8HlTnpX4xTNPTOseWuhO1J0=
 github.com/teivah/onecontext v1.3.0/go.mod h1:hoW1nmdPVK/0jrvGtcx8sCKYs2PiS4z0zzfdeuEVyb0=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -886,27 +818,12 @@ github.com/volkszaehler/mbmd v0.0.0-20230819073623-ab54b0355354 h1:6rhritdmrLjG0
 github.com/volkszaehler/mbmd v0.0.0-20230819073623-ab54b0355354/go.mod h1:kUsZrJ4IyhmhDERIxJDysTuO0wtZFavwL8AInnCZ0Oo=
 github.com/writeas/go-strip-markdown/v2 v2.1.1 h1:hAxUM21Uhznf/FnbVGiJciqzska6iLei22Ijc3q2e28=
 github.com/writeas/go-strip-markdown/v2 v2.1.1/go.mod h1:UvvgPJgn1vvN8nWuE5e7v/+qmDu3BSVnKAB6Gl7hFzA=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
-github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
-github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
-github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
-github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
-github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
-github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
-github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
-github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 gitlab.com/bboehmke/sunny v0.15.1-0.20211022160056-2fba1c86ade6 h1:73pM5aQqFkQfmXyGf3+xeWWg98J03xtDIkA5TE37ERU=
 gitlab.com/bboehmke/sunny v0.15.1-0.20211022160056-2fba1c86ade6/go.mod h1:F5AIuL7kYteSJFR5E+YEocxIdpyCXmtDciFmMQVjP88=
@@ -975,9 +892,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
 golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -989,7 +905,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 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.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
@@ -1005,7 +920,6 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1039,7 +953,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
@@ -1097,7 +1010,6 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1140,7 +1052,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1182,8 +1093,6 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
-golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1242,7 +1151,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
@@ -1362,7 +1270,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
@@ -1414,8 +1321,6 @@ modernc.org/memory v1.7.1 h1:9J+2/GKTlV503mk3yv8QJ6oEpRCUrRy0ad8TXEPoV8M=
 modernc.org/memory v1.7.1/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
 modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
 modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
-moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
-moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
 nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
 nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
 nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
diff --git a/i18n/da.toml b/i18n/da.toml
index 731b3ad6..d9c3c997 100644
--- a/i18n/da.toml
+++ b/i18n/da.toml
@@ -14,9 +14,40 @@ note = "OBS: Disse indstillinger gælder kun for solar modus. Indstillingerne æ
 
 [batterySettings.bufferStart]
 full = "næsten fuld"
-low = "en smule fyld"
-medium = "halv fuld"
-never = "kun med nok solar overskuld"
+low = "en smule fyldt"
+medium = "halv fyldt"
+never = "kun med nok solar overskud"
+
+[config]
+
+[config.form]
+example = "Eksempel"
+optional = "valgfrit"
+
+[config.main]
+addVehicle = "Tilføj bil"
+edit = "rediger"
+title = "Konfiguration"
+vehicles = "Mine biler"
+
+[config.validation]
+failed = "mislykkedes"
+label = "Status"
+running = "validering"
+success = "succes"
+unknown = "ukendt"
+validate = "validerer"
+
+[config.vehicle]
+cancel = "Annuller"
+delete = "Slet bilen"
+generic = "Andre integrationer"
+offline = "Generisk bil"
+online = "Biler med online API"
+save = "Gem"
+scooter = "Scooter"
+template = "Fabrikant"
+titleAdd = "Tilføj køretøj"
 
 [footer]
 
diff --git a/i18n/de.toml b/i18n/de.toml
index f8da6dc1..8f9293b7 100644
--- a/i18n/de.toml
+++ b/i18n/de.toml
@@ -46,11 +46,13 @@ delete = "Fahrzeug löschen"
 generic = "Weitere Integrationen"
 offline = "Generisches Fahrzeug"
 online = "Fahrzeuge mit Schnittstelle"
+save = "Speichern"
 scooter = "Scooter"
 template = "Hersteller"
 titleAdd = "Fahrzeug hinzufügen"
 titleEdit = "Fahrzeug bearbeiten"
 update = "Prüfen & speichern"
+validateSave = "Prüfen & speichern"
 
 [footer]
 
diff --git a/meter/homematic/connection.go b/meter/homematic/connection.go
index 8ff02d20..c21865be 100644
--- a/meter/homematic/connection.go
+++ b/meter/homematic/connection.go
@@ -29,8 +29,8 @@ type Connection struct {
 	log *util.Logger
 	*request.Helper
 	*Settings
-	meterCache  provider.Cacheable[MethodResponse]
-	switchCache provider.Cacheable[MethodResponse]
+	meterG  provider.Cacheable[MethodResponse]
+	switchG provider.Cacheable[MethodResponse]
 }
 
 // NewConnection creates a new Homematic device connection.
@@ -58,11 +58,11 @@ func NewConnection(uri, device, meterchannel, switchchannel, user, password stri
 		conn.Client.Transport = transport.BasicAuth(user, password, conn.Client.Transport)
 	}
 
-	conn.switchCache = provider.ResettableCached(func() (MethodResponse, error) {
+	conn.switchG = provider.ResettableCached(func() (MethodResponse, error) {
 		return conn.XmlCmd("getParamset", conn.SwitchChannel, Param{CCUString: "VALUES"})
 	}, conn.Cache)
 
-	conn.meterCache = provider.ResettableCached(func() (MethodResponse, error) {
+	conn.meterG = provider.ResettableCached(func() (MethodResponse, error) {
 		return conn.XmlCmd("getParamset", conn.MeterChannel, Param{CCUString: "VALUES"})
 	}, conn.Cache)
 
@@ -74,45 +74,45 @@ func (c *Connection) Enable(enable bool) error {
 	onoff := map[bool]string{true: "1", false: "0"}
 	_, err := c.XmlCmd("setValue", c.SwitchChannel, Param{CCUString: "STATE"}, Param{CCUBool: onoff[enable]})
 	if err == nil {
-		c.switchCache.Reset()
-		c.meterCache.Reset()
+		c.switchG.Reset()
+		c.meterG.Reset()
 	}
 	return err
 }
 
 // Enabled reads the homematic HMIP-PSM switchchannel state true=on/false=off
 func (c *Connection) Enabled() (bool, error) {
-	res, err := c.switchCache.Get()
+	res, err := c.switchG.Get()
 	return res.BoolValue("STATE"), err
 }
 
 // CurrentPower reads the homematic HMIP-PSM meterchannel power in W
 func (c *Connection) CurrentPower() (float64, error) {
-	res, err := c.meterCache.Get()
+	res, err := c.meterG.Get()
 	return res.FloatValue("POWER"), err
 }
 
 // TotalEnergy reads the homematic HMIP-PSM meterchannel energy in Wh
 func (c *Connection) TotalEnergy() (float64, error) {
-	res, err := c.meterCache.Get()
+	res, err := c.meterG.Get()
 	return res.FloatValue("ENERGY_COUNTER") / 1e3, err
 }
 
 // Currents reads the homematic HMIP-PSM meterchannel L1 current in A
 func (c *Connection) Currents() (float64, float64, float64, error) {
-	res, err := c.meterCache.Get()
+	res, err := c.meterG.Get()
 	return res.FloatValue("CURRENT") / 1e3, 0, 0, err
 }
 
 // GridCurrentPower reads the homematic HM-ES-TX-WM grid meterchannel power in W
 func (c *Connection) GridCurrentPower() (float64, error) {
-	res, err := c.meterCache.Get()
+	res, err := c.meterG.Get()
 	return res.FloatValue("IEC_POWER"), err
 }
 
 // GridTotalEnergy reads the homematic HM-ES-TX-WM grid meterchannel energy in kWh
 func (c *Connection) GridTotalEnergy() (float64, error) {
-	res, err := c.meterCache.Get()
+	res, err := c.meterG.Get()
 	return res.FloatValue("IEC_ENERGY_COUNTER"), err
 }
 
diff --git a/meter/homewizard/connection.go b/meter/homewizard/connection.go
index 4418fdca..3486349f 100644
--- a/meter/homewizard/connection.go
+++ b/meter/homewizard/connection.go
@@ -18,8 +18,8 @@ type Connection struct {
 	*request.Helper
 	uri         string
 	ProductType string
-	dataCache   provider.Cacheable[DataResponse]
-	stateCache  provider.Cacheable[StateResponse]
+	dataG       provider.Cacheable[DataResponse]
+	stateG      provider.Cacheable[StateResponse]
 }
 
 // NewConnection creates a homewizard connection
@@ -48,13 +48,13 @@ func NewConnection(uri string, cache time.Duration) (*Connection, error) {
 	c.uri = c.uri + "/" + res.ApiVersion
 	c.ProductType = res.ProductType
 
-	c.dataCache = provider.ResettableCached(func() (DataResponse, error) {
+	c.dataG = provider.ResettableCached(func() (DataResponse, error) {
 		var res DataResponse
 		err := c.GetJSON(fmt.Sprintf("%s/data", c.uri), &res)
 		return res, err
 	}, cache)
 
-	c.stateCache = provider.ResettableCached(func() (StateResponse, error) {
+	c.stateG = provider.ResettableCached(func() (StateResponse, error) {
 		var res StateResponse
 		err := c.GetJSON(fmt.Sprintf("%s/state", c.uri), &res)
 		return res, err
@@ -79,8 +79,8 @@ func (c *Connection) Enable(enable bool) error {
 	}
 
 	if err == nil {
-		c.stateCache.Reset()
-		c.dataCache.Reset()
+		c.stateG.Reset()
+		c.dataG.Reset()
 	}
 
 	switch {
@@ -95,18 +95,18 @@ func (c *Connection) Enable(enable bool) error {
 
 // Enabled reads the homewizard switch state true=on/false=off
 func (c *Connection) Enabled() (bool, error) {
-	res, err := c.stateCache.Get()
+	res, err := c.stateG.Get()
 	return res.PowerOn, err
 }
 
 // CurrentPower implements the api.Meter interface
 func (c *Connection) CurrentPower() (float64, error) {
-	res, err := c.dataCache.Get()
+	res, err := c.dataG.Get()
 	return res.ActivePowerW, err
 }
 
 // TotalEnergy implements the api.MeterEnergy interface
 func (c *Connection) TotalEnergy() (float64, error) {
-	res, err := c.dataCache.Get()
+	res, err := c.dataG.Get()
 	return res.TotalPowerImportT1kWh + res.TotalPowerImportT2kWh + res.TotalPowerImportT3kWh + res.TotalPowerImportT4kWh, err
 }
diff --git a/meter/tasmota/connection.go b/meter/tasmota/connection.go
index d55ab33e..9803765d 100644
--- a/meter/tasmota/connection.go
+++ b/meter/tasmota/connection.go
@@ -18,8 +18,8 @@ type Connection struct {
 	*request.Helper
 	uri, user, password string
 	channel             int
-	statusSNSCache      provider.Cacheable[StatusSNSResponse]
-	statusSTSCache      provider.Cacheable[StatusSTSResponse]
+	statusSnsG          provider.Cacheable[StatusSNSResponse]
+	statusStsG          provider.Cacheable[StatusSTSResponse]
 }
 
 // NewConnection creates a Tasmota connection
@@ -39,7 +39,7 @@ func NewConnection(uri, user, password string, channel int, cache time.Duration)
 
 	c.Client.Transport = request.NewTripper(log, transport.Insecure())
 
-	c.statusSNSCache = provider.ResettableCached(func() (StatusSNSResponse, error) {
+	c.statusSnsG = provider.ResettableCached(func() (StatusSNSResponse, error) {
 		parameters := url.Values{
 			"user":     []string{c.user},
 			"password": []string{c.password},
@@ -50,7 +50,7 @@ func NewConnection(uri, user, password string, channel int, cache time.Duration)
 		return res, err
 	}, cache)
 
-	c.statusSTSCache = provider.ResettableCached(func() (StatusSTSResponse, error) {
+	c.statusStsG = provider.ResettableCached(func() (StatusSTSResponse, error) {
 		parameters := url.Values{
 			"user":     []string{c.user},
 			"password": []string{c.password},
@@ -66,7 +66,7 @@ func NewConnection(uri, user, password string, channel int, cache time.Duration)
 
 // channelExists checks the existence of the configured relay channel interface
 func (c *Connection) ChannelExists(channel int) error {
-	res, err := c.statusSTSCache.Get()
+	res, err := c.statusStsG.Get()
 	if err != nil {
 		return err
 	}
@@ -136,8 +136,8 @@ func (c *Connection) Enable(enable bool) error {
 		on = strings.ToUpper(res.Power) == "ON" || strings.ToUpper(res.Power1) == "ON"
 	}
 
-	c.statusSNSCache.Reset()
-	c.statusSTSCache.Reset()
+	c.statusSnsG.Reset()
+	c.statusStsG.Reset()
 
 	switch {
 	case enable && !on:
@@ -151,7 +151,7 @@ func (c *Connection) Enable(enable bool) error {
 
 // Enabled implements the api.Charger interface
 func (c *Connection) Enabled() (bool, error) {
-	res, err := c.statusSTSCache.Get()
+	res, err := c.statusStsG.Get()
 	if err != nil {
 		return false, err
 	}
@@ -178,7 +178,7 @@ func (c *Connection) Enabled() (bool, error) {
 
 // CurrentPower implements the api.Meter interface
 func (c *Connection) CurrentPower() (float64, error) {
-	res, err := c.statusSNSCache.Get()
+	res, err := c.statusSnsG.Get()
 	if err != nil {
 		return 0, err
 	}
@@ -187,18 +187,18 @@ func (c *Connection) CurrentPower() (float64, error) {
 
 // TotalEnergy implements the api.MeterEnergy interface
 func (c *Connection) TotalEnergy() (float64, error) {
-	res, err := c.statusSNSCache.Get()
+	res, err := c.statusSnsG.Get()
 	return res.StatusSNS.Energy.Total, err
 }
 
 // SmlPower provides the sml sensor power
 func (c *Connection) SmlPower() (float64, error) {
-	res, err := c.statusSNSCache.Get()
+	res, err := c.statusSnsG.Get()
 	return float64(res.StatusSNS.SML.PowerCurr), err
 }
 
 // SmlTotalEnergy provides the sml sensor total import energy
 func (c *Connection) SmlTotalEnergy() (float64, error) {
-	res, err := c.statusSNSCache.Get()
+	res, err := c.statusSnsG.Get()
 	return res.StatusSNS.SML.TotalIn, err
 }
diff --git a/package-lock.json b/package-lock.json
index 5d2641ae..1ae70346 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4948,9 +4948,9 @@
       }
     },
     "node_modules/get-func-name": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
-      "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+      "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
       "engines": {
         "node": "*"
       }
diff --git a/templates/definition/vehicle/tesla.yaml b/templates/definition/vehicle/tesla.yaml
index 0a09d60f..b16be511 100644
--- a/templates/definition/vehicle/tesla.yaml
+++ b/templates/definition/vehicle/tesla.yaml
@@ -26,13 +26,13 @@ params:
     required: true
     mask: true
     help:
-      en: "See https://docs.evcc.io/docs/devices/vehicles#tesla"
+      en: "See https://docs.evcc.io/en/docs/devices/vehicles#tesla"
       de: "Siehe https://docs.evcc.io/docs/devices/vehicles#tesla"
   - name: refreshToken
     required: true
     mask: true
     help:
-      en: "See https://docs.evcc.io/docs/devices/vehicles#tesla"
+      en: "See https://docs.evcc.io/en/docs/devices/vehicles#tesla"
       de: "Siehe https://docs.evcc.io/docs/devices/vehicles#tesla"
   - name: vin
     example: W...
diff --git a/util/log.go b/util/log.go
index 979f27ec..707767b5 100644
--- a/util/log.go
+++ b/util/log.go
@@ -31,10 +31,20 @@ var LogAreaPadding = 6
 type Logger struct {
 	*jww.Notepad
 	*Redactor
+	lp int
 }
 
 // NewLogger creates a logger with the given log area and adds it to the registry
 func NewLogger(area string) *Logger {
+	return newLogger(area, 0)
+}
+
+// NewLoggerWithLoadpoint creates a logger with reference to at loadpoint
+func NewLoggerWithLoadpoint(area string, lp int) *Logger {
+	return newLogger(area, lp)
+}
+
+func newLogger(area string, lp int) *Logger {
 	loggersMux.Lock()
 	defer loggersMux.Unlock()
 
@@ -54,6 +64,7 @@ func NewLogger(area string) *Logger {
 	logger := &Logger{
 		Notepad:  notepad,
 		Redactor: redactor,
+		lp:       lp,
 	}
 
 	// capture loggers created after uiChan is initialized
@@ -130,17 +141,29 @@ var uiChan chan<- Param
 type uiWriter struct {
 	re    *regexp.Regexp
 	level string
+	lp    int
 }
 
 func (w *uiWriter) Write(p []byte) (n int, err error) {
 	// trim level and timestamp
 	s := string(w.re.ReplaceAll(p, []byte{}))
 
-	uiChan <- Param{
-		Key: w.level,
-		Val: strings.Trim(strconv.Quote(strings.TrimSpace(s)), "\""),
+	val := struct {
+		Message   string `json:"message"`
+		Level     string `json:"level"`
+		Loadpoint int    `json:"lp,omitempty"`
+	}{
+		Message:   strings.Trim(strconv.Quote(strings.TrimSpace(s)), "\""),
+		Level:     w.level,
+		Loadpoint: w.lp,
+	}
+
+	param := Param{
+		Key: "log",
+		Val: val,
 	}
 
+	uiChan <- param
 	return 0, nil
 }
 
@@ -159,18 +182,19 @@ func CaptureLogs(c chan<- Param) {
 }
 
 func captureLogger(l *Logger) {
-	captureLogLevel("warn", l.Notepad.WARN)
-	captureLogLevel("error", l.Notepad.ERROR)
-	captureLogLevel("error", l.Notepad.FATAL)
+	captureLogLevel("warn", l.lp, l.Notepad.WARN)
+	captureLogLevel("error", l.lp, l.Notepad.ERROR)
+	captureLogLevel("error", l.lp, l.Notepad.FATAL)
 }
 
-func captureLogLevel(level string, l *log.Logger) {
+func captureLogLevel(level string, lp int, l *log.Logger) {
 	re, err := regexp.Compile(`^\[[a-zA-Z0-9-]+\s*\] \w+ .{19} `)
 	if err != nil {
 		panic(err)
 	}
 
 	ui := uiWriter{
+		lp:    lp,
 		re:    re,
 		level: level,
 	}
diff --git a/util/templates/defaults.yaml b/util/templates/defaults.yaml
index 447ef9ff..6fb5a26b 100644
--- a/util/templates/defaults.yaml
+++ b/util/templates/defaults.yaml
@@ -209,7 +209,7 @@ params:
       en: Identification
     help:
       de: "Kann meist erst später eingetragen werden, siehe: https://docs.evcc.io/docs/guides/vehicles/#erkennung-des-fahrzeugs-an-der-wallbox"
-      en: "Mostly this can be added later, see: https://docs.evcc.io/docs/guides/vehicles/#erkennung-des-fahrzeugs-an-der-wallbox"
+      en: "Mostly this can be added later, see: https://docs.evcc.io/en/docs/guides/vehicles/#determining-which-vehicle-is-at-which-charger"
     type: stringlist
   - name: priority
     description:


Test patch:
diff --git a/core/loadpoint_phases_test.go b/core/loadpoint_phases_test.go
index 857dce20..19a83420 100644
--- a/core/loadpoint_phases_test.go
+++ b/core/loadpoint_phases_test.go
@@ -192,6 +192,7 @@ func TestPvScalePhases(t *testing.T) {
 			vehicle:          vehicle,
 			ConfiguredPhases: 0, // allow switching
 			phases:           tc.physical,
+			status:           api.StatusC,
 		}
 
 		if phaseCharger != nil {
@@ -326,6 +327,15 @@ func TestPvScalePhasesTimer(t *testing.T) {
 			lp.phaseTimer = lp.clock.Now().Add(-dt)
 		}},
 
+		// switch down from 3p/0p while not yet charging
+		{"3/0->1, not enough power, not charging", 3, 0, 0, 1, true, func(lp *Loadpoint) {
+			lp.status = api.StatusB
+		}},
+		// switch up from 1p/0p while not yet charging
+		{"1/0->3, enough power, not charging", 1, 0, 3 * Voltage * minA, 3, true, func(lp *Loadpoint) {
+			lp.status = api.StatusB
+		}},
+
 		// error states from 1p/3p misconfig - no correction for time being (stay at 1p)
 		{"1/3->1, enough power", 1, 3, 1 * Voltage * maxA, 1, false, nil},
 		{"1/3->1, kickoff, correct phase setting", 1, 3, 1 * Voltage * maxA, 1, false, func(lp *Loadpoint) {
@@ -352,6 +362,7 @@ func TestPvScalePhasesTimer(t *testing.T) {
 			MaxCurrent:     maxA,
 			phases:         tc.phases,
 			measuredPhases: tc.measuredPhases,
+			status:         api.StatusC,
 			Enable: ThresholdConfig{
 				Delay: dt,
 			},