From f2adbfaa3fb5e761c3a9d5a2379a3b8f5f1befc1 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sat, 10 Jan 2026 20:46:34 +0000 Subject: [PATCH 1/7] initial commit --- shore.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 shore.py diff --git a/shore.py b/shore.py new file mode 100644 index 0000000..177263b --- /dev/null +++ b/shore.py @@ -0,0 +1,64 @@ +#!/data/data/com.termux/files/usr/bin/python + +import termuxgui as tg +import time +import sys + +peer_hostname = None +started = False + +def choose_hostname(hostname): + if hostname == None: + started = False + peer_hostname = hostname + +hosts = { + "eculogical.local" : None, + "eculogical2.local" : None, + "eculogical3.local" : None +} + + # Intercept the back button. Instead of finishing the Activity, it now sends an event instead + # a.interceptbackbutton(True) + # Also possible: a = tg.Activity(c, intercept=True) + + + +with tg.Connection() as c: + # create a new Activity. By default, a new Task as created. + a = tg.Activity(c) + # you can find the Task under a.t + + def add_host(activity, name, parent): + selected = (peer_hostname == name) + if selected: + label = f"* {name}" + else: + label = name + button = tg.Button(activity, label, parent) + button.setlinearlayoutparams(0) + button.setheight(tg.View.WRAP_CONTENT) + hosts[name] = button + + def refresh_buttons(): + for name, widget in hosts.items(): + if name == peer_hostname: + label = f"* {name}" + else: + label = name + widget.settext(label) + + instances = tg.LinearLayout(a) + + add_host(a, "eculogical.local", instances) + add_host(a, "eculogical2.local", instances) + add_host(a, "eculogical3.local", instances) + + for ev in c.events(): # waits for events from the gui + if ev.type == tg.Event.destroy: + sys.exit() + if ev.type == tg.Event.click: + for k, v in hosts.items(): + if v == ev.value["id"]: + peer_hostname = k + refresh_buttons() From d9e20e4ca572389a30983016176554a16c044d08 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sat, 10 Jan 2026 21:54:35 +0000 Subject: [PATCH 2/7] working (fake) start/stop button --- shore.py | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/shore.py b/shore.py index 177263b..252b31a 100644 --- a/shore.py +++ b/shore.py @@ -7,11 +7,6 @@ import sys peer_hostname = None started = False -def choose_hostname(hostname): - if hostname == None: - started = False - peer_hostname = hostname - hosts = { "eculogical.local" : None, "eculogical2.local" : None, @@ -23,7 +18,6 @@ hosts = { # Also possible: a = tg.Activity(c, intercept=True) - with tg.Connection() as c: # create a new Activity. By default, a new Task as created. a = tg.Activity(c) @@ -40,6 +34,13 @@ with tg.Connection() as c: button.setheight(tg.View.WRAP_CONTENT) hosts[name] = button + def choose_hostname(hostname): + global peer_hostname + if hostname == None: + started = False + peer_hostname = hostname + refresh_buttons() + def refresh_buttons(): for name, widget in hosts.items(): if name == peer_hostname: @@ -47,12 +48,33 @@ with tg.Connection() as c: else: label = name widget.settext(label) + if peer_hostname == None: + start_button.settext("----") + else: + start_button.settext("STOP" if started else "START") + + def start_recording(start_or_stop): + global started + if start_or_stop and peer_hostname != None: + print("pretending to do udp things") + else: + print("pretending to close udp socket") + started = start_or_stop instances = tg.LinearLayout(a) add_host(a, "eculogical.local", instances) add_host(a, "eculogical2.local", instances) add_host(a, "eculogical3.local", instances) + tg.Space(a, instances) + + buttons = tg.LinearLayout(a, instances, False) + + start_button = tg.Button(a, "(start)", buttons) + start_button.setlinearlayoutparams(0) + start_button.setheight(tg.View.WRAP_CONTENT) + # start_button.setwidth(tg.View.WRAP_CONTENT) + start_button.setgravity(1, 1) for ev in c.events(): # waits for events from the gui if ev.type == tg.Event.destroy: @@ -60,5 +82,7 @@ with tg.Connection() as c: if ev.type == tg.Event.click: for k, v in hosts.items(): if v == ev.value["id"]: - peer_hostname = k - refresh_buttons() + choose_hostname(k) + if ev.value["id"] == start_button: + start_recording(not started) + refresh_buttons() From 9bb4f025d45f3401772a87654b33e8d885ef8947 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sat, 10 Jan 2026 22:15:40 +0000 Subject: [PATCH 3/7] send udp packet --- shore.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/shore.py b/shore.py index 252b31a..531be9d 100644 --- a/shore.py +++ b/shore.py @@ -3,8 +3,12 @@ import termuxgui as tg import time import sys +import socket + peer_hostname = None +peer_socket = None + started = False hosts = { @@ -55,11 +59,21 @@ with tg.Connection() as c: def start_recording(start_or_stop): global started + global peer_socket if start_or_stop and peer_hostname != None: + started = True print("pretending to do udp things") + peer_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + peer_socket.connect((peer_hostname, 5000)) + interval = 500 + b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] ) + peer_socket.send(b) else: print("pretending to close udp socket") - started = start_or_stop + if peer_socket != None: + peer_socket.close() + peer_socket = None + started = False instances = tg.LinearLayout(a) From b93061917d29f842c78607ceaed7e838e11647bc Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sun, 11 Jan 2026 15:09:34 +0000 Subject: [PATCH 4/7] send and receive udp --- shore.py | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/shore.py b/shore.py index 531be9d..2c3d2de 100644 --- a/shore.py +++ b/shore.py @@ -4,10 +4,11 @@ import termuxgui as tg import time import sys import socket - +import threading +import select peer_hostname = None -peer_socket = None +peer_thread = None started = False @@ -17,6 +18,27 @@ hosts = { "eculogical3.local" : None } +def recording_thread_main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.connect((peer_hostname, 5000)) + sock.setblocking(0) + interval = 500 + while started: + print("sending subscribe") + b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] ) + sock.send(b) + timeout = time.time() + 60 + while started and time.time() < timeout: + ready = select.select([sock], [], [], timeout - time.time()) + if ready[0]: + data = sock.recv(4000) + print(f"got data {len(data)}" ) + sock.close() + print("recording thread terminated") + + + + # Intercept the back button. Instead of finishing the Activity, it now sends an event instead # a.interceptbackbutton(True) # Also possible: a = tg.Activity(c, intercept=True) @@ -57,22 +79,16 @@ with tg.Connection() as c: else: start_button.settext("STOP" if started else "START") + def start_recording(start_or_stop): global started - global peer_socket if start_or_stop and peer_hostname != None: started = True - print("pretending to do udp things") - peer_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - peer_socket.connect((peer_hostname, 5000)) - interval = 500 - b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] ) - peer_socket.send(b) + print("starting record") + peer_thread = threading.Thread(target=recording_thread_main, daemon = True) + peer_thread.start() else: - print("pretending to close udp socket") - if peer_socket != None: - peer_socket.close() - peer_socket = None + print("stopping record") started = False instances = tg.LinearLayout(a) From 67d9474a8e8befb50244f7be0ee9a53b045739b3 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sun, 11 Jan 2026 15:34:18 +0000 Subject: [PATCH 5/7] display count of bytes received --- shore.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/shore.py b/shore.py index 2c3d2de..048cd3c 100644 --- a/shore.py +++ b/shore.py @@ -9,6 +9,7 @@ import select peer_hostname = None peer_thread = None +bytes_recorded = 0 started = False @@ -23,6 +24,8 @@ def recording_thread_main(): sock.connect((peer_hostname, 5000)) sock.setblocking(0) interval = 500 + global bytes_recorded + bytes_recorded = 0 while started: print("sending subscribe") b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] ) @@ -32,6 +35,7 @@ def recording_thread_main(): ready = select.select([sock], [], [], timeout - time.time()) if ready[0]: data = sock.recv(4000) + bytes_recorded = bytes_recorded + len(data) print(f"got data {len(data)}" ) sock.close() print("recording thread terminated") @@ -96,6 +100,11 @@ with tg.Connection() as c: add_host(a, "eculogical.local", instances) add_host(a, "eculogical2.local", instances) add_host(a, "eculogical3.local", instances) + + tg.Space(a, instances) + + status = tg.TextView(a, "initializing", instances) + tg.Space(a, instances) buttons = tg.LinearLayout(a, instances, False) @@ -106,6 +115,15 @@ with tg.Connection() as c: # start_button.setwidth(tg.View.WRAP_CONTENT) start_button.setgravity(1, 1) + def update_byte_counter(): + global started + while True: + label = f"recorded {bytes_recorded} bytes" if started else "not recording" + status.settext(label) + time.sleep(1) + + threading.Thread(target = update_byte_counter, daemon = True).start() + for ev in c.events(): # waits for events from the gui if ev.type == tg.Event.destroy: sys.exit() From 6f9d14995908b95c8f93bad0f7ea68a77cc3aef0 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sun, 11 Jan 2026 17:03:27 +0000 Subject: [PATCH 6/7] write data to file --- shore.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/shore.py b/shore.py index 048cd3c..ae0e3fc 100644 --- a/shore.py +++ b/shore.py @@ -6,6 +6,9 @@ import sys import socket import threading import select +import datetime +from pathlib import Path +import os peer_hostname = None peer_thread = None @@ -19,6 +22,13 @@ hosts = { "eculogical3.local" : None } +def output_pathname(): + root = Path(os.getenv("HOME")) + dir = root / "storage" / "capital-shore" + dir.mkdir(0o777, False, True) + name = datetime.datetime.utcfromtimestamp(int(time.time())).isoformat() + ".raw" + return dir / name + def recording_thread_main(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.connect((peer_hostname, 5000)) @@ -26,17 +36,21 @@ def recording_thread_main(): interval = 500 global bytes_recorded bytes_recorded = 0 - while started: - print("sending subscribe") - b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] ) - sock.send(b) - timeout = time.time() + 60 - while started and time.time() < timeout: - ready = select.select([sock], [], [], timeout - time.time()) - if ready[0]: - data = sock.recv(4000) - bytes_recorded = bytes_recorded + len(data) - print(f"got data {len(data)}" ) + + with open(output_pathname(), "wb") as outf: + while started: + print("sending subscribe") + b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] ) + sock.send(b) + timeout = time.time() + 60 + while started and time.time() < timeout: + ready = select.select([sock], [], [], timeout - time.time()) + if ready[0]: + data = sock.recv(4000) + bytes_recorded = bytes_recorded + len(data) + outf.write(data) + outf.flush() + sock.close() print("recording thread terminated") From 9457c717d44b31b13d7aebc7aade98a3746eb8a0 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sun, 11 Jan 2026 17:18:46 +0000 Subject: [PATCH 7/7] rename file --- shore.py => android-termux-client/shore.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename shore.py => android-termux-client/shore.py (100%) diff --git a/shore.py b/android-termux-client/shore.py similarity index 100% rename from shore.py rename to android-termux-client/shore.py