Need Help: Using Webserial API for USB-Barcode-Scanners

Hi,
I have problems creating a new custom component and would like some help.

How it is now:

I have multiple clients using chromium Browsers to acces two local pages:

  • Webscan
  • Streamlit
    The Webscan page is a simple html page using Web Serial API where a user can connect a usb barcode scanner in usb/com mode to it. On every scan a request gets made to a server that processes the scan.
    The Streamlit Page also uses requests to gather the processed data. As the streamlit page needs to feel responive i have a while loop reloading it every second. I dont like this as it overloads my backend with requests and it doesnt feel clean using two pages.

What I want:

I want to embed the connection to a serial device into my streamlit page. I already searched a lot for a solution, but everything i tried disabled the function of my jscript being able to interact with the clients browser.
I am pretty shure it would be possible to create a custom component, but I am not able to create it.

The working Webscan-page code:

<h1 style="font-size: 600%" align="center">Scanner web portal</h1>
<body align="center">
    <label for="deviceName" style="font-size: 400%">Device Name:  </label>
    <select name="deviceName" id="deviceName" style="font-size: 400%">
        <option value="webscan-frog">Frog</option>
        <option value="webscan-croco">Croco</option>
    </select>
    &nbsp
    <button onclick="connectToSerial()" style="font-size: 400%">Connect to Serial</button>
    <h2 style="font-size: 300%" align="center">Data log</h2>
    <div id="output" align="center" style="font-size: 140%"></div>
</body>
<script>
    async function connectToSerial() {
        const deviceName = document.getElementById('deviceName').value;
        if (!deviceName.trim()) {
            alert('Please enter a valid device name.');
            return;
        }
        try {
            const port = await navigator.serial.requestPort();
            await port.open({ baudRate: 9600 });
            let receivedData = '';
            // Start reading data from the serial port
            while (true) {
                const reader = port.readable.getReader();
                const { value, done } = await Promise.race([
                    reader.read(),
                    new Promise(resolve => setTimeout(() => resolve({ done: false }), 500))
                ]);
                reader.releaseLock();
                if (done) {
                    break;
                }
                receivedData += new TextDecoder().decode(value);
                // Check for newline character or timeout
                if (receivedData.includes('\n') || receivedData.includes('\r') ||done) {
                    // Append the received data to the output div
                    appendOutput(`Send from ${deviceName}: ${receivedData}`);
                    try {
                        const csrfToken = getCookie('csrftoken');
                        // Call the API endpoint on the server to process the received data
                        const response = await fetch('http://127.0.0.1:8000/api/', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                                'Authorization': 'Basic ' + btoa('admin:password'),
                                'Accept': 'application/json',
                                'X-CSRFToken': csrfToken,
                            },
                            body: JSON.stringify({ name: deviceName, data: receivedData }),
                            credentials: 'include',
                        });
                        const responseData = await response.json();
                        appendOutput(`Server Response: ${JSON.stringify(responseData)}`);
                    } catch (postError) {
                        console.error('Error sending data to server:', postError);
                        appendOutput(`Error sending data to server: ${postError.message}`);
                    }
                    // Reset the receivedData buffer
                    receivedData = '';
                }
            }
        } catch (error) {
            appendOutput(`Error: ${error.message}`);
        }
    }
    function appendOutput(message) {
        const outputDiv = document.getElementById('output');
        outputDiv.innerHTML = `<p>${message}</p>` + outputDiv.innerHTML;
    }
    function getCookie(name) {
        const value = `; ${document.cookie}`;
        const parts = value.split(`; ${name}=`);
        if (parts.length === 2) return parts.pop().split(';').shift();
    }

</script>

Rough Streamlit Sketch:

import streamlit as st
import requests

with st.container(border= True):
    st.selectbox("Select Scanner", options= ["webscan-frog", "webscan-croco"], format_func= lambda x: x.split("-")[-1])
    if st.button("Connect"):
        pass # <-- Execute jscript to open the interface to select and connect to com port

e1 = st.empty()

    # make request with data
if "\n" in receiveddata:
    req = requests.post("http://127.0.0.1:8000/api/scan/", {"data": receiveddata}, auth=('admin', 'password')).json()
    # show returned data of request
    e1.container().json(req)

How can I implement the code of the webserial api? Or is my Idea not working at all?
I could also imagine doing all of the computation (selecting, connecting, and making requests with new data) apart from the streamlit page, but I only want to update the Page, after I press the Trigger of the Scanner.
Thank you all for helping.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.