diff --git a/src/components/EditMenu.vue b/src/components/EditMenu.vue
index f67424c96..af3572047 100644
--- a/src/components/EditMenu.vue
+++ b/src/components/EditMenu.vue
@@ -43,6 +43,16 @@
+
+
diff --git a/src/libs/blueos.ts b/src/libs/blueos.ts
new file mode 100644
index 000000000..3abdc7195
--- /dev/null
+++ b/src/libs/blueos.ts
@@ -0,0 +1,75 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+export const getBagOfHoldingFromVehicle = async (
+ vehicleAddress: string,
+ bagName: string
+): Promise> => {
+ try {
+ const response = await fetch(`http://${vehicleAddress}/bag/v1.0/get/${bagName}`)
+ if (!(await response.ok)) {
+ throw new Error(await response.text())
+ }
+ return await response.json()
+ } catch (error) {
+ throw new Error(`Could not get bag of holdings for ${bagName}. ${error}`)
+ }
+}
+
+export const getCockpitStorageFromVehicle = async (vehicleAddress: string): Promise> => {
+ try {
+ return await getBagOfHoldingFromVehicle(vehicleAddress, 'cockpit')
+ } catch (error) {
+ throw new Error(`Could not get Cockpit's storage data from vehicle. ${error}`)
+ }
+}
+
+export const getKeyDataFromCockpitVehicleStorage = async (
+ vehicleAddress: string,
+ storageKey: string
+): Promise | undefined> => {
+ const cockpitVehicleStorage = await getCockpitStorageFromVehicle(vehicleAddress)
+ return cockpitVehicleStorage[storageKey]
+}
+
+export const setBagOfHoldingOnVehicle = async (
+ vehicleAddress: string,
+ bagName: string,
+ bagData: Record | any
+): Promise => {
+ try {
+ await fetch(`http://${vehicleAddress}/bag/v1.0/set/${bagName}`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(bagData),
+ })
+ } catch (error) {
+ throw new Error(`Could not set bag of holdings for ${bagName}. ${error}`)
+ }
+}
+
+export const setCockpitStorageOnVehicle = async (
+ vehicleAddress: string,
+ storageData: Record | any
+): Promise => {
+ try {
+ await setBagOfHoldingOnVehicle(vehicleAddress, 'cockpit', storageData)
+ } catch (error) {
+ throw new Error(`Could not set Cockpit's storage data on vehicle. ${error}`)
+ }
+}
+
+export const setKeyDataOnCockpitVehicleStorage = async (
+ vehicleAddress: string,
+ storageKey: string,
+ storageData: Record | any
+): Promise => {
+ let previousVehicleStorage: Record = {}
+ try {
+ previousVehicleStorage = await getCockpitStorageFromVehicle(vehicleAddress)
+ } catch (error) {
+ console.error(error)
+ }
+ const newVehicleStorage = previousVehicleStorage
+ newVehicleStorage[storageKey] = storageData
+
+ await setCockpitStorageOnVehicle(vehicleAddress, newVehicleStorage)
+}
diff --git a/src/stores/widgetManager.ts b/src/stores/widgetManager.ts
index cdf1a483a..7c9262b1c 100644
--- a/src/stores/widgetManager.ts
+++ b/src/stores/widgetManager.ts
@@ -9,6 +9,7 @@ import { computed, onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
import { widgetProfiles } from '@/assets/defaults'
import { miniWidgetsProfile } from '@/assets/defaults'
+import { getKeyDataFromCockpitVehicleStorage, setKeyDataOnCockpitVehicleStorage } from '@/libs/blueos'
import * as Words from '@/libs/funny-name/words'
import { CockpitAction, registerActionCallback, unregisterActionCallback } from '@/libs/joystick/protocols'
import { isEqual } from '@/libs/utils'
@@ -16,7 +17,10 @@ import type { Point2D, SizeRect2D } from '@/types/general'
import type { MiniWidget, MiniWidgetContainer } from '@/types/miniWidgets'
import { type Profile, type View, type Widget, isProfile, isView, WidgetType } from '@/types/widgets'
+import { useMainVehicleStore } from './mainVehicle'
+
export const useWidgetManagerStore = defineStore('widget-manager', () => {
+ const vehicleStore = useMainVehicleStore()
const editingMode = ref(false)
const showGrid = ref(true)
const gridInterval = ref(0.01)
@@ -159,6 +163,28 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => {
reader.readAsText(e.target.files[0])
}
+ const importProfilesFromVehicle = async (): Promise => {
+ const newProfiles = await getKeyDataFromCockpitVehicleStorage(
+ vehicleStore.globalAddress,
+ 'cockpit-saved-profiles-v7'
+ )
+ if (!Array.isArray(newProfiles) || !newProfiles.every((profile) => isProfile(profile))) {
+ Swal.fire({ icon: 'error', text: 'Could not import profiles from vehicle. Invalid data.', timer: 3000 })
+ return
+ }
+ savedProfiles.value = newProfiles
+ Swal.fire({ icon: 'success', text: 'Cockpit profiles imported from vehicle.', timer: 3000 })
+ }
+
+ const exportProfilesToVehicle = async (): Promise => {
+ await setKeyDataOnCockpitVehicleStorage(
+ vehicleStore.globalAddress,
+ 'cockpit-saved-profiles-v7',
+ savedProfiles.value
+ )
+ Swal.fire({ icon: 'success', text: 'Cockpit profiles exported to vehicle.', timer: 3000 })
+ }
+
/**
* Adds new view to the store, with a randomly generated hash with UUID4 pattern
*/
@@ -396,8 +422,9 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => {
}
}
- // If the user does not have it's own profiles yet, create them
+ // If the user does not have it's own profiles yet, try to fetch them from the vehicle, and if it fails, create default ones
if (savedProfiles.value.isEmpty()) {
+ importProfilesFromVehicle()
widgetProfiles.forEach((profile) => {
// @ts-ignore: structuredClone is a thing since a long time ago
const userProfile = structuredClone(profile)
@@ -483,5 +510,7 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => {
openWidgetConfigMenu,
toggleFullScreen,
isFullScreen,
+ importProfilesFromVehicle,
+ exportProfilesToVehicle,
}
})