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>
 
<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.