# pylint: disable=too-many-lines """ Recorder Adapter for SMP """ import enum import logging from typing import TypeVar, Union from backend import LrcException from backend.recorder_adapters import telnetlib, TelnetAdapter, RecorderAdapter from backend.tools.exception_decorator import exception_decorator logger = logging.getLogger("lrc.recorder_adapters.extron_smp") RECORDER_MODEL_NAME = "Recorder Adapter for SMP 351 and 352" VERSION = "0.9.0" REQUIRES_USER = False REQUIRES_PW = True # HOST = "localhost" # HOST = "129.13.51.102" # Audimax SMP 351 # HOST = "129.13.51.106" # Tulla SMP 351 HOST = "172.22.246.207" # Test SMP MZ # HOST = "129.13.51.109" # Hertz USER = "admin" PW = "123mzsmp" # PW = "audimaxsmp" # PW = "smphertz" class SMP35x(TelnetAdapter, RecorderAdapter): """ A class representing the Extron SMP recorder. """ S = TypeVar("S", bound=enum.IntEnum) def _get_number_from_enum( self, number_or_enum: Union[S, int], enum_class: S ) -> int: if isinstance(number_or_enum, enum.IntEnum): return number_or_enum.value elif isinstance(number_or_enum, int): if number_or_enum in iter(enum_class): return number_or_enum raise ValueError( f"number must be a {enum_class} or one of " f"{','.join([str(x.value) for x in iter(enum_class)])}, " f"but was {number_or_enum}" ) else: raise TypeError(f"channel_number must be a {enum_class} or int") class InputNumber(enum.IntEnum): """ An enumeration representing the input numbers for an Extron SMP recorder. Attributes: Input_1 (int): The input number for Input 1. Input_2 (int): The input number for Input 2. Input_3 (int): The input number for Input 3. Input_4 (int): The input number for Input 4. Input_5 (int): The input number for Input 5. """ INPUT_1 = 1 INPUT_2 = 2 INPUT_3 = 3 INPUT_4 = 4 INPUT_5 = 5 class OutputChannel(enum.IntEnum): """ Enum representing the output channels of an Extron SMP recorder. Attributes: A (int): The first output channel. B (int): The second output channel. """ A = 1 # channel A B = 2 # channel B class AudioChannels(enum.IntEnum): """ Enum representing the available audio channels on the Extron SMP recorder. Attributes: ANALOG_INPUT_A_LEFT (int): The left analog input channel A. ANALOG_INPUT_A_RIGHT (int): The right analog input channel A. DIGITAL_INPUT_A_LEFT (int): The left digital input channel A. DIGITAL_INPUT_A_RIGHT (int): The right digital input channel A. ANALOG_INPUT_B_LEFT (int): The left analog input channel B. ANALOG_INPUT_B_RIGHT (int): The right analog input channel B. DIGITAL_INPUT_B_LEFT (int): The left digital input channel B. DIGITAL_INPUT_B_RIGHT (int): The right digital input channel B. OUTPUT_LEFT (int): The left output channel. OUTPUT_RIGHT (int): The right output channel. """ ANALOG_INPUT_A_LEFT = 40000 ANALOG_INPUT_A_RIGHT = 40001 DIGITAL_INPUT_A_LEFT = 40002 DIGITAL_INPUT_A_RIGHT = 40003 ANALOG_INPUT_B_LEFT = 40004 ANALOG_INPUT_B_RIGHT = 40005 DIGITAL_INPUT_B_LEFT = 40006 DIGITAL_INPUT_B_RIGHT = 40007 OUTPUT_LEFT = 60000 OUTPUT_RIGHT = 60001 class VerboseMode(enum.IntEnum): """ Enum class representing the different verbose modes for Extron SMP devices. Attributes: CLEAR_NONE (int): Default mode for telnet connections, clears non-essential information. VERBOSE (int): Default mode for USB and RS-232 host control, provides verbose information. TAGGED (int): Provides tagged responses for queries. VERBOSE_TAGGED (int): Provides verbose information and tagged responses for queries. """ CLEAR_NONE = 0 VERBOSE = 1 TAGGED = 2 VERBOSE_TAGGED = 3 class FrontPanelLockMode(enum.IntEnum): """ Enumeration of the front panel lock modes for an Extron SMP recorder. Attributes: OFF (int): No front panel lock. COMPLETE_LOCKOUT (int): Complete front panel lockout. MENU_LOCKOUT (int): Lockout of front panel menu controls. RECORDING_CONTROLS_ONLY (int): Lockout of all front panel controls except for recording controls. """ OFF = 0 COMPLETE_LOCKOUT = 1 MENU_LOCKOUT = 2 RECORDING_CONTROLS_ONLY = 3 class ConfigurationType(enum.IntEnum): """ An enumeration of the types of configurations on an Extron SMP device. Attributes: IP_CONFIG (int): The configuration type for IP settings. BOX_SPECIFIC_CONFIG (int): The configuration type for device-specific settings. """ IP_CONFIG = 0 BOX_SPECIFIC_CONFIG = 2 def __init__(self, address, password, auto_login=True, **_kwargs): """ Initializes a new instance of the SMP35x class. Args: address (str): The IP address of the recorder. password (str): The password for the recorder. auto_login (bool): Whether to automatically login to the recorder. Defaults to True. **kwargs: Additional keyword arguments. """ RecorderAdapter.__init__(self, address, "", password) TelnetAdapter.__init__(self, address) if auto_login: self._login() def _login(self): """ Logs in to the recorder. """ logger.debug("Connecting to %s ...", self.address) try: self.tn = telnetlib.Telnet(self.address) except TimeoutError as e: raise LrcException(str(e)) from e except ConnectionRefusedError as e: raise LrcException(str(e)) from e self.tn.read_until("\r\nPassword:") # password = getpass.getpass() password = self.password self.tn.write(password + "\n\r") out = self.tn.assert_string_in_output("Login Administrator") print(out) if not out[0]: print(out[1]) if "Password:" in out[1]: # TODO: loop until logged in... logger.warning( "Could not login (as admin) with given password! %s", self.address ) print("re-enter pw") self.tn.write(self.password + "\n\r") self.tn.assert_string_in_output("Login Administrator") logger.error("WRONG (admin) password!! Exiting! %s", self.password) self.tn = None logger.error( "Could definitely not login (as admin) with given password! %s", self.address, ) raise LrcException("Could not login as administrator with given pw!") logger.info("OK, we have admin rights!") def _get_name(self): """ Gets the name of the recorder. Returns: str: The name of the recorder. """ return RECORDER_MODEL_NAME def _get_version(self): """ Gets the version of the recorder. Returns: str: The version of the recorder. """ return VERSION def get_version(self, include_build=False, verbose_info=False): """ Gets the version of the recorder. Args: include_build (bool): Whether to include the build number. Defaults to False. verbose_info (bool): Whether to include verbose information. Defaults to False. Returns: str: The version of the recorder. """ if verbose_info: self.tn.write("0Q") else: if include_build: self.tn.write("*Q\n") else: self.tn.write("1Q\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_bootstrap_version(self): """ Gets the bootstrap version of the recorder. Returns: str: The bootstrap version of the recorder. """ self.tn.write("2Q") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_factory_firmware_version(self): """ Gets the factory firmware version of the recorder. Returns: str: The factory firmware version of the recorder. """ self.tn.write("3Q") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_updated_firmware_version(self): """ Gets the updated firmware version of the recorder. Returns: str: The updated firmware version of the recorder. """ self.tn.write("4Q") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_part_number(self): """ Gets the part number of the recorder. Returns: str: The part number of the recorder. """ self.tn.write("N") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_model_name(self): """ Gets the model name of the recorder. Returns: str: The model name of the recorder. """ self.tn.write("1I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_model_description(self): """ Gets the model description of the recorder. Returns: str: The model description of the recorder. """ self.tn.write("2I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_system_memory_usage(self): """ Gets the system memory usage of the recorder. Returns: str: The system memory usage of the recorder. """ self.tn.write("3I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_number_of_connected_users(self): """ Gets the number of connected users to the recorder. Returns: str: The number of connected users to the recorder. """ self.tn.write("10I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_system_processer_usage(self): """ Gets the system processor usage of the recorder. Returns: str: The system processor usage of the recorder. """ self.tn.write("11I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_system_processor_idle(self): """ Gets the system processor idle of the recorder. Returns: str: The system processor idle of the recorder. """ self.tn.write("12I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_eth0_link_status(self): """ Gets the eth0 link status of the recorder. Returns: str: The eth0 link status of the recorder. """ self.tn.write("13I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_file_transfer_config(self): """ Gets the file transfer configuration of the recorder. Returns: str: The file transfer configuration of the recorder. """ self.tn.write("38I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_active_alarms(self): """ Gets the active alarms of the recorder. Returns: str: The active alarms of the recorder. """ self.tn.write("39I") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_unit_name(self, name: str): """ Sets the name of the recorder. Args: name (str): The name of the recorder. """ # TODO: check name (must comply with internet host name standards) self.tn.write(self.esc_char + name + "CN\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def reset_unit_name(self): """ Resets the unit name of the Extron SMP recorder. Returns: str: The response from the recorder after resetting the unit name. """ self.tn.write(self.esc_char + " CN\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_unit_name(self): """ Sends a command to the Extron SMP device to retrieve its unit name. Returns: str: The unit name of the Extron SMP device. """ self.tn.write(self.esc_char + "CN\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_telnet_connections(self): """ Sends a command to the Extron SMP device to retrieve a list of active Telnet connections, and returns the response. Returns: str: A string containing the response from the device. """ self.tn.write(self.esc_char + "CC\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_verbose_mode(self, mode: Union[VerboseMode, int]): """ Sets the verbose mode of the Extron SMP recorder. Args: mode (Union[VerboseMode, int]): The verbose mode to set. This can be either a `VerboseMode` enum value or an integer representing the verbose mode. Returns: str: The response from the recorder after setting the verbose mode. """ mode = self._get_number_from_enum(mode, SMP35x.VerboseMode) self.tn.write(self.esc_char + str(mode) + "CV\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_verbose_mode(self): """ Sends the 'CV' command to the Extron SMP device to get the current verbose mode setting. Returns: str: The response from the device, indicating the current verbose mode setting. """ self.tn.write(self.esc_char + "CV\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def save_configuration(self, config_type: Union[ConfigurationType, int] = 2): """ Saves the current configuration of the Extron SMP device to non-volatile memory. Args: config_type (Union[ConfigurationType, int], optional): Type of configuration to save. Defaults to 2. Returns: str: The response from the device after the configuration is saved. """ config_type = self._get_number_from_enum(config_type, SMP35x.ConfigurationType) self.tn.write(self.esc_char + f"1*{config_type}XF\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def restore_configuration(self, config_type: Union[ConfigurationType, int] = 2): """ Restores the configuration of the Extron SMP device to the specified configuration type. Args: config_type (Union[ConfigurationType, int], optional): Configuration type to restore. Defaults to 2. Returns: str: The response from the device. """ config_type = self._get_number_from_enum(config_type, SMP35x.ConfigurationType) self.tn.write(self.esc_char + f"0*{config_type}XF\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def reboot(self): """ Reboots the Extron SMP device by sending the '1BOOT' command over Telnet. Returns: str: The response from the device after sending the '1BOOT' command. """ self.tn.write(self.esc_char + "1BOOT\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def restart_network(self): """ Restarts the Extron SMP's network connection by sending the '2BOOT' command over Telnet. Returns: str: The response from the Extron SMP after sending the '2BOOT' command. """ self.tn.write(self.esc_char + "2BOOT\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def reset_flash(self): """ Reset flash memory (excludes recording files). :return: """ self.tn.write(self.esc_char + "ZFFF\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def system_reset(self): """ Resets device to default and deletes recorded files :return: """ self.tn.write(self.esc_char + "ZXXX\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def reset_settings_and_delete_all_files(self): """ Reset to default except IP address, delete all user and recorded files :return: """ self.tn.write(self.esc_char + "ZY\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def absolute_reset(self): """ Same as System Reset, plus returns the IP address and subnet mask to defaults. :return: """ self.tn.write(self.esc_char + "ZQQQ\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_front_panel_lock(self, mode: Union["SMP35x.FrontPanelLockMode", int]): """ Sets the front panel lock mode of the Extron SMP device. Args: mode (Union["SMP35x.FrontPanelLockMode", int]): The front panel lock mode to set. Returns: str: The response from the device after setting the front panel lock mode. """ mode = self._get_number_from_enum(mode, SMP35x.FrontPanelLockMode) self.tn.write(str(mode) + "X\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_front_panel_lock(self): """ View executive mode. 0=Off 1=complete lockout (no front panel control) 2=menu lockout 3=recording controls :return: """ self.tn.write("X\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) ### A lot of stuff related to network settings (ports of services, SNMP, IP, DHCP, etc.) ### Only some stuff will be implemented here! def get_date_time(self): """ Gets the current date and time from the Extron SMP recorder. Returns: A datetime object representing the current date and time. """ raise NotImplementedError("get_date_time not implemented yet!") def get_time_zone(self): """ Gets the time zone of the Extron SMP recorder. Returns: str: The time zone of the recorder. """ raise NotImplementedError("get_time_zone not implemented yet!") def get_dhcp_mode(self): """ Gets the current DHCP mode of the Extron SMP recorder. Returns: str: The current DHCP mode of the recorder. Possible values are "on", "off", or "auto". """ raise NotImplementedError("get_dhcp_mode not implemented yet!") def get_network_settings(self): """ Retrieves the current network settings for the Extron SMP recorder. Returns: A dictionary containing the current network settings for the recorder. """ raise NotImplementedError("get_network_settings not implemented yet!") def get_ip_address(self): """ Returns the IP address of the Extron SMP recorder. """ raise NotImplementedError("get_ip_address not implemented yet!") def get_mac_address(self): """ Gets the MAC address of the Extron SMP recorder. :return: A string representing the MAC address of the recorder. """ raise NotImplementedError("get_mac_address not implemented yet!") def get_subnet_mask(self): """ Gets the subnet mask for the Extron SMP recorder. :return: The subnet mask as a string. """ raise NotImplementedError("get_subnet_mask not implemented yet!") def get_gateway_ip(self): """ Gets the IP address of the gateway device for the Extron SMP recorder. This method is not yet implemented. """ raise NotImplementedError("get_gateway_ip not implemented yet!") def get_dns_server_ip(self): """ Returns the IP address of the DNS server configured on the Extron SMP device. Raises: NotImplementedError: This method has not been implemented yet. """ raise NotImplementedError("get_dns_server_ip not implemented yet!") ### RS-232 / serial port related stuff not implemented. ### Password and security related stuff not implemented. ### File related stuff not implemented. (-> use sftp) def set_input(self, input_num: Union[InputNumber, int], channel_num: Union[OutputChannel, int]): """ Switches input # (1 to 5) to output channel (1=A [input 1 and 2], 2=B [input 3, 4 and 5]) :param input_num: :param channel_num: :return: """ input_num = self._get_number_from_enum(input_num, SMP35x.InputNumber) channel_num = self._get_number_from_enum(channel_num, SMP35x.OutputChannel) self.tn.write(f"{input_num}*{channel_num}!\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_input(self, channel_num: Union[OutputChannel, int]) -> str: """ Sends a command to the Extron SMP device to get the current input for the specified channel. Args: channel_num (Union[OutputChannel, int]): The channel number to get the input for. This can be either an integer value or an OutputChannel enum value. Returns: str: The current input for the specified channel, as a string. Raises: TelnetError: If there was an error communicating with the Extron SMP device. """ channel_num = self._get_number_from_enum(channel_num, SMP35x.OutputChannel) self.tn.write(f"{channel_num}!\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_input_format(self, input_num: int, input_format: int): """ Sets the input to the format, where the input_format parameter may be: 1 = YUVp / HDTV (default) 2 = YUVi 3 = Composite :param input_num: :param input_format: :return: """ if input_num not in range(1, 6): raise LrcException("input_num must be a value between 1 and 5!") if input_format not in range(1, 4): raise LrcException("input_num must be a value between 1 and 3!") self.tn.write(f"{input_num}*{input_format}\\\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_input_format(self, input_num: int): if input_num not in range(1, 6): raise LrcException("input_num must be a value between 1 and 5!") self.tn.write(f"{input_num}\\\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_input_name(self, input_num: int, input_name: str): if input_num not in range(1, 6): raise LrcException("input_num must be a value between 1 and 5!") if len(input_name) > 16: raise LrcException("input_name must be no longer than 16 chars") try: input_name.encode("ascii") except UnicodeEncodeError as exc: raise LrcException("input_name must only contain ascii characters") from exc self.tn.write(f"{self.esc_char}{input_num},{input_name}NI\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_input_name(self, input_num: int): if input_num not in range(1, 6): raise LrcException("input_num must be a value between 1 and 5!") self.tn.write(f"{self.esc_char}{input_num}NI\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_input_selction_per_channel(self): self.tn.write("32I\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) ### Input configuration part skipped def stop_recording(self): """ Sends a command to stop recording on the Extron SMP device and returns the response. Returns: str: The response from the device. """ self.tn.write(f"{self.esc_char}Y0RCDR\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def start_recording(self): """ Sends a command to start recording on the Extron SMP device and returns the response. Returns: A string containing the response from the device. """ self.tn.write(f"{self.esc_char}Y1RCDR\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def pause_recording(self): """ Sends a command to pause recording on the Extron SMP recorder. Returns: str: The response from the recorder as a string. """ self.tn.write(f"{self.esc_char}Y2RCDR\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) @exception_decorator(ConnectionError) def get_recording_status(self): """ Status may be one of: 0=stop 1=record 2=pause :return: status """ self.tn.write(f"{self.esc_char}YRCDR\n") return int(TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line())) def is_recording(self) -> bool: """ Returns True if the Extron SMP is currently recording, False otherwise. """ return self.get_recording_status() == 1 def extent_recording_time(self, extension_time: int): """ Extends a scheduled recording by extension_time minutes :param extension_time: must be an int from 0 to 99 :return: """ if extension_time not in range(0, 100): raise LrcException("extension_time must be a value between 0 and 99!") self.tn.write(f"{self.esc_char}E{extension_time}RCDR\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def add_chapter_marker(self): """ Sends a command to the Extron SMP recorder to add a chapter marker to the current recording. Returns: str: A response string from the recorder indicating success or failure. """ self.tn.write(f"{self.esc_char}BRCDR\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def swap_channel_positions(self): """ Sends a command to the Extron SMP device to swap the positions of the current input and output channels. Returns: str: The response from the device after sending the command. """ self.tn.write("%\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_recording_status_text(self): """ Sends the 'I' command to the Extron SMP device to retrieve the current recording status text. Returns: A string representing the current recording status text. """ self.tn.write("I\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_elapsed_recording_time(self): """ Sends a command to the Extron SMP recorder to retrieve the elapsed recording time. Returns: A string representing the elapsed recording time in the format "HH:MM:SS". """ self.tn.write("35I\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_remaining_recording_time(self): """ Sends a command to the Extron SMP device to retrieve the remaining recording time. Returns: str: The remaining recording time in the format "HH:MM:SS". """ self.tn.write("36I\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_recording_destination(self): """ Sends a command to the Extron SMP device to retrieve the current recording destination. Returns: str: The recording destination as a string. """ self.tn.write("37I\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) ### Metadata part skipped def recall_user_preset(self, channel_number: int, preset_number: int): if channel_number not in range(1, 3): raise LrcException("channel_number must be a value between 1 and 2!") if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"1*{channel_number}*{preset_number}.\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def save_user_preset(self, channel_number: int, preset_number: int): if channel_number not in range(1, 3): raise LrcException("channel_number must be a value between 1 and 2!") if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"1*{channel_number}*{preset_number},\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_user_preset_name(self, preset_number: int, preset_name: str): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") if len(preset_name) > 16: raise LrcException("preset_name must be no longer than 16 chars") try: preset_name.encode("ascii") except UnicodeEncodeError as exc: raise LrcException( "preset_name must only contain ascii characters" ) from exc self.tn.write(f"{self.esc_char}1*{preset_number},{preset_name}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_user_preset_name(self, preset_number: int): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"{self.esc_char}1*{preset_number}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_user_presets(self, input_number: int): if input_number not in range(1, 6): raise LrcException("input_number must be a value between 1 and 5!") self.tn.write(f"52*{input_number}#\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) # Input Presets def recall_input_preset(self, channel_number: int, preset_number: int): if channel_number not in range(1, 3): raise LrcException("channel_number must be a value between 1 and 2!") if preset_number not in range(1, 129): raise LrcException("preset_number must be a value between 1 and 128!") self.tn.write(f"2*{channel_number}*{preset_number}.\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def save_input_preset(self, channel_number: int, preset_number: int): if channel_number not in range(1, 3): raise LrcException("channel_number must be a value between 1 and 2!") if preset_number not in range(1, 129): raise LrcException("preset_number must be a value between 1 and 128!") self.tn.write(f"1*{channel_number}*{preset_number},\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_input_preset_name(self, preset_number: int, preset_name: str): if preset_number not in range(1, 129): raise LrcException("preset_number must be a value between 1 and 128!") if len(preset_name) > 16: raise LrcException("preset_name must be no longer than 16 chars") try: preset_name.encode("ascii") except UnicodeEncodeError as exc: raise LrcException( "preset_name must only contain ascii characters" ) from exc self.tn.write(f"{self.esc_char}2*{preset_number},{preset_name}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_input_preset_name(self, preset_number: int): if preset_number not in range(1, 129): raise LrcException("preset_number must be a value between 1 and 128!") self.tn.write(f"{self.esc_char}2*{preset_number}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def delete_input_preset(self, preset_number: int): if preset_number not in range(1, 129): raise LrcException("preset_number must be a value between 1 and 128!") self.tn.write(f"{self.esc_char}X2*{preset_number}PRST\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_input_presets(self): """ Sends a command to the Extron SMP device to retrieve the available input presets. Returns the response from the device as a string. """ self.tn.write("51#\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) # Streaming Presets def recall_streaming_preset(self, output_number: int, preset_number: int): """ Output_number: 1 = Channel A 2 = Channel B 3 = Confidence Stream :param preset_number: :param output_number: :return: """ if output_number not in range(1, 4): raise LrcException("output_number must be a value between 1 and 3!") if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"3*{output_number}*{preset_number}.\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def save_streaming_preset(self, output_number: int, preset_number: int): """ Output_number: 1 = Channel A 2 = Channel B 3 = Confidence Stream :param output_number: :param preset_number: :return: """ if output_number not in range(1, 4): raise LrcException("output_number must be a value between 1 and 3!") if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"3*{output_number}*{preset_number},\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_streaming_preset_name(self, preset_number: int, preset_name: str): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") if len(preset_name) > 16: raise LrcException("preset_name must be no longer than 16 chars") try: preset_name.encode("ascii") except UnicodeEncodeError as exc: raise LrcException( "preset_name must only contain ascii characters" ) from exc self.tn.write(f"{self.esc_char}3*{preset_number},{preset_name}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_streaming_preset_name(self, preset_number: int): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"{self.esc_char}3*{preset_number}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def reset_streaming_preset_to_default(self, preset_number: int): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"{self.esc_char}X3*{preset_number}PRST\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) # Encoder Presets def recall_encoder_preset(self, output_number: int, preset_number: int): """ Output_number: 1 = Channel A 2 = Channel B 3 = Confidence Stream :param preset_number: :param output_number: :return: """ if output_number not in range(1, 4): raise LrcException("output_number must be a value between 1 and 3!") if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"4*{output_number}*{preset_number}.\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def save_encoder_preset(self, output_number: int, preset_number: int): """ Output_number: 1 = Channel A 2 = Channel B 3 = Confidence Stream :param preset_number: :param output_number: :return: """ if output_number not in range(1, 4): raise LrcException("output_number must be a value between 1 and 3!") if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"4*{output_number}*{preset_number},\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_encoder_preset_name(self, preset_number: int, preset_name: str): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") if len(preset_name) > 16: raise LrcException("preset_name must be no longer than 16 chars") try: preset_name.encode("ascii") except UnicodeEncodeError as exc: raise LrcException( "preset_name must only contain ascii characters" ) from exc self.tn.write(f"{self.esc_char}4*{preset_number},{preset_name}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_encoder_preset_name(self, preset_number: int): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"{self.esc_char}4*{preset_number}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def reset_encoder_preset_to_default(self, preset_number: int): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"{self.esc_char}X4*{preset_number}PRST\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) # Layout Presets def save_layout_preset(self, preset_number: int): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"7*{preset_number},\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def recall_layout_preset( self, preset_number: int, include_input_selections: bool = True ): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") if include_input_selections: self.tn.write(f"7*{preset_number}.\n") else: self.tn.write(f"8*{preset_number}.\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_layout_preset_name(self, preset_number: int, preset_name: str): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") if len(preset_name) > 16: raise LrcException("preset_name must be no longer than 16 chars") try: preset_name.encode("ascii") except UnicodeEncodeError as exc: raise LrcException( "preset_name must only contain ascii characters" ) from exc self.tn.write( "{}7*{},{}PNAM\n".format(self.esc_char, preset_number, preset_name) ) return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_layout_preset_name(self, preset_number: int): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"{self.esc_char}7*{preset_number}PNAM\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def reset_layout_preset_to_default(self, preset_number: int): if preset_number not in range(1, 17): raise LrcException("preset_number must be a value between 1 and 16!") self.tn.write(f"{self.esc_char}X7*{preset_number}PRST\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) ### Input adjustments skipped ### Picture adjustments skipped def mute_output(self, output_number: int): """ Output_number: 1 = Channel A 2 = Channel B :param output_number: :return: """ if output_number not in range(1, 3): raise LrcException("output_number must be a value between 1 and 2!") self.tn.write(f"{output_number}*1B\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def unmute_output(self, output_number: int): """ Output_number: 1 = Channel A 2 = Channel B :param output_number: :return: """ if output_number not in range(1, 3): raise LrcException("output_number must be a value between 1 and 2!") self.tn.write(f"{output_number}*0B\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def is_muted(self, output_number: int): """ Output_number: 1 = Channel A 2 = Channel B :param output_number: :return: """ if output_number not in range(1, 3): raise LrcException("output_number must be a value between 1 and 2!") self.tn.write(f"{output_number}B\n") return ( int(TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line())) > 0 ) ### EDID skipped ### Encoder settings skipped ### some advanced options skipped @classmethod def get_recorder_params(cls) -> dict: return {"_requires_user": False, "_requires_password": True} def get_input_hdcp_status(self, input_number: int): """ returns: 0 = no sink / source detected 1 = sink / source detected with HDCP 2 = sink / source detected without HDCP :param input_number: from 1 to 5 :return: """ if input_number not in range(1, 6): raise LrcException("input_number must be a value between 1 and 6!") self.tn.write(f"{self.esc_char}I{input_number}HDCP\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_input_authorization_hdcp_on(self, input_number: int): if input_number not in range(1, 6): raise LrcException("input_number must be a value between 1 and 6!") self.tn.write(f"{self.esc_char}E{input_number}*1HDCP\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def set_input_authorization_hdcp_off(self, input_number: int): if input_number not in range(1, 6): raise LrcException("input_number must be a value between 1 and 6!") self.tn.write(f"{self.esc_char}E{input_number}*0HDCP\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_input_authorization_hdcp_status(self, input_number: int): if input_number not in range(1, 6): raise LrcException("input_number must be a value between 1 and 6!") self.tn.write(f"{self.esc_char}E{input_number}HDCP\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def enable_hdcp_notification(self): self.tn.write(f"{self.esc_char}N1HDCP\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def disable_hdcp_notification(self): self.tn.write(f"{self.esc_char}N0HDCP\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_hdcp_notification_status(self): self.tn.write(f"{self.esc_char}NHDCP\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) # background image settings def set_background_image(self, filename: str): self.tn.write(f"{self.esc_char}{filename}RF\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def get_background_image_filename(self): self.tn.write(f"{self.esc_char}RF\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def mute_background_image(self): self.tn.write(f"{self.esc_char}0RF\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) # Audio settings def mute_audio_channel(self, channel_number: Union["SMP35x.AudioChannels", int]): num = self._get_number_from_enum(channel_number, SMP35x.AudioChannels) self.tn.write(f"{self.esc_char}M{num}*1AU\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def unmute_audio_channel(self, channel_number: Union["SMP35x.AudioChannels", int]): num = self._get_number_from_enum(channel_number, SMP35x.AudioChannels) self.tn.write(f"{self.esc_char}M{num}*0AU\n") return TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line()) def is_audio_channel_muted( self, channel_number: Union["SMP35x.AudioChannels", int] ): num = self._get_number_from_enum(channel_number, SMP35x.AudioChannels) self.tn.write(f"{self.esc_char}M{num}AU\n") return ( int(TelnetAdapter._get_response_str(self.tn.read_until_non_empty_line())) > 0 ) def main(): smp = SMP35x(HOST, PW, True) print(smp) print(smp.get_recording_status()) print(smp.is_recording()) print(smp.is_muted(1)) print(smp.is_muted(2)) print(smp.is_audio_channel_muted(SMP35x.AudioChannels.ANALOG_INPUT_A_LEFT)) print(smp.is_audio_channel_muted(SMP35x.AudioChannels.DIGITAL_INPUT_A_LEFT)) print(smp.is_audio_channel_muted(60000)) exit() smp._login() print(smp.get_version(verbose_info=False)) print(smp.get_file_transfer_config()) print(smp.save_configuration()) print(smp.restore_configuration()) return print(smp.get_bootstrap_version()) print(smp.get_part_number()) print(smp.get_model_name()) print(smp.get_model_description()) print(smp.get_system_memory_usage()) print(smp.get_file_transfer_config()) # print(smp.get_unit_name()) # print(smp.set_unit_name("mzsmp")) # print(smp.get_unit_name()) # print(smp.reset_unit_name()) print(smp.set_front_panel_lock(0)) print(smp.get_front_panel_lock()) print(smp.get_input_name(1)) print(smp.get_input_selction_per_channel()) print(smp.get_recording_status()) print("Preset Name: " + smp.get_user_preset_name(2)) print(smp.get_user_presets(1)) print(smp.get_input_presets()) print(smp.get_layout_preset_name(2)) print(smp.get_encoder_preset_name(1)) print(smp.get_streaming_preset_name(2)) print(smp.recall_encoder_preset(3, 1)) print(smp.is_muted(2)) print(smp.mute_output(2)) print(smp.is_muted(2)) print(smp.unmute_output(2)) print(smp.is_muted(2)) if __name__ == "__main__": main()