commit 7e59e40883f085175627bf566f3cfd64714abe3b
parent 7239e54166371a99dd81201b6e2462edd2277a6c
Author: Daniel Barlow <dan@telent.net>
Date: Wed, 28 Jan 2026 01:08:44 +0000
cli client program
this is presently android.py minus the gui bits, but the plan is to
extract a module that can be used both from linux and from
termux-android
Diffstat:
2 files changed, 127 insertions(+), 0 deletions(-)
diff --git a/client/eculocate.py b/client/eculocate.py
@@ -0,0 +1,126 @@
+#!/data/data/com.termux/files/usr/bin/python
+
+# import termuxgui as tg
+import time
+import sys
+import socket
+import threading
+import select
+import datetime
+from pathlib import Path
+import os
+from zeroconf import ServiceBrowser, ServiceListener, Zeroconf
+
+class ZListener(ServiceListener):
+ def __init__(self, client):
+ self.client = client;
+
+ def update_service(self, zc: Zeroconf, type_: str, name: str) -> None:
+ print(f"Service {name} updated")
+
+ def remove_service(self, zc: Zeroconf, type_: str, name: str) -> None:
+ self.hosts[name] = 0
+ print(f"Service {name} removed")
+
+ def add_service(self, zc: Zeroconf, type_: str, name: str) -> None:
+ info = zc.get_service_info(type_, name)
+ print(f"Service {name} added, service info: {info}")
+ self.client.hosts[name] = None
+ self.client.host_addresses[name] = (socket.inet_ntoa(info.addresses[0]), info.port)
+
+
+class Client():
+ def __init__(self):
+ self.peer_hostname = None
+ self.peer_thread = None
+ self.bytes_recorded = 0
+
+ self.started = False
+ self.hosts = {}
+ self.host_addresses = {}
+ zeroconf = Zeroconf()
+ ServiceBrowser(zeroconf, "_keihin._udp.local.", ZListener(self))
+
+ def knows_service(self, servicename):
+ return not (not (self.host_addresses.get(servicename, False)))
+
+ def output_pathname(self):
+ dir = os.getenv("ECULOCATE_STORAGE")
+ if dir:
+ dir = Path(dir)
+ else:
+ dir = Path("/storage") / "emulated" / "0" / "eculocate"
+ dir.mkdir(0o777, False, True)
+ name = f"{int(time.time())}.raw"
+ return dir / name
+
+ def recording_thread_main(self):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ addr = self.host_addresses[self.peer_hostname]
+ print(self.peer_hostname)
+ print(addr)
+ sock.connect(addr)
+ sock.setblocking(0)
+ interval = 500
+
+ self.bytes_recorded = 0
+
+ with open(self.output_pathname(), "wb") as outf:
+ while self.started:
+ print("sending subscribe")
+ b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] )
+ sock.send(b)
+ timeout = time.time() + 60
+ while self.started and time.time() < timeout:
+ ready = select.select([sock], [], [], timeout - time.time())
+ if ready[0]:
+ data = sock.recv(4000)
+ self.bytes_recorded = self.bytes_recorded + len(data)
+ outf.write(data)
+ outf.flush()
+ sock.close()
+ print("recording thread terminated")
+
+ def choose_service_name(self, hostname):
+ if hostname == None:
+ self.started = False
+ self.peer_hostname = hostname
+
+ def start_recording(self, start_or_stop):
+ if start_or_stop and self.peer_hostname != None:
+ self.started = True
+ print("starting record")
+ peer_thread = threading.Thread(target=self.recording_thread_main, daemon = True)
+ peer_thread.start()
+ else:
+ print("stopping record")
+ self.started = False
+
+
+
+# ./eculocate "service name" 60 # connect to service name, record for 60s
+
+# discover zeroconf until we have the requested service name
+
+name = sys.argv[1]
+fqname = f"{name}._keihin._udp.local."
+
+client = Client()
+
+timeout = int(sys.argv[2])
+exists = False
+
+while not exists:
+ time.sleep(2)
+ exists = client.knows_service(fqname)
+client.choose_service_name(fqname)
+
+print("record")
+client.start_recording(True)
+time.sleep(timeout)
+client.start_recording(False)
+print("fin")
+
+# register session
+# start to record to stdout
+# stop after requested time or 15s
diff --git a/shell.nix b/shell.nix
@@ -6,6 +6,7 @@
];
python = (pkgs.python3.withPackages (python-pkgs: [
python-pkgs.pynacl
+ python-pkgs.zeroconf
]));
in
pkgs.mkShell rec {