The following is a Python script you can use to find your LabSat Lite unit on the network and identify the IP Address. You can then enter the IP address into a browser to access the LabSat Lite Web Interface.
#!/usr/bin/env python3
"""
findlabsat.py - Discover LabSat Lite devices on the local network
Sends UDP broadcast on port 43686 and listens for responses on port 43687
"""
import socket
import struct
import sys
import argparse
from typing import List, Dict, Any
# Network configuration
BROADCAST_PORT = 43686
LISTEN_PORT = 43687
SEARCH_MESSAGE = b"LSRT_SEARCH"
TIMEOUT = 3.0 # seconds to wait for responses
# Response structure constants
HOSTNAME_LENGTH = 24
RESPONSE_FORMAT = '<4s4sH24sBBH' # Header(4), IP(4), Port(2), Name(24), Ver(1), SubVer(1), Build(2)
RESPONSE_SIZE = struct.calcsize(RESPONSE_FORMAT)
def get_local_interfaces():
"""Get list of local network interfaces with their IP addresses"""
interfaces = []
try:
# Get hostname
hostname = socket.gethostname()
# Get all addresses for this host
addr_info = socket.getaddrinfo(hostname, None, socket.AF_INET)
for info in addr_info:
ip = info[4][0]
if ip not in [i[1] for i in interfaces]:
interfaces.append(('Local', ip))
except:
pass
# Always add localhost
if '127.0.0.1' not in [i[1] for i in interfaces]:
interfaces.append(('Loopback', '127.0.0.1'))
return interfaces
def parse_response(data: bytes) -> Dict[str, Any]:
"""Parse the UDP response from a LabSat Lite device"""
# The response includes the search string echo first, skip it
search_echo = b"LSRT_SEARCH"
if data.startswith(search_echo):
data = data[len(search_echo):]
if len(data) < RESPONSE_SIZE:
raise ValueError(f"Response too short: {len(data)} bytes, expected {RESPONSE_SIZE}")
# Unpack the binary structure
header, ip_bytes, port, name_bytes, version, subversion, build = struct.unpack(
RESPONSE_FORMAT, data[:RESPONSE_SIZE]
)
# Convert IP address bytes to string
ip_address = '.'.join(str(b) for b in ip_bytes)
# Convert name bytes to string, stripping null terminators
device_name = name_bytes.rstrip(b'\x00').decode('ascii', errors='replace')
return {
'ip': ip_address,
'port': port,
'name': device_name,
'version': version,
'subversion': subversion,
'build': build
}
def find_labsat_devices(timeout: float = TIMEOUT, bind_addr: str = '0.0.0.0') -> List[Dict[str, Any]]:
"""
Broadcast search request and collect responses from LabSat Lite devices
Args:
timeout: How long to wait for responses (seconds)
bind_addr: Local IP address to bind to (use '0.0.0.0' for all interfaces)
Returns:
List of dictionaries containing device information
"""
devices = []
# Create broadcast socket
broadcast_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
broadcast_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
# Bind to specific interface if requested
if bind_addr != '0.0.0.0':
try:
broadcast_sock.bind((bind_addr, 0))
print(f"Bound broadcast socket to {bind_addr}")
except OSError as e:
print(f"Warning: Could not bind to {bind_addr}: {e}")
print("Using default interface instead.")
# Create listening socket
listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_sock.bind(('', LISTEN_PORT))
listen_sock.settimeout(timeout)
try:
# Send broadcast search request
print(f"Broadcasting search request on port {BROADCAST_PORT}...")
broadcast_sock.sendto(SEARCH_MESSAGE, ('<broadcast>', BROADCAST_PORT))
print(f"Listening for responses on port {LISTEN_PORT}...")
print(f"Waiting {timeout} seconds for devices to respond...\n")
# Collect responses until timeout
while True:
try:
data, addr = listen_sock.recvfrom(1024)
try:
device_info = parse_response(data)
device_info['source_addr'] = addr[0] # Add source address for reference
devices.append(device_info)
print(f"Found device:")
print(f" Name: {device_info['name']}")
print(f" IP: {device_info['ip']}")
print(f" Port: {device_info['port']}")
print(f" Version: {device_info['version']}.{device_info['subversion']}.{device_info['build']}")
print(f" Source: {device_info['source_addr']}")
print()
except ValueError as e:
print(f"Warning: Could not parse response from {addr[0]}: {e}")
except socket.timeout:
break
finally:
broadcast_sock.close()
listen_sock.close()
return devices
def main():
"""Main entry point"""
parser = argparse.ArgumentParser(
description='Discover LabSat Lite devices on the local network',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python findlabsat.py # Search on all interfaces
python findlabsat.py --list # List available interfaces
python findlabsat.py --bind 192.168.1.5 # Search on specific interface
python findlabsat.py --timeout 5 # Wait 5 seconds for responses
"""
)
parser.add_argument('--bind', '-b',
default='0.0.0.0',
help='Local IP address to bind to (default: 0.0.0.0 for all)')
parser.add_argument('--timeout', '-t',
type=float,
default=TIMEOUT,
help=f'Timeout in seconds to wait for responses (default: {TIMEOUT})')
parser.add_argument('--list', '-l',
action='store_true',
help='List available network interfaces and exit')
args = parser.parse_args()
# List interfaces if requested
if args.list:
print("Available network interfaces:")
print("=" * 50)
interfaces = get_local_interfaces()
for name, ip in interfaces:
print(f" {ip:15s} ({name})")
print()
print("Use --bind <ip> to broadcast on a specific interface")
return
print("LabSat Lite Device Discovery")
print("=" * 50)
if args.bind != '0.0.0.0':
print(f"Using interface: {args.bind}")
else:
print("Using all interfaces (default route)")
print()
try:
devices = find_labsat_devices(timeout=args.timeout, bind_addr=args.bind)
if devices:
print(f"\nDiscovery complete. Found {len(devices)} device(s).")
else:
print("\nNo LabSat Lite devices found on the network.")
print("Make sure devices are powered on and connected to the same network.")
print("\nTip: Use --list to see available interfaces")
except KeyboardInterrupt:
print("\n\nSearch interrupted by user.")
sys.exit(1)
except Exception as e:
print(f"\nError during discovery: {e}")
sys.exit(1)
if __name_, == "__main__":
main()