-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
general: Add vehicle discovery system (electron-only)
- Loading branch information
1 parent
cfe761a
commit 519bbb8
Showing
9 changed files
with
368 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { contextBridge, ipcRenderer } from 'electron' | ||
|
||
contextBridge.exposeInMainWorld('electronAPI', { | ||
getNetworkInfo: () => ipcRenderer.invoke('get-network-info'), | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { ipcMain } from 'electron' | ||
import { networkInterfaces } from 'os' | ||
|
||
/** | ||
* Information about the network | ||
*/ | ||
interface NetworkInfo { | ||
/** | ||
* The subnet of the local machine | ||
*/ | ||
subnet: string | ||
} | ||
|
||
/** | ||
* Get the network information | ||
* @returns {NetworkInfo} The network information | ||
*/ | ||
const getNetworkInfo = (): NetworkInfo => { | ||
const nets = networkInterfaces() | ||
|
||
for (const name of Object.keys(nets)) { | ||
for (const net of nets[name] ?? []) { | ||
// Skip over non-IPv4 and internal addresses | ||
if (net.family === 'IPv4' && !net.internal) { | ||
// Return the subnet (e.g., if IP is 192.168.1.5, return 192.168.1) | ||
return { subnet: net.address.split('.').slice(0, 3).join('.') } | ||
} | ||
} | ||
} | ||
|
||
throw new Error('No network interface found.') | ||
} | ||
|
||
/** | ||
* Setup the network service | ||
*/ | ||
export const setupNetworkService = (): void => { | ||
ipcMain.handle('get-network-info', getNetworkInfo) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
<template> | ||
<InteractionDialog | ||
v-model="isOpen" | ||
:title="searching ? 'Searching for vehicles...' : 'Vehicle Discovery'" | ||
:actions="dialogActions" | ||
:persistent="searching" | ||
:variant="'text-only'" | ||
> | ||
<template #content> | ||
<div v-if="props.showAutoSearchOption && preventAutoSearch"> | ||
<div class="text-sm mb-4">You can still search for vehicles in the general configuration menu.</div> | ||
</div> | ||
<div v-else class="flex flex-col items-center justify-center gap-4 min-w-[300px] min-h-[100px]"> | ||
<div v-if="searching" class="flex items-center gap-2"> | ||
<v-progress-circular indeterminate /> | ||
<span>Searching for vehicles in your network...</span> | ||
</div> | ||
|
||
<div v-else-if="vehicles.length > 0" class="flex flex-col gap-2 mb-6"> | ||
<div class="text-sm mb-2">Found the following vehicles:</div> | ||
<div v-for="vehicle in vehicles" :key="vehicle.address" class="flex items-center gap-2"> | ||
<v-btn variant="outlined" class="w-full justify-start" @click="selectVehicle(vehicle.address)"> | ||
{{ vehicle.name }} | ||
<span class="text-xs ml-2 opacity-50">({{ vehicle.address }})</span> | ||
</v-btn> | ||
</div> | ||
</div> | ||
|
||
<div v-else-if="searched" class="text-sm">No vehicles found in your network.</div> | ||
|
||
<div v-if="!searching && !searched" class="flex flex-col gap-2 items-center justify-center text-center"> | ||
<p v-if="props.showAutoSearchOption" class="font-bold">It looks like you're not connected to a vehicle!</p> | ||
<p>The vehicle discovery feature can help you find your vehicle and connect to it.</p> | ||
<p>It will search for vehicles in your network and let you select one.</p> | ||
</div> | ||
|
||
<div v-if="!searching" class="flex justify-center items-center"> | ||
<v-btn variant="outlined" :disabled="searching" class="mb-5" @click="searchVehicles"> | ||
{{ searched ? 'Search again' : 'Search for vehicles' }} | ||
</v-btn> | ||
</div> | ||
</div> | ||
</template> | ||
</InteractionDialog> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { useStorage } from '@vueuse/core' | ||
import { ref, watch } from 'vue' | ||
import { useSnackbar } from '@/composables/snackbar' | ||
import vehicleDiscover, { NetworkVehicle } from '@/libs/electron/vehicle-discovery' | ||
import { useMainVehicleStore } from '@/stores/mainVehicle' | ||
import InteractionDialog, { Action } from './InteractionDialog.vue' | ||
const props = defineProps<{ | ||
/** | ||
* | ||
*/ | ||
modelValue: boolean | ||
/** | ||
* | ||
*/ | ||
showAutoSearchOption?: boolean | ||
}>() | ||
const emit = defineEmits<{ | ||
(e: 'update:modelValue', value: boolean): void | ||
}>() | ||
const { showSnackbar } = useSnackbar() | ||
const mainVehicleStore = useMainVehicleStore() | ||
const discoveryService = vehicleDiscover | ||
const isOpen = ref(props.modelValue) | ||
const searching = ref(false) | ||
const searched = ref(false) | ||
const vehicles = ref<NetworkVehicle[]>([]) | ||
const preventAutoSearch = useStorage('cockpit-prevent-auto-vehicle-discovery-dialog', false) | ||
const originalActions = [ | ||
{ | ||
text: 'Close', | ||
action: () => { | ||
isOpen.value = false | ||
}, | ||
}, | ||
] | ||
if (props.showAutoSearchOption) { | ||
originalActions.unshift({ | ||
text: "Don't show again", | ||
action: () => preventFutureAutoSearchs(), | ||
}) | ||
} | ||
const dialogActions = ref<Action[]>(originalActions) | ||
watch( | ||
() => props.modelValue, | ||
(value) => { | ||
isOpen.value = value | ||
} | ||
) | ||
watch(isOpen, (value) => { | ||
emit('update:modelValue', value) | ||
}) | ||
const searchVehicles = async (): Promise<void> => { | ||
searching.value = true | ||
disableButtons() | ||
vehicles.value = await discoveryService.findVehicles() | ||
searching.value = false | ||
enableButtons() | ||
searched.value = true | ||
} | ||
const selectVehicle = (address: string): void => { | ||
mainVehicleStore.globalAddress = address | ||
isOpen.value = false | ||
showSnackbar({ message: 'Vehicle address updated', variant: 'success', duration: 5000 }) | ||
} | ||
const preventFutureAutoSearchs = (): void => { | ||
preventAutoSearch.value = true | ||
disableButtons() | ||
setTimeout(() => { | ||
isOpen.value = false | ||
}, 5000) | ||
} | ||
const disableButtons = (): void => { | ||
dialogActions.value = originalActions.map((action) => ({ ...action, disabled: true })) | ||
} | ||
const enableButtons = (): void => { | ||
dialogActions.value = originalActions | ||
} | ||
watch(isOpen, (isNowOpen) => { | ||
if (isNowOpen) return | ||
setTimeout(() => { | ||
vehicles.value = [] | ||
searching.value = false | ||
searched.value = false | ||
}, 1000) | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.