Spaces:
Sleeping
Sleeping
Merge branch 'main' of hf.co:spaces/AccelerationConsortium/cobot280pi
Browse files- .gitignore +2 -0
- app.py +72 -164
- client.py +170 -0
- requirements.txt +2 -1
- utils.py +43 -0
.gitignore
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
__pycache__/
|
2 |
+
runner.py
|
app.py
CHANGED
@@ -1,181 +1,89 @@
|
|
1 |
import streamlit as st
|
2 |
-
|
3 |
-
|
4 |
-
from PIL import Image
|
5 |
-
|
6 |
-
def create_paho_client(tls=True):
|
7 |
-
client = mqtt.Client(client_id="", userdata=None, protocol=mqtt.MQTTv5)
|
8 |
-
if tls:
|
9 |
-
client.tls_set(tls_version=mqtt.ssl.PROTOCOL_TLS_CLIENT)
|
10 |
-
return client
|
11 |
-
|
12 |
-
def setup_paho_client(client, username, password, cloud, port, device_id, response_queue):
|
13 |
-
def on_message(client, userdata, msg):
|
14 |
-
payload_dict = json.loads(msg.payload)
|
15 |
-
response_queue.put(payload_dict)
|
16 |
-
|
17 |
-
client.username_pw_set(username, password)
|
18 |
-
client.connect(cloud, port)
|
19 |
-
client.subscribe(device_id + "/response")
|
20 |
-
client.on_message = on_message
|
21 |
-
client.loop_start()
|
22 |
-
|
23 |
-
def publish_and_wait(client, device_id, payload, response_queue):
|
24 |
-
client.publish(device_id, payload=json.dumps(payload), qos=2)
|
25 |
-
try:
|
26 |
-
response = response_queue.get(block=True, timeout=10)
|
27 |
-
except queue.Empty:
|
28 |
-
response = None
|
29 |
-
return response
|
30 |
-
|
31 |
-
def handle_image_display(image):
|
32 |
-
b64_bytes = base64.b64decode(image)
|
33 |
-
img_bytes = io.BytesIO(b64_bytes)
|
34 |
-
img = Image.open(img_bytes)
|
35 |
-
st.image(img, caption="Cobot camera view")
|
36 |
|
37 |
st.title("MyCobot280 Pi control demo")
|
38 |
|
39 |
st.markdown("""
|
40 |
-
|
41 |
""")
|
42 |
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
-
|
46 |
-
HIVEMQ_USERNAME = st.text_input("HIVEMQ Username", type="password")
|
47 |
-
HIVEMQ_PASSWORD = st.text_input("HIVEMQ Password", type="password")
|
48 |
-
HIVEMQ_HOST = st.text_input("HIVEMQ Host", type="password")
|
49 |
-
DEVICE_ID = st.text_input("Device ID", type="password")
|
50 |
-
PORT = st.number_input("Port", min_value=1, step=1, value=8883)
|
51 |
-
else:
|
52 |
-
HIVEMQ_USERNAME = os.environ.get("HIVEMQ_USERNAME")
|
53 |
-
HIVEMQ_PASSWORD = os.environ.get("HIVEMQ_PASSWORD")
|
54 |
-
HIVEMQ_HOST = os.environ.get("HIVEMQ_HOST")
|
55 |
-
DEVICE_ID = os.environ.get("DEVICE_ID")
|
56 |
-
PORT = int(os.environ.get("PORT", 8883))
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
st.session_state['HIVEMQ_PASSWORD'] = None
|
63 |
-
st.session_state['HIVEMQ_HOST'] = None
|
64 |
-
st.session_state['DEVICE_ID'] = None
|
65 |
-
st.session_state['PORT'] = None
|
66 |
|
67 |
st.markdown("## Commands")
|
68 |
commands = [
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
]
|
77 |
selected = st.selectbox("Select command: ", commands, key="selected_command", disabled=False)
|
78 |
|
79 |
args = {}
|
80 |
-
|
81 |
-
pass
|
82 |
-
elif st.session_state.selected_command == "query/coords":
|
83 |
-
pass
|
84 |
-
elif st.session_state.selected_command == "query/gripper":
|
85 |
-
pass
|
86 |
-
elif st.session_state.selected_command == "query/camera":
|
87 |
-
quality = st.slider("Image quality", 1, 100, 50)
|
88 |
-
args = {"quality": quality}
|
89 |
-
elif st.session_state.selected_command == "control/angles":
|
90 |
-
col1, col2, col3 = st.columns(3)
|
91 |
-
with col1:
|
92 |
-
angle_1 = st.number_input("Angle 1", format="%.2f", step=5.0)
|
93 |
-
angle_4 = st.number_input("Angle 4", format="%.2f", step=5.0)
|
94 |
-
with col2:
|
95 |
-
angle_2 = st.number_input("Angle 2", format="%.2f", step=5.0)
|
96 |
-
angle_5 = st.number_input("Angle 5", format="%.2f", step=5.0)
|
97 |
-
with col3:
|
98 |
-
angle_3 = st.number_input("Angle 3", format="%.2f", step=5.0)
|
99 |
-
angle_6 = st.number_input("Angle 6", format="%.2f", step=5.0)
|
100 |
-
speed = st.slider("Speed", 1, 100, 50)
|
101 |
-
args = {"angles": [angle_1, angle_2, angle_3, angle_4, angle_5, angle_6], "speed": speed}
|
102 |
-
elif st.session_state.selected_command == "control/coords":
|
103 |
-
col1, col2, col3 = st.columns(3)
|
104 |
-
with col1:
|
105 |
-
x = st.number_input("x", format="%.2f", step=5.0)
|
106 |
-
rx = st.number_input("rx (Rotation x)", format="%.2f", step=5.0)
|
107 |
-
with col2:
|
108 |
-
y = st.number_input("y", format="%.2f", step=5.0)
|
109 |
-
ry = st.number_input("ry (Rotation y)", format="%.2f", step=5.0)
|
110 |
-
with col3:
|
111 |
-
z = st.number_input("z", format="%.2f", step=5.0)
|
112 |
-
rz = st.number_input("rz (Rotation z)", format="%.2f", step=5.0)
|
113 |
-
speed = st.slider("Speed", 1, 100, 50)
|
114 |
-
args = {"coords": [x, y, z, rx, ry, rz], "speed": speed}
|
115 |
-
elif st.session_state.selected_command == "control/gripper":
|
116 |
-
gripper_value = st.slider("Gripper value", 1, 100, 50)
|
117 |
-
speed = st.slider("Speed", 1, 100, 50)
|
118 |
-
args = {"gripper_value": gripper_value, "speed": speed}
|
119 |
-
|
120 |
with st.form("mqtt_form"):
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
st.write(response)
|
171 |
-
else:
|
172 |
-
st.write("Timed out waiting for response. Is the on-device server running?")
|
173 |
-
|
174 |
-
cobot_stream_url = "https://youtube.com/live/fF4zEp6LSkg?feature=share"
|
175 |
-
|
176 |
-
st.video(
|
177 |
-
cobot_stream_url
|
178 |
-
# Optionally enable autoplay which requires muting as browsers don't allow autoplay of unmuted video
|
179 |
-
# muted=True,
|
180 |
-
# autoplay=True
|
181 |
-
)
|
|
|
1 |
import streamlit as st
|
2 |
+
from utils import *
|
3 |
+
from client import CobotController
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
st.title("MyCobot280 Pi control demo")
|
6 |
|
7 |
st.markdown("""
|
8 |
+
This app is a public demo of remote control of the MyCobot280 Pi through the MQTT Communication Protocol.
|
9 |
""")
|
10 |
|
11 |
+
cobot_stream_url = "https://youtube.com/live/w7zn-Sk3pG0?feature=share"
|
12 |
+
st.video(
|
13 |
+
cobot_stream_url
|
14 |
+
# Optionally enable autoplay which requires muting as browsers don't allow autoplay of unmuted video
|
15 |
+
# muted=True,
|
16 |
+
# autoplay=True
|
17 |
+
)
|
18 |
|
19 |
+
render_log_window()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
+
user_id = get_user_id()
|
22 |
+
user, passwd, host, endpoint, port = get_credentials(False)
|
23 |
+
client = CobotController(user, passwd, host, port, endpoint, user_id)
|
24 |
+
# refresh_once()
|
|
|
|
|
|
|
|
|
25 |
|
26 |
st.markdown("## Commands")
|
27 |
commands = [
|
28 |
+
"query/angles",
|
29 |
+
"query/coords",
|
30 |
+
"query/gripper",
|
31 |
+
"query/camera",
|
32 |
+
"control/angles",
|
33 |
+
"control/coords",
|
34 |
+
"control/gripper"
|
35 |
]
|
36 |
selected = st.selectbox("Select command: ", commands, key="selected_command", disabled=False)
|
37 |
|
38 |
args = {}
|
39 |
+
command = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
with st.form("mqtt_form"):
|
41 |
+
if st.session_state.selected_command == "query/angles":
|
42 |
+
command = client.get_angles
|
43 |
+
elif st.session_state.selected_command == "query/coords":
|
44 |
+
command = client.get_coords
|
45 |
+
elif st.session_state.selected_command == "query/gripper":
|
46 |
+
command = client.get_gripper_value
|
47 |
+
elif st.session_state.selected_command == "query/camera":
|
48 |
+
quality = st.slider("Image quality", 1, 100, 100)
|
49 |
+
command = client.get_camera
|
50 |
+
args = {"quality": quality}
|
51 |
+
elif st.session_state.selected_command == "control/angles":
|
52 |
+
angle_1 = st.number_input("Angle 1", format="%.2f", step=5.0)
|
53 |
+
angle_2 = st.number_input("Angle 2", format="%.2f", step=5.0)
|
54 |
+
angle_3 = st.number_input("Angle 3", format="%.2f", step=5.0)
|
55 |
+
angle_4 = st.number_input("Angle 4", format="%.2f", step=5.0)
|
56 |
+
angle_5 = st.number_input("Angle 5", format="%.2f", step=5.0)
|
57 |
+
angle_6 = st.number_input("Angle 6", format="%.2f", step=5.0)
|
58 |
+
speed = st.slider("Speed", 1, 100, 50)
|
59 |
+
command = client.send_angles
|
60 |
+
args = {"angle_list": [angle_1, angle_2, angle_3, angle_4, angle_5, angle_6], "speed": speed}
|
61 |
+
elif st.session_state.selected_command == "control/coords":
|
62 |
+
x = st.number_input("x", format="%.2f", step=5.0)
|
63 |
+
y = st.number_input("y", format="%.2f", step=5.0)
|
64 |
+
z = st.number_input("z", format="%.2f", step=5.0)
|
65 |
+
rx = st.number_input("rx (Rotation x)", format="%.2f", step=5.0)
|
66 |
+
ry = st.number_input("ry (Rotation y)", format="%.2f", step=5.0)
|
67 |
+
rz = st.number_input("rz (Rotation z)", format="%.2f", step=5.0)
|
68 |
+
speed = st.slider("Speed", 1, 100, 50)
|
69 |
+
command = client.send_coords
|
70 |
+
args = {"coord_list": [x, y, z, rx, ry, rz], "speed": speed}
|
71 |
+
elif st.session_state.selected_command == "control/gripper":
|
72 |
+
gripper_value = st.slider("Gripper value", 1, 100, 50)
|
73 |
+
speed = st.slider("Speed", 1, 100, 50)
|
74 |
+
command = client.send_gripper_value
|
75 |
+
args = {"value": gripper_value, "speed": speed}
|
76 |
+
|
77 |
+
submitted = st.form_submit_button("Send Command")
|
78 |
+
if submitted:
|
79 |
+
response = command(**args)
|
80 |
+
st.markdown("### Response")
|
81 |
+
if response is not None:
|
82 |
+
if "image" in response:
|
83 |
+
image = response.pop("image")
|
84 |
+
st.write(response)
|
85 |
+
st.image(image, caption="Cobot camera view")
|
86 |
+
else:
|
87 |
+
st.write(response)
|
88 |
+
else:
|
89 |
+
st.write("Timed out waiting for response. Is the on-device server running?")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client.py
ADDED
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# installed packages
|
2 |
+
from PIL import Image
|
3 |
+
import paho.mqtt.client as paho
|
4 |
+
|
5 |
+
# base python packages
|
6 |
+
import json, io, base64
|
7 |
+
from queue import Queue
|
8 |
+
from datetime import datetime
|
9 |
+
import uuid
|
10 |
+
|
11 |
+
def print_with_timestamp(message):
|
12 |
+
print(f"[{datetime.now().strftime('%b %d, %H:%M:%S')}] {message}")
|
13 |
+
|
14 |
+
class CobotController:
|
15 |
+
user_id = str(uuid.uuid4())
|
16 |
+
|
17 |
+
def __init__(
|
18 |
+
self,
|
19 |
+
hive_mq_username: str,
|
20 |
+
hive_mq_password: str,
|
21 |
+
hive_mq_cloud: str,
|
22 |
+
port: int,
|
23 |
+
device_endpoint: str,
|
24 |
+
user_id: str = None
|
25 |
+
):
|
26 |
+
# setup client and response queues
|
27 |
+
self.client = paho.Client(client_id="", userdata=None, protocol=paho.MQTTv5)
|
28 |
+
self.client.tls_set()
|
29 |
+
self.client.username_pw_set(hive_mq_username, hive_mq_password)
|
30 |
+
self.client.connect(hive_mq_cloud, port)
|
31 |
+
|
32 |
+
self.response_queue = Queue()
|
33 |
+
def on_message(client, userdata, msg):
|
34 |
+
payload_dict = json.loads(msg.payload)
|
35 |
+
self.response_queue.put(payload_dict)
|
36 |
+
|
37 |
+
def on_connect(client, userdata, flags, rc, properties=None):
|
38 |
+
print_with_timestamp("Connected to HiveMQ broker...")
|
39 |
+
|
40 |
+
self.client.on_connect = on_connect
|
41 |
+
self.client.on_message = on_message
|
42 |
+
self.client.loop_start()
|
43 |
+
|
44 |
+
# initialize user id and endpoints
|
45 |
+
if user_id is not None:
|
46 |
+
CobotController.user_id = user_id
|
47 |
+
self.user_id = CobotController.user_id
|
48 |
+
|
49 |
+
self.device_endpoint = device_endpoint
|
50 |
+
self.init_endpoint = self.device_endpoint + "/init"
|
51 |
+
self.publish_endpoint = self.device_endpoint + "/" + self.user_id
|
52 |
+
self.incoming_endpoint = self.publish_endpoint + "/response"
|
53 |
+
self.client.subscribe(self.incoming_endpoint, qos=2)
|
54 |
+
|
55 |
+
connected = self.check_connection_status()
|
56 |
+
if connected:
|
57 |
+
return
|
58 |
+
|
59 |
+
# send an init request
|
60 |
+
print_with_timestamp("Sending a connection request...")
|
61 |
+
pub_handle = self.client.publish(
|
62 |
+
self.init_endpoint,
|
63 |
+
payload=json.dumps({"id": self.user_id}),
|
64 |
+
qos=2
|
65 |
+
)
|
66 |
+
pub_handle.wait_for_publish()
|
67 |
+
|
68 |
+
# get a response for the init message, if no response, have to wait for current users time to end
|
69 |
+
print_with_timestamp("Waiting for cobot access...")
|
70 |
+
prev_pos = None
|
71 |
+
while True:
|
72 |
+
try:
|
73 |
+
payload = self.response_queue.get(timeout=10)
|
74 |
+
if payload["status"] == "ready":
|
75 |
+
self.client.publish(
|
76 |
+
self.publish_endpoint,
|
77 |
+
payload=json.dumps({"yeehaw": []}),
|
78 |
+
qos=2
|
79 |
+
)
|
80 |
+
print_with_timestamp("Connected to server successfully.")
|
81 |
+
break
|
82 |
+
|
83 |
+
except Exception as e:
|
84 |
+
resp = self.handle_publish_and_response(
|
85 |
+
payload=json.dumps({"id": self.user_id}),
|
86 |
+
custom_endpoint=self.device_endpoint + "/queuequery"
|
87 |
+
)
|
88 |
+
if "queue_pos" not in resp:
|
89 |
+
break
|
90 |
+
pos = resp["queue_pos"]
|
91 |
+
if prev_pos == None:
|
92 |
+
prev_pos = pos
|
93 |
+
elif prev_pos == pos:
|
94 |
+
continue
|
95 |
+
prev_pos = pos
|
96 |
+
print_with_timestamp(f"Waiting for cobot access. There are {pos - 1} users ahead of you...")
|
97 |
+
|
98 |
+
def check_connection_status(self):
|
99 |
+
self.client.publish(
|
100 |
+
self.publish_endpoint,
|
101 |
+
payload=json.dumps({"command":"query/angles", "args": {}}),
|
102 |
+
qos=2
|
103 |
+
)
|
104 |
+
try: # if we recieve any response, it means the server is currently servicing our requests
|
105 |
+
_ = self.response_queue.get(timeout=5)
|
106 |
+
return True
|
107 |
+
except Exception as _:
|
108 |
+
return False
|
109 |
+
|
110 |
+
def handle_publish_and_response(self, payload, custom_endpoint=None):
|
111 |
+
if custom_endpoint is None:
|
112 |
+
self.client.publish(self.publish_endpoint, payload=payload, qos=2)
|
113 |
+
else:
|
114 |
+
self.client.publish(custom_endpoint, payload=payload, qos=2)
|
115 |
+
return self.response_queue.get(block=True)
|
116 |
+
|
117 |
+
def send_angles(
|
118 |
+
self,
|
119 |
+
angle_list: list[float] = [0.0] * 6,
|
120 |
+
speed: int = 50
|
121 |
+
):
|
122 |
+
payload = json.dumps({"command": "control/angles",
|
123 |
+
"args": {"angles": angle_list, "speed": speed}})
|
124 |
+
return self.handle_publish_and_response(payload)
|
125 |
+
|
126 |
+
def send_coords(
|
127 |
+
self,
|
128 |
+
coord_list: list[float] = [0.0] * 6,
|
129 |
+
speed: int = 50
|
130 |
+
):
|
131 |
+
payload = json.dumps({"command": "control/coords",
|
132 |
+
"args": {"coords": coord_list, "speed": speed}})
|
133 |
+
return self.handle_publish_and_response(payload)
|
134 |
+
|
135 |
+
def send_gripper_value(
|
136 |
+
self,
|
137 |
+
value: int = 100,
|
138 |
+
speed: int = 50
|
139 |
+
):
|
140 |
+
payload = json.dumps({"command": "control/gripper",
|
141 |
+
"args": {"gripper_value": value, "speed": speed}})
|
142 |
+
return self.handle_publish_and_response(payload)
|
143 |
+
|
144 |
+
def get_angles(self):
|
145 |
+
payload = json.dumps({"command": "query/angles", "args": {}})
|
146 |
+
return self.handle_publish_and_response(payload)
|
147 |
+
|
148 |
+
def get_coords(self):
|
149 |
+
payload = json.dumps({"command": "query/coords", "args": {}})
|
150 |
+
return self.handle_publish_and_response(payload)
|
151 |
+
|
152 |
+
def get_gripper_value(self):
|
153 |
+
payload = json.dumps({"command": "query/gripper", "args": {}})
|
154 |
+
return self.handle_publish_and_response(payload)
|
155 |
+
|
156 |
+
def get_camera(self, quality=100, save_path=None):
|
157 |
+
payload = json.dumps({"command": "query/camera", "args": {"quality": quality}})
|
158 |
+
response = self.handle_publish_and_response(payload)
|
159 |
+
if not response["success"]:
|
160 |
+
return response
|
161 |
+
|
162 |
+
b64_bytes = base64.b64decode(response["image"])
|
163 |
+
img_bytes = io.BytesIO(b64_bytes)
|
164 |
+
img = Image.open(img_bytes)
|
165 |
+
|
166 |
+
response["image"] = img
|
167 |
+
if save_path is not None:
|
168 |
+
img.save(save_path)
|
169 |
+
|
170 |
+
return response
|
requirements.txt
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
paho-mqtt==2.1.0
|
2 |
pillow==10.4.0
|
3 |
setuptools==75.1.0
|
4 |
-
wheel==0.44.0
|
|
|
|
1 |
paho-mqtt==2.1.0
|
2 |
pillow==10.4.0
|
3 |
setuptools==75.1.0
|
4 |
+
wheel==0.44.0
|
5 |
+
streamlit-js-eval==0.1.7
|
utils.py
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from streamlit_js_eval import streamlit_js_eval
|
3 |
+
import uuid, io, sys, os
|
4 |
+
|
5 |
+
def get_user_id():
|
6 |
+
if "user_id" not in st.session_state:
|
7 |
+
query_params = st.query_params
|
8 |
+
user_id = query_params.get("user_id", None)
|
9 |
+
if not user_id:
|
10 |
+
user_id = str(uuid.uuid4())
|
11 |
+
st.query_params["user_id"] = user_id
|
12 |
+
st.session_state.user_id = user_id
|
13 |
+
return st.session_state.user_id
|
14 |
+
|
15 |
+
def get_credentials(use_own_creds: bool):
|
16 |
+
if use_own_creds:
|
17 |
+
HIVEMQ_USERNAME = st.text_input("HIVEMQ Username", type="password")
|
18 |
+
HIVEMQ_PASSWORD = st.text_input("HIVEMQ Password", type="password")
|
19 |
+
HIVEMQ_HOST = st.text_input("HIVEMQ Host", type="password")
|
20 |
+
DEVICE_ENDPOINT = st.text_input("Device ID", type="password")
|
21 |
+
PORT = st.number_input("Port", min_value=1, step=1, value=8883)
|
22 |
+
else:
|
23 |
+
HIVEMQ_USERNAME = os.environ.get("HIVEMQ_USERNAME")
|
24 |
+
HIVEMQ_PASSWORD = os.environ.get("HIVEMQ_PASSWORD")
|
25 |
+
HIVEMQ_HOST = os.environ.get("HIVEMQ_HOST")
|
26 |
+
DEVICE_ENDPOINT = os.environ.get("DEVICE_ID")
|
27 |
+
PORT = int(os.environ.get("PORT", 8883))
|
28 |
+
return HIVEMQ_USERNAME, HIVEMQ_PASSWORD, HIVEMQ_HOST, DEVICE_ENDPOINT, PORT
|
29 |
+
|
30 |
+
def render_log_window():
|
31 |
+
class StreamlitRedirector:
|
32 |
+
def write(self, message):
|
33 |
+
if message.strip():
|
34 |
+
st.write(message)
|
35 |
+
|
36 |
+
sys.stdout = StreamlitRedirector()
|
37 |
+
|
38 |
+
def refresh_once():
|
39 |
+
has_refreshed = st.query_params.get("has_refreshed", None)
|
40 |
+
if not has_refreshed:
|
41 |
+
st.query_params["has_refreshed"] = True
|
42 |
+
sys.stdout = sys.__stdout__
|
43 |
+
streamlit_js_eval(js_expressions="parent.window.location.reload()")
|