init
This commit is contained in:
commit
451223816b
338 changed files with 9938 additions and 0 deletions
424
src/lib/components/dashboard-quick-adb.svelte
Normal file
424
src/lib/components/dashboard-quick-adb.svelte
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
<script lang="ts">
|
||||
import { Button, type ButtonVariant } from './ui/button';
|
||||
import * as Card from './ui/card/index';
|
||||
import Spinner from '$lib/components/ui/spinner/spinner.svelte';
|
||||
import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
|
||||
import Label from '$lib/components/ui/label/label.svelte';
|
||||
|
||||
import { LockIcon, UnlockIcon, Circle, AlertCircleIcon } from '@lucide/svelte/icons';
|
||||
import * as adb from '$lib/core/adb/adb';
|
||||
import { machineInfoStore } from '$lib/core/stores/machineInfoStore';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { handleIncomingMessages } from '$lib/core/handlers/messageHandler';
|
||||
import { auth as authStore } from '$lib/core/stores/auth';
|
||||
import { get } from 'svelte/store';
|
||||
import { AdbDaemonWebUsbDeviceManager } from '@yume-chan/adb-daemon-webusb';
|
||||
import AdbWebCredentialStore from '@yume-chan/adb-credential-web';
|
||||
import { onMount } from 'svelte';
|
||||
import { deviceCredentialManager } from '$lib/core/adb/deviceCredManager';
|
||||
import { file } from 'zod/mini';
|
||||
|
||||
let { enableComponent = true } = $props();
|
||||
|
||||
// connection button states
|
||||
let connectionButtonText = $state('Connect');
|
||||
let connectionButtonDisable = $state(false);
|
||||
let connectionButtonVariant: ButtonVariant = $state('default');
|
||||
|
||||
let connectDeviceOk = $state(false);
|
||||
let hasStoredDevice = $state(false);
|
||||
|
||||
let openAppBrewWhenConnected = $state(true);
|
||||
let hasOpenedBrewOnce = $state(false);
|
||||
|
||||
// progress
|
||||
let showLoadProgress = $state(false);
|
||||
//
|
||||
let recipe: any | undefined = $state(undefined);
|
||||
|
||||
let machineStatus: string = $state('Ok');
|
||||
// machineInfoStore.subscribe((mInfo) => {});
|
||||
|
||||
const essentialFiles = ['/sdcard/coffeevending/versions/'];
|
||||
async function loadEssentialFiles() {
|
||||
showLoadProgress = true;
|
||||
machineStatus = 'Loading infos ...';
|
||||
|
||||
let instance = adb.getAdbInstance();
|
||||
if (instance) {
|
||||
// check country
|
||||
let country = await adb.pull('/sdcard/coffeevending/country/short');
|
||||
machineStatus = `Found country: ${country}`;
|
||||
// check dev
|
||||
let devMode = await adb.pull('/sdcard/coffeevending/CURR_TEST');
|
||||
if (devMode?.includes('1')) {
|
||||
machineStatus = `Dev mode enabled`;
|
||||
} else {
|
||||
machineStatus = `Dev mode disabled`;
|
||||
}
|
||||
// check .bid
|
||||
let boxid = await adb.pull('/sdcard/coffeevending/.bid');
|
||||
if (boxid) {
|
||||
machineStatus = `Box id is ${boxid}`;
|
||||
} else {
|
||||
machineStatus = 'No box id';
|
||||
}
|
||||
|
||||
machineInfoStore.set({
|
||||
boxId: boxid,
|
||||
versions: {
|
||||
firmware: '',
|
||||
brew: '',
|
||||
xmlengine: '',
|
||||
netcore: '',
|
||||
devbox: ''
|
||||
},
|
||||
devMode: devMode?.includes('1') ?? false,
|
||||
country: country ?? '',
|
||||
status: '',
|
||||
errors: []
|
||||
});
|
||||
|
||||
handleIncomingMessages(
|
||||
JSON.stringify({
|
||||
type: 'chat',
|
||||
payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has connected to ${boxid}`
|
||||
})
|
||||
);
|
||||
} else {
|
||||
machineStatus = 'Instance lost, try disconnect and re-connect again';
|
||||
toast.error('Unexpected Error');
|
||||
}
|
||||
showLoadProgress = false;
|
||||
}
|
||||
|
||||
async function openBrewApp() {
|
||||
try {
|
||||
let instance = adb.getAdbInstance();
|
||||
if (instance) {
|
||||
try {
|
||||
// bypass
|
||||
await adb.executeCmd('echo -n hurr > /sdcard/coffeevending/ignore_pass');
|
||||
} catch (e) {}
|
||||
|
||||
let result = await adb.executeCmd(
|
||||
'am start -n com.forthvending.coffeemain/com.forthvending.coffeemain.MainActivity'
|
||||
);
|
||||
if (result?.output) {
|
||||
toast.success('Open app success!');
|
||||
machineStatus = 'open app success, check the screen and put the password';
|
||||
} else if (result?.error) {
|
||||
// case usb connection cutoff
|
||||
if (result.error === 'ExactReadableEndedError') {
|
||||
toast.warning('Connection unstable');
|
||||
machineStatus = 'app maybe opened, check the screen';
|
||||
} else {
|
||||
throw new Error(`Exit ${result.exitCode}. ${result.error}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Instance not found or error while executing');
|
||||
}
|
||||
|
||||
hasOpenedBrewOnce = true;
|
||||
|
||||
try {
|
||||
// bypass
|
||||
await adb.executeCmd('echo -n hurr > /sdcard/coffeevending/ignore_pass');
|
||||
} catch (e) {}
|
||||
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
// bypass
|
||||
await adb.executeCmd('input tap 336 795');
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
}
|
||||
} catch (e: any) {
|
||||
machineStatus = 'Cannot open brew app';
|
||||
toast.error('Error while trying to open brew app, please check the screen. ', {
|
||||
description: e.toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function testPushPullFile() {
|
||||
try {
|
||||
let instance = adb.getAdbInstance();
|
||||
if (instance) {
|
||||
await adb.push(
|
||||
'/sdcard/coffeevending/test.json',
|
||||
JSON.stringify({ test: new Date().toLocaleTimeString() })
|
||||
);
|
||||
|
||||
let result = await adb.pull('/sdcard/coffeevending/test.json');
|
||||
|
||||
if (result) {
|
||||
if (result === '') {
|
||||
console.log('push pull not ok, get empty');
|
||||
} else {
|
||||
console.log('push pull ok', result);
|
||||
}
|
||||
} else {
|
||||
console.log('push pull not ok', result);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('test push file failed', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function connectAdb() {
|
||||
connectionButtonText = '...';
|
||||
// lock away no spam
|
||||
connectionButtonDisable = true;
|
||||
connectionButtonVariant = 'outline';
|
||||
|
||||
try {
|
||||
if (!AdbDaemonWebUsbDeviceManager.BROWSER) {
|
||||
throw new Error('WebUSB not supported, try using fallback or different browser');
|
||||
}
|
||||
|
||||
await adb.connnectViaWebUSB();
|
||||
let instance = adb.getAdbInstance();
|
||||
|
||||
if (instance) {
|
||||
await loadEssentialFiles();
|
||||
if (openAppBrewWhenConnected) await openBrewApp();
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.message === 'CREDENTIAL_EXPIRED') {
|
||||
try {
|
||||
await deviceCredentialManager.clearAllCredentials();
|
||||
hasStoredDevice = false;
|
||||
} catch (ignored) {}
|
||||
}
|
||||
|
||||
console.log('error on quick adb: ', e);
|
||||
toast.error(`Machine Connection Error`, {
|
||||
description: e.toString()
|
||||
});
|
||||
connectionButtonText = 'Retry';
|
||||
connectionButtonVariant = 'default';
|
||||
connectDeviceOk = false;
|
||||
}
|
||||
connectionButtonDisable = false;
|
||||
}
|
||||
|
||||
async function disconnectAdb() {
|
||||
await adb.disconnect();
|
||||
connectionButtonText = 'Connect';
|
||||
connectionButtonVariant = 'default';
|
||||
|
||||
connectDeviceOk = false;
|
||||
|
||||
handleIncomingMessages(
|
||||
JSON.stringify({
|
||||
type: 'chat',
|
||||
payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has disconnected!`
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function checkDeviceConnection() {
|
||||
try {
|
||||
let instance = adb.getAdbInstance();
|
||||
|
||||
if (instance) {
|
||||
// ready
|
||||
connectionButtonText = 'Disconnect';
|
||||
connectionButtonVariant = 'destructive';
|
||||
|
||||
connectDeviceOk = true;
|
||||
} else {
|
||||
connectionButtonText = 'Connect';
|
||||
connectionButtonVariant = 'default';
|
||||
|
||||
connectDeviceOk = false;
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.log('error on quick adb: ', e);
|
||||
toast.error(`Machine Connection Error`, {
|
||||
description: e.toString()
|
||||
});
|
||||
connectionButtonText = 'Retry';
|
||||
connectionButtonVariant = 'default';
|
||||
connectDeviceOk = false;
|
||||
}
|
||||
connectionButtonDisable = false;
|
||||
}
|
||||
|
||||
async function checkStoredCredentials() {
|
||||
try {
|
||||
if (!AdbDaemonWebUsbDeviceManager.BROWSER) {
|
||||
hasStoredDevice = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const devices = await AdbDaemonWebUsbDeviceManager.BROWSER.getDevices();
|
||||
if (!devices || devices.length === 0) {
|
||||
hasStoredDevice = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const credentialStore = new AdbWebCredentialStore();
|
||||
let hasKeys = false;
|
||||
|
||||
try {
|
||||
for await (const key of credentialStore.iterateKeys()) {
|
||||
hasKeys = true;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('check stored error', e);
|
||||
}
|
||||
|
||||
hasStoredDevice = devices.length > 0 && hasKeys;
|
||||
} catch (e) {
|
||||
console.error('check stored error', e);
|
||||
hasStoredDevice = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function tryAutoConnect(): Promise<boolean> {
|
||||
try {
|
||||
connectionButtonText = '...';
|
||||
// lock away no spam
|
||||
connectionButtonDisable = true;
|
||||
connectionButtonVariant = 'outline';
|
||||
|
||||
if (!AdbDaemonWebUsbDeviceManager.BROWSER) {
|
||||
throw new Error('WebUSB not supported, try using fallback or different browser');
|
||||
}
|
||||
|
||||
const devices = await AdbDaemonWebUsbDeviceManager.BROWSER?.getDevices();
|
||||
if (!devices || devices.length == 0) {
|
||||
throw new Error('No device found');
|
||||
}
|
||||
|
||||
if (devices.length > 1) {
|
||||
throw new Error('Too many connected devices');
|
||||
}
|
||||
|
||||
const device = devices[0];
|
||||
const credStore = new AdbWebCredentialStore();
|
||||
|
||||
try {
|
||||
await adb.connectDeviceByCred(device, credStore);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
if (e.message === 'CREDENTIAL_EXPIRED') {
|
||||
try {
|
||||
await deviceCredentialManager.clearAllCredentials();
|
||||
hasStoredDevice = false;
|
||||
} catch (ignored) {}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.log('error on auto connect adb: ', e);
|
||||
toast.error(`Machine Connection Error`, {
|
||||
description: e.toString()
|
||||
});
|
||||
connectionButtonText = 'Connect';
|
||||
connectionButtonVariant = 'default';
|
||||
connectDeviceOk = false;
|
||||
}
|
||||
|
||||
connectionButtonDisable = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// update every 1s
|
||||
setInterval(async function () {
|
||||
checkDeviceConnection();
|
||||
}, 1000);
|
||||
|
||||
onMount(async () => {
|
||||
await checkStoredCredentials();
|
||||
await tryAutoConnect();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="p-4">
|
||||
<Card.Root class="h-full bg-muted/50">
|
||||
<Card.Header>
|
||||
<Card.Title>Machine Shortcuts</Card.Title>
|
||||
<Card.Description>Shortcuts for machine i.e. connect or hotfix</Card.Description>
|
||||
{#if enableComponent}
|
||||
<Card.Action>
|
||||
<Button
|
||||
variant={connectionButtonVariant}
|
||||
disabled={connectionButtonDisable}
|
||||
onclick={connectDeviceOk ? disconnectAdb : connectAdb}>{connectionButtonText}</Button
|
||||
>
|
||||
</Card.Action>
|
||||
{/if}
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
{#if enableComponent}
|
||||
<!-- -->
|
||||
<div class="items-center space-y-8">
|
||||
<!-- <div class="flex w-full items-center gap-2">
|
||||
<UnlockIcon />
|
||||
<span>This feature is enabled </span>
|
||||
</div> -->
|
||||
|
||||
<div class="flex w-full items-center justify-between gap-2">
|
||||
<p>
|
||||
Device: {connectDeviceOk ? 'Online' : 'Offline'}
|
||||
</p>
|
||||
<Circle
|
||||
class=""
|
||||
color={connectDeviceOk ? 'green' : 'red'}
|
||||
fill={connectDeviceOk ? 'green' : 'red'}
|
||||
size={16}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex w-full items-start gap-2">
|
||||
<Checkbox
|
||||
id="open_brew_now"
|
||||
checked={openAppBrewWhenConnected}
|
||||
onCheckedChange={() => {
|
||||
openAppBrewWhenConnected = !openAppBrewWhenConnected;
|
||||
}}
|
||||
/>
|
||||
<div class="grid gap-2">
|
||||
<Label for="open_brew_now">Show brew app when connected</Label>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Immediately try to open brew app when first connected
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- open app brew manual -->
|
||||
<div class="flex w-full items-center gap-2">
|
||||
<Button variant="default" onclick={openBrewApp} disabled={!connectDeviceOk}
|
||||
>Open Brew App</Button
|
||||
>
|
||||
<Button variant="default" disabled={true}>Refresh Infos</Button>
|
||||
|
||||
<!-- test push file -->
|
||||
<Button variant="default" onclick={testPushPullFile} disabled={!connectDeviceOk}
|
||||
>Test Push</Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- show lock -->
|
||||
<div class="flex w-full items-center justify-center gap-2">
|
||||
<LockIcon /> <span>This feature is not enabled </span>
|
||||
</div>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<!-- Display status -->
|
||||
{#if connectDeviceOk}
|
||||
{#if showLoadProgress}
|
||||
<Spinner />
|
||||
{/if}
|
||||
<p class="mx-4 font-mono text-sm text-muted-foreground">{machineStatus}</p>
|
||||
{/if}
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue