LabSat Lite Discovery Script
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() |
