# # hdhomerun.py # # Copyright (c) 2011 Tako Schotanus . # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . # # As a special exception to the GNU Lesser General Public License, # you may link, statically or dynamically, an application with a # publicly distributed version of the Library to produce an # executable file containing portions of the Library, and # distribute that executable file under terms of your choice, # without any of the additional requirements listed in clause 4 of # the GNU Lesser General Public License. # # By "a publicly distributed version of the Library", we mean # either the unmodified Library as distributed by Silicondust, or a # modified version of the Library that is distributed under the # conditions defined in the GNU Lesser General Public License. # ################################################################################### # PS: Parts of the documentation in this file are copied from the C header files # Copyright (c) 2006-2007 Silicondust USA Inc. . # which are distributed under the LGPL as well, it's assumed the original copyright # holders are okay with this. ################################################################################### from ctypes import * import sys HDHOMERUN_DEVICE_TYPE_WILDCARD = 0xFFFFFFFF HDHOMERUN_DEVICE_TYPE_TUNER = 0x00000001 HDHOMERUN_DEVICE_ID_WILDCARD = 0xFFFFFFFF HDHOMERUN_STATUS_COLOR_NEUTRAL = 0xFFFFFFFF HDHOMERUN_STATUS_COLOR_RED = 0xFFFF0000 HDHOMERUN_STATUS_COLOR_YELLOW = 0xFFFFFF00 HDHOMERUN_STATUS_COLOR_GREEN = 0xFF00C000 HDHOMERUN_CHANNELSCAN_PROGRAM_NORMAL = 0 HDHOMERUN_CHANNELSCAN_PROGRAM_NODATA = 1 HDHOMERUN_CHANNELSCAN_PROGRAM_CONTROL = 2 HDHOMERUN_CHANNELSCAN_PROGRAM_ENCRYPTED = 3 _c_bool = c_int32 class hdhomerun_discover_device_t(Structure): _fields_ = [ ("ip_addr", c_uint32), ("device_type", c_uint32), ("device_id", c_uint32), ("tuner_count", c_ubyte) ] class hdhomerun_tuner_status_t(Structure): _fields_ = [ ("channel", c_char * 32), ("lock_str", c_char * 32), ("signal_present", _c_bool), ("lock_supported", _c_bool), ("lock_unsupported", _c_bool), ("signal_strength", c_int32), ("signal_to_noise_quality", c_int32), ("symbol_error_quality", c_int32), ("raw_bits_per_second", c_uint32), ("packets_per_second", c_uint32) ] class hdhomerun_channelscan_program_t(Structure): _fields_ = [ ("program_str", c_char * 64), ("program_number", c_ushort), ("virtual_major", c_ushort), ("virtual_minor", c_ushort), ("type", c_ushort), ("name", c_char * 32) ] class hdhomerun_channelscan_result_t(Structure): _fields_ = [ ("channel_str", c_char * 64), ("channelmap", c_uint32), ("frequency", c_uint32), ("status", hdhomerun_tuner_status_t), ("program_count", c_int32), ("programs", hdhomerun_channelscan_program_t * 64), ("transport_stream_id_detected", _c_bool), ("transport_stream_id", c_ushort) ] class hdhomerun_plotsample_t(Structure): _fields_ = [ ("real", c_ushort), ("imag", c_ushort) ] # # Low-level interface to libhdhomerun # class LibHdhr: def __init__(self): lib = self._libhdhr = cdll.LoadLibrary("libhdhomerun.so.1") # Discovery related functions LibHdhr._libfunc(lib.hdhomerun_discover_find_devices_custom, [ c_uint32, c_uint32, c_uint32, POINTER(hdhomerun_discover_device_t), c_int32 ], c_int32) LibHdhr._libfunc(lib.hdhomerun_discover_validate_device_id, [ c_uint32 ], _c_bool) # Device related functions LibHdhr._libfunc(lib.hdhomerun_device_create, [ c_uint32, c_uint32, c_uint32, c_void_p ], c_void_p) LibHdhr._libfunc(lib.hdhomerun_device_create_from_str, [ c_char_p, c_void_p ], c_void_p) LibHdhr._libfunc(lib.hdhomerun_device_destroy, [ c_void_p ]) LibHdhr._libfunc(lib.hdhomerun_device_get_name, [ c_void_p ], c_char_p) LibHdhr._libfunc(lib.hdhomerun_device_get_device_id, [ c_void_p ], c_uint32) LibHdhr._libfunc(lib.hdhomerun_device_get_device_ip, [ c_void_p ], c_uint32) LibHdhr._libfunc(lib.hdhomerun_device_get_device_id_requested, [ c_void_p ], c_uint32) LibHdhr._libfunc(lib.hdhomerun_device_get_device_ip_requested, [ c_void_p ], c_uint32) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner, [ c_void_p ], c_uint32) LibHdhr._libfunc(lib.hdhomerun_device_set_device, [ c_void_p, c_uint32, c_uint32 ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_tuner, [ c_void_p, c_uint32 ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_tuner_from_str, [ c_void_p, c_char_p ],c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_local_machine_addr, [ c_void_p ], c_uint32) LibHdhr._libfunc(lib.hdhomerun_device_get_model_str, [ c_void_p ], c_char_p) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner_status, [ c_void_p, POINTER(c_char_p), POINTER(hdhomerun_tuner_status_t) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner_streaminfo, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner_channel, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner_channelmap, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner_filter, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner_program, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner_target, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_tuner_lockkey_owner, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_ir_target, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_lineup_location, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_tuner_channel, [ c_void_p, c_char_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_tuner_channelmap, [ c_void_p, c_char_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_tuner_filter, [ c_void_p, c_char_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_tuner_program, [ c_void_p, c_char_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_tuner_target, [ c_void_p, c_char_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_ir_target, [ c_void_p, c_char_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_set_lineup_location, [ c_void_p, c_char_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_version, [ c_void_p, POINTER(c_char_p), POINTER(c_uint32) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_get_supported, [ c_void_p, c_char_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_tuner_lockkey_request, [ c_void_p, POINTER(c_char_p) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_tuner_lockkey_release, [ c_void_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_tuner_lockkey_force, [ c_void_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_tuner_lockkey_use_value, [ c_void_p, c_uint32 ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_wait_for_lock, [ c_void_p, POINTER(hdhomerun_tuner_status_t) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_channelscan_init, [ c_void_p, c_char_p ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_channelscan_advance, [ c_void_p, POINTER(hdhomerun_channelscan_result_t) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_channelscan_detect, [ c_void_p, POINTER(hdhomerun_channelscan_result_t) ], c_int32) LibHdhr._libfunc(lib.hdhomerun_device_channelscan_get_progress, [ c_void_p ], c_ubyte) # Channel related functions LibHdhr._libfunc(lib.hdhomerun_channelmap_get_channelmap_scan_group, [ c_char_p ], c_char_p) @staticmethod def _libfunc(func, argtypes, restype = None): func.argtypes = argtypes func.restype = restype @staticmethod def _get_str_attr(func, device_p): p = pointer(c_char_p()) res = func(device_p, p) return (res, None) if (res != 1) else (res, p.contents.value) # # Find devices. # # The device information is stored in caller-supplied array of hdhomerun_discover_device_t vars. # Multiple attempts are made to find devices. # Execution time is typically 400ms if max_count is not reached. # # Set target_ip to zero to auto-detect the IP address. # Set device_type to HDHOMERUN_DEVICE_TYPE_TUNER to detect HDHomeRun tuner devices. # Set device_id to HDHOMERUN_DEVICE_ID_WILDCARD to detect all device ids. # # Returns the number of devices found. # Retruns -1 on error. # def discover_find_devices_custom(self, target_ip, device_type, device_id, result_list = None, max_count = 64): result_list = result_list or (hdhomerun_discover_device_t * 64)() res = self._libhdhr.hdhomerun_discover_find_devices_custom(target_ip, device_type, device_id, pointer(result_list[0]), max_count) return (res, None) if (res < 0) else (res, result_list) # # Verify that the device ID given is valid. # # The device ID contains a self-check sequence that detects common user input errors including # single-digit errors and two digit transposition errors. # # Returns TRUE if valid. # Returns FALSE if not valid. # def discover_validate_device_id(self, device_id): return self._libhdhr.hdhomerun_discover_validate_device_id(device_id) # # uint32_t device_id = 32-bit device id of device. Set to HDHOMERUN_DEVICE_ID_WILDCARD to match any device ID. # uint32_t device_ip = IP address of device. Set to 0 to auto-detect. # unsigned int tuner = tuner index (0 or 1). Can be changed later by calling device_set_tuner(). # struct hdhomerun_debug_t *dbg: Pointer to debug logging object. Optional. # # Returns a pointer to the newly created device object. # # When no longer needed, the socket should be destroyed by calling device_destroy(). # def device_create(self, device_id, device_ip, tuner, debug_t = None): return self._libhdhr.hdhomerun_device_create(device_id, device_ip, tuner, debug_t) # # The device_create_from_str function creates a device object from the given device_str. # The device_str parameter can be any of the following forms: # # - # # If the tuner index is not included in the device_str then it is set to zero. Use hdhomerun_device_set_tuner # or hdhomerun_device_set_tuner_from_str to set the tuner. # def device_create_from_str(self, device_str, debug_t = None): return self._libhdhr.hdhomerun_device_create_from_str(device_str, debug_t) # # Call when socket no longer needed # def device_destroy(self, device_p): self._libhdhr.hdhomerun_device_destroy(device_p) # # Get the device id, ip, or tuner of the device instance. # def device_get_name(self, device_p): return self._libhdhr.hdhomerun_device_get_name(device_p) def device_get_device_id(self, device_p): return self._libhdhr.hdhomerun_device_get_device_id(device_p) def device_get_device_ip(self, device_p): return self._libhdhr.hdhomerun_device_get_device_ip(device_p) def device_get_device_id_requested(self, device_p): return self._libhdhr.hdhomerun_device_get_device_id_requested(device_p) def device_get_device_ip_requested(self, device_p): return self._libhdhr.hdhomerun_device_get_device_ip_requested(device_p) def device_get_tuner(self, device_p): return self._libhdhr.hdhomerun_device_get_tuner(device_p) # # Get the device id, ip, or tuner of the device instance. # def device_set_device(self, device_p, device_id, device_ip): return self._libhdhr.hdhomerun_device_set_device(device_p, device_id, device_ip) def device_set_tuner(self, device_p, tuner): return self._libhdhr.hdhomerun_device_set_tuner(device_p, tuner) # The hdhomerun_device_set_tuner_from_str function sets the tuner from the given tuner_str. # The tuner_str parameter can be any of the following forms: # # /tuner def device_set_tuner_from_str(self, device_p, tuner_str): return self._libhdhr.hdhomerun_device_set_tuner_from_str(device_p, tuner_str) # # Get the local machine IP address used when communicating with the device. # # This function is useful for determining the IP address to use with set target commands. # # Returns 32-bit IP address with native endianness, or 0 on error. # def device_get_local_machine_addr(self, device_p): return self._libhdhr.hdhomerun_device_get_local_machine_addr(device_p) def device_get_model_str(self, device_p): return self._libhdhr.hdhomerun_device_get_model_str(device_p) # # Get operations. # # Returns a string with the information if the operation was successful. # Returns 0 if the operation was rejected. # Returns -1 if a communication error occurred. # def device_get_tuner_status(self, device_p, status = None): p = pointer(c_char_p()) status = status or hdhomerun_tuner_status_t() res = self._libhdhr.hdhomerun_device_get_tuner_status(device_p, p, status) return (res, None, None) if (res != 1) else (res, p.contents.value, status) def device_get_tuner_streaminfo(self, device_p): func = self._libhdhr.hdhomerun_device_get_tuner_streaminfo return LibHdhr._get_str_attr(func, device_p) def device_get_tuner_channel(self, device_p): func = self._libhdhr.hdhomerun_device_get_tuner_channel return LibHdhr._get_str_attr(func, device_p) def device_get_tuner_channelmap(self, device_p): func = self._libhdhr.hdhomerun_device_get_tuner_channelmap return LibHdhr._get_str_attr(func, device_p) def device_get_tuner_filter(self, device_p): func = self._libhdhr.hdhomerun_device_get_tuner_filter return LibHdhr._get_str_attr(func, device_p) def device_get_tuner_program(self, device_p): func = self._libhdhr.hdhomerun_device_get_tuner_program return LibHdhr._get_str_attr(func, device_p) def device_get_tuner_target(self, device_p): func = self._libhdhr.hdhomerun_device_get_tuner_target return LibHdhr._get_str_attr(func, device_p) def device_get_tuner_lockkey_owner(self, device_p): func = self._libhdhr.hdhomerun_device_get_tuner_lockkey_owner return LibHdhr._get_str_attr(func, device_p) def device_get_ir_target(self, device_p): func = self._libhdhr.hdhomerun_device_get_ir_target return LibHdhr._get_str_attr(func, device_p) def device_get_lineup_location(self, device_p): func = self._libhdhr.hdhomerun_device_get_lineup_location return LibHdhr._get_str_attr(func, device_p) def device_get_version(self, device_p): p = pointer(c_char_p()) n = pointer(c_uint(0)) res = self._libhdhr.hdhomerun_device_get_version(device_p, p, n) return (res, p.contents.value, n.contents.value) def device_get_supported(self, device_p, prefix): p = pointer(c_char_p()) res = self._libhdhr.hdhomerun_device_get_supported(device_p, prefix, p) return (res, p.contents.value) # # Set operations. # # const char * = String to send to device. # # Returns 1 if the operation was successful. # Returns 0 if the operation was rejected. # Returns -1 if a communication error occurred. # def device_set_tuner_channel(self, device_p, channel): return self._libhdhr.hdhomerun_device_set_tuner_channel(device_p, channel) def device_set_tuner_channelmap(self, device_p, channelmap): return self._libhdhr.hdhomerun_device_set_tuner_channelmap(device_p, channelmap) def device_set_tuner_filter(self, device_p, filter): return self._libhdhr.hdhomerun_device_set_tuner_filter(device_p, filter) def device_set_tuner_program(self, device_p, program): return self._libhdhr.hdhomerun_device_set_tuner_program(device_p, program) def device_set_tuner_target(self, device_p, target): return self._libhdhr.hdhomerun_device_set_tuner_target(device_p, target) def device_set_ir_target(self, device_p, target): return self._libhdhr.hdhomerun_device_set_ir_target(device_p, target) def device_set_lineup_location(self, device_p, location): return self._libhdhr.hdhomerun_device_set_lineup_location(device_p, location) # # Tuner locking. # # The hdhomerun_device_tuner_lockkey_request function is used to obtain a lock # or to verify that the hdhomerun_device object still holds the lock. # Returns 1 if the lock request was successful and the lock was obtained. # Returns 0 if the lock request was rejected. # Returns -1 if a communication error occurs. # # The hdhomerun_device_tuner_lockkey_release function is used to release a # previously held lock. If locking is used then this function must be called # before destroying the hdhomerun_device object. # def device_tuner_lockkey_request(self, device_p): err = pointer(c_char_p()) res = self._libhdhr.hdhomerun_device_tuner_lockkey_request(device_p, err) return (res, err.contents.value) def device_tuner_lockkey_release(self, device_p): return self._libhdhr.hdhomerun_device_tuner_lockkey_release(device_p) def device_tuner_lockkey_force(self, device_p): return self._libhdhr.hdhomerun_device_tuner_lockkey_force(device_p) # # Intended only for non persistent connections; eg, hdhomerun_config. # def device_tuner_lockkey_use_value(self, device_p, lockkey): return self._libhdhr.hdhomerun_device_tuner_lockkey_use_value(device_p, lockkey) # # Wait for tuner lock after channel change. # # The hdhomerun_device_wait_for_lock function is used to detect/wait for a lock vs no lock indication # after a channel change. # # It will return quickly if a lock is aquired. # It will return quickly if there is no signal detected. # Worst case it will time out after 1.5 seconds - the case where there is signal but no lock. # def device_wait_for_lock(self, device_p, status = None): status = status or hdhomerun_tuner_status_t() res = self._libhdhr.hdhomerun_device_wait_for_lock(device_p, status) return (res, None) if (res != 1) else (res, status) # # Channel scan API. # def device_channelscan_init(self, device_p, channelmap): return self._libhdhr.hdhomerun_device_channelscan_init(device_p, channelmap) def device_channelscan_advance(self, device_p, result = None): result = result or hdhomerun_channelscan_result_t() res = self._libhdhr.hdhomerun_device_channelscan_advance(device_p, result) return (res, None) if (res != 1) else (res, result) def device_channelscan_detect(self, device_p, result = None): result = result or hdhomerun_channelscan_result_t() res = self._libhdhr.hdhomerun_device_channelscan_detect(device_p, result) return (res, None) if (res != 1) else (res, result) def device_channelscan_get_progress(self, device_p): return self._libhdhr.hdhomerun_device_channelscan_get_progress(device_p) def channelmap_get_channelmap_scan_group(self, channelmap): return self._libhdhr.hdhomerun_channelmap_get_channelmap_scan_group(channelmap) def ip2str(ip): return '%d.%d.%d.%d' % (ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff) def id2str(id): return '%08X' % id def show_device_info(lib, dev): print " Id: ", id2str(lib.device_get_device_id(dev)), "(", id2str(lib.device_get_device_id_requested(dev)), ")" print " Address: ", ip2str(lib.device_get_device_ip(dev)), "(", ip2str(lib.device_get_device_ip_requested(dev)), ")" print " Local IP: ", ip2str(lib.device_get_local_machine_addr(dev)) print " Model: ", lib.device_get_model_str(dev) print " Version: ", lib.device_get_version(dev) print " Festures: ", lib.device_get_supported(dev, None) print " IR Target: ", lib.device_get_ir_target(dev) print " Lineup Loc: ", lib.device_get_lineup_location(dev) print "" def show_tuner_info(lib, dev): print " Tuner: ", lib.device_get_tuner(dev) print " Name: ", lib.device_get_name(dev) print " StreamInfo: ", lib.device_get_tuner_streaminfo(dev) print " Channel: ", lib.device_get_tuner_channel(dev) print " ChannelMap: ", lib.device_get_tuner_channelmap(dev) print " Filter: ", lib.device_get_tuner_filter(dev) print " Program: ", lib.device_get_tuner_program(dev) print " Target: ", lib.device_get_tuner_target(dev) print " Lock Owner: ", lib.device_get_tuner_lockkey_owner(dev) (res, s1, s2) = lib.device_get_tuner_status(dev) print " Status: ", s1 print " Channel: ", s2.channel print " Lock Str: ", s2.lock_str print " Signal Present: ", s2.signal_present print " Lock Supported: ", s2.lock_supported print " Lock Unsupported: ", s2.lock_unsupported print " Signal Strength: ", s2.signal_strength print " Signal Quality: ", s2.signal_to_noise_quality print " Symbol Quality: ", s2.symbol_error_quality print " Bits / sec: ", s2.raw_bits_per_second print " Packets / sec: ", s2.packets_per_second print "" lib = LibHdhr() if True: (res, devs) = lib.discover_find_devices_custom(0, HDHOMERUN_DEVICE_TYPE_TUNER, HDHOMERUN_DEVICE_ID_WILDCARD) if res > 0: print "Devices found:" for i in range(res): dd = devs[i] dev = lib.device_create(dd.device_id, dd.ip_addr, 0) if not not dev: show_device_info(lib, dev) for j in range(dd.tuner_count): lib.device_set_tuner(dev, j) show_tuner_info(lib, dev) lib.device_destroy(dev) else: print "Could not create device", dd.device_id print "" elif res == 0: print "NO devices found!" else: print "Error trying to discover devices" if True: dev = lib.device_create(HDHOMERUN_DEVICE_ID_WILDCARD, 0, 1) #dev = lib.device_create_from_str("111035F2") if not dev: print "Could not create device!" sys.exit() device_id_requested = lib.device_get_device_id_requested(dev) if not lib.discover_validate_device_id(device_id_requested): print "Invalid device Id:", hex(device_id_requested) lib.device_destroy(dev) sys.exit() #print lib.device_set_tuner_from_str(dev, "1") (res, err) = lib.device_tuner_lockkey_request(dev) if res != 1: print "Could not lock tuner: ", err lib.device_destroy(dev) sys.exit() print "Scanning..." lib.device_set_tuner_target(dev, "none") (res, chmap) = lib.device_get_tuner_channelmap(dev) group = lib.channelmap_get_channelmap_scan_group(chmap) if not not group: if lib.device_channelscan_init(dev, group) == 1: cont = True while cont: (res, scan) = lib.device_channelscan_advance(dev) if res == 1: print scan.frequency, scan.channel_str, scan.status.signal_present, (res, detres) = lib.device_channelscan_detect(dev, scan) if res == 1: print lib.device_channelscan_get_progress(dev) for i in range(detres.program_count): p = detres.programs[i] print " ", p.program_str, p.type elif res == 0: print "No signal" else: print "*** Communication error ***" cont = False else: cont = False else: print "Could not initialize channel scan" else: print "Unknown channel map ", chmap lib.device_tuner_lockkey_release(dev) lib.device_destroy(dev)