././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1736112705.0172837
python-samsung-mdc-1.15.0/ 0000755 0001751 0000200 00000000000 14736575101 014734 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/LICENSE 0000644 0001751 0000200 00000002733 14736575073 015756 0 ustar 00runner docker BSD 3-Clause License
Copyright (c) 2021, Victor Gavro
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1736112705.0172837
python-samsung-mdc-1.15.0/PKG-INFO 0000644 0001751 0000200 00000116734 14736575101 016045 0 ustar 00runner docker Metadata-Version: 2.1
Name: python-samsung-mdc
Version: 1.15.0
Summary: Samsung Multiple Display Control (MDC) protocol implementation (asyncio library + CLI interface)
Home-page: http://github.com/vgavro/samsung-mdc
Author: Victor Gavro
Author-email: vgavro@gmail.com
License: BSD-3-Clause
Keywords: samsung,mdc
Classifier: License :: OSI Approved :: BSD License
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Telecommunications Industry
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Development Status :: 5 - Production/Stable
Classifier: Operating System :: OS Independent
Classifier: Topic :: Multimedia :: Video :: Display
Classifier: Topic :: Home Automation
Classifier: Topic :: Utilities
Requires-Python: >=3.7,<4.0
Description-Content-Type: text/markdown
Provides-Extra: test
Provides-Extra: serial
Provides-Extra: all
License-File: LICENSE
# Samsung-MDC
This is implementation of Samsung MDC (Multiple Display Control) protocol on **python3.7+** and **asyncio** with most comprehensive CLI (command line interface).
It allows you to control a variety of different sources (TV, Monitor) through the built-in RS-232C or Ethernet interface.
[MDC Protocol specification - v15.0 2020-11-06](https://vgavro.github.io/samsung-mdc/MDC-Protocol.pdf)
* Implemented *83* commands
* Easy to extend using simple declarative API - see [samsung_mdc/commands.py](https://github.com/vgavro/samsung-mdc/blob/master/samsung_mdc/commands.py)
* Detailed [CLI](#usage) help and parameters validation
* Run commands async on numerous targets (using asyncio)
* TCP and SERIAL mode (for RJ45 and RS232C connection types)
* TCP over TLS mode ("Secured Protocol" using PIN)
* [script](#script) command for advanced usage
* [Python example](#python-example)
Not implemented: some more commands (PRs are welcome)
Also see: [Samsung MDC Unified](http://www.samsung-mcloud.com/01_Software/04_Tools/MDC/v1235/) - Reference Application (GUI, Windows) with partially implemented functionality.
## Install
```
# using pipx https://pypa.github.io/pipx/
pipx run python-samsung-mdc --help
# OR global install/upgrade
sudo pip3 install --upgrade python-samsung-mdc
samsung-mdc --help
# OR local
git clone https://github.com/vgavro/samsung-mdc
cd ./samsung-mdc
python3 -m venv venv
./venv/bin/pip3 install -e ./
./venv/bin/samsung-mdc --help
```
### Windows install
1. Install Git && Git Bash: https://git-scm.com/download/win
2. Install Python 3 latest release (tested with 3.9): https://www.python.org/downloads/windows/
3. Run "Git Bash", type in console:
```
pip3 install --upgrade python-samsung-mdc
# NOTE: python "Scripts" folder is not in %PATH% in Windows by default,
# so you may want to create alias for Git Bash
echo alias samsung-mdc=\'python3 -m samsung_mdc\' >> ~/.bash_profile
source ~/.bash_profile
# test it
samsung-mdc --help
```
## Usage
```
Usage: samsung-mdc [OPTIONS] TARGET COMMAND [ARGS]...
Try 'samsung-mdc --help COMMAND' for command info
For multiple targets commands will be running async, so result order may
differ.
TARGET may be:
DISPLAY_ID@IP[:PORT] (default port: 1515, example: 0@192.168.0.10:1515)
FILENAME with target list (separated by newline)
For serial port connection:
DISPLAY_ID@PORT_NAME for Windows (example: 1@COM1)
DISPLAY_ID@PORT_PATH (example: 1@/dev/ttyUSB0)
We're trying to make autodetection of connection mode by port name, but you
may want to use --mode option.
Options:
--version Show the version and exit.
-v, --verbose
-m, --mode [auto|tcp|serial] default: auto
-p, --pin INTEGER 4-digit PIN for secured TLS connection. If PIN
provided, "Secured Protocol" must be enabled
on remote device.
-t, --timeout FLOAT read/write/connect timeout in seconds
(default: 5) (connect can be overridden with
separate option)
--connect-timeout FLOAT
-h, --help Show this message and exit.
```
### Commands:
* [status](#status) `(POWER_STATE VOLUME MUTE_STATE INPUT_SOURCE_STATE PICTURE_ASPECT_STATE N_TIME_NF F_TIME_NF)`
* [video](#video) `(CONTRAST BRIGHTNESS SHARPNESS COLOR TINT COLOR_TONE_STATE COLOR_TEMPERATURE _IGNORE)`
* [rgb](#rgb) `(CONTRAST BRIGHTNESS COLOR_TONE_STATE COLOR_TEMPERATURE _IGNORE RED_GAIN GREEN_GAIN BLUE_GAIN)`
* [serial_number](#serial_number) `(SERIAL_NUMBER)`
* [error_status](#error_status) `(LAMP_ERROR_STATE TEMPERATURE_ERROR_STATE BRIGHTNESS_SENSOR_ERROR_STATE INPUT_SOURCE_ERROR_STATE TEMPERATURE FAN_ERROR_STATE)`
* [software_version](#software_version) `(SOFTWARE_VERSION)`
* [model_number](#model_number) `(MODEL_SPECIES MODEL_CODE TV_SUPPORT)`
* [power](#power) `[POWER_STATE]`
* [volume](#volume) `[VOLUME]`
* [mute](#mute) `[MUTE_STATE]`
* [input_source](#input_source) `[INPUT_SOURCE_STATE]`
* [picture_aspect](#picture_aspect) `[PICTURE_ASPECT_STATE]`
* [screen_mode](#screen_mode) `[SCREEN_MODE_STATE]`
* [screen_size](#screen_size) `(INCHES)`
* [network_configuration](#network_configuration) `[IP_ADDRESS SUBNET_MASK GATEWAY_ADDRESS DNS_SERVER_ADDRESS]`
* [network_mode](#network_mode) `[NETWORK_MODE_STATE]`
* [network_ap_config](#network_ap_config) `SSID PASSWORD`
* [weekly_restart](#weekly_restart) `[WEEKDAY TIME]`
* [magicinfo_channel](#magicinfo_channel) `CHANNEL_NUMBER`
* [magicinfo_server](#magicinfo_server) `[MAGICINFO_SERVER_URL]`
* [magicinfo_content_orientation](#magicinfo_content_orientation) `[ORIENTATION_MODE_STATE]`
* [mdc_connection](#mdc_connection) `[MDC_CONNECTION_TYPE]`
* [contrast](#contrast) `[CONTRAST]`
* [brightness](#brightness) `[BRIGHTNESS]`
* [sharpness](#sharpness) `[SHARPNESS]`
* [color](#color) `[COLOR]`
* [tint](#tint) `[TINT]`
* [h_position](#h_position) `H_POSITION_MOVE_TO`
* [v_position](#v_position) `V_POSITION_MOVE_TO`
* [auto_power](#auto_power) `[AUTO_POWER_STATE]`
* [clear_menu](#clear_menu)
* [ir_state](#ir_state) `[IR_STATE]`
* [rgb_contrast](#rgb_contrast) `[CONTRAST]`
* [rgb_brightness](#rgb_brightness) `[BRIGHTNESS]`
* [auto_adjustment_on](#auto_adjustment_on)
* [color_tone](#color_tone) `[COLOR_TONE_STATE]`
* [color_temperature](#color_temperature) `[HECTO_KELVIN]`
* [standby](#standby) `[STANDBY_STATE]`
* [auto_lamp](#auto_lamp) `[MAX_TIME MAX_LAMP_VALUE MIN_TIME MIN_LAMP_VALUE]`
* [manual_lamp](#manual_lamp) `[LAMP_VALUE]`
* [inverse](#inverse) `[INVERSE_STATE]`
* [video_wall_mode](#video_wall_mode) `[VIDEO_WALL_MODE]`
* [safety_lock](#safety_lock) `[LOCK_STATE]`
* [panel_lock](#panel_lock) `[LOCK_STATE]`
* [channel_change](#channel_change) `CHANGE_TO`
* [volume_change](#volume_change) `CHANGE_TO`
* [ticker](#ticker) `[ON_OFF START_TIME END_TIME POS_HORIZ POS_VERTI MOTION_ON_OFF MOTION_DIR MOTION_SPEED FONT_SIZE FOREGROUND_COLOR BACKGROUND_COLOR FOREGROUND_OPACITY BACKGROUND_OPACITY MESSAGE]`
* [device_name](#device_name) `(DEVICE_NAME)`
* [osd](#osd) `[OSD_ENABLED]`
* [picture_mode](#picture_mode) `[PICTURE_MODE_STATE]`
* [sound_mode](#sound_mode) `[SOUND_MODE_STATE]`
* [all_keys_lock](#all_keys_lock) `[LOCK_STATE]`
* [panel_on_time](#panel_on_time) `(MIN10)`
* [video_wall_state](#video_wall_state) `[VIDEO_WALL_STATE]`
* [video_wall_model](#video_wall_model) `[MODEL SERIAL]`
* [model_name](#model_name) `(MODEL_NAME)`
* [energy_saving](#energy_saving) `[ENERGY_SAVING_STATE]`
* [reset](#reset) `RESET_TARGET`
* [osd_type](#osd_type) `[OSD_TYPE OSD_ENABLED]`
* [timer_13](#timer_13) `TIMER_ID [ON_TIME ON_ENABLED OFF_TIME OFF_ENABLED REPEAT MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]`
* [timer_15](#timer_15) `TIMER_ID [ON_TIME ON_ENABLED OFF_TIME OFF_ENABLED ON_REPEAT ON_MANUAL_WEEKDAY OFF_REPEAT OFF_MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]`
* [clock_m](#clock_m) `[DATETIME]`
* [holiday_set](#holiday_set) `HOLIDAY_MANAGE START_MONTH START_DAY END_MONTH END_DAY`
* [holiday_get](#holiday_get) `[INDEX]`
* [virtual_remote](#virtual_remote) `KEY_CODE`
* [network_standby](#network_standby) `[NETWORK_STANDBY_STATE]`
* [dst](#dst) `[DST_STATE START_MONTH START_WEEK START_WEEKDAY START_TIME END_MONTH END_WEEK END_WEEKDAY END_TIME OFFSET]`
* [auto_id_setting](#auto_id_setting) `[AUTO_ID_SETTING_STATE]`
* [display_id](#display_id) `DISPLAY_ID_STATE`
* [clock_s](#clock_s) `[DATETIME]`
* [launcher_play_via](#launcher_play_via) `[PLAY_VIA_MODE]`
* [launcher_url_address](#launcher_url_address) `[URL_ADDRESS]`
* [osd_menu_orientation](#osd_menu_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_source_content_orientation](#osd_source_content_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_aspect_ratio](#osd_aspect_ratio) `[ASPECT_RATIO_STATE]`
* [osd_pip_orientation](#osd_pip_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_menu_size](#osd_menu_size) `[MENU_SIZE_STATE]`
* [auto_source_switch](#auto_source_switch) `[AUTO_SOURCE_SWITCH_STATE]`
* [auto_source](#auto_source) `[PRIMARY_SOURCE_RECOVERY PRIMARY_SOURCE SECONDARY_SOURCE]`
* [panel](#panel) `[PANEL_STATE]`
* [screen_mute](#screen_mute) `[SCREEN_MUTE_STATUS]`
* [script](#script) `[OPTIONS] SCRIPT_FILE`
* [raw](#raw) `[OPTIONS] COMMAND [DATA]`
#### status
```
Usage: samsung-mdc [OPTIONS] TARGET status
Get the device various state like power, volume, sound mute, input source,
picture aspect ratio.
Note: For no audio models volume and mute returns 0xFF (255).
N_TIME_NF, F_TIME_NF: OnTime/OffTime ON/OFF value (old type timer, now it's
always 0x00).
Data:
POWER_STATE OFF | ON | REBOOT
VOLUME int (0-100)
MUTE_STATE OFF | ON | NONE
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
PICTURE_ASPECT_STATE PC_16_9 | PC_4_3 | PC_ORIGINAL_RATIO | PC_21_9 |
PC_CUSTOM | VIDEO_AUTO_WIDE | VIDEO_16_9 | VIDEO_ZOOM
| VIDEO_ZOOM_1 | VIDEO_ZOOM_2 | VIDEO_SCREEN_FIT |
VIDEO_4_3 | VIDEO_WIDE_FIT | VIDEO_CUSTOM |
VIDEO_SMART_VIEW_1 | VIDEO_SMART_VIEW_2 |
VIDEO_WIDE_ZOOM | VIDEO_21_9
N_TIME_NF int
F_TIME_NF int
```
#### video
```
Usage: samsung-mdc [OPTIONS] TARGET video
Data:
CONTRAST int (0-100)
BRIGHTNESS int (0-100)
SHARPNESS int (0-100)
COLOR int (0-100)
TINT int (0-100)
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
COLOR_TEMPERATURE int
_IGNORE int (0-0)
```
#### rgb
```
Usage: samsung-mdc [OPTIONS] TARGET rgb
Data:
CONTRAST int (0-100)
BRIGHTNESS int (0-100)
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
COLOR_TEMPERATURE int
_IGNORE int (0-0)
RED_GAIN int
GREEN_GAIN int
BLUE_GAIN int
```
#### serial_number
```
Usage: samsung-mdc [OPTIONS] TARGET serial_number
Data:
SERIAL_NUMBER str
```
#### error_status
```
Usage: samsung-mdc [OPTIONS] TARGET error_status
Data:
LAMP_ERROR_STATE NORMAL | ERROR
TEMPERATURE_ERROR_STATE NORMAL | ERROR
BRIGHTNESS_SENSOR_ERROR_STATE NONE | ERROR | NORMAL
INPUT_SOURCE_ERROR_STATE NORMAL | ERROR | INVALID
TEMPERATURE int
FAN_ERROR_STATE NORMAL | ERROR | NONE
```
#### software_version
```
Usage: samsung-mdc [OPTIONS] TARGET software_version
Data:
SOFTWARE_VERSION str
```
#### model_number
```
Usage: samsung-mdc [OPTIONS] TARGET model_number
Data:
MODEL_SPECIES PDP | LCD | DLP | LED | CRT | OLED
MODEL_CODE int
TV_SUPPORT SUPPORTED | NOT_SUPPORTED
```
#### power
```
Usage: samsung-mdc [OPTIONS] TARGET power [POWER_STATE]
Data:
POWER_STATE OFF | ON | REBOOT
```
#### volume
```
Usage: samsung-mdc [OPTIONS] TARGET volume [VOLUME]
Data:
VOLUME int (0-100)
```
#### mute
```
Usage: samsung-mdc [OPTIONS] TARGET mute [MUTE_STATE]
Data:
MUTE_STATE OFF | ON | NONE
```
#### input_source
```
Usage: samsung-mdc [OPTIONS] TARGET input_source [INPUT_SOURCE_STATE]
Get/Set the device source which is shown on the screen.
DVI_VIDEO, HDMI1_PC, HDMI2_PC, HDMI3_PC, HDMI4_PC: get only.
URL_LAUNCHER, MAGIC_INFO, TV or some ports require support by model.
On TIMER functions, Do not use WIDI_SCREEN_MIRRORING.
Data:
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
```
#### picture_aspect
```
Usage: samsung-mdc [OPTIONS] TARGET picture_aspect [PICTURE_ASPECT_STATE]
Get/Set the device picture size (aspect ratio).
Working Condition: Will not work with VIDEO_WALL_STATE is ON.
Note: Some of the image sizes are not supported depending on input signals.
Data:
PICTURE_ASPECT_STATE PC_16_9 | PC_4_3 | PC_ORIGINAL_RATIO | PC_21_9 |
PC_CUSTOM | VIDEO_AUTO_WIDE | VIDEO_16_9 | VIDEO_ZOOM
| VIDEO_ZOOM_1 | VIDEO_ZOOM_2 | VIDEO_SCREEN_FIT |
VIDEO_4_3 | VIDEO_WIDE_FIT | VIDEO_CUSTOM |
VIDEO_SMART_VIEW_1 | VIDEO_SMART_VIEW_2 |
VIDEO_WIDE_ZOOM | VIDEO_21_9
```
#### screen_mode
```
Usage: samsung-mdc [OPTIONS] TARGET screen_mode [SCREEN_MODE_STATE]
Data:
SCREEN_MODE_STATE MODE_16_9 | MODE_ZOOM | MODE_4_3 | MODE_WIDE_ZOOM
```
#### screen_size
```
Usage: samsung-mdc [OPTIONS] TARGET screen_size
Data:
INCHES int (0-255)
```
#### network_configuration
```
Usage: samsung-mdc [OPTIONS] TARGET network_configuration [IP_ADDRESS
SUBNET_MASK GATEWAY_ADDRESS DNS_SERVER_ADDRESS]
Data:
IP_ADDRESS IP address
SUBNET_MASK IP address
GATEWAY_ADDRESS IP address
DNS_SERVER_ADDRESS IP address
```
#### network_mode
```
Usage: samsung-mdc [OPTIONS] TARGET network_mode [NETWORK_MODE_STATE]
Data:
NETWORK_MODE_STATE DYNAMIC | STATIC
```
#### network_ap_config
```
Usage: samsung-mdc [OPTIONS] TARGET network_ap_config SSID PASSWORD
Add new SSID info to device connection history with its password.
Note: device may change network and response may not be received.
Data:
SSID str
PASSWORD str
```
#### weekly_restart
```
Usage: samsung-mdc [OPTIONS] TARGET weekly_restart [WEEKDAY TIME]
Data:
WEEKDAY list(,) SUN | SAT | FRI | THU | WED | TUE | MON
TIME time (format: %H:%M)
```
#### magicinfo_channel
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_channel CHANNEL_NUMBER
Set MagicInfo Channel by Direct Channel Number which is used by MagicInfo S
Player.
Data:
CHANNEL_NUMBER int
```
#### magicinfo_server
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_server [MAGICINFO_SERVER_URL]
MagicInfo Server URL.
Example: "http://example.com:80"
Data:
MAGICINFO_SERVER_URL str
```
#### magicinfo_content_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_content_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### mdc_connection
```
Usage: samsung-mdc [OPTIONS] TARGET mdc_connection [MDC_CONNECTION_TYPE]
Note: Depends on the product specification, if it is set as RJ45 then serial
MDC will not work.
Data:
MDC_CONNECTION_TYPE RS232C | RJ45
```
#### contrast
```
Usage: samsung-mdc [OPTIONS] TARGET contrast [CONTRAST]
Data:
CONTRAST int (0-100)
```
#### brightness
```
Usage: samsung-mdc [OPTIONS] TARGET brightness [BRIGHTNESS]
Data:
BRIGHTNESS int (0-100)
```
#### sharpness
```
Usage: samsung-mdc [OPTIONS] TARGET sharpness [SHARPNESS]
Data:
SHARPNESS int (0-100)
```
#### color
```
Usage: samsung-mdc [OPTIONS] TARGET color [COLOR]
Data:
COLOR int (0-100)
```
#### tint
```
Usage: samsung-mdc [OPTIONS] TARGET tint [TINT]
Control the device tint. Adjust the ratio of green to red tint level.
Red: TINT value, Green: ( 100 - TINT ) value.
Note: Tint could only be set in 50 Steps (0, 2, 4, 6... 100).
Data:
TINT int (0-100)
```
#### h_position
```
Usage: samsung-mdc [OPTIONS] TARGET h_position H_POSITION_MOVE_TO
Data:
H_POSITION_MOVE_TO LEFT | RIGHT
```
#### v_position
```
Usage: samsung-mdc [OPTIONS] TARGET v_position V_POSITION_MOVE_TO
Data:
V_POSITION_MOVE_TO UP | DOWN
```
#### auto_power
```
Usage: samsung-mdc [OPTIONS] TARGET auto_power [AUTO_POWER_STATE]
Data:
AUTO_POWER_STATE OFF | ON
```
#### clear_menu
```
Usage: samsung-mdc [OPTIONS] TARGET clear_menu
```
#### ir_state
```
Usage: samsung-mdc [OPTIONS] TARGET ir_state [IR_STATE]
Enables/disables IR (Infrared) receiving function (Remote Control).
Working Condition: * Can operate regardless of whether power is ON/OFF. (If
DPMS Situation in LFD, it operate Remocon regardless of set value).
Data:
IR_STATE DISABLED | ENABLED
```
#### rgb_contrast
```
Usage: samsung-mdc [OPTIONS] TARGET rgb_contrast [CONTRAST]
Data:
CONTRAST int (0-100)
```
#### rgb_brightness
```
Usage: samsung-mdc [OPTIONS] TARGET rgb_brightness [BRIGHTNESS]
Data:
BRIGHTNESS int (0-100)
```
#### auto_adjustment_on
```
Usage: samsung-mdc [OPTIONS] TARGET auto_adjustment_on
```
#### color_tone
```
Usage: samsung-mdc [OPTIONS] TARGET color_tone [COLOR_TONE_STATE]
Data:
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
```
#### color_temperature
```
Usage: samsung-mdc [OPTIONS] TARGET color_temperature [HECTO_KELVIN]
Color temperature function.
Unit is hectoKelvin (hK) (x*100 Kelvin) (example: 28 = 2800K).
Supported values - 28, 30, 35, 40... 160.
For older models: 0-10=(x*100K + 5000K), 253=2800K, 254=3000K, 255=4000K
Data:
HECTO_KELVIN int
```
#### standby
```
Usage: samsung-mdc [OPTIONS] TARGET standby [STANDBY_STATE]
Data:
STANDBY_STATE OFF | ON | AUTO
```
#### auto_lamp
```
Usage: samsung-mdc [OPTIONS] TARGET auto_lamp [MAX_TIME MAX_LAMP_VALUE
MIN_TIME MIN_LAMP_VALUE]
Auto Lamp function (backlight).
Note: When Manual Lamp Control is on, Auto Lamp Control will automatically
turn off.
Data:
MAX_TIME time (format: %H:%M)
MAX_LAMP_VALUE int (0-100)
MIN_TIME time (format: %H:%M)
MIN_LAMP_VALUE int (0-100)
```
#### manual_lamp
```
Usage: samsung-mdc [OPTIONS] TARGET manual_lamp [LAMP_VALUE]
Manual Lamp function (backlight).
Note: When Auto Lamp Control is on, Manual Lamp Control will automatically
turn off.
Data:
LAMP_VALUE int (0-100)
```
#### inverse
```
Usage: samsung-mdc [OPTIONS] TARGET inverse [INVERSE_STATE]
Data:
INVERSE_STATE OFF | ON
```
#### video_wall_mode
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_mode [VIDEO_WALL_MODE]
Get/Set the device in aspect ratio of the video wall.
FULL: stretch input source to fill display
NATURAL: Keep aspect ratio of input source; do not fill display.
Note: Needs VIDEO_WALL_STATE to be ON.
Data:
VIDEO_WALL_MODE NATURAL | FULL
```
#### safety_lock
```
Usage: samsung-mdc [OPTIONS] TARGET safety_lock [LOCK_STATE]
Data:
LOCK_STATE OFF | ON
```
#### panel_lock
```
Usage: samsung-mdc [OPTIONS] TARGET panel_lock [LOCK_STATE]
Data:
LOCK_STATE OFF | ON
```
#### channel_change
```
Usage: samsung-mdc [OPTIONS] TARGET channel_change CHANGE_TO
Data:
CHANGE_TO UP | DOWN
```
#### volume_change
```
Usage: samsung-mdc [OPTIONS] TARGET volume_change CHANGE_TO
Data:
CHANGE_TO UP | DOWN
```
#### ticker
```
Usage: samsung-mdc [OPTIONS] TARGET ticker [ON_OFF START_TIME END_TIME
POS_HORIZ POS_VERTI MOTION_ON_OFF MOTION_DIR MOTION_SPEED
FONT_SIZE FOREGROUND_COLOR BACKGROUND_COLOR
FOREGROUND_OPACITY BACKGROUND_OPACITY MESSAGE]
Get/Set the device ticker. (Show text message overlay on the screen)
Note: POS_HORIZ or POS_VERT are NONE in GET response if unsupported by the
display.
Data:
ON_OFF bool
START_TIME time (format: %H:%M)
END_TIME time (format: %H:%M)
POS_HORIZ CENTER | LEFT | RIGHT | NONE
POS_VERTI MIDDLE | TOP | BOTTOM | NONE
MOTION_ON_OFF bool
MOTION_DIR LEFT | RIGHT | UP | DOWN
MOTION_SPEED NORMAL | SLOW | FAST
FONT_SIZE STANDARD | SMALL | LARGE
FOREGROUND_COLOR BLACK | WHITE | RED | GREEN | BLUE | YELLOW | MAGENTA |
CYAN
BACKGROUND_COLOR BLACK | WHITE | RED | GREEN | BLUE | YELLOW | MAGENTA |
CYAN
FOREGROUND_OPACITY FLASHING | FLASH_ALL | OFF
BACKGROUND_OPACITY SOLID | TRANSPARENT | TRANSLUCENT | UNKNOWN
MESSAGE str
```
#### device_name
```
Usage: samsung-mdc [OPTIONS] TARGET device_name
It reads the device name which user set up in network. Shows the information
about entered device name.
Data:
DEVICE_NAME str
```
#### osd
```
Usage: samsung-mdc [OPTIONS] TARGET osd [OSD_ENABLED]
Turns OSD (On-screen display) on/off.
Data:
OSD_ENABLED bool
```
#### picture_mode
```
Usage: samsung-mdc [OPTIONS] TARGET picture_mode [PICTURE_MODE_STATE]
Data:
PICTURE_MODE_STATE DYNAMIC | STANDARD | MOVIE | CUSTOM_TV | NATURAL |
CALIBRATION_TV | ENTERTAIN | INTERNET | TEXT | CUSTOM |
ADVERTISEMENT | INFORMATION | CALIBRATION |
SHOP_MALL_VIDEO | SHOP_MALL_TEXT | OFFICE_SCHOOL_VIDEO |
OFFICE_SCHOOL_TEXT | TERMINAL_STATION_VIDEO |
TERMINAL_STATION_TEXT | VIDEO_WALL_VIDEO |
VIDEO_WALL_TEXT | HDR_PLUS | OFF | RESERVED_OTHER
```
#### sound_mode
```
Usage: samsung-mdc [OPTIONS] TARGET sound_mode [SOUND_MODE_STATE]
Data:
SOUND_MODE_STATE STANDARD | MUSIC | MOVIE | SPEECH | CUSTOM | AMPLIFY |
OPTIMIZED
```
#### all_keys_lock
```
Usage: samsung-mdc [OPTIONS] TARGET all_keys_lock [LOCK_STATE]
Turns both REMOCON and Panel Key Lock function on/off.
Note: Can operate regardless of whether power is on/off.
Data:
LOCK_STATE OFF | ON
```
#### panel_on_time
```
Usage: samsung-mdc [OPTIONS] TARGET panel_on_time
Get the device panel on total time.
Return value increased every 10 mins. To get hours use "MIN10 / 6".
Data:
MIN10 int
```
#### video_wall_state
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_state [VIDEO_WALL_STATE]
Get/Set the device in video wall state. This will split the primary input
source into smaller N number of squares and display them instead.
Note: The device needs to be capable of this operation. Usually a primary
high resolution source signal is daisy chained to lower resolution displays
in a video wall using HDMI/DP.
Data:
VIDEO_WALL_STATE OFF | ON
```
#### video_wall_model
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_model [MODEL SERIAL]
Get/Set video wall model.
MODEL: Size of the wall in (x, y) coordinates; ie. "2,2" or "4,1"
SERIAL: Serial number - position of the display in the video wall, counting
from the first display.
Note: Needs VIDEO_WALL_STATE to be ON.
Data:
MODEL Video Wall model (format: X,Y eg. 4,5)
SERIAL int (1-255)
```
#### model_name
```
Usage: samsung-mdc [OPTIONS] TARGET model_name
Data:
MODEL_NAME str
```
#### energy_saving
```
Usage: samsung-mdc [OPTIONS] TARGET energy_saving [ENERGY_SAVING_STATE]
Data:
ENERGY_SAVING_STATE OFF | LOW | MEDIUM | HIGH | PICTURE_OFF
```
#### reset
```
Usage: samsung-mdc [OPTIONS] TARGET reset RESET_TARGET
Data:
RESET_TARGET PICTURE | SOUND | SETUP | ALL | SCREEN_DISPLAY
```
#### osd_type
```
Usage: samsung-mdc [OPTIONS] TARGET osd_type [OSD_TYPE OSD_ENABLED]
Turns OSD (On-screen display) specific message types on/off.
Data:
OSD_TYPE SOURCE | NOT_OPTIMUM_MODE | NO_SIGNAL | MDC | SCHEDULE_CHANNEL
OSD_ENABLED bool
```
#### timer_13
```
Usage: samsung-mdc [OPTIONS] TARGET timer_13 TIMER_ID [ON_TIME ON_ENABLED
OFF_TIME OFF_ENABLED REPEAT MANUAL_WEEKDAY VOLUME
INPUT_SOURCE_STATE HOLIDAY_APPLY]
Integrated timer function (13 data-length version).
Note: This depends on product and will not work on newer versions.
Data:
TIMER_ID int (1-7)
ON_TIME time (format: %H:%M)
ON_ENABLED bool
OFF_TIME time (format: %H:%M)
OFF_ENABLED bool
REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
VOLUME int (0-100)
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
HOLIDAY_APPLY DONT_APPLY_BOTH | APPLY_BOTH | ON_TIMER_ONLY_APPLY |
OFF_TIMER_ONLY_APPLY
```
#### timer_15
```
Usage: samsung-mdc [OPTIONS] TARGET timer_15 TIMER_ID [ON_TIME ON_ENABLED
OFF_TIME OFF_ENABLED ON_REPEAT ON_MANUAL_WEEKDAY OFF_REPEAT
OFF_MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]
Integrated timer function (15 data-length version).
Note: This depends on product and will not work on older versions.
ON_TIME/OFF_TIME: turn ON/OFF display at specific time of day
ON_ACTIVE/OFF_ACTIVE: if timer is not active, values are ignored, so there
may be only OFF timer, ON timer, or both.
REPEAT: On which day timer is enabled (combined with HOLIDAY_APPLY and
MANUAL_WEEKDAY)
Data:
TIMER_ID int (1-7)
ON_TIME time (format: %H:%M)
ON_ENABLED bool
OFF_TIME time (format: %H:%M)
OFF_ENABLED bool
ON_REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
ON_MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
OFF_REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
OFF_MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
VOLUME int (0-100)
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
HOLIDAY_APPLY DONT_APPLY_BOTH | APPLY_BOTH | ON_TIMER_ONLY_APPLY |
OFF_TIMER_ONLY_APPLY
```
#### clock_m
```
Usage: samsung-mdc [OPTIONS] TARGET clock_m [DATETIME]
Current time function (minute precision).
Note: This is for models developed until 2013. For newer models see CLOCK_S
function (seconds precision).
Data:
DATETIME datetime (format: %Y-%m-%dT%H:%M / %Y-%m-%d %H:%M)
```
#### holiday_set
```
Usage: samsung-mdc [OPTIONS] TARGET holiday_set HOLIDAY_MANAGE START_MONTH
START_DAY END_MONTH END_DAY
Add/Delete the device holiday schedule with the holiday schedule itself
start month/day and end month/day.
Note: On DELETE_ALL all parameters should be 0x00.
Data:
HOLIDAY_MANAGE ADD | DELETE | DELETE_ALL
START_MONTH int (0-12)
START_DAY int (0-31)
END_MONTH int (0-12)
END_DAY int (0-31)
```
#### holiday_get
```
Usage: samsung-mdc [OPTIONS] TARGET holiday_get [INDEX]
Get the device holiday schedule.
If INDEX is not specified, returns total number of Holiday Information.
Data:
INDEX int
Response extra:
START_MONTH int
START_DAY int
END_MONTH int
END_DAY int
```
#### virtual_remote
```
Usage: samsung-mdc [OPTIONS] TARGET virtual_remote KEY_CODE
This function support that MDC command can work same as remote control.
Note: In a certain model, 0x79 CONTENT key works as HOME and 0x1F DISPLAY
key works as INFO.
Data:
KEY_CODE KEY_SOURCE | KEY_POWER | KEY_1 | KEY_2 | KEY_3 | KEY_VOLUME_UP |
KEY_4 | KEY_5 | KEY_6 | KEY_VOLUME_DOWN | KEY_7 | KEY_8 | KEY_9 |
KEY_MUTE | KEY_CHANNEL_DOWN | KEY_0 | KEY_CHANNEL_UP | KEY_GREEN |
KEY_YELLOW | KEY_CYAN | KEY_MENU | KEY_DISPLAY | KEY_DIGIT |
KEY_PIP_TV_VIDEO | KEY_EXIT | KEY_MAGICINFO | KEY_REW | KEY_STOP |
KEY_PLAY | KEY_FF | KEY_PAUSE | KEY_TOOLS | KEY_RETURN |
KEY_MAGICINFO_LITE | KEY_CURSOR_UP | KEY_CURSOR_DOWN |
KEY_CURSOR_RIGHT | KEY_CURSOR_LEFT | KEY_ENTER | KEY_RED |
KEY_LOCK | KEY_CONTENT | DISCRET_POWER_OFF | KEY_3D
```
#### network_standby
```
Usage: samsung-mdc [OPTIONS] TARGET network_standby [NETWORK_STANDBY_STATE]
Data:
NETWORK_STANDBY_STATE OFF | ON
```
#### dst
```
Usage: samsung-mdc [OPTIONS] TARGET dst [DST_STATE START_MONTH START_WEEK
START_WEEKDAY START_TIME END_MONTH END_WEEK END_WEEKDAY
END_TIME OFFSET]
Data:
DST_STATE OFF | AUTO | MANUAL
START_MONTH JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT |
NOV | DEC
START_WEEK WEEK_1 | WEEK_2 | WEEK_3 | WEEK_4 | WEEK_LAST
START_WEEKDAY SUN | MON | TUE | WED | THU | FRI | SAT
START_TIME time (format: %H:%M)
END_MONTH JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT |
NOV | DEC
END_WEEK WEEK_1 | WEEK_2 | WEEK_3 | WEEK_4 | WEEK_LAST
END_WEEKDAY SUN | MON | TUE | WED | THU | FRI | SAT
END_TIME time (format: %H:%M)
OFFSET PLUS_1_00 | PLUS_2_00
Response extra:
TUNER_SUPPORT bool
```
#### auto_id_setting
```
Usage: samsung-mdc [OPTIONS] TARGET auto_id_setting [AUTO_ID_SETTING_STATE]
Data:
AUTO_ID_SETTING_STATE START | END
```
#### display_id
```
Usage: samsung-mdc [OPTIONS] TARGET display_id DISPLAY_ID_STATE
Data:
DISPLAY_ID_STATE OFF | ON
```
#### clock_s
```
Usage: samsung-mdc [OPTIONS] TARGET clock_s [DATETIME]
Current time function (second precision).
Note: This is for models developed after 2013. For older models see CLOCK_M
function (minute precision).
Data:
DATETIME datetime (format: %Y-%m-%dT%H:%M:%S / %Y-%m-%d %H:%M:%S)
```
#### launcher_play_via
```
Usage: samsung-mdc [OPTIONS] TARGET launcher_play_via [PLAY_VIA_MODE]
Data:
PLAY_VIA_MODE MAGIC_INFO | URL_LAUNCHER | MAGIC_IWB
```
#### launcher_url_address
```
Usage: samsung-mdc [OPTIONS] TARGET launcher_url_address [URL_ADDRESS]
Data:
URL_ADDRESS str
```
#### osd_menu_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_menu_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_source_content_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_source_content_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_aspect_ratio
```
Usage: samsung-mdc [OPTIONS] TARGET osd_aspect_ratio [ASPECT_RATIO_STATE]
Get/Set the device aspect ratio under portrait mode which set the rotated
screen to be full or original.
Data:
ASPECT_RATIO_STATE FULL_SCREEN | ORIGINAL
```
#### osd_pip_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_pip_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_menu_size
```
Usage: samsung-mdc [OPTIONS] TARGET osd_menu_size [MENU_SIZE_STATE]
Data:
MENU_SIZE_STATE ORIGINAL | MEDIUM | SMALL
```
#### auto_source_switch
```
Usage: samsung-mdc [OPTIONS] TARGET auto_source_switch
[AUTO_SOURCE_SWITCH_STATE]
Data:
AUTO_SOURCE_SWITCH_STATE OFF | ON
```
#### auto_source
```
Usage: samsung-mdc [OPTIONS] TARGET auto_source [PRIMARY_SOURCE_RECOVERY
PRIMARY_SOURCE SECONDARY_SOURCE]
Data:
PRIMARY_SOURCE_RECOVERY OFF | ON
PRIMARY_SOURCE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 |
DVI | PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 |
HDMI1_PC | HDMI2 | HDMI2_PC | DISPLAY_PORT_1 |
DISPLAY_PORT_2 | DISPLAY_PORT_3 | RF_TV | HDMI3 |
HDMI3_PC | HDMI4 | HDMI4_PC | TV_DTV | PLUG_IN_MODE
| HD_BASE_T | OCM | MEDIA_MAGIC_INFO_S |
WIDI_SCREEN_MIRRORING | INTERNAL_USB | URL_LAUNCHER
| IWB | WEB_BROWSER | REMOTE_WORKSPACE
SECONDARY_SOURCE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 |
DVI | PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 |
HDMI1_PC | HDMI2 | HDMI2_PC | DISPLAY_PORT_1 |
DISPLAY_PORT_2 | DISPLAY_PORT_3 | RF_TV | HDMI3 |
HDMI3_PC | HDMI4 | HDMI4_PC | TV_DTV | PLUG_IN_MODE
| HD_BASE_T | OCM | MEDIA_MAGIC_INFO_S |
WIDI_SCREEN_MIRRORING | INTERNAL_USB | URL_LAUNCHER
| IWB | WEB_BROWSER | REMOTE_WORKSPACE
```
#### panel
```
Usage: samsung-mdc [OPTIONS] TARGET panel [PANEL_STATE]
Data:
PANEL_STATE ON | OFF
```
#### screen_mute
```
Usage: samsung-mdc [OPTIONS] TARGET screen_mute [SCREEN_MUTE_STATUS]
Data:
SCREEN_MUTE_STATUS ON | OFF
```
#### script
```
Usage: samsung-mdc [OPTIONS] TARGET script [OPTIONS] SCRIPT_FILE
Script file with commands to execute.
Commands for multiple targets will be running async, but commands order is
preserved for device (and is running on same connection), exit on first fail
unless retry options provided.
You may use jinja2 templating engine to {% include "other_script" %} or {{
VAR_KEY }} rendering in combination with --var VAR_KEY VAR_VALUE options.
It's highly recommended to use sleep option for virtual_remote!
Additional commands:
sleep SECONDS (FLOAT, --sleep option for this command is ignored)
disconnect
Format:
command1 [ARGS]...
command2 [ARGS]...
Example: samsung-mdc ./targets.txt script -s 3 -r 1 -v KEY enter ./commands.txt
# commands.txt content
power on
sleep 5
clear_menu
virtual_remote key_menu
virtual_remote key_down
virtual_remote {{ KEY }}
clear_menu
Arguments:
script_file Text file with commands, separated by newline.
Options:
-s, --sleep FLOAT Pause between commands (seconds)
--retry-command INTEGER Retry command if failed (count)
--retry-command-sleep FLOAT Sleep before command retry (seconds)
-r, --retry-script INTEGER Retry script if failed (count)
--retry-script-sleep FLOAT Sleep before script retry (seconds)
--ignore-nak Ignore negative acknowledgement errors
-v, --var NAME VALUE Variable "{{ NAME }}" in script will be
replaced by VALUE
--help Show this message and exit.
```
#### raw
```
Usage: samsung-mdc [OPTIONS] TARGET raw [OPTIONS] COMMAND [DATA]
Helper command to send raw data for test purposes.
Arguments:
command Command and (optionally) subcommand (example: a1 or a1:b2)
data Data payload if any (example: a1:b2)
```
## Troubleshooting
### Finding DISPLAY ID
On most devices it's usually `0` or `1`. Some devices may use `255` (0xFF) or `254` (0xFE) as all/any display, but behavior in such cases for more than 1 display is undefined.
Display id can be found using remote control: `Home` -> `ID Settings`.
### NAKError
If you receive NAK errors on some commands, you may try to:
* Ensure that device is powered on and completely loaded
* Switch to input source HDMI1
* Reboot device
* Reset all settings
* Disable MagicINFO
* Factory reset (using "Service Menu")
## Python example
```python3
import asyncio
from samsung_mdc import MDC
async def main(ip, display_id):
async with MDC(ip, verbose=True) as mdc:
# First argument of command is always display_id
status = await mdc.status(display_id)
print(status) # Result is always tuple
if status[0] != MDC.power.POWER_STATE.ON:
# Command arguments are always Sequence (tuple, list)
await mdc.power(display_id, [MDC.power.POWER_STATE.ON])
await mdc.close() # Force reconnect on next command
await asyncio.sleep(15)
await mdc.display_id(display_id, [MDC.display_id.DISPLAY_ID_STATE.ON])
# You may also use names or values instead of enums
await mdc.display_id(display_id, ['ON']) # same
await mdc.display_id(display_id, [1]) # same
# If you see "Connected" and timeout error, try other display_id (0, 1)
asyncio.run(main('192.168.0.10', 1))
```
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/README.md 0000644 0001751 0000200 00000114773 14736575073 016240 0 ustar 00runner docker # Samsung-MDC
This is implementation of Samsung MDC (Multiple Display Control) protocol on **python3.7+** and **asyncio** with most comprehensive CLI (command line interface).
It allows you to control a variety of different sources (TV, Monitor) through the built-in RS-232C or Ethernet interface.
[MDC Protocol specification - v15.0 2020-11-06](https://vgavro.github.io/samsung-mdc/MDC-Protocol.pdf)
* Implemented *83* commands
* Easy to extend using simple declarative API - see [samsung_mdc/commands.py](https://github.com/vgavro/samsung-mdc/blob/master/samsung_mdc/commands.py)
* Detailed [CLI](#usage) help and parameters validation
* Run commands async on numerous targets (using asyncio)
* TCP and SERIAL mode (for RJ45 and RS232C connection types)
* TCP over TLS mode ("Secured Protocol" using PIN)
* [script](#script) command for advanced usage
* [Python example](#python-example)
Not implemented: some more commands (PRs are welcome)
Also see: [Samsung MDC Unified](http://www.samsung-mcloud.com/01_Software/04_Tools/MDC/v1235/) - Reference Application (GUI, Windows) with partially implemented functionality.
## Install
```
# using pipx https://pypa.github.io/pipx/
pipx run python-samsung-mdc --help
# OR global install/upgrade
sudo pip3 install --upgrade python-samsung-mdc
samsung-mdc --help
# OR local
git clone https://github.com/vgavro/samsung-mdc
cd ./samsung-mdc
python3 -m venv venv
./venv/bin/pip3 install -e ./
./venv/bin/samsung-mdc --help
```
### Windows install
1. Install Git && Git Bash: https://git-scm.com/download/win
2. Install Python 3 latest release (tested with 3.9): https://www.python.org/downloads/windows/
3. Run "Git Bash", type in console:
```
pip3 install --upgrade python-samsung-mdc
# NOTE: python "Scripts" folder is not in %PATH% in Windows by default,
# so you may want to create alias for Git Bash
echo alias samsung-mdc=\'python3 -m samsung_mdc\' >> ~/.bash_profile
source ~/.bash_profile
# test it
samsung-mdc --help
```
## Usage
```
Usage: samsung-mdc [OPTIONS] TARGET COMMAND [ARGS]...
Try 'samsung-mdc --help COMMAND' for command info
For multiple targets commands will be running async, so result order may
differ.
TARGET may be:
DISPLAY_ID@IP[:PORT] (default port: 1515, example: 0@192.168.0.10:1515)
FILENAME with target list (separated by newline)
For serial port connection:
DISPLAY_ID@PORT_NAME for Windows (example: 1@COM1)
DISPLAY_ID@PORT_PATH (example: 1@/dev/ttyUSB0)
We're trying to make autodetection of connection mode by port name, but you
may want to use --mode option.
Options:
--version Show the version and exit.
-v, --verbose
-m, --mode [auto|tcp|serial] default: auto
-p, --pin INTEGER 4-digit PIN for secured TLS connection. If PIN
provided, "Secured Protocol" must be enabled
on remote device.
-t, --timeout FLOAT read/write/connect timeout in seconds
(default: 5) (connect can be overridden with
separate option)
--connect-timeout FLOAT
-h, --help Show this message and exit.
```
### Commands:
* [status](#status) `(POWER_STATE VOLUME MUTE_STATE INPUT_SOURCE_STATE PICTURE_ASPECT_STATE N_TIME_NF F_TIME_NF)`
* [video](#video) `(CONTRAST BRIGHTNESS SHARPNESS COLOR TINT COLOR_TONE_STATE COLOR_TEMPERATURE _IGNORE)`
* [rgb](#rgb) `(CONTRAST BRIGHTNESS COLOR_TONE_STATE COLOR_TEMPERATURE _IGNORE RED_GAIN GREEN_GAIN BLUE_GAIN)`
* [serial_number](#serial_number) `(SERIAL_NUMBER)`
* [error_status](#error_status) `(LAMP_ERROR_STATE TEMPERATURE_ERROR_STATE BRIGHTNESS_SENSOR_ERROR_STATE INPUT_SOURCE_ERROR_STATE TEMPERATURE FAN_ERROR_STATE)`
* [software_version](#software_version) `(SOFTWARE_VERSION)`
* [model_number](#model_number) `(MODEL_SPECIES MODEL_CODE TV_SUPPORT)`
* [power](#power) `[POWER_STATE]`
* [volume](#volume) `[VOLUME]`
* [mute](#mute) `[MUTE_STATE]`
* [input_source](#input_source) `[INPUT_SOURCE_STATE]`
* [picture_aspect](#picture_aspect) `[PICTURE_ASPECT_STATE]`
* [screen_mode](#screen_mode) `[SCREEN_MODE_STATE]`
* [screen_size](#screen_size) `(INCHES)`
* [network_configuration](#network_configuration) `[IP_ADDRESS SUBNET_MASK GATEWAY_ADDRESS DNS_SERVER_ADDRESS]`
* [network_mode](#network_mode) `[NETWORK_MODE_STATE]`
* [network_ap_config](#network_ap_config) `SSID PASSWORD`
* [weekly_restart](#weekly_restart) `[WEEKDAY TIME]`
* [magicinfo_channel](#magicinfo_channel) `CHANNEL_NUMBER`
* [magicinfo_server](#magicinfo_server) `[MAGICINFO_SERVER_URL]`
* [magicinfo_content_orientation](#magicinfo_content_orientation) `[ORIENTATION_MODE_STATE]`
* [mdc_connection](#mdc_connection) `[MDC_CONNECTION_TYPE]`
* [contrast](#contrast) `[CONTRAST]`
* [brightness](#brightness) `[BRIGHTNESS]`
* [sharpness](#sharpness) `[SHARPNESS]`
* [color](#color) `[COLOR]`
* [tint](#tint) `[TINT]`
* [h_position](#h_position) `H_POSITION_MOVE_TO`
* [v_position](#v_position) `V_POSITION_MOVE_TO`
* [auto_power](#auto_power) `[AUTO_POWER_STATE]`
* [clear_menu](#clear_menu)
* [ir_state](#ir_state) `[IR_STATE]`
* [rgb_contrast](#rgb_contrast) `[CONTRAST]`
* [rgb_brightness](#rgb_brightness) `[BRIGHTNESS]`
* [auto_adjustment_on](#auto_adjustment_on)
* [color_tone](#color_tone) `[COLOR_TONE_STATE]`
* [color_temperature](#color_temperature) `[HECTO_KELVIN]`
* [standby](#standby) `[STANDBY_STATE]`
* [auto_lamp](#auto_lamp) `[MAX_TIME MAX_LAMP_VALUE MIN_TIME MIN_LAMP_VALUE]`
* [manual_lamp](#manual_lamp) `[LAMP_VALUE]`
* [inverse](#inverse) `[INVERSE_STATE]`
* [video_wall_mode](#video_wall_mode) `[VIDEO_WALL_MODE]`
* [safety_lock](#safety_lock) `[LOCK_STATE]`
* [panel_lock](#panel_lock) `[LOCK_STATE]`
* [channel_change](#channel_change) `CHANGE_TO`
* [volume_change](#volume_change) `CHANGE_TO`
* [ticker](#ticker) `[ON_OFF START_TIME END_TIME POS_HORIZ POS_VERTI MOTION_ON_OFF MOTION_DIR MOTION_SPEED FONT_SIZE FOREGROUND_COLOR BACKGROUND_COLOR FOREGROUND_OPACITY BACKGROUND_OPACITY MESSAGE]`
* [device_name](#device_name) `(DEVICE_NAME)`
* [osd](#osd) `[OSD_ENABLED]`
* [picture_mode](#picture_mode) `[PICTURE_MODE_STATE]`
* [sound_mode](#sound_mode) `[SOUND_MODE_STATE]`
* [all_keys_lock](#all_keys_lock) `[LOCK_STATE]`
* [panel_on_time](#panel_on_time) `(MIN10)`
* [video_wall_state](#video_wall_state) `[VIDEO_WALL_STATE]`
* [video_wall_model](#video_wall_model) `[MODEL SERIAL]`
* [model_name](#model_name) `(MODEL_NAME)`
* [energy_saving](#energy_saving) `[ENERGY_SAVING_STATE]`
* [reset](#reset) `RESET_TARGET`
* [osd_type](#osd_type) `[OSD_TYPE OSD_ENABLED]`
* [timer_13](#timer_13) `TIMER_ID [ON_TIME ON_ENABLED OFF_TIME OFF_ENABLED REPEAT MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]`
* [timer_15](#timer_15) `TIMER_ID [ON_TIME ON_ENABLED OFF_TIME OFF_ENABLED ON_REPEAT ON_MANUAL_WEEKDAY OFF_REPEAT OFF_MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]`
* [clock_m](#clock_m) `[DATETIME]`
* [holiday_set](#holiday_set) `HOLIDAY_MANAGE START_MONTH START_DAY END_MONTH END_DAY`
* [holiday_get](#holiday_get) `[INDEX]`
* [virtual_remote](#virtual_remote) `KEY_CODE`
* [network_standby](#network_standby) `[NETWORK_STANDBY_STATE]`
* [dst](#dst) `[DST_STATE START_MONTH START_WEEK START_WEEKDAY START_TIME END_MONTH END_WEEK END_WEEKDAY END_TIME OFFSET]`
* [auto_id_setting](#auto_id_setting) `[AUTO_ID_SETTING_STATE]`
* [display_id](#display_id) `DISPLAY_ID_STATE`
* [clock_s](#clock_s) `[DATETIME]`
* [launcher_play_via](#launcher_play_via) `[PLAY_VIA_MODE]`
* [launcher_url_address](#launcher_url_address) `[URL_ADDRESS]`
* [osd_menu_orientation](#osd_menu_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_source_content_orientation](#osd_source_content_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_aspect_ratio](#osd_aspect_ratio) `[ASPECT_RATIO_STATE]`
* [osd_pip_orientation](#osd_pip_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_menu_size](#osd_menu_size) `[MENU_SIZE_STATE]`
* [auto_source_switch](#auto_source_switch) `[AUTO_SOURCE_SWITCH_STATE]`
* [auto_source](#auto_source) `[PRIMARY_SOURCE_RECOVERY PRIMARY_SOURCE SECONDARY_SOURCE]`
* [panel](#panel) `[PANEL_STATE]`
* [screen_mute](#screen_mute) `[SCREEN_MUTE_STATUS]`
* [script](#script) `[OPTIONS] SCRIPT_FILE`
* [raw](#raw) `[OPTIONS] COMMAND [DATA]`
#### status
```
Usage: samsung-mdc [OPTIONS] TARGET status
Get the device various state like power, volume, sound mute, input source,
picture aspect ratio.
Note: For no audio models volume and mute returns 0xFF (255).
N_TIME_NF, F_TIME_NF: OnTime/OffTime ON/OFF value (old type timer, now it's
always 0x00).
Data:
POWER_STATE OFF | ON | REBOOT
VOLUME int (0-100)
MUTE_STATE OFF | ON | NONE
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
PICTURE_ASPECT_STATE PC_16_9 | PC_4_3 | PC_ORIGINAL_RATIO | PC_21_9 |
PC_CUSTOM | VIDEO_AUTO_WIDE | VIDEO_16_9 | VIDEO_ZOOM
| VIDEO_ZOOM_1 | VIDEO_ZOOM_2 | VIDEO_SCREEN_FIT |
VIDEO_4_3 | VIDEO_WIDE_FIT | VIDEO_CUSTOM |
VIDEO_SMART_VIEW_1 | VIDEO_SMART_VIEW_2 |
VIDEO_WIDE_ZOOM | VIDEO_21_9
N_TIME_NF int
F_TIME_NF int
```
#### video
```
Usage: samsung-mdc [OPTIONS] TARGET video
Data:
CONTRAST int (0-100)
BRIGHTNESS int (0-100)
SHARPNESS int (0-100)
COLOR int (0-100)
TINT int (0-100)
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
COLOR_TEMPERATURE int
_IGNORE int (0-0)
```
#### rgb
```
Usage: samsung-mdc [OPTIONS] TARGET rgb
Data:
CONTRAST int (0-100)
BRIGHTNESS int (0-100)
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
COLOR_TEMPERATURE int
_IGNORE int (0-0)
RED_GAIN int
GREEN_GAIN int
BLUE_GAIN int
```
#### serial_number
```
Usage: samsung-mdc [OPTIONS] TARGET serial_number
Data:
SERIAL_NUMBER str
```
#### error_status
```
Usage: samsung-mdc [OPTIONS] TARGET error_status
Data:
LAMP_ERROR_STATE NORMAL | ERROR
TEMPERATURE_ERROR_STATE NORMAL | ERROR
BRIGHTNESS_SENSOR_ERROR_STATE NONE | ERROR | NORMAL
INPUT_SOURCE_ERROR_STATE NORMAL | ERROR | INVALID
TEMPERATURE int
FAN_ERROR_STATE NORMAL | ERROR | NONE
```
#### software_version
```
Usage: samsung-mdc [OPTIONS] TARGET software_version
Data:
SOFTWARE_VERSION str
```
#### model_number
```
Usage: samsung-mdc [OPTIONS] TARGET model_number
Data:
MODEL_SPECIES PDP | LCD | DLP | LED | CRT | OLED
MODEL_CODE int
TV_SUPPORT SUPPORTED | NOT_SUPPORTED
```
#### power
```
Usage: samsung-mdc [OPTIONS] TARGET power [POWER_STATE]
Data:
POWER_STATE OFF | ON | REBOOT
```
#### volume
```
Usage: samsung-mdc [OPTIONS] TARGET volume [VOLUME]
Data:
VOLUME int (0-100)
```
#### mute
```
Usage: samsung-mdc [OPTIONS] TARGET mute [MUTE_STATE]
Data:
MUTE_STATE OFF | ON | NONE
```
#### input_source
```
Usage: samsung-mdc [OPTIONS] TARGET input_source [INPUT_SOURCE_STATE]
Get/Set the device source which is shown on the screen.
DVI_VIDEO, HDMI1_PC, HDMI2_PC, HDMI3_PC, HDMI4_PC: get only.
URL_LAUNCHER, MAGIC_INFO, TV or some ports require support by model.
On TIMER functions, Do not use WIDI_SCREEN_MIRRORING.
Data:
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
```
#### picture_aspect
```
Usage: samsung-mdc [OPTIONS] TARGET picture_aspect [PICTURE_ASPECT_STATE]
Get/Set the device picture size (aspect ratio).
Working Condition: Will not work with VIDEO_WALL_STATE is ON.
Note: Some of the image sizes are not supported depending on input signals.
Data:
PICTURE_ASPECT_STATE PC_16_9 | PC_4_3 | PC_ORIGINAL_RATIO | PC_21_9 |
PC_CUSTOM | VIDEO_AUTO_WIDE | VIDEO_16_9 | VIDEO_ZOOM
| VIDEO_ZOOM_1 | VIDEO_ZOOM_2 | VIDEO_SCREEN_FIT |
VIDEO_4_3 | VIDEO_WIDE_FIT | VIDEO_CUSTOM |
VIDEO_SMART_VIEW_1 | VIDEO_SMART_VIEW_2 |
VIDEO_WIDE_ZOOM | VIDEO_21_9
```
#### screen_mode
```
Usage: samsung-mdc [OPTIONS] TARGET screen_mode [SCREEN_MODE_STATE]
Data:
SCREEN_MODE_STATE MODE_16_9 | MODE_ZOOM | MODE_4_3 | MODE_WIDE_ZOOM
```
#### screen_size
```
Usage: samsung-mdc [OPTIONS] TARGET screen_size
Data:
INCHES int (0-255)
```
#### network_configuration
```
Usage: samsung-mdc [OPTIONS] TARGET network_configuration [IP_ADDRESS
SUBNET_MASK GATEWAY_ADDRESS DNS_SERVER_ADDRESS]
Data:
IP_ADDRESS IP address
SUBNET_MASK IP address
GATEWAY_ADDRESS IP address
DNS_SERVER_ADDRESS IP address
```
#### network_mode
```
Usage: samsung-mdc [OPTIONS] TARGET network_mode [NETWORK_MODE_STATE]
Data:
NETWORK_MODE_STATE DYNAMIC | STATIC
```
#### network_ap_config
```
Usage: samsung-mdc [OPTIONS] TARGET network_ap_config SSID PASSWORD
Add new SSID info to device connection history with its password.
Note: device may change network and response may not be received.
Data:
SSID str
PASSWORD str
```
#### weekly_restart
```
Usage: samsung-mdc [OPTIONS] TARGET weekly_restart [WEEKDAY TIME]
Data:
WEEKDAY list(,) SUN | SAT | FRI | THU | WED | TUE | MON
TIME time (format: %H:%M)
```
#### magicinfo_channel
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_channel CHANNEL_NUMBER
Set MagicInfo Channel by Direct Channel Number which is used by MagicInfo S
Player.
Data:
CHANNEL_NUMBER int
```
#### magicinfo_server
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_server [MAGICINFO_SERVER_URL]
MagicInfo Server URL.
Example: "http://example.com:80"
Data:
MAGICINFO_SERVER_URL str
```
#### magicinfo_content_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_content_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### mdc_connection
```
Usage: samsung-mdc [OPTIONS] TARGET mdc_connection [MDC_CONNECTION_TYPE]
Note: Depends on the product specification, if it is set as RJ45 then serial
MDC will not work.
Data:
MDC_CONNECTION_TYPE RS232C | RJ45
```
#### contrast
```
Usage: samsung-mdc [OPTIONS] TARGET contrast [CONTRAST]
Data:
CONTRAST int (0-100)
```
#### brightness
```
Usage: samsung-mdc [OPTIONS] TARGET brightness [BRIGHTNESS]
Data:
BRIGHTNESS int (0-100)
```
#### sharpness
```
Usage: samsung-mdc [OPTIONS] TARGET sharpness [SHARPNESS]
Data:
SHARPNESS int (0-100)
```
#### color
```
Usage: samsung-mdc [OPTIONS] TARGET color [COLOR]
Data:
COLOR int (0-100)
```
#### tint
```
Usage: samsung-mdc [OPTIONS] TARGET tint [TINT]
Control the device tint. Adjust the ratio of green to red tint level.
Red: TINT value, Green: ( 100 - TINT ) value.
Note: Tint could only be set in 50 Steps (0, 2, 4, 6... 100).
Data:
TINT int (0-100)
```
#### h_position
```
Usage: samsung-mdc [OPTIONS] TARGET h_position H_POSITION_MOVE_TO
Data:
H_POSITION_MOVE_TO LEFT | RIGHT
```
#### v_position
```
Usage: samsung-mdc [OPTIONS] TARGET v_position V_POSITION_MOVE_TO
Data:
V_POSITION_MOVE_TO UP | DOWN
```
#### auto_power
```
Usage: samsung-mdc [OPTIONS] TARGET auto_power [AUTO_POWER_STATE]
Data:
AUTO_POWER_STATE OFF | ON
```
#### clear_menu
```
Usage: samsung-mdc [OPTIONS] TARGET clear_menu
```
#### ir_state
```
Usage: samsung-mdc [OPTIONS] TARGET ir_state [IR_STATE]
Enables/disables IR (Infrared) receiving function (Remote Control).
Working Condition: * Can operate regardless of whether power is ON/OFF. (If
DPMS Situation in LFD, it operate Remocon regardless of set value).
Data:
IR_STATE DISABLED | ENABLED
```
#### rgb_contrast
```
Usage: samsung-mdc [OPTIONS] TARGET rgb_contrast [CONTRAST]
Data:
CONTRAST int (0-100)
```
#### rgb_brightness
```
Usage: samsung-mdc [OPTIONS] TARGET rgb_brightness [BRIGHTNESS]
Data:
BRIGHTNESS int (0-100)
```
#### auto_adjustment_on
```
Usage: samsung-mdc [OPTIONS] TARGET auto_adjustment_on
```
#### color_tone
```
Usage: samsung-mdc [OPTIONS] TARGET color_tone [COLOR_TONE_STATE]
Data:
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
```
#### color_temperature
```
Usage: samsung-mdc [OPTIONS] TARGET color_temperature [HECTO_KELVIN]
Color temperature function.
Unit is hectoKelvin (hK) (x*100 Kelvin) (example: 28 = 2800K).
Supported values - 28, 30, 35, 40... 160.
For older models: 0-10=(x*100K + 5000K), 253=2800K, 254=3000K, 255=4000K
Data:
HECTO_KELVIN int
```
#### standby
```
Usage: samsung-mdc [OPTIONS] TARGET standby [STANDBY_STATE]
Data:
STANDBY_STATE OFF | ON | AUTO
```
#### auto_lamp
```
Usage: samsung-mdc [OPTIONS] TARGET auto_lamp [MAX_TIME MAX_LAMP_VALUE
MIN_TIME MIN_LAMP_VALUE]
Auto Lamp function (backlight).
Note: When Manual Lamp Control is on, Auto Lamp Control will automatically
turn off.
Data:
MAX_TIME time (format: %H:%M)
MAX_LAMP_VALUE int (0-100)
MIN_TIME time (format: %H:%M)
MIN_LAMP_VALUE int (0-100)
```
#### manual_lamp
```
Usage: samsung-mdc [OPTIONS] TARGET manual_lamp [LAMP_VALUE]
Manual Lamp function (backlight).
Note: When Auto Lamp Control is on, Manual Lamp Control will automatically
turn off.
Data:
LAMP_VALUE int (0-100)
```
#### inverse
```
Usage: samsung-mdc [OPTIONS] TARGET inverse [INVERSE_STATE]
Data:
INVERSE_STATE OFF | ON
```
#### video_wall_mode
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_mode [VIDEO_WALL_MODE]
Get/Set the device in aspect ratio of the video wall.
FULL: stretch input source to fill display
NATURAL: Keep aspect ratio of input source; do not fill display.
Note: Needs VIDEO_WALL_STATE to be ON.
Data:
VIDEO_WALL_MODE NATURAL | FULL
```
#### safety_lock
```
Usage: samsung-mdc [OPTIONS] TARGET safety_lock [LOCK_STATE]
Data:
LOCK_STATE OFF | ON
```
#### panel_lock
```
Usage: samsung-mdc [OPTIONS] TARGET panel_lock [LOCK_STATE]
Data:
LOCK_STATE OFF | ON
```
#### channel_change
```
Usage: samsung-mdc [OPTIONS] TARGET channel_change CHANGE_TO
Data:
CHANGE_TO UP | DOWN
```
#### volume_change
```
Usage: samsung-mdc [OPTIONS] TARGET volume_change CHANGE_TO
Data:
CHANGE_TO UP | DOWN
```
#### ticker
```
Usage: samsung-mdc [OPTIONS] TARGET ticker [ON_OFF START_TIME END_TIME
POS_HORIZ POS_VERTI MOTION_ON_OFF MOTION_DIR MOTION_SPEED
FONT_SIZE FOREGROUND_COLOR BACKGROUND_COLOR
FOREGROUND_OPACITY BACKGROUND_OPACITY MESSAGE]
Get/Set the device ticker. (Show text message overlay on the screen)
Note: POS_HORIZ or POS_VERT are NONE in GET response if unsupported by the
display.
Data:
ON_OFF bool
START_TIME time (format: %H:%M)
END_TIME time (format: %H:%M)
POS_HORIZ CENTER | LEFT | RIGHT | NONE
POS_VERTI MIDDLE | TOP | BOTTOM | NONE
MOTION_ON_OFF bool
MOTION_DIR LEFT | RIGHT | UP | DOWN
MOTION_SPEED NORMAL | SLOW | FAST
FONT_SIZE STANDARD | SMALL | LARGE
FOREGROUND_COLOR BLACK | WHITE | RED | GREEN | BLUE | YELLOW | MAGENTA |
CYAN
BACKGROUND_COLOR BLACK | WHITE | RED | GREEN | BLUE | YELLOW | MAGENTA |
CYAN
FOREGROUND_OPACITY FLASHING | FLASH_ALL | OFF
BACKGROUND_OPACITY SOLID | TRANSPARENT | TRANSLUCENT | UNKNOWN
MESSAGE str
```
#### device_name
```
Usage: samsung-mdc [OPTIONS] TARGET device_name
It reads the device name which user set up in network. Shows the information
about entered device name.
Data:
DEVICE_NAME str
```
#### osd
```
Usage: samsung-mdc [OPTIONS] TARGET osd [OSD_ENABLED]
Turns OSD (On-screen display) on/off.
Data:
OSD_ENABLED bool
```
#### picture_mode
```
Usage: samsung-mdc [OPTIONS] TARGET picture_mode [PICTURE_MODE_STATE]
Data:
PICTURE_MODE_STATE DYNAMIC | STANDARD | MOVIE | CUSTOM_TV | NATURAL |
CALIBRATION_TV | ENTERTAIN | INTERNET | TEXT | CUSTOM |
ADVERTISEMENT | INFORMATION | CALIBRATION |
SHOP_MALL_VIDEO | SHOP_MALL_TEXT | OFFICE_SCHOOL_VIDEO |
OFFICE_SCHOOL_TEXT | TERMINAL_STATION_VIDEO |
TERMINAL_STATION_TEXT | VIDEO_WALL_VIDEO |
VIDEO_WALL_TEXT | HDR_PLUS | OFF | RESERVED_OTHER
```
#### sound_mode
```
Usage: samsung-mdc [OPTIONS] TARGET sound_mode [SOUND_MODE_STATE]
Data:
SOUND_MODE_STATE STANDARD | MUSIC | MOVIE | SPEECH | CUSTOM | AMPLIFY |
OPTIMIZED
```
#### all_keys_lock
```
Usage: samsung-mdc [OPTIONS] TARGET all_keys_lock [LOCK_STATE]
Turns both REMOCON and Panel Key Lock function on/off.
Note: Can operate regardless of whether power is on/off.
Data:
LOCK_STATE OFF | ON
```
#### panel_on_time
```
Usage: samsung-mdc [OPTIONS] TARGET panel_on_time
Get the device panel on total time.
Return value increased every 10 mins. To get hours use "MIN10 / 6".
Data:
MIN10 int
```
#### video_wall_state
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_state [VIDEO_WALL_STATE]
Get/Set the device in video wall state. This will split the primary input
source into smaller N number of squares and display them instead.
Note: The device needs to be capable of this operation. Usually a primary
high resolution source signal is daisy chained to lower resolution displays
in a video wall using HDMI/DP.
Data:
VIDEO_WALL_STATE OFF | ON
```
#### video_wall_model
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_model [MODEL SERIAL]
Get/Set video wall model.
MODEL: Size of the wall in (x, y) coordinates; ie. "2,2" or "4,1"
SERIAL: Serial number - position of the display in the video wall, counting
from the first display.
Note: Needs VIDEO_WALL_STATE to be ON.
Data:
MODEL Video Wall model (format: X,Y eg. 4,5)
SERIAL int (1-255)
```
#### model_name
```
Usage: samsung-mdc [OPTIONS] TARGET model_name
Data:
MODEL_NAME str
```
#### energy_saving
```
Usage: samsung-mdc [OPTIONS] TARGET energy_saving [ENERGY_SAVING_STATE]
Data:
ENERGY_SAVING_STATE OFF | LOW | MEDIUM | HIGH | PICTURE_OFF
```
#### reset
```
Usage: samsung-mdc [OPTIONS] TARGET reset RESET_TARGET
Data:
RESET_TARGET PICTURE | SOUND | SETUP | ALL | SCREEN_DISPLAY
```
#### osd_type
```
Usage: samsung-mdc [OPTIONS] TARGET osd_type [OSD_TYPE OSD_ENABLED]
Turns OSD (On-screen display) specific message types on/off.
Data:
OSD_TYPE SOURCE | NOT_OPTIMUM_MODE | NO_SIGNAL | MDC | SCHEDULE_CHANNEL
OSD_ENABLED bool
```
#### timer_13
```
Usage: samsung-mdc [OPTIONS] TARGET timer_13 TIMER_ID [ON_TIME ON_ENABLED
OFF_TIME OFF_ENABLED REPEAT MANUAL_WEEKDAY VOLUME
INPUT_SOURCE_STATE HOLIDAY_APPLY]
Integrated timer function (13 data-length version).
Note: This depends on product and will not work on newer versions.
Data:
TIMER_ID int (1-7)
ON_TIME time (format: %H:%M)
ON_ENABLED bool
OFF_TIME time (format: %H:%M)
OFF_ENABLED bool
REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
VOLUME int (0-100)
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
HOLIDAY_APPLY DONT_APPLY_BOTH | APPLY_BOTH | ON_TIMER_ONLY_APPLY |
OFF_TIMER_ONLY_APPLY
```
#### timer_15
```
Usage: samsung-mdc [OPTIONS] TARGET timer_15 TIMER_ID [ON_TIME ON_ENABLED
OFF_TIME OFF_ENABLED ON_REPEAT ON_MANUAL_WEEKDAY OFF_REPEAT
OFF_MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]
Integrated timer function (15 data-length version).
Note: This depends on product and will not work on older versions.
ON_TIME/OFF_TIME: turn ON/OFF display at specific time of day
ON_ACTIVE/OFF_ACTIVE: if timer is not active, values are ignored, so there
may be only OFF timer, ON timer, or both.
REPEAT: On which day timer is enabled (combined with HOLIDAY_APPLY and
MANUAL_WEEKDAY)
Data:
TIMER_ID int (1-7)
ON_TIME time (format: %H:%M)
ON_ENABLED bool
OFF_TIME time (format: %H:%M)
OFF_ENABLED bool
ON_REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
ON_MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
OFF_REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
OFF_MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
VOLUME int (0-100)
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
HOLIDAY_APPLY DONT_APPLY_BOTH | APPLY_BOTH | ON_TIMER_ONLY_APPLY |
OFF_TIMER_ONLY_APPLY
```
#### clock_m
```
Usage: samsung-mdc [OPTIONS] TARGET clock_m [DATETIME]
Current time function (minute precision).
Note: This is for models developed until 2013. For newer models see CLOCK_S
function (seconds precision).
Data:
DATETIME datetime (format: %Y-%m-%dT%H:%M / %Y-%m-%d %H:%M)
```
#### holiday_set
```
Usage: samsung-mdc [OPTIONS] TARGET holiday_set HOLIDAY_MANAGE START_MONTH
START_DAY END_MONTH END_DAY
Add/Delete the device holiday schedule with the holiday schedule itself
start month/day and end month/day.
Note: On DELETE_ALL all parameters should be 0x00.
Data:
HOLIDAY_MANAGE ADD | DELETE | DELETE_ALL
START_MONTH int (0-12)
START_DAY int (0-31)
END_MONTH int (0-12)
END_DAY int (0-31)
```
#### holiday_get
```
Usage: samsung-mdc [OPTIONS] TARGET holiday_get [INDEX]
Get the device holiday schedule.
If INDEX is not specified, returns total number of Holiday Information.
Data:
INDEX int
Response extra:
START_MONTH int
START_DAY int
END_MONTH int
END_DAY int
```
#### virtual_remote
```
Usage: samsung-mdc [OPTIONS] TARGET virtual_remote KEY_CODE
This function support that MDC command can work same as remote control.
Note: In a certain model, 0x79 CONTENT key works as HOME and 0x1F DISPLAY
key works as INFO.
Data:
KEY_CODE KEY_SOURCE | KEY_POWER | KEY_1 | KEY_2 | KEY_3 | KEY_VOLUME_UP |
KEY_4 | KEY_5 | KEY_6 | KEY_VOLUME_DOWN | KEY_7 | KEY_8 | KEY_9 |
KEY_MUTE | KEY_CHANNEL_DOWN | KEY_0 | KEY_CHANNEL_UP | KEY_GREEN |
KEY_YELLOW | KEY_CYAN | KEY_MENU | KEY_DISPLAY | KEY_DIGIT |
KEY_PIP_TV_VIDEO | KEY_EXIT | KEY_MAGICINFO | KEY_REW | KEY_STOP |
KEY_PLAY | KEY_FF | KEY_PAUSE | KEY_TOOLS | KEY_RETURN |
KEY_MAGICINFO_LITE | KEY_CURSOR_UP | KEY_CURSOR_DOWN |
KEY_CURSOR_RIGHT | KEY_CURSOR_LEFT | KEY_ENTER | KEY_RED |
KEY_LOCK | KEY_CONTENT | DISCRET_POWER_OFF | KEY_3D
```
#### network_standby
```
Usage: samsung-mdc [OPTIONS] TARGET network_standby [NETWORK_STANDBY_STATE]
Data:
NETWORK_STANDBY_STATE OFF | ON
```
#### dst
```
Usage: samsung-mdc [OPTIONS] TARGET dst [DST_STATE START_MONTH START_WEEK
START_WEEKDAY START_TIME END_MONTH END_WEEK END_WEEKDAY
END_TIME OFFSET]
Data:
DST_STATE OFF | AUTO | MANUAL
START_MONTH JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT |
NOV | DEC
START_WEEK WEEK_1 | WEEK_2 | WEEK_3 | WEEK_4 | WEEK_LAST
START_WEEKDAY SUN | MON | TUE | WED | THU | FRI | SAT
START_TIME time (format: %H:%M)
END_MONTH JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT |
NOV | DEC
END_WEEK WEEK_1 | WEEK_2 | WEEK_3 | WEEK_4 | WEEK_LAST
END_WEEKDAY SUN | MON | TUE | WED | THU | FRI | SAT
END_TIME time (format: %H:%M)
OFFSET PLUS_1_00 | PLUS_2_00
Response extra:
TUNER_SUPPORT bool
```
#### auto_id_setting
```
Usage: samsung-mdc [OPTIONS] TARGET auto_id_setting [AUTO_ID_SETTING_STATE]
Data:
AUTO_ID_SETTING_STATE START | END
```
#### display_id
```
Usage: samsung-mdc [OPTIONS] TARGET display_id DISPLAY_ID_STATE
Data:
DISPLAY_ID_STATE OFF | ON
```
#### clock_s
```
Usage: samsung-mdc [OPTIONS] TARGET clock_s [DATETIME]
Current time function (second precision).
Note: This is for models developed after 2013. For older models see CLOCK_M
function (minute precision).
Data:
DATETIME datetime (format: %Y-%m-%dT%H:%M:%S / %Y-%m-%d %H:%M:%S)
```
#### launcher_play_via
```
Usage: samsung-mdc [OPTIONS] TARGET launcher_play_via [PLAY_VIA_MODE]
Data:
PLAY_VIA_MODE MAGIC_INFO | URL_LAUNCHER | MAGIC_IWB
```
#### launcher_url_address
```
Usage: samsung-mdc [OPTIONS] TARGET launcher_url_address [URL_ADDRESS]
Data:
URL_ADDRESS str
```
#### osd_menu_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_menu_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_source_content_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_source_content_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_aspect_ratio
```
Usage: samsung-mdc [OPTIONS] TARGET osd_aspect_ratio [ASPECT_RATIO_STATE]
Get/Set the device aspect ratio under portrait mode which set the rotated
screen to be full or original.
Data:
ASPECT_RATIO_STATE FULL_SCREEN | ORIGINAL
```
#### osd_pip_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_pip_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_menu_size
```
Usage: samsung-mdc [OPTIONS] TARGET osd_menu_size [MENU_SIZE_STATE]
Data:
MENU_SIZE_STATE ORIGINAL | MEDIUM | SMALL
```
#### auto_source_switch
```
Usage: samsung-mdc [OPTIONS] TARGET auto_source_switch
[AUTO_SOURCE_SWITCH_STATE]
Data:
AUTO_SOURCE_SWITCH_STATE OFF | ON
```
#### auto_source
```
Usage: samsung-mdc [OPTIONS] TARGET auto_source [PRIMARY_SOURCE_RECOVERY
PRIMARY_SOURCE SECONDARY_SOURCE]
Data:
PRIMARY_SOURCE_RECOVERY OFF | ON
PRIMARY_SOURCE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 |
DVI | PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 |
HDMI1_PC | HDMI2 | HDMI2_PC | DISPLAY_PORT_1 |
DISPLAY_PORT_2 | DISPLAY_PORT_3 | RF_TV | HDMI3 |
HDMI3_PC | HDMI4 | HDMI4_PC | TV_DTV | PLUG_IN_MODE
| HD_BASE_T | OCM | MEDIA_MAGIC_INFO_S |
WIDI_SCREEN_MIRRORING | INTERNAL_USB | URL_LAUNCHER
| IWB | WEB_BROWSER | REMOTE_WORKSPACE
SECONDARY_SOURCE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 |
DVI | PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 |
HDMI1_PC | HDMI2 | HDMI2_PC | DISPLAY_PORT_1 |
DISPLAY_PORT_2 | DISPLAY_PORT_3 | RF_TV | HDMI3 |
HDMI3_PC | HDMI4 | HDMI4_PC | TV_DTV | PLUG_IN_MODE
| HD_BASE_T | OCM | MEDIA_MAGIC_INFO_S |
WIDI_SCREEN_MIRRORING | INTERNAL_USB | URL_LAUNCHER
| IWB | WEB_BROWSER | REMOTE_WORKSPACE
```
#### panel
```
Usage: samsung-mdc [OPTIONS] TARGET panel [PANEL_STATE]
Data:
PANEL_STATE ON | OFF
```
#### screen_mute
```
Usage: samsung-mdc [OPTIONS] TARGET screen_mute [SCREEN_MUTE_STATUS]
Data:
SCREEN_MUTE_STATUS ON | OFF
```
#### script
```
Usage: samsung-mdc [OPTIONS] TARGET script [OPTIONS] SCRIPT_FILE
Script file with commands to execute.
Commands for multiple targets will be running async, but commands order is
preserved for device (and is running on same connection), exit on first fail
unless retry options provided.
You may use jinja2 templating engine to {% include "other_script" %} or {{
VAR_KEY }} rendering in combination with --var VAR_KEY VAR_VALUE options.
It's highly recommended to use sleep option for virtual_remote!
Additional commands:
sleep SECONDS (FLOAT, --sleep option for this command is ignored)
disconnect
Format:
command1 [ARGS]...
command2 [ARGS]...
Example: samsung-mdc ./targets.txt script -s 3 -r 1 -v KEY enter ./commands.txt
# commands.txt content
power on
sleep 5
clear_menu
virtual_remote key_menu
virtual_remote key_down
virtual_remote {{ KEY }}
clear_menu
Arguments:
script_file Text file with commands, separated by newline.
Options:
-s, --sleep FLOAT Pause between commands (seconds)
--retry-command INTEGER Retry command if failed (count)
--retry-command-sleep FLOAT Sleep before command retry (seconds)
-r, --retry-script INTEGER Retry script if failed (count)
--retry-script-sleep FLOAT Sleep before script retry (seconds)
--ignore-nak Ignore negative acknowledgement errors
-v, --var NAME VALUE Variable "{{ NAME }}" in script will be
replaced by VALUE
--help Show this message and exit.
```
#### raw
```
Usage: samsung-mdc [OPTIONS] TARGET raw [OPTIONS] COMMAND [DATA]
Helper command to send raw data for test purposes.
Arguments:
command Command and (optionally) subcommand (example: a1 or a1:b2)
data Data payload if any (example: a1:b2)
```
## Troubleshooting
### Finding DISPLAY ID
On most devices it's usually `0` or `1`. Some devices may use `255` (0xFF) or `254` (0xFE) as all/any display, but behavior in such cases for more than 1 display is undefined.
Display id can be found using remote control: `Home` -> `ID Settings`.
### NAKError
If you receive NAK errors on some commands, you may try to:
* Ensure that device is powered on and completely loaded
* Switch to input source HDMI1
* Reboot device
* Reset all settings
* Disable MagicINFO
* Factory reset (using "Service Menu")
## Python example
```python3
import asyncio
from samsung_mdc import MDC
async def main(ip, display_id):
async with MDC(ip, verbose=True) as mdc:
# First argument of command is always display_id
status = await mdc.status(display_id)
print(status) # Result is always tuple
if status[0] != MDC.power.POWER_STATE.ON:
# Command arguments are always Sequence (tuple, list)
await mdc.power(display_id, [MDC.power.POWER_STATE.ON])
await mdc.close() # Force reconnect on next command
await asyncio.sleep(15)
await mdc.display_id(display_id, [MDC.display_id.DISPLAY_ID_STATE.ON])
# You may also use names or values instead of enums
await mdc.display_id(display_id, ['ON']) # same
await mdc.display_id(display_id, [1]) # same
# If you see "Connected" and timeout error, try other display_id (0, 1)
asyncio.run(main('192.168.0.10', 1))
```
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1736112705.0172837
python-samsung-mdc-1.15.0/python_samsung_mdc.egg-info/ 0000755 0001751 0000200 00000000000 14736575101 022327 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112704.0
python-samsung-mdc-1.15.0/python_samsung_mdc.egg-info/PKG-INFO 0000644 0001751 0000200 00000116734 14736575100 023437 0 ustar 00runner docker Metadata-Version: 2.1
Name: python-samsung-mdc
Version: 1.15.0
Summary: Samsung Multiple Display Control (MDC) protocol implementation (asyncio library + CLI interface)
Home-page: http://github.com/vgavro/samsung-mdc
Author: Victor Gavro
Author-email: vgavro@gmail.com
License: BSD-3-Clause
Keywords: samsung,mdc
Classifier: License :: OSI Approved :: BSD License
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Telecommunications Industry
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Development Status :: 5 - Production/Stable
Classifier: Operating System :: OS Independent
Classifier: Topic :: Multimedia :: Video :: Display
Classifier: Topic :: Home Automation
Classifier: Topic :: Utilities
Requires-Python: >=3.7,<4.0
Description-Content-Type: text/markdown
Provides-Extra: test
Provides-Extra: serial
Provides-Extra: all
License-File: LICENSE
# Samsung-MDC
This is implementation of Samsung MDC (Multiple Display Control) protocol on **python3.7+** and **asyncio** with most comprehensive CLI (command line interface).
It allows you to control a variety of different sources (TV, Monitor) through the built-in RS-232C or Ethernet interface.
[MDC Protocol specification - v15.0 2020-11-06](https://vgavro.github.io/samsung-mdc/MDC-Protocol.pdf)
* Implemented *83* commands
* Easy to extend using simple declarative API - see [samsung_mdc/commands.py](https://github.com/vgavro/samsung-mdc/blob/master/samsung_mdc/commands.py)
* Detailed [CLI](#usage) help and parameters validation
* Run commands async on numerous targets (using asyncio)
* TCP and SERIAL mode (for RJ45 and RS232C connection types)
* TCP over TLS mode ("Secured Protocol" using PIN)
* [script](#script) command for advanced usage
* [Python example](#python-example)
Not implemented: some more commands (PRs are welcome)
Also see: [Samsung MDC Unified](http://www.samsung-mcloud.com/01_Software/04_Tools/MDC/v1235/) - Reference Application (GUI, Windows) with partially implemented functionality.
## Install
```
# using pipx https://pypa.github.io/pipx/
pipx run python-samsung-mdc --help
# OR global install/upgrade
sudo pip3 install --upgrade python-samsung-mdc
samsung-mdc --help
# OR local
git clone https://github.com/vgavro/samsung-mdc
cd ./samsung-mdc
python3 -m venv venv
./venv/bin/pip3 install -e ./
./venv/bin/samsung-mdc --help
```
### Windows install
1. Install Git && Git Bash: https://git-scm.com/download/win
2. Install Python 3 latest release (tested with 3.9): https://www.python.org/downloads/windows/
3. Run "Git Bash", type in console:
```
pip3 install --upgrade python-samsung-mdc
# NOTE: python "Scripts" folder is not in %PATH% in Windows by default,
# so you may want to create alias for Git Bash
echo alias samsung-mdc=\'python3 -m samsung_mdc\' >> ~/.bash_profile
source ~/.bash_profile
# test it
samsung-mdc --help
```
## Usage
```
Usage: samsung-mdc [OPTIONS] TARGET COMMAND [ARGS]...
Try 'samsung-mdc --help COMMAND' for command info
For multiple targets commands will be running async, so result order may
differ.
TARGET may be:
DISPLAY_ID@IP[:PORT] (default port: 1515, example: 0@192.168.0.10:1515)
FILENAME with target list (separated by newline)
For serial port connection:
DISPLAY_ID@PORT_NAME for Windows (example: 1@COM1)
DISPLAY_ID@PORT_PATH (example: 1@/dev/ttyUSB0)
We're trying to make autodetection of connection mode by port name, but you
may want to use --mode option.
Options:
--version Show the version and exit.
-v, --verbose
-m, --mode [auto|tcp|serial] default: auto
-p, --pin INTEGER 4-digit PIN for secured TLS connection. If PIN
provided, "Secured Protocol" must be enabled
on remote device.
-t, --timeout FLOAT read/write/connect timeout in seconds
(default: 5) (connect can be overridden with
separate option)
--connect-timeout FLOAT
-h, --help Show this message and exit.
```
### Commands:
* [status](#status) `(POWER_STATE VOLUME MUTE_STATE INPUT_SOURCE_STATE PICTURE_ASPECT_STATE N_TIME_NF F_TIME_NF)`
* [video](#video) `(CONTRAST BRIGHTNESS SHARPNESS COLOR TINT COLOR_TONE_STATE COLOR_TEMPERATURE _IGNORE)`
* [rgb](#rgb) `(CONTRAST BRIGHTNESS COLOR_TONE_STATE COLOR_TEMPERATURE _IGNORE RED_GAIN GREEN_GAIN BLUE_GAIN)`
* [serial_number](#serial_number) `(SERIAL_NUMBER)`
* [error_status](#error_status) `(LAMP_ERROR_STATE TEMPERATURE_ERROR_STATE BRIGHTNESS_SENSOR_ERROR_STATE INPUT_SOURCE_ERROR_STATE TEMPERATURE FAN_ERROR_STATE)`
* [software_version](#software_version) `(SOFTWARE_VERSION)`
* [model_number](#model_number) `(MODEL_SPECIES MODEL_CODE TV_SUPPORT)`
* [power](#power) `[POWER_STATE]`
* [volume](#volume) `[VOLUME]`
* [mute](#mute) `[MUTE_STATE]`
* [input_source](#input_source) `[INPUT_SOURCE_STATE]`
* [picture_aspect](#picture_aspect) `[PICTURE_ASPECT_STATE]`
* [screen_mode](#screen_mode) `[SCREEN_MODE_STATE]`
* [screen_size](#screen_size) `(INCHES)`
* [network_configuration](#network_configuration) `[IP_ADDRESS SUBNET_MASK GATEWAY_ADDRESS DNS_SERVER_ADDRESS]`
* [network_mode](#network_mode) `[NETWORK_MODE_STATE]`
* [network_ap_config](#network_ap_config) `SSID PASSWORD`
* [weekly_restart](#weekly_restart) `[WEEKDAY TIME]`
* [magicinfo_channel](#magicinfo_channel) `CHANNEL_NUMBER`
* [magicinfo_server](#magicinfo_server) `[MAGICINFO_SERVER_URL]`
* [magicinfo_content_orientation](#magicinfo_content_orientation) `[ORIENTATION_MODE_STATE]`
* [mdc_connection](#mdc_connection) `[MDC_CONNECTION_TYPE]`
* [contrast](#contrast) `[CONTRAST]`
* [brightness](#brightness) `[BRIGHTNESS]`
* [sharpness](#sharpness) `[SHARPNESS]`
* [color](#color) `[COLOR]`
* [tint](#tint) `[TINT]`
* [h_position](#h_position) `H_POSITION_MOVE_TO`
* [v_position](#v_position) `V_POSITION_MOVE_TO`
* [auto_power](#auto_power) `[AUTO_POWER_STATE]`
* [clear_menu](#clear_menu)
* [ir_state](#ir_state) `[IR_STATE]`
* [rgb_contrast](#rgb_contrast) `[CONTRAST]`
* [rgb_brightness](#rgb_brightness) `[BRIGHTNESS]`
* [auto_adjustment_on](#auto_adjustment_on)
* [color_tone](#color_tone) `[COLOR_TONE_STATE]`
* [color_temperature](#color_temperature) `[HECTO_KELVIN]`
* [standby](#standby) `[STANDBY_STATE]`
* [auto_lamp](#auto_lamp) `[MAX_TIME MAX_LAMP_VALUE MIN_TIME MIN_LAMP_VALUE]`
* [manual_lamp](#manual_lamp) `[LAMP_VALUE]`
* [inverse](#inverse) `[INVERSE_STATE]`
* [video_wall_mode](#video_wall_mode) `[VIDEO_WALL_MODE]`
* [safety_lock](#safety_lock) `[LOCK_STATE]`
* [panel_lock](#panel_lock) `[LOCK_STATE]`
* [channel_change](#channel_change) `CHANGE_TO`
* [volume_change](#volume_change) `CHANGE_TO`
* [ticker](#ticker) `[ON_OFF START_TIME END_TIME POS_HORIZ POS_VERTI MOTION_ON_OFF MOTION_DIR MOTION_SPEED FONT_SIZE FOREGROUND_COLOR BACKGROUND_COLOR FOREGROUND_OPACITY BACKGROUND_OPACITY MESSAGE]`
* [device_name](#device_name) `(DEVICE_NAME)`
* [osd](#osd) `[OSD_ENABLED]`
* [picture_mode](#picture_mode) `[PICTURE_MODE_STATE]`
* [sound_mode](#sound_mode) `[SOUND_MODE_STATE]`
* [all_keys_lock](#all_keys_lock) `[LOCK_STATE]`
* [panel_on_time](#panel_on_time) `(MIN10)`
* [video_wall_state](#video_wall_state) `[VIDEO_WALL_STATE]`
* [video_wall_model](#video_wall_model) `[MODEL SERIAL]`
* [model_name](#model_name) `(MODEL_NAME)`
* [energy_saving](#energy_saving) `[ENERGY_SAVING_STATE]`
* [reset](#reset) `RESET_TARGET`
* [osd_type](#osd_type) `[OSD_TYPE OSD_ENABLED]`
* [timer_13](#timer_13) `TIMER_ID [ON_TIME ON_ENABLED OFF_TIME OFF_ENABLED REPEAT MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]`
* [timer_15](#timer_15) `TIMER_ID [ON_TIME ON_ENABLED OFF_TIME OFF_ENABLED ON_REPEAT ON_MANUAL_WEEKDAY OFF_REPEAT OFF_MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]`
* [clock_m](#clock_m) `[DATETIME]`
* [holiday_set](#holiday_set) `HOLIDAY_MANAGE START_MONTH START_DAY END_MONTH END_DAY`
* [holiday_get](#holiday_get) `[INDEX]`
* [virtual_remote](#virtual_remote) `KEY_CODE`
* [network_standby](#network_standby) `[NETWORK_STANDBY_STATE]`
* [dst](#dst) `[DST_STATE START_MONTH START_WEEK START_WEEKDAY START_TIME END_MONTH END_WEEK END_WEEKDAY END_TIME OFFSET]`
* [auto_id_setting](#auto_id_setting) `[AUTO_ID_SETTING_STATE]`
* [display_id](#display_id) `DISPLAY_ID_STATE`
* [clock_s](#clock_s) `[DATETIME]`
* [launcher_play_via](#launcher_play_via) `[PLAY_VIA_MODE]`
* [launcher_url_address](#launcher_url_address) `[URL_ADDRESS]`
* [osd_menu_orientation](#osd_menu_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_source_content_orientation](#osd_source_content_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_aspect_ratio](#osd_aspect_ratio) `[ASPECT_RATIO_STATE]`
* [osd_pip_orientation](#osd_pip_orientation) `[ORIENTATION_MODE_STATE]`
* [osd_menu_size](#osd_menu_size) `[MENU_SIZE_STATE]`
* [auto_source_switch](#auto_source_switch) `[AUTO_SOURCE_SWITCH_STATE]`
* [auto_source](#auto_source) `[PRIMARY_SOURCE_RECOVERY PRIMARY_SOURCE SECONDARY_SOURCE]`
* [panel](#panel) `[PANEL_STATE]`
* [screen_mute](#screen_mute) `[SCREEN_MUTE_STATUS]`
* [script](#script) `[OPTIONS] SCRIPT_FILE`
* [raw](#raw) `[OPTIONS] COMMAND [DATA]`
#### status
```
Usage: samsung-mdc [OPTIONS] TARGET status
Get the device various state like power, volume, sound mute, input source,
picture aspect ratio.
Note: For no audio models volume and mute returns 0xFF (255).
N_TIME_NF, F_TIME_NF: OnTime/OffTime ON/OFF value (old type timer, now it's
always 0x00).
Data:
POWER_STATE OFF | ON | REBOOT
VOLUME int (0-100)
MUTE_STATE OFF | ON | NONE
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
PICTURE_ASPECT_STATE PC_16_9 | PC_4_3 | PC_ORIGINAL_RATIO | PC_21_9 |
PC_CUSTOM | VIDEO_AUTO_WIDE | VIDEO_16_9 | VIDEO_ZOOM
| VIDEO_ZOOM_1 | VIDEO_ZOOM_2 | VIDEO_SCREEN_FIT |
VIDEO_4_3 | VIDEO_WIDE_FIT | VIDEO_CUSTOM |
VIDEO_SMART_VIEW_1 | VIDEO_SMART_VIEW_2 |
VIDEO_WIDE_ZOOM | VIDEO_21_9
N_TIME_NF int
F_TIME_NF int
```
#### video
```
Usage: samsung-mdc [OPTIONS] TARGET video
Data:
CONTRAST int (0-100)
BRIGHTNESS int (0-100)
SHARPNESS int (0-100)
COLOR int (0-100)
TINT int (0-100)
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
COLOR_TEMPERATURE int
_IGNORE int (0-0)
```
#### rgb
```
Usage: samsung-mdc [OPTIONS] TARGET rgb
Data:
CONTRAST int (0-100)
BRIGHTNESS int (0-100)
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
COLOR_TEMPERATURE int
_IGNORE int (0-0)
RED_GAIN int
GREEN_GAIN int
BLUE_GAIN int
```
#### serial_number
```
Usage: samsung-mdc [OPTIONS] TARGET serial_number
Data:
SERIAL_NUMBER str
```
#### error_status
```
Usage: samsung-mdc [OPTIONS] TARGET error_status
Data:
LAMP_ERROR_STATE NORMAL | ERROR
TEMPERATURE_ERROR_STATE NORMAL | ERROR
BRIGHTNESS_SENSOR_ERROR_STATE NONE | ERROR | NORMAL
INPUT_SOURCE_ERROR_STATE NORMAL | ERROR | INVALID
TEMPERATURE int
FAN_ERROR_STATE NORMAL | ERROR | NONE
```
#### software_version
```
Usage: samsung-mdc [OPTIONS] TARGET software_version
Data:
SOFTWARE_VERSION str
```
#### model_number
```
Usage: samsung-mdc [OPTIONS] TARGET model_number
Data:
MODEL_SPECIES PDP | LCD | DLP | LED | CRT | OLED
MODEL_CODE int
TV_SUPPORT SUPPORTED | NOT_SUPPORTED
```
#### power
```
Usage: samsung-mdc [OPTIONS] TARGET power [POWER_STATE]
Data:
POWER_STATE OFF | ON | REBOOT
```
#### volume
```
Usage: samsung-mdc [OPTIONS] TARGET volume [VOLUME]
Data:
VOLUME int (0-100)
```
#### mute
```
Usage: samsung-mdc [OPTIONS] TARGET mute [MUTE_STATE]
Data:
MUTE_STATE OFF | ON | NONE
```
#### input_source
```
Usage: samsung-mdc [OPTIONS] TARGET input_source [INPUT_SOURCE_STATE]
Get/Set the device source which is shown on the screen.
DVI_VIDEO, HDMI1_PC, HDMI2_PC, HDMI3_PC, HDMI4_PC: get only.
URL_LAUNCHER, MAGIC_INFO, TV or some ports require support by model.
On TIMER functions, Do not use WIDI_SCREEN_MIRRORING.
Data:
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
```
#### picture_aspect
```
Usage: samsung-mdc [OPTIONS] TARGET picture_aspect [PICTURE_ASPECT_STATE]
Get/Set the device picture size (aspect ratio).
Working Condition: Will not work with VIDEO_WALL_STATE is ON.
Note: Some of the image sizes are not supported depending on input signals.
Data:
PICTURE_ASPECT_STATE PC_16_9 | PC_4_3 | PC_ORIGINAL_RATIO | PC_21_9 |
PC_CUSTOM | VIDEO_AUTO_WIDE | VIDEO_16_9 | VIDEO_ZOOM
| VIDEO_ZOOM_1 | VIDEO_ZOOM_2 | VIDEO_SCREEN_FIT |
VIDEO_4_3 | VIDEO_WIDE_FIT | VIDEO_CUSTOM |
VIDEO_SMART_VIEW_1 | VIDEO_SMART_VIEW_2 |
VIDEO_WIDE_ZOOM | VIDEO_21_9
```
#### screen_mode
```
Usage: samsung-mdc [OPTIONS] TARGET screen_mode [SCREEN_MODE_STATE]
Data:
SCREEN_MODE_STATE MODE_16_9 | MODE_ZOOM | MODE_4_3 | MODE_WIDE_ZOOM
```
#### screen_size
```
Usage: samsung-mdc [OPTIONS] TARGET screen_size
Data:
INCHES int (0-255)
```
#### network_configuration
```
Usage: samsung-mdc [OPTIONS] TARGET network_configuration [IP_ADDRESS
SUBNET_MASK GATEWAY_ADDRESS DNS_SERVER_ADDRESS]
Data:
IP_ADDRESS IP address
SUBNET_MASK IP address
GATEWAY_ADDRESS IP address
DNS_SERVER_ADDRESS IP address
```
#### network_mode
```
Usage: samsung-mdc [OPTIONS] TARGET network_mode [NETWORK_MODE_STATE]
Data:
NETWORK_MODE_STATE DYNAMIC | STATIC
```
#### network_ap_config
```
Usage: samsung-mdc [OPTIONS] TARGET network_ap_config SSID PASSWORD
Add new SSID info to device connection history with its password.
Note: device may change network and response may not be received.
Data:
SSID str
PASSWORD str
```
#### weekly_restart
```
Usage: samsung-mdc [OPTIONS] TARGET weekly_restart [WEEKDAY TIME]
Data:
WEEKDAY list(,) SUN | SAT | FRI | THU | WED | TUE | MON
TIME time (format: %H:%M)
```
#### magicinfo_channel
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_channel CHANNEL_NUMBER
Set MagicInfo Channel by Direct Channel Number which is used by MagicInfo S
Player.
Data:
CHANNEL_NUMBER int
```
#### magicinfo_server
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_server [MAGICINFO_SERVER_URL]
MagicInfo Server URL.
Example: "http://example.com:80"
Data:
MAGICINFO_SERVER_URL str
```
#### magicinfo_content_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET magicinfo_content_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### mdc_connection
```
Usage: samsung-mdc [OPTIONS] TARGET mdc_connection [MDC_CONNECTION_TYPE]
Note: Depends on the product specification, if it is set as RJ45 then serial
MDC will not work.
Data:
MDC_CONNECTION_TYPE RS232C | RJ45
```
#### contrast
```
Usage: samsung-mdc [OPTIONS] TARGET contrast [CONTRAST]
Data:
CONTRAST int (0-100)
```
#### brightness
```
Usage: samsung-mdc [OPTIONS] TARGET brightness [BRIGHTNESS]
Data:
BRIGHTNESS int (0-100)
```
#### sharpness
```
Usage: samsung-mdc [OPTIONS] TARGET sharpness [SHARPNESS]
Data:
SHARPNESS int (0-100)
```
#### color
```
Usage: samsung-mdc [OPTIONS] TARGET color [COLOR]
Data:
COLOR int (0-100)
```
#### tint
```
Usage: samsung-mdc [OPTIONS] TARGET tint [TINT]
Control the device tint. Adjust the ratio of green to red tint level.
Red: TINT value, Green: ( 100 - TINT ) value.
Note: Tint could only be set in 50 Steps (0, 2, 4, 6... 100).
Data:
TINT int (0-100)
```
#### h_position
```
Usage: samsung-mdc [OPTIONS] TARGET h_position H_POSITION_MOVE_TO
Data:
H_POSITION_MOVE_TO LEFT | RIGHT
```
#### v_position
```
Usage: samsung-mdc [OPTIONS] TARGET v_position V_POSITION_MOVE_TO
Data:
V_POSITION_MOVE_TO UP | DOWN
```
#### auto_power
```
Usage: samsung-mdc [OPTIONS] TARGET auto_power [AUTO_POWER_STATE]
Data:
AUTO_POWER_STATE OFF | ON
```
#### clear_menu
```
Usage: samsung-mdc [OPTIONS] TARGET clear_menu
```
#### ir_state
```
Usage: samsung-mdc [OPTIONS] TARGET ir_state [IR_STATE]
Enables/disables IR (Infrared) receiving function (Remote Control).
Working Condition: * Can operate regardless of whether power is ON/OFF. (If
DPMS Situation in LFD, it operate Remocon regardless of set value).
Data:
IR_STATE DISABLED | ENABLED
```
#### rgb_contrast
```
Usage: samsung-mdc [OPTIONS] TARGET rgb_contrast [CONTRAST]
Data:
CONTRAST int (0-100)
```
#### rgb_brightness
```
Usage: samsung-mdc [OPTIONS] TARGET rgb_brightness [BRIGHTNESS]
Data:
BRIGHTNESS int (0-100)
```
#### auto_adjustment_on
```
Usage: samsung-mdc [OPTIONS] TARGET auto_adjustment_on
```
#### color_tone
```
Usage: samsung-mdc [OPTIONS] TARGET color_tone [COLOR_TONE_STATE]
Data:
COLOR_TONE_STATE COOL_2 | COOL_1 | NORMAL | WARM_1 | WARM_2 | OFF
```
#### color_temperature
```
Usage: samsung-mdc [OPTIONS] TARGET color_temperature [HECTO_KELVIN]
Color temperature function.
Unit is hectoKelvin (hK) (x*100 Kelvin) (example: 28 = 2800K).
Supported values - 28, 30, 35, 40... 160.
For older models: 0-10=(x*100K + 5000K), 253=2800K, 254=3000K, 255=4000K
Data:
HECTO_KELVIN int
```
#### standby
```
Usage: samsung-mdc [OPTIONS] TARGET standby [STANDBY_STATE]
Data:
STANDBY_STATE OFF | ON | AUTO
```
#### auto_lamp
```
Usage: samsung-mdc [OPTIONS] TARGET auto_lamp [MAX_TIME MAX_LAMP_VALUE
MIN_TIME MIN_LAMP_VALUE]
Auto Lamp function (backlight).
Note: When Manual Lamp Control is on, Auto Lamp Control will automatically
turn off.
Data:
MAX_TIME time (format: %H:%M)
MAX_LAMP_VALUE int (0-100)
MIN_TIME time (format: %H:%M)
MIN_LAMP_VALUE int (0-100)
```
#### manual_lamp
```
Usage: samsung-mdc [OPTIONS] TARGET manual_lamp [LAMP_VALUE]
Manual Lamp function (backlight).
Note: When Auto Lamp Control is on, Manual Lamp Control will automatically
turn off.
Data:
LAMP_VALUE int (0-100)
```
#### inverse
```
Usage: samsung-mdc [OPTIONS] TARGET inverse [INVERSE_STATE]
Data:
INVERSE_STATE OFF | ON
```
#### video_wall_mode
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_mode [VIDEO_WALL_MODE]
Get/Set the device in aspect ratio of the video wall.
FULL: stretch input source to fill display
NATURAL: Keep aspect ratio of input source; do not fill display.
Note: Needs VIDEO_WALL_STATE to be ON.
Data:
VIDEO_WALL_MODE NATURAL | FULL
```
#### safety_lock
```
Usage: samsung-mdc [OPTIONS] TARGET safety_lock [LOCK_STATE]
Data:
LOCK_STATE OFF | ON
```
#### panel_lock
```
Usage: samsung-mdc [OPTIONS] TARGET panel_lock [LOCK_STATE]
Data:
LOCK_STATE OFF | ON
```
#### channel_change
```
Usage: samsung-mdc [OPTIONS] TARGET channel_change CHANGE_TO
Data:
CHANGE_TO UP | DOWN
```
#### volume_change
```
Usage: samsung-mdc [OPTIONS] TARGET volume_change CHANGE_TO
Data:
CHANGE_TO UP | DOWN
```
#### ticker
```
Usage: samsung-mdc [OPTIONS] TARGET ticker [ON_OFF START_TIME END_TIME
POS_HORIZ POS_VERTI MOTION_ON_OFF MOTION_DIR MOTION_SPEED
FONT_SIZE FOREGROUND_COLOR BACKGROUND_COLOR
FOREGROUND_OPACITY BACKGROUND_OPACITY MESSAGE]
Get/Set the device ticker. (Show text message overlay on the screen)
Note: POS_HORIZ or POS_VERT are NONE in GET response if unsupported by the
display.
Data:
ON_OFF bool
START_TIME time (format: %H:%M)
END_TIME time (format: %H:%M)
POS_HORIZ CENTER | LEFT | RIGHT | NONE
POS_VERTI MIDDLE | TOP | BOTTOM | NONE
MOTION_ON_OFF bool
MOTION_DIR LEFT | RIGHT | UP | DOWN
MOTION_SPEED NORMAL | SLOW | FAST
FONT_SIZE STANDARD | SMALL | LARGE
FOREGROUND_COLOR BLACK | WHITE | RED | GREEN | BLUE | YELLOW | MAGENTA |
CYAN
BACKGROUND_COLOR BLACK | WHITE | RED | GREEN | BLUE | YELLOW | MAGENTA |
CYAN
FOREGROUND_OPACITY FLASHING | FLASH_ALL | OFF
BACKGROUND_OPACITY SOLID | TRANSPARENT | TRANSLUCENT | UNKNOWN
MESSAGE str
```
#### device_name
```
Usage: samsung-mdc [OPTIONS] TARGET device_name
It reads the device name which user set up in network. Shows the information
about entered device name.
Data:
DEVICE_NAME str
```
#### osd
```
Usage: samsung-mdc [OPTIONS] TARGET osd [OSD_ENABLED]
Turns OSD (On-screen display) on/off.
Data:
OSD_ENABLED bool
```
#### picture_mode
```
Usage: samsung-mdc [OPTIONS] TARGET picture_mode [PICTURE_MODE_STATE]
Data:
PICTURE_MODE_STATE DYNAMIC | STANDARD | MOVIE | CUSTOM_TV | NATURAL |
CALIBRATION_TV | ENTERTAIN | INTERNET | TEXT | CUSTOM |
ADVERTISEMENT | INFORMATION | CALIBRATION |
SHOP_MALL_VIDEO | SHOP_MALL_TEXT | OFFICE_SCHOOL_VIDEO |
OFFICE_SCHOOL_TEXT | TERMINAL_STATION_VIDEO |
TERMINAL_STATION_TEXT | VIDEO_WALL_VIDEO |
VIDEO_WALL_TEXT | HDR_PLUS | OFF | RESERVED_OTHER
```
#### sound_mode
```
Usage: samsung-mdc [OPTIONS] TARGET sound_mode [SOUND_MODE_STATE]
Data:
SOUND_MODE_STATE STANDARD | MUSIC | MOVIE | SPEECH | CUSTOM | AMPLIFY |
OPTIMIZED
```
#### all_keys_lock
```
Usage: samsung-mdc [OPTIONS] TARGET all_keys_lock [LOCK_STATE]
Turns both REMOCON and Panel Key Lock function on/off.
Note: Can operate regardless of whether power is on/off.
Data:
LOCK_STATE OFF | ON
```
#### panel_on_time
```
Usage: samsung-mdc [OPTIONS] TARGET panel_on_time
Get the device panel on total time.
Return value increased every 10 mins. To get hours use "MIN10 / 6".
Data:
MIN10 int
```
#### video_wall_state
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_state [VIDEO_WALL_STATE]
Get/Set the device in video wall state. This will split the primary input
source into smaller N number of squares and display them instead.
Note: The device needs to be capable of this operation. Usually a primary
high resolution source signal is daisy chained to lower resolution displays
in a video wall using HDMI/DP.
Data:
VIDEO_WALL_STATE OFF | ON
```
#### video_wall_model
```
Usage: samsung-mdc [OPTIONS] TARGET video_wall_model [MODEL SERIAL]
Get/Set video wall model.
MODEL: Size of the wall in (x, y) coordinates; ie. "2,2" or "4,1"
SERIAL: Serial number - position of the display in the video wall, counting
from the first display.
Note: Needs VIDEO_WALL_STATE to be ON.
Data:
MODEL Video Wall model (format: X,Y eg. 4,5)
SERIAL int (1-255)
```
#### model_name
```
Usage: samsung-mdc [OPTIONS] TARGET model_name
Data:
MODEL_NAME str
```
#### energy_saving
```
Usage: samsung-mdc [OPTIONS] TARGET energy_saving [ENERGY_SAVING_STATE]
Data:
ENERGY_SAVING_STATE OFF | LOW | MEDIUM | HIGH | PICTURE_OFF
```
#### reset
```
Usage: samsung-mdc [OPTIONS] TARGET reset RESET_TARGET
Data:
RESET_TARGET PICTURE | SOUND | SETUP | ALL | SCREEN_DISPLAY
```
#### osd_type
```
Usage: samsung-mdc [OPTIONS] TARGET osd_type [OSD_TYPE OSD_ENABLED]
Turns OSD (On-screen display) specific message types on/off.
Data:
OSD_TYPE SOURCE | NOT_OPTIMUM_MODE | NO_SIGNAL | MDC | SCHEDULE_CHANNEL
OSD_ENABLED bool
```
#### timer_13
```
Usage: samsung-mdc [OPTIONS] TARGET timer_13 TIMER_ID [ON_TIME ON_ENABLED
OFF_TIME OFF_ENABLED REPEAT MANUAL_WEEKDAY VOLUME
INPUT_SOURCE_STATE HOLIDAY_APPLY]
Integrated timer function (13 data-length version).
Note: This depends on product and will not work on newer versions.
Data:
TIMER_ID int (1-7)
ON_TIME time (format: %H:%M)
ON_ENABLED bool
OFF_TIME time (format: %H:%M)
OFF_ENABLED bool
REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
VOLUME int (0-100)
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
HOLIDAY_APPLY DONT_APPLY_BOTH | APPLY_BOTH | ON_TIMER_ONLY_APPLY |
OFF_TIMER_ONLY_APPLY
```
#### timer_15
```
Usage: samsung-mdc [OPTIONS] TARGET timer_15 TIMER_ID [ON_TIME ON_ENABLED
OFF_TIME OFF_ENABLED ON_REPEAT ON_MANUAL_WEEKDAY OFF_REPEAT
OFF_MANUAL_WEEKDAY VOLUME INPUT_SOURCE_STATE HOLIDAY_APPLY]
Integrated timer function (15 data-length version).
Note: This depends on product and will not work on older versions.
ON_TIME/OFF_TIME: turn ON/OFF display at specific time of day
ON_ACTIVE/OFF_ACTIVE: if timer is not active, values are ignored, so there
may be only OFF timer, ON timer, or both.
REPEAT: On which day timer is enabled (combined with HOLIDAY_APPLY and
MANUAL_WEEKDAY)
Data:
TIMER_ID int (1-7)
ON_TIME time (format: %H:%M)
ON_ENABLED bool
OFF_TIME time (format: %H:%M)
OFF_ENABLED bool
ON_REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
ON_MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
OFF_REPEAT ONCE | EVERYDAY | MON_FRI | MON_SAT | SAT_SUN |
MANUAL_WEEKDAY
OFF_MANUAL_WEEKDAY list(,) SUN | MON | TUE | WED | THU | FRI | SAT
VOLUME int (0-100)
INPUT_SOURCE_STATE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 | DVI |
PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 | HDMI1_PC |
HDMI2 | HDMI2_PC | DISPLAY_PORT_1 | DISPLAY_PORT_2 |
DISPLAY_PORT_3 | RF_TV | HDMI3 | HDMI3_PC | HDMI4 |
HDMI4_PC | TV_DTV | PLUG_IN_MODE | HD_BASE_T | OCM |
MEDIA_MAGIC_INFO_S | WIDI_SCREEN_MIRRORING |
INTERNAL_USB | URL_LAUNCHER | IWB | WEB_BROWSER |
REMOTE_WORKSPACE
HOLIDAY_APPLY DONT_APPLY_BOTH | APPLY_BOTH | ON_TIMER_ONLY_APPLY |
OFF_TIMER_ONLY_APPLY
```
#### clock_m
```
Usage: samsung-mdc [OPTIONS] TARGET clock_m [DATETIME]
Current time function (minute precision).
Note: This is for models developed until 2013. For newer models see CLOCK_S
function (seconds precision).
Data:
DATETIME datetime (format: %Y-%m-%dT%H:%M / %Y-%m-%d %H:%M)
```
#### holiday_set
```
Usage: samsung-mdc [OPTIONS] TARGET holiday_set HOLIDAY_MANAGE START_MONTH
START_DAY END_MONTH END_DAY
Add/Delete the device holiday schedule with the holiday schedule itself
start month/day and end month/day.
Note: On DELETE_ALL all parameters should be 0x00.
Data:
HOLIDAY_MANAGE ADD | DELETE | DELETE_ALL
START_MONTH int (0-12)
START_DAY int (0-31)
END_MONTH int (0-12)
END_DAY int (0-31)
```
#### holiday_get
```
Usage: samsung-mdc [OPTIONS] TARGET holiday_get [INDEX]
Get the device holiday schedule.
If INDEX is not specified, returns total number of Holiday Information.
Data:
INDEX int
Response extra:
START_MONTH int
START_DAY int
END_MONTH int
END_DAY int
```
#### virtual_remote
```
Usage: samsung-mdc [OPTIONS] TARGET virtual_remote KEY_CODE
This function support that MDC command can work same as remote control.
Note: In a certain model, 0x79 CONTENT key works as HOME and 0x1F DISPLAY
key works as INFO.
Data:
KEY_CODE KEY_SOURCE | KEY_POWER | KEY_1 | KEY_2 | KEY_3 | KEY_VOLUME_UP |
KEY_4 | KEY_5 | KEY_6 | KEY_VOLUME_DOWN | KEY_7 | KEY_8 | KEY_9 |
KEY_MUTE | KEY_CHANNEL_DOWN | KEY_0 | KEY_CHANNEL_UP | KEY_GREEN |
KEY_YELLOW | KEY_CYAN | KEY_MENU | KEY_DISPLAY | KEY_DIGIT |
KEY_PIP_TV_VIDEO | KEY_EXIT | KEY_MAGICINFO | KEY_REW | KEY_STOP |
KEY_PLAY | KEY_FF | KEY_PAUSE | KEY_TOOLS | KEY_RETURN |
KEY_MAGICINFO_LITE | KEY_CURSOR_UP | KEY_CURSOR_DOWN |
KEY_CURSOR_RIGHT | KEY_CURSOR_LEFT | KEY_ENTER | KEY_RED |
KEY_LOCK | KEY_CONTENT | DISCRET_POWER_OFF | KEY_3D
```
#### network_standby
```
Usage: samsung-mdc [OPTIONS] TARGET network_standby [NETWORK_STANDBY_STATE]
Data:
NETWORK_STANDBY_STATE OFF | ON
```
#### dst
```
Usage: samsung-mdc [OPTIONS] TARGET dst [DST_STATE START_MONTH START_WEEK
START_WEEKDAY START_TIME END_MONTH END_WEEK END_WEEKDAY
END_TIME OFFSET]
Data:
DST_STATE OFF | AUTO | MANUAL
START_MONTH JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT |
NOV | DEC
START_WEEK WEEK_1 | WEEK_2 | WEEK_3 | WEEK_4 | WEEK_LAST
START_WEEKDAY SUN | MON | TUE | WED | THU | FRI | SAT
START_TIME time (format: %H:%M)
END_MONTH JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT |
NOV | DEC
END_WEEK WEEK_1 | WEEK_2 | WEEK_3 | WEEK_4 | WEEK_LAST
END_WEEKDAY SUN | MON | TUE | WED | THU | FRI | SAT
END_TIME time (format: %H:%M)
OFFSET PLUS_1_00 | PLUS_2_00
Response extra:
TUNER_SUPPORT bool
```
#### auto_id_setting
```
Usage: samsung-mdc [OPTIONS] TARGET auto_id_setting [AUTO_ID_SETTING_STATE]
Data:
AUTO_ID_SETTING_STATE START | END
```
#### display_id
```
Usage: samsung-mdc [OPTIONS] TARGET display_id DISPLAY_ID_STATE
Data:
DISPLAY_ID_STATE OFF | ON
```
#### clock_s
```
Usage: samsung-mdc [OPTIONS] TARGET clock_s [DATETIME]
Current time function (second precision).
Note: This is for models developed after 2013. For older models see CLOCK_M
function (minute precision).
Data:
DATETIME datetime (format: %Y-%m-%dT%H:%M:%S / %Y-%m-%d %H:%M:%S)
```
#### launcher_play_via
```
Usage: samsung-mdc [OPTIONS] TARGET launcher_play_via [PLAY_VIA_MODE]
Data:
PLAY_VIA_MODE MAGIC_INFO | URL_LAUNCHER | MAGIC_IWB
```
#### launcher_url_address
```
Usage: samsung-mdc [OPTIONS] TARGET launcher_url_address [URL_ADDRESS]
Data:
URL_ADDRESS str
```
#### osd_menu_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_menu_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_source_content_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_source_content_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_aspect_ratio
```
Usage: samsung-mdc [OPTIONS] TARGET osd_aspect_ratio [ASPECT_RATIO_STATE]
Get/Set the device aspect ratio under portrait mode which set the rotated
screen to be full or original.
Data:
ASPECT_RATIO_STATE FULL_SCREEN | ORIGINAL
```
#### osd_pip_orientation
```
Usage: samsung-mdc [OPTIONS] TARGET osd_pip_orientation
[ORIENTATION_MODE_STATE]
Data:
ORIENTATION_MODE_STATE LANDSCAPE_0 | PORTRAIT_270 | LANDSCAPE_180 |
PORTRAIT_90
```
#### osd_menu_size
```
Usage: samsung-mdc [OPTIONS] TARGET osd_menu_size [MENU_SIZE_STATE]
Data:
MENU_SIZE_STATE ORIGINAL | MEDIUM | SMALL
```
#### auto_source_switch
```
Usage: samsung-mdc [OPTIONS] TARGET auto_source_switch
[AUTO_SOURCE_SWITCH_STATE]
Data:
AUTO_SOURCE_SWITCH_STATE OFF | ON
```
#### auto_source
```
Usage: samsung-mdc [OPTIONS] TARGET auto_source [PRIMARY_SOURCE_RECOVERY
PRIMARY_SOURCE SECONDARY_SOURCE]
Data:
PRIMARY_SOURCE_RECOVERY OFF | ON
PRIMARY_SOURCE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 |
DVI | PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 |
HDMI1_PC | HDMI2 | HDMI2_PC | DISPLAY_PORT_1 |
DISPLAY_PORT_2 | DISPLAY_PORT_3 | RF_TV | HDMI3 |
HDMI3_PC | HDMI4 | HDMI4_PC | TV_DTV | PLUG_IN_MODE
| HD_BASE_T | OCM | MEDIA_MAGIC_INFO_S |
WIDI_SCREEN_MIRRORING | INTERNAL_USB | URL_LAUNCHER
| IWB | WEB_BROWSER | REMOTE_WORKSPACE
SECONDARY_SOURCE NONE | S_VIDEO | COMPONENT | AV | AV2 | SCART1 |
DVI | PC | BNC | DVI_VIDEO | MAGIC_INFO | HDMI1 |
HDMI1_PC | HDMI2 | HDMI2_PC | DISPLAY_PORT_1 |
DISPLAY_PORT_2 | DISPLAY_PORT_3 | RF_TV | HDMI3 |
HDMI3_PC | HDMI4 | HDMI4_PC | TV_DTV | PLUG_IN_MODE
| HD_BASE_T | OCM | MEDIA_MAGIC_INFO_S |
WIDI_SCREEN_MIRRORING | INTERNAL_USB | URL_LAUNCHER
| IWB | WEB_BROWSER | REMOTE_WORKSPACE
```
#### panel
```
Usage: samsung-mdc [OPTIONS] TARGET panel [PANEL_STATE]
Data:
PANEL_STATE ON | OFF
```
#### screen_mute
```
Usage: samsung-mdc [OPTIONS] TARGET screen_mute [SCREEN_MUTE_STATUS]
Data:
SCREEN_MUTE_STATUS ON | OFF
```
#### script
```
Usage: samsung-mdc [OPTIONS] TARGET script [OPTIONS] SCRIPT_FILE
Script file with commands to execute.
Commands for multiple targets will be running async, but commands order is
preserved for device (and is running on same connection), exit on first fail
unless retry options provided.
You may use jinja2 templating engine to {% include "other_script" %} or {{
VAR_KEY }} rendering in combination with --var VAR_KEY VAR_VALUE options.
It's highly recommended to use sleep option for virtual_remote!
Additional commands:
sleep SECONDS (FLOAT, --sleep option for this command is ignored)
disconnect
Format:
command1 [ARGS]...
command2 [ARGS]...
Example: samsung-mdc ./targets.txt script -s 3 -r 1 -v KEY enter ./commands.txt
# commands.txt content
power on
sleep 5
clear_menu
virtual_remote key_menu
virtual_remote key_down
virtual_remote {{ KEY }}
clear_menu
Arguments:
script_file Text file with commands, separated by newline.
Options:
-s, --sleep FLOAT Pause between commands (seconds)
--retry-command INTEGER Retry command if failed (count)
--retry-command-sleep FLOAT Sleep before command retry (seconds)
-r, --retry-script INTEGER Retry script if failed (count)
--retry-script-sleep FLOAT Sleep before script retry (seconds)
--ignore-nak Ignore negative acknowledgement errors
-v, --var NAME VALUE Variable "{{ NAME }}" in script will be
replaced by VALUE
--help Show this message and exit.
```
#### raw
```
Usage: samsung-mdc [OPTIONS] TARGET raw [OPTIONS] COMMAND [DATA]
Helper command to send raw data for test purposes.
Arguments:
command Command and (optionally) subcommand (example: a1 or a1:b2)
data Data payload if any (example: a1:b2)
```
## Troubleshooting
### Finding DISPLAY ID
On most devices it's usually `0` or `1`. Some devices may use `255` (0xFF) or `254` (0xFE) as all/any display, but behavior in such cases for more than 1 display is undefined.
Display id can be found using remote control: `Home` -> `ID Settings`.
### NAKError
If you receive NAK errors on some commands, you may try to:
* Ensure that device is powered on and completely loaded
* Switch to input source HDMI1
* Reboot device
* Reset all settings
* Disable MagicINFO
* Factory reset (using "Service Menu")
## Python example
```python3
import asyncio
from samsung_mdc import MDC
async def main(ip, display_id):
async with MDC(ip, verbose=True) as mdc:
# First argument of command is always display_id
status = await mdc.status(display_id)
print(status) # Result is always tuple
if status[0] != MDC.power.POWER_STATE.ON:
# Command arguments are always Sequence (tuple, list)
await mdc.power(display_id, [MDC.power.POWER_STATE.ON])
await mdc.close() # Force reconnect on next command
await asyncio.sleep(15)
await mdc.display_id(display_id, [MDC.display_id.DISPLAY_ID_STATE.ON])
# You may also use names or values instead of enums
await mdc.display_id(display_id, ['ON']) # same
await mdc.display_id(display_id, [1]) # same
# If you see "Connected" and timeout error, try other display_id (0, 1)
asyncio.run(main('192.168.0.10', 1))
```
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112705.0
python-samsung-mdc-1.15.0/python_samsung_mdc.egg-info/SOURCES.txt 0000644 0001751 0000200 00000001012 14736575101 024205 0 ustar 00runner docker LICENSE
README.md
setup.cfg
setup.py
python_samsung_mdc.egg-info/PKG-INFO
python_samsung_mdc.egg-info/SOURCES.txt
python_samsung_mdc.egg-info/dependency_links.txt
python_samsung_mdc.egg-info/entry_points.txt
python_samsung_mdc.egg-info/requires.txt
python_samsung_mdc.egg-info/top_level.txt
samsung_mdc/__init__.py
samsung_mdc/__main__.py
samsung_mdc/cli.py
samsung_mdc/command.py
samsung_mdc/commands.py
samsung_mdc/connection.py
samsung_mdc/exceptions.py
samsung_mdc/fields.py
samsung_mdc/utils.py
samsung_mdc/version.py ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112704.0
python-samsung-mdc-1.15.0/python_samsung_mdc.egg-info/dependency_links.txt 0000644 0001751 0000200 00000000001 14736575100 026374 0 ustar 00runner docker
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112704.0
python-samsung-mdc-1.15.0/python_samsung_mdc.egg-info/entry_points.txt 0000644 0001751 0000200 00000000064 14736575100 025624 0 ustar 00runner docker [console_scripts]
samsung-mdc = samsung_mdc.cli:cli
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112704.0
python-samsung-mdc-1.15.0/python_samsung_mdc.egg-info/requires.txt 0000644 0001751 0000200 00000000174 14736575100 024730 0 ustar 00runner docker click
jinja2
pyserial-asyncio
[all]
pyserial-asyncio
[serial]
pyserial-asyncio
[test]
pytest
pytest-asyncio
nest-asyncio
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112704.0
python-samsung-mdc-1.15.0/python_samsung_mdc.egg-info/top_level.txt 0000644 0001751 0000200 00000000014 14736575100 025053 0 ustar 00runner docker samsung_mdc
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1736112705.0172837
python-samsung-mdc-1.15.0/samsung_mdc/ 0000755 0001751 0000200 00000000000 14736575101 017234 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/__init__.py 0000644 0001751 0000200 00000001115 14736575073 021353 0 ustar 00runner docker from typing import Dict
import inspect
from .version import __version__ # noqa
from .connection import MDCConnection
from .command import Command
from . import commands
class MDC(MDCConnection):
_commands: Dict[str, Command] = {}
@classmethod
def register_command(cls, command):
cls._commands[command.name] = command
setattr(cls, command.name, command)
for name, cls in inspect.getmembers(commands, inspect.isclass):
if (issubclass(cls, Command)
and cls is not Command
and not name.startswith('_')):
MDC.register_command(cls())
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/__main__.py 0000644 0001751 0000200 00000000125 14736575073 021334 0 ustar 00runner docker if __name__ == '__main__':
from .cli import cli
cli(prog_name='samsung-mdc')
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/cli.py 0000644 0001751 0000200 00000070015 14736575073 020370 0 ustar 00runner docker # NOTE: This module is hacking some internals of "click" library
# and may not work stable in case of click major changes in future versions.
# Tested with click==7.1.2
# BEWARE: If you want something flexible and overridable for cli processing,
# "click" just may not be your choice...
from datetime import time, datetime
from enum import Enum
import asyncio
import re
import os.path
from sys import argv as sys_argv
import platform
from traceback import print_exception as _print_exception
import click
from . import MDC, fields, __version__
from .utils import parse_hex, repr_hex
from .exceptions import NAKError
def print_exception(exc):
# compatibility, not required after Python 3.10
_print_exception(type(exc), exc, exc.__traceback__)
def _parse_int(x):
return int(x, 16) if x.startswith('0x') else int(x)
def _repr(val, root=True):
if isinstance(val, list):
# quickfix for script command repr
# maybe this should better be fixed in create_mdc_call
return ' '.join(_repr(x, root) for x in val)
if isinstance(val, tuple):
return (' ' if root else ',').join(_repr(x, False) for x in val)
if isinstance(val, (datetime, time)):
return val.isoformat()
elif isinstance(val, Enum):
return f'<{val.__class__.__name__}.{val.name}:{val.value}>'
return str(val)
def trim_docstring(docstring):
# from https://www.python.org/dev/peps/pep-0257/
if not docstring:
return ''
# Convert tabs to spaces (following the normal Python rules)
# and split into a list of lines:
lines = docstring.expandtabs().splitlines()
# Determine minimum indentation (first line doesn't count):
indent = 1024
for line in lines[1:]:
stripped = line.lstrip()
if stripped:
indent = min(indent, len(line) - len(stripped))
# Remove indentation (first line is special):
trimmed = [lines[0].strip()]
if indent < 1024:
for line in lines[1:]:
trimmed.append(line[indent:].rstrip())
# Strip off trailing and leading blank lines:
while trimmed and not trimmed[-1]:
trimmed.pop()
while trimmed and not trimmed[0]:
trimmed.pop(0)
# Return a single string:
return '\n'.join(trimmed)
class EnumChoice(click.ParamType):
# This is not part of a click because of... WTF?
# https://github.com/pallets/click/issues/605
# All proposals in ticket are bad anyway, so impementing
# another one...
name = 'enum_choice'
def __init__(self, enum):
self.enum = enum
def get_metavar(self, param):
return f'{self.enum.__name__}'
def get_missing_message(self, param):
return "Choose from:\n\t{}.".format(",\n\t".join(
self.enum.__members__))
def convert(self, value, param, ctx):
if value.upper() in self.enum.__members__:
return self.enum[value.upper()]
else:
# NOTE: Specific part for this project...
# not sure how to make it more universal
try:
value = _parse_int(value)
except ValueError:
pass
if value in [v.value for v in self.enum]:
return value
missing_message = self.get_missing_message(param)
self.fail(f"Invalid choice: {value}\n{missing_message}")
def __repr__(self):
return f"EnumChoice({self.enum})"
class EnumTuple(EnumChoice):
name = "enum_tuple"
def convert(self, value, param, ctx):
if not value:
return tuple()
convert_ = super().convert
return tuple(convert_(v, param, ctx)
for v in value.split(','))
def __repr__(self):
return f"EnumTuple({self.enum})"
class Time(click.ParamType):
name = "time"
def convert(self, value, param, ctx):
try:
return time(*map(int, value.split(':')))
except ValueError:
self.fail("{} is not a valid time".format(value), param, ctx)
def __repr__(self):
return "TIME"
class ArgumentWithHelp(click.Argument):
# Extends Argument with "help" parameter,
# so they can be rendered in help same way as options
# See ArgumentWithHelpCommandMixin
def __init__(self, *args, help=None, **kwargs):
super().__init__(*args, **kwargs)
self.help = help
class ArgumentWithHelpCommandMixin:
def __init__(self, *args, help_arguments_label='Arguments', **kwargs):
self.help_arguments_label = help_arguments_label
super().__init__(*args, **kwargs)
def format_arguments(self, ctx, formatter):
args = []
for param in self.get_params(ctx):
if isinstance(param, click.Argument):
help = getattr(param, 'help', None)
args.append((param.metavar or param.name, help or ''))
if args:
with formatter.section(self.help_arguments_label):
formatter.write_dl(args)
def format_options(self, ctx, formatter):
# Override this to format ArgumentWithHelp before options
self.format_arguments(ctx, formatter)
super().format_options(ctx, formatter)
class FixedSubcommand(ArgumentWithHelpCommandMixin, click.Command):
# This mixin contains some common overrides and fixes for click
# subcommand processing
def parse_args(self, ctx, args):
# Avoid "Try 'samsung-mdc {command} --help' for help." message
# that renders in UsageError,
# because it's wrong command because of required group command args
# (maybe this will be fixed in future click versions?)
try:
super().parse_args(ctx, args)
except click.UsageError as exc:
exc.cmd = None
raise
def format_usage(self, ctx, formatter):
# 1. Invoking this on subcommand doesn't show group required arguments
# and options, so Usage is no valid in this case.
# 2. Invoking this on wrong context mess things up completely,
# but we need it from group context to be able to do
# "samsung-mdc --help command1"
# NOTE: this works only on 1-st level subcommand, for nesting
# you may want to make root parts recursive
root_path = ctx.command_path.split()[0]
root_command = ctx.parent and ctx.parent.command or ctx.command
pieces = click.Command.collect_usage_pieces(root_command, ctx)
pieces.append(self.name)
pieces.extend(self.collect_usage_pieces(ctx))
formatter.write_usage(root_path, " ".join(pieces))
class Group(click.Group):
def get_help_option(self, ctx):
# Override this to pass parameters to --help
# This is needed to be able to do "--help COMMAND"
# without group required arguments
def show_help(ctx, param, value):
if value and not ctx.resilient_parsing:
# Match registered commands and show help for all of them
commands = [
ctx.command.commands[name]
for name in sys_argv[1:]
if name in ctx.command.commands
]
if commands:
for i, command in enumerate(commands):
if i:
click.echo()
click.echo(f'Command: {command.name}')
click.echo(command.get_help(ctx), color=ctx.color)
ctx.exit()
else:
# Show default help if no commands were matched
click.echo(ctx.get_help(), color=ctx.color)
ctx.exit()
return click.Option(
['-h', '--help'],
is_flag=True,
is_eager=True,
expose_value=False,
callback=show_help,
help="Show this message and exit.",
)
def list_commands(self, ctx):
# Avoid sorting commands by name, sort by CMD code instead
# (as it goes in documentation)
def key(c):
# not mdc commands ("script") should go last
return (hasattr(c, 'mdc_command')
and c.mdc_command.get_order() or (1000,))
return [c.name for c in sorted(self.commands.values(), key=key)]
class MDCClickCommand(FixedSubcommand):
def __init__(self, name, mdc_command, **kwargs):
self.mdc_command = mdc_command
name = mdc_command.name
kwargs['short_help'] = self._get_params_hint()
if not mdc_command.SET:
kwargs['short_help'] = f'({kwargs["short_help"]})'
kwargs['help_arguments_label'] = 'Data'
kwargs['help'] = trim_docstring(mdc_command.__doc__)
# Registering params from DATA format
kwargs.setdefault('params', [])
if isinstance(mdc_command.CMD, fields.Field):
kwargs['params'].append(
self._get_argument_from_mdc_field(mdc_command.CMD, 'cmd'))
for i, field in enumerate(mdc_command.DATA):
kwargs['params'].append(
self._get_argument_from_mdc_field(field, i))
super().__init__(name, **kwargs)
def format_arguments(self, ctx, formatter):
super().format_arguments(ctx, formatter)
if getattr(self.mdc_command, 'RESPONSE_EXTRA', []):
args = [
self._get_argument_from_mdc_field(field)
for field in self.mdc_command.RESPONSE_EXTRA
]
with formatter.section('Response extra'):
formatter.write_dl((
param.metavar or param.name, getattr(param, 'help', '')
) for param in args)
def _get_argument_from_mdc_field(self, field, ident=None):
if isinstance(field, fields.Bitmask):
type = EnumTuple(field.enum)
help = ('list(,) ' +
(' | '.join(field.enum.__members__.keys())))
elif isinstance(field, fields.Enum):
type = EnumChoice(field.enum)
help = ' | '.join(field.enum.__members__.keys())
elif isinstance(field, fields.DateTime):
if field.seconds:
formats = [
"%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S",
]
else:
formats = [
"%Y-%m-%dT%H:%M", "%Y-%m-%d %H:%M",
]
type = click.DateTime(formats)
help = f'datetime (format: {" / ".join(formats)})'
elif isinstance(field, (fields.Time, fields.Time12H)):
type = Time()
if isinstance(field, fields.Time12H) or not field.seconds:
help = 'time (format: %H:%M)'
else:
help = 'time (format: %H:%M:%S)'
elif isinstance(field, fields.IPAddress):
type = str
help = 'IP address'
elif isinstance(field, fields.VideoWallModel):
type = str
help = 'Video Wall model (format: X,Y eg. 4,5)'
else:
type = {
fields.Str: str,
fields.StrCoded: str,
fields.Bool: bool,
fields.Int: int,
}[field.__class__]
help = type.__name__.lower()
if type is int and field.range:
help += f' ({field.range.start}-{field.range.stop - 1})'
return ArgumentWithHelp(
[f'data_{ident}' if ident else field.name],
metavar=field.name, type=type, help=help)
def _get_params_hint(self):
params = ' '.join([f.name for f in self.mdc_command.DATA])
if self.mdc_command.GET and self.mdc_command.SET and params:
params = f'[{params}]'
if isinstance(self.mdc_command.CMD, fields.Field):
# parametrized CMD is always required argument
params = f'{self.mdc_command.CMD.name} {params}'
return params
def collect_usage_pieces(self, ctx):
# We need ALL arguments to be OR optional, OR required,
# so we can't use required on arguments and need to show
# it properly in usage string
if self.mdc_command.SET:
return [self._get_params_hint()]
return []
def parse_args(self, ctx, args):
# We need ALL arguments to be OR optional, OR required,
# so if there is no arguments supplied - this is proper
# GET command
# Except parametrized CMD - special case for timer,
# we have 14 almost identical commands otherwise...
if isinstance(self.mdc_command.CMD, fields.Field):
if self.mdc_command.GET and len(args) == 1:
if '--help' in sys_argv:
return super().parse_args(ctx, args)
parser = self.make_parser(ctx)
opts, args, param_order = parser.parse_args(args=args)
for param in self.get_params(ctx):
value, args = param.handle_parse_result(
ctx, opts, args)
break # after first argument
ctx.args = args
return
elif not args and self.mdc_command.GET:
ctx.args = args
return args
try:
super().parse_args(ctx, args)
except click.UsageError as exc:
# Avoid parameters validation on GET-only commands
if not self.mdc_command.SET:
exc = click.UsageError('Readonly command doesn\'t accept '
'any arguments', ctx)
exc.cmd = None # see FixedSubcommand for reason
raise exc
raise
def create_mdc_call(self, params):
args = tuple(params.values())
if isinstance(self.mdc_command.CMD, fields.Field):
args = args[0], args[1:]
else:
args = [args] if args else []
if args and not self.mdc_command.SET:
raise click.UsageError('Readonly command doesn\'t accept '
'any arguments')
async def mdc_call(connection, display_id):
try:
print(f'{display_id}@{connection.target}',
_repr(await self.mdc_command(connection, display_id,
*args)))
except Exception as exc:
print(f'{display_id}@{connection.target}',
f'{exc.__class__.__name__}: {exc}')
raise
mdc_call.name = self.name
mdc_call.args = args
return mdc_call
class MDCTargetParamType(click.ParamType):
name = 'mdc_target'
_win_com_port_regexp = re.compile(r'COM\d+', re.IGNORECASE)
# def get_missing_message(self, param):
# return param.help
def convert_target(self, value, param, ctx):
if '@' not in value:
self.fail('DISPLAY_ID required (try 0, 1)')
display_id, addr = value.split('@')
try:
display_id = _parse_int(display_id)
except ValueError:
self.fail(
f'Invalid DISPLAY_ID "{display_id}" '
'(int or hex, example: 1, 0x01, 254, 0xFE)')
if ':' in addr:
ip, port = addr.split(':')
try:
port = int(port)
except ValueError:
self.fail(f'Invalid PORT "{port}"')
return 'tcp', f'{ip}:{port}', display_id
elif (
'/' in addr or addr.startswith('.')
or self._win_com_port_regexp.match(addr)
):
return 'serial', addr, display_id
return 'tcp', addr, display_id
def convert(self, value, param, ctx):
if '@' in value:
return [self.convert_target(value, param, ctx)]
elif (
self._win_com_port_regexp.match(value)
or value.startswith('/dev/')
):
self.fail('Looks like you want to use serial port, '
'but DISPLAY_ID required (try 0, 1)')
else:
if not os.path.exists(value):
self.fail(f'FILENAME "{value}" does not exist.')
data = open(value).read()
data = [
(i + 1, line.strip())
for i, line in enumerate(data.split('\n'))
if line.strip() and not line.strip().startswith('#')
]
targets = []
for lineno, line in data:
try:
targets.append(self.convert_target(line, param, ctx))
except click.UsageError as exc:
exc.message = (f'{value}:{lineno}: "{line}": '
f'{exc.message}')
raise
if not targets:
self.fail(
f'FILENAME "{value} is empty.')
return targets
MAIN_HELP = """
Try 'samsung-mdc --help COMMAND' for command info\n
For multiple targets commands will be running async,
so result order may differ.
TARGET may be:
\b
DISPLAY_ID@IP[:PORT] (default port: 1515, example: 0@192.168.0.10:1515)
FILENAME with target list (separated by newline)
\b
For serial port connection:
DISPLAY_ID@PORT_NAME for Windows (example: 1@COM1)
DISPLAY_ID@PORT_PATH (example: 1@/dev/ttyUSB0)
We're trying to make autodetection of connection mode by port name,
but you may want to use --mode option.
"""
@click.group(cls=Group, help=MAIN_HELP)
@click.version_option(version=__version__)
@click.argument('target', metavar='TARGET',
type=MDCTargetParamType())
@click.option('-v', '--verbose', is_flag=True, default=False, type=bool)
@click.option('-m', '--mode', default='auto', help='default: auto',
type=click.Choice(('auto', 'tcp', 'serial'),
case_sensitive=False))
@click.option('-p', '--pin', default=None, type=int,
help='4-digit PIN for secured TLS connection. '
'If PIN provided, "Secured Protocol" must be enabled '
'on remote device.')
@click.option(
'-t', '--timeout', default=5, type=float, help=(
'read/write/connect timeout in seconds (default: 5) '
'(connect can be overridden with separate option)'))
@click.option('--connect-timeout', default=None, type=float)
@click.pass_context
def cli(ctx, target, verbose, mode, pin, **kwargs):
ctx.ensure_object(dict)
ctx.obj['targets'] = [(
MDC(target, auto_mode if mode == 'auto' else mode,
**{'verbose': verbose, 'pin': pin, **kwargs}),
display_id
) for auto_mode, target, display_id in target]
ctx.obj['verbose'] = verbose
def asyncio_run(call, targets, verbose=False):
if platform.system() == 'Windows':
asyncio.set_event_loop_policy(
asyncio.WindowsSelectorEventLoopPolicy())
try:
loop = asyncio.get_running_loop()
is_running_loop = True
except RuntimeError:
loop = asyncio.get_event_loop()
is_running_loop = False
loop.run_until_complete(asyncio.wait([
loop.create_task(call(*target))
for target in targets
]))
async def close(connection):
try:
await connection.close()
except Exception as exc:
if verbose:
print(f'{connection.target}',
f'{exc.__class__.__name__}: {exc}')
print_exception(exc)
# Gracefully close connections
connections = [target[0] for target in targets if target[0].is_opened]
if connections:
loop.run_until_complete(asyncio.wait([
loop.create_task(close(connection))
for connection in connections
]))
if not is_running_loop:
loop.close()
def register_command(command):
@click.pass_context
def _cmd(ctx, **kwargs):
mdc_call = ctx.command.create_mdc_call(kwargs)
failed_targets = []
async def call(connection, display_id):
try:
await mdc_call(connection, display_id)
except Exception as exc:
failed_targets.append((connection, display_id, exc))
if ctx.obj['verbose']:
print_exception(exc)
asyncio_run(call, ctx.obj['targets'], ctx.obj['verbose'])
if failed_targets:
if len(ctx.obj['targets']) > 1:
print('Failed targets:', len(failed_targets))
ctx.exit(1)
cli.command(cls=MDCClickCommand, mdc_command=command)(_cmd)
for command in MDC._commands.values():
register_command(command)
SCRIPT_HELP = """
Script file with commands to execute.
Commands for multiple targets will be running async, but
commands order is preserved for device (and is running on same connection),
exit on first fail unless retry options provided.
You may use jinja2 templating engine to {% include "other_script" %}
or {{ VAR_KEY }} rendering in combination with --var VAR_KEY VAR_VALUE options.
It\'s highly recommended to use sleep option for virtual_remote!
\b
Additional commands:
sleep SECONDS (FLOAT, --sleep option for this command is ignored)
disconnect
\b
Format:
command1 [ARGS]...
command2 [ARGS]...
\b
Example: samsung-mdc ./targets.txt script -s 3 -r 1 -v KEY enter ./commands.txt
# commands.txt content
power on
sleep 5
clear_menu
virtual_remote key_menu
virtual_remote key_down
virtual_remote {{ KEY }}
clear_menu
"""
@cli.command(help=SCRIPT_HELP, cls=FixedSubcommand)
@click.option('-s', '--sleep', default=0, type=float,
help='Pause between commands (seconds)')
@click.option('--retry-command', default=0, type=int,
help='Retry command if failed (count)')
@click.option('--retry-command-sleep', default=0, type=float,
help='Sleep before command retry (seconds)')
@click.option('-r', '--retry-script', default=0, type=int,
help='Retry script if failed (count)')
@click.option('--retry-script-sleep', default=0, type=float,
help='Sleep before script retry (seconds)')
@click.option('--ignore-nak', is_flag=True,
help='Ignore negative acknowledgement errors')
@click.option('--var', '-v', multiple=True, nargs=2, type=(str, str),
help='Variable "{{ NAME }}" in script will be replaced by VALUE',
metavar='NAME VALUE')
@click.argument('script_file', type=click.File(),
help='Text file with commands, separated by newline.',
cls=ArgumentWithHelp)
@click.pass_context
def script(ctx, script_file, sleep, retry_command, retry_command_sleep,
retry_script, retry_script_sleep, ignore_nak, var):
import shlex
var = dict(var)
retry_command_sleep = retry_command_sleep or sleep
retry_script_sleep = retry_script_sleep or sleep
script_content = script_file.read()
if var or '{{' in script_content or '{%' in script_content:
from jinja2 import Environment, FileSystemLoader, StrictUndefined
class RelativeEnvironment(Environment):
def join_path(self, template, parent):
# Allowing include based on path relative to script
return os.path.join(os.path.dirname(parent), template)
env = RelativeEnvironment(loader=FileSystemLoader(['/', './']),
undefined=StrictUndefined)
template = env.get_template(script_file.name)
script_content = template.render(**var)
def fail(lineno, line, reason):
raise click.UsageError(
f'{script_file.name}:{lineno}:"{line}": {reason}')
def create_disconnect():
async def disconnect(connection, display_id):
await connection.close()
return tuple()
disconnect.name = 'disconnect'
disconnect.args = []
return disconnect
def create_sleep(seconds):
async def sleep(connection, display_id):
await asyncio.sleep(seconds)
return tuple()
sleep.name = 'sleep'
sleep.args = [seconds]
return sleep
lines = [
(i + 1, line.strip())
for i, line in enumerate(script_content.splitlines())
]
calls = []
for lineno, line in lines:
if not line or line.startswith('#'):
continue
command, *args = shlex.split(line)
command = command.lower()
if (command not in cli.commands
and command not in ['sleep', 'disconnect']):
fail(lineno, line, f'Unknown command: {command}')
if command == 'sleep':
if len(args) != 1:
fail(lineno, line, 'Sleep command accept exactly one argument')
try:
seconds = float(args[0])
except ValueError as exc:
fail(lineno, line, f'Sleep argument must be int/float: {exc}')
calls.append(create_sleep(seconds))
elif command == 'disconnect':
if len(args):
fail(lineno, line, 'Disconnect command does not accept '
'arguments')
calls.append(create_disconnect())
else:
ctx.params.clear()
command = cli.commands[command]
try:
command.parse_args(ctx, args)
except click.UsageError as exc:
fail(lineno, line, str(exc))
calls.append(command.create_mdc_call(ctx.params))
failed_targets = []
async def call(connection, display_id):
last_exc = None
for retry_script_i in range(retry_script + 1):
if retry_script_i and retry_script_sleep:
await asyncio.sleep(retry_script_sleep)
for command_i, call_ in enumerate(calls):
if command_i and call_.name != 'sleep' and sleep:
await asyncio.sleep(sleep)
for retry_command_i in range(retry_command + 1):
if retry_command_i and retry_command_sleep:
await asyncio.sleep(retry_command_sleep)
if ctx.obj['verbose']:
print(
f'{display_id}@{connection.target}',
f'{retry_script_i}:{command_i}:{retry_command_i}',
f'{call_.name} {_repr(call_.args)}')
try:
await call_(connection, display_id)
except Exception as exc:
if ignore_nak and isinstance(exc, NAKError):
last_exc = None
break
last_exc = exc
else:
last_exc = None
break
if last_exc is not None:
break
if last_exc is None:
break
if last_exc is not None:
failed_targets.append((connection, display_id, last_exc))
print(f'{display_id}@{connection.target}',
f'Script failed indefinitely: {last_exc}')
if ctx.obj['verbose']:
print_exception(last_exc)
asyncio_run(call, ctx.obj['targets'], ctx.obj['verbose'])
if failed_targets:
if len(ctx.obj['targets']) > 1:
print('Failed targets:', len(failed_targets))
ctx.exit(1)
@cli.command(help='Helper command to send raw data for test purposes.',
cls=FixedSubcommand)
@click.argument(
'command', type=str, cls=ArgumentWithHelp,
help='Command and (optionally) subcommand (example: a1 or a1:b2)')
@click.argument(
'data', type=str, default='', cls=ArgumentWithHelp,
help='Data payload if any (example: a1:b2)')
@click.pass_context
def raw(ctx, command, data):
failed_targets = []
async def call(connection, display_id):
try:
ack, rcmd, resp_data = await connection.send(
tuple(parse_hex(command)), display_id,
parse_hex(data))
print(
f'{display_id}@{connection.target}',
'A' if ack else 'N', repr_hex(rcmd), repr_hex(resp_data)
)
except Exception as exc:
print(f'{display_id}@{connection.target}',
f'{exc.__class__.__name__}: {exc}')
failed_targets.append((connection, display_id, exc))
if ctx.obj['verbose']:
print_exception(exc)
asyncio_run(call, ctx.obj['targets'], ctx.obj['verbose'])
if failed_targets:
if len(ctx.obj['targets']) > 1:
print('Failed targets:', len(failed_targets))
ctx.exit(1)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/command.py 0000644 0001751 0000200 00000006713 14736575073 021243 0 ustar 00runner docker from typing import List, Union, Type
from functools import partial, partialmethod
from enum import Enum
from .fields import Field, Enum as EnumField
from .exceptions import MDCResponseError, NAKError
class CommandMcs(type):
def __new__(mcs, name, bases, dict):
if name.startswith('_') or name == 'Command':
return type.__new__(mcs, name, bases, dict)
if 'name' not in dict:
dict['name'] = name.lower()
if 'DATA' not in dict and bases:
# allow naive DATA inheritance
dict['DATA'] = bases[0].DATA
if '__doc__' not in dict and bases and bases[0].__doc__:
# doc is not inherited by default
dict['__doc__'] = bases[0].__doc__
dict['DATA'] = [
# convert Enum to EnumField
EnumField(x) if isinstance(x, type) and issubclass(x, Enum) else x
for x in dict['DATA']
]
dict['RESPONSE_DATA'] = [
EnumField(x) if isinstance(x, type) and issubclass(x, Enum) else x
for x in dict.get(
'RESPONSE_DATA',
dict['DATA'] + dict.get('RESPONSE_EXTRA', []))
]
cls = type.__new__(mcs, name, bases, dict)
if cls.GET:
cls.__call__.__defaults__ = (b'',)
if not cls.SET or not cls.DATA:
cls.__call__ = partialmethod(cls.__call__, data=b'')
return cls
class Command(metaclass=CommandMcs):
name: str
CMD: Union[int, Field]
SUBCMD: Union[int, None] = None
GET: bool
SET: bool
DATA: List[Union[Type[Enum], Field]]
RESPONSE_DATA: List[Union[Type[Enum], Field]]
RESPONSE_EXTRA: List[Union[Type[Enum], Field]]
async def __call__(self, connection, display_id, data):
data = self.parse_response(
await connection.send(
(self.CMD, self.SUBCMD)
if self.SUBCMD is not None else self.CMD, display_id,
self.pack_payload_data(data) if data else []
),
)
return tuple(self.parse_response_data(data))
def __get__(self, connection, cls):
# Allow Command to be bounded as instance method
if connection is None:
return self # bind to class
return partial(self, connection) # bind to instance
@staticmethod
def parse_response(response):
ack, rcmd, data = response
if not ack:
raise NAKError(data[0])
return data
@classmethod
def parse_response_data(cls, data, strict_enum=True):
rv, cursor = [], 0
for field in cls.RESPONSE_DATA:
try:
value, cursor_shift = field.parse(data[cursor:])
except Exception as exc:
raise MDCResponseError(
f'Error parsing {field.name}: {exc}',
data[cursor:]) from exc
rv.append(value)
cursor += cursor_shift
if data[cursor:]:
# Not consumed data left
raise MDCResponseError('Unparsed data left', data[cursor:])
return tuple(rv)
@classmethod
def pack_payload_data(cls, data):
rv = bytes()
for i, field in enumerate(cls.DATA):
rv += bytes(field.pack(data[i]))
if cls.DATA and len(data[i+1:]):
raise ValueError('Unpacked data left '
'(more data provided than needed)')
return rv
@classmethod
def get_order(cls):
return (cls.CMD, cls.SUBCMD)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/commands.py 0000644 0001751 0000200 00000074567 14736575073 021442 0 ustar 00runner docker from typing import TYPE_CHECKING
from .command import Command
from .fields import (
Enum as EnumField, Int, Bool, Str, StrCoded,
Time12H, Time, DateTime, Bitmask, IPAddress, VideoWallModel)
if TYPE_CHECKING:
from enum import IntEnum
else:
try:
from enum import IntEnum
except ImportError:
from enum import Enum
class IntEnum(int, Enum):
...
class _COMMON:
# It's better to re-use enumerations from "main" related commands,
# like INPUT_SOURCE.INPUT_SOURCE_STATE / PICTURE_ASPECT.PICTURE_ASPECT_STATE
# Use _COMMON namespace if there is no obvious "main" command,
# avoid using it for too simple definitions.
class ORIENTATION_MODE_STATE(IntEnum):
LANDSCAPE_0 = 0x00
PORTRAIT_270 = 0x01
LANDSCAPE_180 = 0x02
PORTRAIT_90 = 0x03
class SERIAL_NUMBER(Command):
CMD = 0x0B
GET, SET = True, False
DATA = [Str('SERIAL_NUMBER')]
class ERROR_STATUS(Command):
CMD = 0x0D
GET, SET = True, False
class LAMP_ERROR_STATE(IntEnum):
NORMAL = 0x00
ERROR = 0x01
class TEMPERATURE_ERROR_STATE(IntEnum):
NORMAL = 0x00
ERROR = 0x01
class BRIGHTNESS_SENSOR_ERROR_STATE(IntEnum):
NONE = 0x00
ERROR = 0x01
NORMAL = 0x02
class INPUT_SOURCE_ERROR_STATE(IntEnum):
"""
No_Sync Error
Note: Invalid status will be replied with app source selected state.
Error status will be replied with input signal of not supported
resolution or no signal.
"""
NORMAL = 0x00
ERROR = 0x01
INVALID = 0x02
class FAN_ERROR_STATE(IntEnum):
NORMAL = 0x00
ERROR = 0x01
NONE = 0x02 # Fan is not supported
DATA = [
LAMP_ERROR_STATE,
TEMPERATURE_ERROR_STATE,
BRIGHTNESS_SENSOR_ERROR_STATE,
INPUT_SOURCE_ERROR_STATE,
Int('TEMPERATURE'),
FAN_ERROR_STATE,
]
class SOFTWARE_VERSION(Command):
CMD = 0x0E
GET, SET = True, False
DATA = [Str('SOFTWARE_VERSION')]
class MODEL_NUMBER(Command):
CMD = 0x10
GET, SET = True, False
class MODEL_SPECIES(IntEnum):
PDP = 0x01
LCD = 0x02
DLP = 0x03
LED = 0x04
CRT = 0x05
OLED = 0x06
class TV_SUPPORT(IntEnum):
SUPPORTED = 0x00
NOT_SUPPORTED = 0x01
# NOTE: Actually there is list of MODEL_CODE in specification,
# but it's TOO long, and it's TOO old (newer models gets new code),
# so better use MODEL_NAME for this case
DATA = [MODEL_SPECIES, Int('MODEL_CODE'), TV_SUPPORT]
class POWER(Command):
CMD = 0x11
GET, SET = True, True
class POWER_STATE(IntEnum):
OFF = 0x00
ON = 0x01
REBOOT = 0x02
DATA = [POWER_STATE]
class VOLUME(Command):
CMD = 0x12
GET, SET = True, True
VOLUME = Int('VOLUME', range(101))
DATA = [VOLUME]
class MUTE(Command):
CMD = 0x13
GET, SET = True, True
class MUTE_STATE(IntEnum):
OFF = 0x00
ON = 0x01
NONE = 0xFF # Unavailable
DATA = [MUTE_STATE]
class INPUT_SOURCE(Command):
"""
Get/Set the device source which is shown on the screen.
DVI_VIDEO, HDMI1_PC, HDMI2_PC, HDMI3_PC, HDMI4_PC: get only.
URL_LAUNCHER, MAGIC_INFO, TV or some ports require support by model.
On TIMER functions, Do not use WIDI_SCREEN_MIRRORING.
"""
CMD = 0x14
GET, SET = True, True
class INPUT_SOURCE_STATE(IntEnum):
# not a valid value for INPUT_SOURCE, but valid for auto_source
NONE = 0x00
S_VIDEO = 0x04
COMPONENT = 0x08
AV = 0x0C
AV2 = 0x0D
SCART1 = 0x0E
DVI = 0x18
PC = 0x14
BNC = 0x1E
DVI_VIDEO = 0x1F # get only
MAGIC_INFO = 0x20
HDMI1 = 0x21
HDMI1_PC = 0x22 # get only
HDMI2 = 0x23
HDMI2_PC = 0x24 # get only
DISPLAY_PORT_1 = 0x25
DISPLAY_PORT_2 = 0x26
DISPLAY_PORT_3 = 0x27
RF_TV = 0x30 # deprecated
HDMI3 = 0x31
HDMI3_PC = 0x32 # get only
HDMI4 = 0x33
HDMI4_PC = 0x34 # get only
TV_DTV = 0x40
PLUG_IN_MODE = 0x50
HD_BASE_T = 0x55
OCM = 0x56
MEDIA_MAGIC_INFO_S = 0x60
WIDI_SCREEN_MIRRORING = 0x61
INTERNAL_USB = 0x62
URL_LAUNCHER = 0x63
IWB = 0x64
WEB_BROWSER = 0x65
REMOTE_WORKSPACE = 0x66
DATA = [INPUT_SOURCE_STATE]
class PICTURE_ASPECT(Command):
"""
Get/Set the device picture size (aspect ratio).
Working Condition:
Will not work with VIDEO_WALL_STATE is ON.
Note:
Some of the image sizes are not supported depending on input signals.
"""
CMD = 0x15
GET, SET = True, True
class PICTURE_ASPECT_STATE(IntEnum):
PC_16_9 = 0x10
PC_4_3 = 0x18
PC_ORIGINAL_RATIO = 0x20
PC_21_9 = 0x21
PC_CUSTOM = 0x22
VIDEO_AUTO_WIDE = 0x00
VIDEO_16_9 = 0x01
VIDEO_ZOOM = 0x04
VIDEO_ZOOM_1 = 0x05
VIDEO_ZOOM_2 = 0x06
VIDEO_SCREEN_FIT = 0x09
VIDEO_4_3 = 0x0B
VIDEO_WIDE_FIT = 0x0C
VIDEO_CUSTOM = 0x0D
VIDEO_SMART_VIEW_1 = 0x0E
VIDEO_SMART_VIEW_2 = 0x0F
VIDEO_WIDE_ZOOM = 0x31
VIDEO_21_9 = 0x32
DATA = [PICTURE_ASPECT_STATE]
class SCREEN_MODE(Command):
CMD = 0x18
GET, SET = True, True
class SCREEN_MODE_STATE(IntEnum):
MODE_16_9 = 0x01
MODE_ZOOM = 0x04
MODE_4_3 = 0x0B
MODE_WIDE_ZOOM = 0x31
DATA = [SCREEN_MODE_STATE]
class SCREEN_SIZE(Command):
CMD = 0x19
GET, SET = True, False
DATA = [Int('INCHES', range(256))]
class NETWORK_CONFIGURATION(Command):
CMD = 0x1B
SUBCMD = 0x82
GET, SET = True, True
DATA = [
IPAddress('IP_ADDRESS'),
IPAddress('SUBNET_MASK'),
IPAddress('GATEWAY_ADDRESS'),
IPAddress('DNS_SERVER_ADDRESS'),
]
class NETWORK_MODE(Command):
CMD = 0x1B
SUBCMD = 0x85
GET, SET = True, True
class NETWORK_MODE_STATE(IntEnum):
DYNAMIC = 0x00
STATIC = 0x01
DATA = [NETWORK_MODE_STATE]
class WEEKLY_RESTART(Command):
CMD = 0x1B
SUBCMD = 0xA2
GET, SET = True, True
class WEEKDAY(IntEnum):
# NOTE: codes differs from TIMER_15.WEEKDAY
SUN = 0x00
SAT = 0x01
FRI = 0x02
THU = 0x03
WED = 0x04
TUE = 0x05
MON = 0x06
DATA = [Bitmask(WEEKDAY), Time()]
class NETWORK_AP_CONFIG(Command):
"""
Add new SSID info to device connection history with its password.
Note: device may change network and response may not be received.
"""
CMD = 0x1B
SUBCMD = 0x8A
GET, SET = False, True
DATA = [StrCoded(0x00, 'SSID'), StrCoded(0x01, 'PASSWORD')]
class MAGICINFO_CHANNEL(Command):
"""
Set MagicInfo Channel by Direct Channel Number
which is used by MagicInfo S Player.
"""
CMD = 0x1C
SUBCMD = 0x81
GET, SET = False, True
DATA = [Int('CHANNEL_NUMBER', length=2, byteorder='big')]
class MAGICINFO_SERVER(Command):
"""
MagicInfo Server URL.
Example: "http://example.com:80"
"""
CMD = 0x1C
SUBCMD = 0x82
GET, SET = True, True
DATA = [Str('MAGICINFO_SERVER_URL')]
class MAGICINFO_CONTENT_ORIENTATION(Command):
CMD = 0x1C
SUBCMD = 0x83
GET, SET = True, True
DATA = [_COMMON.ORIENTATION_MODE_STATE]
class MDC_CONNECTION(Command):
"""
Note: Depends on the product specification,
if it is set as RJ45 then serial MDC will not work.
"""
CMD = 0x1D
GET, SET = True, True
class MDC_CONNECTION_TYPE(IntEnum):
RS232C = 0x00
RJ45 = 0x01
DATA = [MDC_CONNECTION_TYPE]
class CONTRAST(Command):
CMD = 0x24
GET, SET = True, True
DATA = [Int('CONTRAST', range(101))]
class BRIGHTNESS(Command):
CMD = 0x25
GET, SET = True, True
DATA = [Int('BRIGHTNESS', range(101))]
class SHARPNESS(Command):
CMD = 0x26
GET, SET = True, True
DATA = [Int('SHARPNESS', range(101))]
class COLOR(Command):
CMD = 0x27
GET, SET = True, True
DATA = [Int('COLOR', range(101))]
class TINT(Command):
"""
Control the device tint. Adjust the ratio of green to red tint level.
Red: TINT value, Green: ( 100 - TINT ) value.
Note: Tint could only be set in 50 Steps (0, 2, 4, 6... 100).
"""
CMD = 0x28
GET, SET = True, True
DATA = [Int('TINT', range(101))]
class H_POSITION(Command):
CMD = 0x31
GET, SET = False, True
class H_POSITION_MOVE_TO(IntEnum):
LEFT = 0x00
RIGHT = 0x01
DATA = [H_POSITION_MOVE_TO]
class V_POSITION(Command):
CMD = 0x32
GET, SET = False, True
class V_POSITION_MOVE_TO(IntEnum):
UP = 0x00
DOWN = 0x01
DATA = [V_POSITION_MOVE_TO]
class AUTO_POWER(Command):
CMD = 0x33
GET, SET = True, True
class AUTO_POWER_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [AUTO_POWER_STATE]
class CLEAR_MENU(Command):
CMD = 0x34
SUBCMD = 0x00
GET, SET = False, True
DATA = []
class IR_STATE(Command):
"""
Enables/disables IR (Infrared) receiving function (Remote Control).
Working Condition:
* Can operate regardless of whether power is ON/OFF.
(If DPMS Situation in LFD, it operate Remocon regardless of set value).
"""
CMD = 0x36
GET, SET = True, True
class IR_STATE(IntEnum):
DISABLED = 0x00
ENABLED = 0x01
DATA = [IR_STATE]
class RGB_CONTRAST(Command):
CMD = 0x37
GET, SET = True, True
DATA = [Int('CONTRAST', range(101))]
class RGB_BRIGHTNESS(Command):
CMD = 0x38
GET, SET = True, True
DATA = [Int('BRIGHTNESS', range(101))]
class AUTO_ADJUSTMENT_ON(Command):
CMD = 0x3D
SUBCMD = 0x00
GET, SET = False, True
DATA = []
class COLOR_TONE(Command):
CMD = 0x3E
GET, SET = True, True
class COLOR_TONE_STATE(IntEnum):
COOL_2 = 0x00
COOL_1 = 0x01
NORMAL = 0x02
WARM_1 = 0x03
WARM_2 = 0x04
OFF = 0x50
DATA = [COLOR_TONE_STATE]
class COLOR_TEMPERATURE(Command):
"""
Color temperature function.
Unit is hectoKelvin (hK) (x*100 Kelvin) (example: 28 = 2800K).
Supported values - 28, 30, 35, 40... 160.
For older models: 0-10=(x*100K + 5000K), 253=2800K, 254=3000K, 255=4000K
"""
CMD = 0x3F
GET, SET = True, True
DATA = [Int('HECTO_KELVIN')]
class STANDBY(Command):
CMD = 0x4A
GET, SET = True, True
class STANDBY_STATE(IntEnum):
OFF = 0x00
ON = 0x01
AUTO = 0x02
DATA = [STANDBY_STATE]
class AUTO_LAMP(Command):
"""
Auto Lamp function (backlight).
Note: When Manual Lamp Control is on,
Auto Lamp Control will automatically turn off.
"""
CMD = 0x57
GET, SET = True, True
DATA = [
Time12H('MAX_TIME'),
Int('MAX_LAMP_VALUE', range(101)),
Time12H('MIN_TIME'),
Int('MIN_LAMP_VALUE', range(101)),
]
class MANUAL_LAMP(Command):
"""
Manual Lamp function (backlight).
Note: When Auto Lamp Control is on,
Manual Lamp Control will automatically turn off.
"""
CMD = 0x58
GET, SET = True, True
DATA = [Int('LAMP_VALUE', range(101))]
class INVERSE(Command):
CMD = 0x5A
GET, SET = True, True
class INVERSE_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [INVERSE_STATE]
class SAFETY_LOCK(Command):
CMD = 0x5D
GET, SET = True, True
class LOCK_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [LOCK_STATE]
class PANEL_LOCK(Command):
CMD = 0x5F
GET, SET = True, True
class LOCK_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [LOCK_STATE]
class CHANNEL_CHANGE(Command):
CMD = 0x61
GET, SET = False, True
class CHANGE_TO(IntEnum):
UP = 0x00
DOWN = 0x01
DATA = [CHANGE_TO]
class VOLUME_CHANGE(Command):
CMD = 0x62
GET, SET = False, True
class CHANGE_TO(IntEnum):
UP = 0x00
DOWN = 0x01
DATA = [CHANGE_TO]
class TICKER(Command):
"""
Get/Set the device ticker. (Show text message overlay on the screen)
Note: POS_HORIZ or POS_VERT are NONE in GET response
if unsupported by the display.
"""
CMD = 0x63
GET, SET = True, True
class POS_HORIZ(IntEnum):
CENTER = 0x00
LEFT = 0x01
RIGHT = 0x02
NONE = 0xFF
class POS_VERTI(IntEnum):
MIDDLE = 0x00
TOP = 0x01
BOTTOM = 0x02
NONE = 0xFF
class MOTION_DIR(IntEnum):
LEFT = 0x00
RIGHT = 0x01
UP = 0x02
DOWN = 0x03
class MOTION_SPEED(IntEnum):
NORMAL = 0x00
SLOW = 0x01
FAST = 0x02
class FONT_SIZE(IntEnum):
STANDARD = 0x00
SMALL = 0x01
LARGE = 0x02
class FOREGROUND_COLOR(IntEnum):
BLACK = 0x00
WHITE = 0x01
RED = 0x02
GREEN = 0x03
BLUE = 0x04
YELLOW = 0x05
MAGENTA = 0x06
CYAN = 0x07
class BACKGROUND_COLOR(IntEnum):
BLACK = 0x00
WHITE = 0x01
RED = 0x02
GREEN = 0x03
BLUE = 0x04
YELLOW = 0x05
MAGENTA = 0x06
CYAN = 0x07
class FOREGROUND_OPACITY(IntEnum):
FLASHING = 0x03
FLASH_ALL = 0x04
OFF = 0x05
class BACKGROUND_OPACITY(IntEnum):
SOLID = 0x00
TRANSPARENT = 0x01
TRANSLUCENT = 0x02
UNKNOWN = 0x03
DATA = [
Bool('ON_OFF'),
Time12H('START_TIME'),
Time12H('END_TIME'),
POS_HORIZ,
POS_VERTI,
Bool('MOTION_ON_OFF'),
MOTION_DIR,
MOTION_SPEED,
FONT_SIZE,
FOREGROUND_COLOR,
BACKGROUND_COLOR,
FOREGROUND_OPACITY,
BACKGROUND_OPACITY,
Str('MESSAGE')
]
class DEVICE_NAME(Command):
"""
It reads the device name which user set up in network.
Shows the information about entered device name.
"""
CMD = 0x67
GET, SET = True, False
DATA = [Str('DEVICE_NAME')]
class OSD(Command):
"""
Turns OSD (On-screen display) on/off.
"""
CMD = 0x70
GET, SET = True, True
DATA = [Bool('OSD_ENABLED')]
class PICTURE_MODE(Command):
CMD = 0x71
GET, SET = True, True
class PICTURE_MODE_STATE(IntEnum):
DYNAMIC = 0x00
STANDARD = 0x01
MOVIE = 0x02
CUSTOM_TV = 0x03
NATURAL = 0x04
CALIBRATION_TV = 0x05
ENTERTAIN = 0x10
INTERNET = 0x11
TEXT = 0x12
CUSTOM = 0x13
ADVERTISEMENT = 0x14
INFORMATION = 0x15
CALIBRATION = 0x16
SHOP_MALL_VIDEO = 0x20
SHOP_MALL_TEXT = 0x21
OFFICE_SCHOOL_VIDEO = 0x22
OFFICE_SCHOOL_TEXT = 0x23
TERMINAL_STATION_VIDEO = 0x24
TERMINAL_STATION_TEXT = 0x25
VIDEO_WALL_VIDEO = 0x26
VIDEO_WALL_TEXT = 0x27
HDR_PLUS = 0x30
OFF = 0x50
RESERVED_OTHER = 0x90
DATA = [PICTURE_MODE_STATE]
class SOUND_MODE(Command):
CMD = 0x72
GET, SET = True, True
class SOUND_MODE_STATE(IntEnum):
STANDARD = 0x00
MUSIC = 0x01
MOVIE = 0x02
SPEECH = 0x03
CUSTOM = 0x04
AMPLIFY = 0x05
OPTIMIZED = 0x06
DATA = [SOUND_MODE_STATE]
class ALL_KEYS_LOCK(Command):
"""
Turns both REMOCON and Panel Key Lock function on/off.
Note: Can operate regardless of whether power is on/off.
"""
# TODO: REMOCON? Remote Control?
CMD = 0x77
GET, SET = True, True
class LOCK_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [LOCK_STATE]
class MODEL_NAME(Command):
CMD = 0x8A
GET, SET = True, False
DATA = [Str('MODEL_NAME')]
class PANEL_ON_TIME(Command):
"""
Get the device panel on total time.
Return value increased every 10 mins. To get hours use "MIN10 / 6".
"""
CMD = 0x83
GET, SET = True, False
DATA = [Int('MIN10', length=2, byteorder='big')]
@classmethod
def parse_response_data(cls, data):
# looks like they increased length on newer models,
# so we're trying to decoce anyway
return (int.from_bytes(data, byteorder='big'),)
class ENERGY_SAVING(Command):
CMD = 0x92
GET, SET = True, True
class ENERGY_SAVING_STATE(IntEnum):
OFF = 0x00
LOW = 0x01
MEDIUM = 0x02
HIGH = 0x03
PICTURE_OFF = 0x04
DATA = [ENERGY_SAVING_STATE]
class RESET(Command):
CMD = 0x9F
GET, SET = False, True
class RESET_TARGET(IntEnum):
PICTURE = 0x00
SOUND = 0x01
SETUP = 0x02 # (System reset)
ALL = 0x03
SCREEN_DISPLAY = 0x04
DATA = [RESET_TARGET]
class OSD_TYPE(Command):
"""
Turns OSD (On-screen display) specific message types on/off.
"""
CMD = 0xA3
GET, SET = True, True
class OSD_TYPE(IntEnum):
SOURCE = 0x00
NOT_OPTIMUM_MODE = 0x01
NO_SIGNAL = 0x02
MDC = 0x03
SCHEDULE_CHANNEL = 0x04
DATA = [OSD_TYPE, Bool('OSD_ENABLED')]
RESPONSE_DATA = [Bitmask(OSD_TYPE, 'OSD_STATUS')]
class TIMER_15(Command):
"""
Integrated timer function (15 data-length version).
Note: This depends on product and will not work on older versions.
ON_TIME/OFF_TIME: turn ON/OFF display at specific time of day
ON_ACTIVE/OFF_ACTIVE: if timer is not active, values are ignored,
so there may be only OFF timer, ON timer, or both.
REPEAT: On which day timer is enabled
(combined with HOLIDAY_APPLY and MANUAL_WEEKDAY)
"""
CMD = Int('TIMER_ID', range(1, 8))
_TIMER_ID_CMD = [0xA4, 0xA5, 0xA6, 0xAB, 0xAC, 0xAD, 0xAE]
GET, SET = True, True
class TIMER_REPEAT(IntEnum):
ONCE = 0x00
EVERYDAY = 0x01
MON_FRI = 0x02
MON_SAT = 0x03
SAT_SUN = 0x04
MANUAL_WEEKDAY = 0x05
class WEEKDAY(IntEnum):
SUN = 0x00
MON = 0x01
TUE = 0x02
WED = 0x03
THU = 0x04
FRI = 0x05
SAT = 0x06
# ignore_bit_7 = 7
class HOLIDAY_APPLY(IntEnum):
DONT_APPLY_BOTH = 0x00
APPLY_BOTH = 0x01
ON_TIMER_ONLY_APPLY = 0x02
OFF_TIMER_ONLY_APPLY = 0x03
DATA = [
Time12H('ON_TIME'),
Bool('ON_ENABLED'),
Time12H('OFF_TIME'),
Bool('OFF_ENABLED'),
EnumField(TIMER_REPEAT, 'ON_REPEAT'),
Bitmask(WEEKDAY, 'ON_MANUAL_WEEKDAY'),
EnumField(TIMER_REPEAT, 'OFF_REPEAT'),
Bitmask(WEEKDAY, 'OFF_MANUAL_WEEKDAY'),
VOLUME.VOLUME,
INPUT_SOURCE.INPUT_SOURCE_STATE,
HOLIDAY_APPLY,
]
async def __call__(self, connection, display_id, timer_id, data):
cmd = self._TIMER_ID_CMD[timer_id - 1]
data = self.parse_response(
await connection.send(
cmd, display_id,
self.pack_payload_data(data) if data else []
),
)
return self.parse_response_data(data)
@classmethod
def parse_response_data(cls, data, *args, _timer_version_check=True,
**kwargs):
if _timer_version_check and len(data) == 13:
raise RuntimeError('13 data-length version of timer received, '
'use timer_13 instead')
return super().parse_response_data(data, *args, **kwargs)
@classmethod
def get_order(cls):
return (0xA4, cls.name)
class TIMER_13(TIMER_15):
"""
Integrated timer function (13 data-length version).
Note: This depends on product and will not work on newer versions.
"""
DATA = [
Time12H('ON_TIME'),
Bool('ON_ENABLED'),
Time12H('OFF_TIME'),
Bool('OFF_ENABLED'),
EnumField(TIMER_15.TIMER_REPEAT, 'REPEAT'),
Bitmask(TIMER_15.WEEKDAY, 'MANUAL_WEEKDAY'),
VOLUME.VOLUME,
INPUT_SOURCE.INPUT_SOURCE_STATE,
TIMER_15.HOLIDAY_APPLY,
]
@classmethod
def parse_response_data(cls, data, *args, **kwargs):
if len(data) == 15:
raise RuntimeError('15 data-length version of timer received, '
'use timer_15 instead')
return super().parse_response_data(
data, *args, _timer_version_check=False, **kwargs)
class CLOCK_S(Command):
"""
Current time function (second precision).
Note: This is for models developed after 2013.
For older models see CLOCK_M function (minute precision).
"""
GET, SET = True, True
CMD = 0xC5
DATA = [DateTime()]
class CLOCK_M(CLOCK_S):
"""
Current time function (minute precision).
Note: This is for models developed until 2013.
For newer models see CLOCK_S function (seconds precision).
"""
CMD = 0xA7
DATA = [DateTime(seconds=False)]
class HOLIDAY_SET(Command):
"""
Add/Delete the device holiday schedule with the holiday schedule itself
start month/day and end month/day.
Note: On DELETE_ALL all parameters should be 0x00.
"""
CMD = 0xA8
GET, SET = False, True
class HOLIDAY_MANAGE(IntEnum):
ADD = 0x00
DELETE = 0x01
DELETE_ALL = 0x02
DATA = [
HOLIDAY_MANAGE,
Int('START_MONTH', range(13)),
Int('START_DAY', range(32)),
Int('END_MONTH', range(13)),
Int('END_DAY', range(32)),
]
class HOLIDAY_GET(Command):
"""
Get the device holiday schedule.
If INDEX is not specified, returns total number of Holiday Information.
"""
CMD = 0xA9
GET, SET = True, True
DATA = [
Int('INDEX'),
]
RESPONSE_EXTRA = [
Int('START_MONTH'),
Int('START_DAY'),
Int('END_MONTH'),
Int('END_DAY'),
]
@classmethod
def parse_response_data(cls, data):
if data[1:] == bytes([0, 0, 0, 0]):
# index was not specified,
# so it's total number of holiday information
return (int(data[0]),)
return super().parse_response_data(data)
class VIRTUAL_REMOTE(Command):
"""
This function support that MDC command can work same as remote control.
Note: In a certain model, 0x79 CONTENT key works as HOME
and 0x1F DISPLAY key works as INFO.
"""
CMD = 0xB0
GET, SET = False, True
class KEY_CODE(IntEnum):
KEY_SOURCE = 0x01
KEY_POWER = 0x02
KEY_1 = 0x04
KEY_2 = 0x05
KEY_3 = 0x06
KEY_VOLUME_UP = 0x07
KEY_4 = 0x08
KEY_5 = 0x09
KEY_6 = 0x0A
KEY_VOLUME_DOWN = 0x0B
KEY_7 = 0x0C
KEY_8 = 0x0D
KEY_9 = 0x0E
KEY_MUTE = 0x0F
KEY_CHANNEL_DOWN = 0x10
KEY_0 = 0x11
KEY_CHANNEL_UP = 0x12
KEY_GREEN = 0x14
KEY_YELLOW = 0x15
KEY_CYAN = 0x16
KEY_MENU = 0x1A
KEY_DISPLAY = 0x1F # or KEY_INFO
KEY_DIGIT = 0x23
KEY_PIP_TV_VIDEO = 0x24
KEY_EXIT = 0x2D
KEY_MAGICINFO = 0x30 # limited support
KEY_REW = 0x45
KEY_STOP = 0x46
KEY_PLAY = 0x47
KEY_FF = 0x48
KEY_PAUSE = 0x4A
KEY_TOOLS = 0x4B
KEY_RETURN = 0x58
KEY_MAGICINFO_LITE = 0x5B # limited support
KEY_CURSOR_UP = 0x60
KEY_CURSOR_DOWN = 0x61
KEY_CURSOR_RIGHT = 0x62
KEY_CURSOR_LEFT = 0x65
KEY_ENTER = 0x68
KEY_RED = 0x6C
KEY_LOCK = 0x77
KEY_CONTENT = 0x79 # HOME
DISCRET_POWER_OFF = 0x98
KEY_3D = 0x9F
# # Corresponding to MediaKey API
# # https://docs.tizen.org/application/web/api/latest/device_api/mobile/tizen/mediakey.html # noqa
# class MEDIA_KEY_CODE(IntEnum):
# PLAY = KEY_CODE.KEY_PLAY
# STOP = KEY_CODE.KEY_STOP
# PAUSE = KEY_CODE.KEY_PAUSE
# # PREVIOUS = ??
# # NEXT = ??
# FAST_FORWARD = KEY_CODE.KEY_FF
# REWIND - KEY_CODE.KEY_REW
# # PLAY_PAUSE = ??
DATA = [KEY_CODE]
class NETWORK_STANDBY(Command):
CMD = 0xB5
GET, SET = True, True
class NETWORK_STANDBY_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [NETWORK_STANDBY_STATE]
class DST(Command):
CMD = 0xB6
GET, SET = True, True
class DST_STATE(IntEnum):
OFF = 0x00
AUTO = 0x01
MANUAL = 0x02
class MONTH(IntEnum):
JAN = 0x00
FEB = 0x01
MAR = 0x02
APR = 0x03
MAY = 0x04
JUN = 0x05
JUL = 0x06
AUG = 0x07
SEP = 0x08
OCT = 0x09
NOV = 0x0A
DEC = 0x0B
class WEEK(IntEnum):
WEEK_1 = 0x00
WEEK_2 = 0x01
WEEK_3 = 0x02
WEEK_4 = 0x03
WEEK_LAST = 0x04
class WEEKDAY(IntEnum):
# NOTE: same as TIMER_15.WEEKDAY
SUN = 0x00
MON = 0x01
TUE = 0x02
WED = 0x03
THU = 0x04
FRI = 0x05
SAT = 0x06
class OFFSET(IntEnum):
PLUS_1_00 = 0x00
PLUS_2_00 = 0x01
DATA = [
DST_STATE,
EnumField(MONTH, 'START_MONTH'),
EnumField(WEEK, 'START_WEEK'),
EnumField(WEEKDAY, 'START_WEEKDAY'),
Time('START_TIME'),
EnumField(MONTH, 'END_MONTH'),
EnumField(WEEK, 'END_WEEK'),
EnumField(WEEKDAY, 'END_WEEKDAY'),
Time('END_TIME'),
OFFSET,
]
RESPONSE_EXTRA = [
Bool('TUNER_SUPPORT'),
]
class AUTO_ID_SETTING(Command):
CMD = 0xB8
GET, SET = True, True
class AUTO_ID_SETTING_STATE(IntEnum):
START = 0x00
END = 0x01
DATA = [AUTO_ID_SETTING_STATE]
class DISPLAY_ID(Command):
CMD = 0xB9
GET, SET = False, True
class DISPLAY_ID_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [DISPLAY_ID_STATE]
class AUTO_SOURCE_SWITCH(Command):
CMD = 0xCA
SUBCMD = 0x81
GET, SET = True, True
class AUTO_SOURCE_SWITCH_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [AUTO_SOURCE_SWITCH_STATE]
class AUTO_SOURCE(Command):
CMD = 0xCA
SUBCMD = 0x82
GET, SET = True, True
class PRIMARY_SOURCE_RECOVERY(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [
PRIMARY_SOURCE_RECOVERY,
EnumField(INPUT_SOURCE.INPUT_SOURCE_STATE, 'PRIMARY_SOURCE'),
EnumField(INPUT_SOURCE.INPUT_SOURCE_STATE, 'SECONDARY_SOURCE')
]
class LAUNCHER_PLAY_VIA(Command):
CMD = 0xC7
SUBCMD = 0x81
GET, SET = True, True
class PLAY_VIA_MODE(IntEnum):
MAGIC_INFO = 0x00
URL_LAUNCHER = 0x01
MAGIC_IWB = 0x02
DATA = [PLAY_VIA_MODE]
class LAUNCHER_URL_ADDRESS(Command):
CMD = 0xC7
SUBCMD = 0x82
GET, SET = True, True
DATA = [Str('URL_ADDRESS')]
class OSD_MENU_ORIENTATION(Command):
CMD = 0xC8
SUBCMD = 0x81
GET, SET = True, True
DATA = [_COMMON.ORIENTATION_MODE_STATE]
class OSD_SOURCE_CONTENT_ORIENTATION(Command):
CMD = 0xC8
SUBCMD = 0x82
GET, SET = True, True
DATA = [_COMMON.ORIENTATION_MODE_STATE]
class OSD_ASPECT_RATIO(Command):
"""
Get/Set the device aspect ratio under portrait mode
which set the rotated screen to be full or original.
"""
CMD = 0xC8
SUBCMD = 0x83
GET, SET = True, True
class ASPECT_RATIO_STATE(IntEnum):
FULL_SCREEN = 0x00
ORIGINAL = 0x01
DATA = [ASPECT_RATIO_STATE]
class OSD_PIP_ORIENTATION(Command):
CMD = 0xC8
SUBCMD = 0x84
GET, SET = True, True
DATA = [_COMMON.ORIENTATION_MODE_STATE]
class OSD_MENU_SIZE(Command):
CMD = 0xC8
SUBCMD = 0x85
GET, SET = True, True
class MENU_SIZE_STATE(IntEnum):
ORIGINAL = 0x00
MEDIUM = 0x01
SMALL = 0x02
DATA = [MENU_SIZE_STATE]
class PANEL(Command):
CMD = 0xF9
GET, SET = True, True
class PANEL_STATE(IntEnum):
ON = 0x00
OFF = 0x01
DATA = [PANEL_STATE]
class SCREEN_MUTE(Command):
# Undocumented (as of Ver. 15 2020-11-06)
# see https://github.com/vgavro/samsung-mdc/issues/19
CMD = 0xFE
SUBCMD = 0x51
GET, SET = True, True
class SCREEN_MUTE_STATUS(IntEnum):
ON = 0x00
OFF = 0xFF
DATA = [SCREEN_MUTE_STATUS]
class STATUS(Command):
"""
Get the device various state like power, volume, sound mute, input source,
picture aspect ratio.
Note:
For no audio models volume and mute returns 0xFF (255).
N_TIME_NF, F_TIME_NF: OnTime/OffTime ON/OFF value
(old type timer, now it's always 0x00).
"""
CMD = 0x00
GET, SET = True, False
DATA = [
POWER.POWER_STATE, VOLUME.VOLUME, MUTE.MUTE_STATE,
INPUT_SOURCE.INPUT_SOURCE_STATE, PICTURE_ASPECT.PICTURE_ASPECT_STATE,
Int('N_TIME_NF'), Int('F_TIME_NF')
]
class VIDEO(Command):
CMD = 0x04
GET, SET = True, False
DATA = [
Int('CONTRAST', range(101)), Int('BRIGHTNESS', range(101)),
Int('SHARPNESS', range(101)), Int('COLOR', range(101)),
Int('TINT', range(101)), COLOR_TONE.COLOR_TONE_STATE,
Int('COLOR_TEMPERATURE'), Int('_IGNORE', range(1)),
]
class RGB(Command):
CMD = 0x06
GET, SET = True, False
DATA = [
Int('CONTRAST', range(101)), Int('BRIGHTNESS', range(101)),
COLOR_TONE.COLOR_TONE_STATE, Int('COLOR_TEMPERATURE'),
Int('_IGNORE', range(1)),
Int('RED_GAIN'), Int('GREEN_GAIN'), Int('BLUE_GAIN'),
]
class VIDEO_WALL_STATE(Command):
"""
Get/Set the device in video wall state.
This will split the primary input source into smaller N number of squares
and display them instead.
Note: The device needs to be capable of this operation.
Usually a primary high resolution source signal is daisy chained
to lower resolution displays in a video wall using HDMI/DP.
"""
CMD = 0x84
GET, SET = True, True
class VIDEO_WALL_STATE(IntEnum):
OFF = 0x00
ON = 0x01
DATA = [VIDEO_WALL_STATE]
class VIDEO_WALL_MODE(Command):
"""
Get/Set the device in aspect ratio of the video wall.
FULL: stretch input source to fill display
NATURAL: Keep aspect ratio of input source; do not fill display.
Note: Needs VIDEO_WALL_STATE to be ON.
"""
CMD = 0x5C
GET, SET = True, True
class VIDEO_WALL_MODE(IntEnum):
NATURAL = 0x00
FULL = 0x01
DATA = [VIDEO_WALL_MODE]
class VIDEO_WALL_MODEL(Command):
"""
Get/Set video wall model.
MODEL: Size of the wall in (x, y) coordinates; ie. "2,2" or "4,1"
SERIAL: Serial number - position of the display in the video wall,
counting from the first display.
Note: Needs VIDEO_WALL_STATE to be ON.
"""
CMD = 0x89
GET, SET = True, True
DATA = [VideoWallModel('MODEL'), Int('SERIAL', range(1, 256))]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/connection.py 0000644 0001751 0000200 00000023405 14736575073 021761 0 ustar 00runner docker from typing import Union, Sequence, Tuple
from functools import partial
from enum import Enum
import asyncio
from .exceptions import MDCResponseError, MDCReadTimeoutError, \
MDCTimeoutError, MDCTLSRequired, MDCTLSAuthFailed
from .utils import repr_hex
HEADER_CODE = 0xAA
RESPONSE_CMD = 0xFF
ACK_CODE = ord('A') # 0x41 65
NAK_CODE = ord('N') # 0x4E 78
def get_checksum(payload):
# payload should be without HEADER_CODE
return sum(payload) % 256
def _normalize_cmd(
cmd: Union[int, Tuple[int], Tuple[int, Union[int, None]]]
) -> Tuple[int, Union[int, None]]:
"""
Returns (cmd, subcmd) tuple
"""
if isinstance(cmd, int):
return cmd, None
elif len(cmd) == 1:
return int(cmd[0]), None
elif not len(cmd) == 2:
raise ValueError('cmd tuple should contain only (cmd, subcmd)')
return int(cmd[0]), None if cmd[1] is None else int(cmd[1])
def pack_payload(
cmd: Union[int, Tuple[int], Tuple[int, Union[int, None]]],
display_id: int,
data: Union[bytes, Sequence] = b''
):
cmd, subcmd = _normalize_cmd(cmd)
data = bytes(data)
if subcmd is not None:
data = bytes([subcmd]) + data
payload = (
bytes([HEADER_CODE, cmd, display_id, len(data)])
+ bytes(data)
)
payload += bytes([get_checksum(payload[1:])])
return payload
def pack_response(
cmd: Union[int, Tuple[int], Tuple[int, int]],
display_id: int,
ack: bool,
data: Union[bytes, Sequence] = b''
):
cmd, subcmd = _normalize_cmd(cmd)
if not ack and len(data) != 1:
raise ValueError(
'Data should contain only error code for NAK response', data)
data = bytes(data)
if subcmd is not None and ack:
# subcmd is not sent on NAK in response
data = bytes([subcmd]) + data
return pack_payload(
RESPONSE_CMD, display_id,
bytes([ACK_CODE if ack else NAK_CODE, cmd]) + data
)
async def wait_for(aw, timeout, reason):
try:
return await asyncio.wait_for(aw, timeout)
except asyncio.TimeoutError as exc:
raise MDCTimeoutError(reason) from exc
async def wait_for_read(reader, count, timeout, reason):
try:
return await asyncio.wait_for(reader.read(count), timeout)
except asyncio.TimeoutError as exc:
raise MDCReadTimeoutError(reason, bytes(reader._buffer)) from exc
class CONNECTION_MODE(Enum):
TCP = 'tcp'
SERIAL = 'serial'
class MDCConnection:
reader, writer = None, None
def __init__(self, target, mode=CONNECTION_MODE.TCP, timeout=5,
connect_timeout=None, verbose=False, **connection_kwargs):
self.target = target
self.mode = CONNECTION_MODE(mode)
self.connection_kwargs = connection_kwargs
self.timeout = timeout
self.connect_timeout = connect_timeout or timeout
self.verbose = (
partial(print, self.target) if verbose is True else verbose)
async def open(self):
connection_kwargs = self.connection_kwargs.copy()
pin = connection_kwargs.pop('pin', None)
if self.mode == CONNECTION_MODE.TCP:
if isinstance(self.target, (list, tuple)):
# make target be compatible with socket.__init__
target, port = self.target
else:
target, *port = self.target.split(':')
port = port and int(port[0]) or 1515
connection_kwargs.setdefault('port', port)
self.reader, self.writer = \
await wait_for(
asyncio.open_connection(target, **connection_kwargs),
self.connect_timeout, 'Connect timeout')
if self.verbose:
self.verbose('Connected')
else:
# Make this package optional
from serial_asyncio import ( # type: ignore[import-untyped]
open_serial_connection
)
self.reader, self.writer = \
await wait_for(
open_serial_connection(
url=self.target,
**connection_kwargs),
self.connect_timeout, 'Connect timeout')
if self.verbose:
self.verbose('Connected')
if pin is not None:
try:
await self._start_tls(pin)
except Exception:
await self.close()
raise
async def _start_tls(self, pin):
if isinstance(pin, int):
pin = str(pin).rjust(4, '0').encode()
elif isinstance(pin, str):
pin = pin.rjust(4, '0').encode()
assert self.is_opened
import ssl
resp = await wait_for_read(self.reader, 15, self.timeout,
'TLS header read timeout')
if not resp == b'MDCSTART<>':
raise MDCResponseError('Unexpected TLS header',
resp + self.reader._buffer)
transport = self.writer.transport
protocol = transport.get_protocol()
loop = asyncio.get_event_loop()
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_ctx.minimum_version = ssl.TLSVersion.MINIMUM_SUPPORTED
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.VerifyMode.CERT_NONE
ssl_transport = await loop.start_tls(
transport, protocol, ssl_ctx)
self.writer._transport = ssl_transport
self.reader._transport = ssl_transport
if self.verbose:
self.verbose('TLS established')
self.writer.write(pin)
await wait_for(self.writer.drain(), self.timeout,
'Write pin timeout')
resp = await wait_for_read(self.reader, 15, self.timeout,
'TLS auth read timeout')
if not resp == b'MDCAUTH<>':
if resp[:14] == b'MDCAUTH<>':
raise MDCResponseError('Unexpected TLS auth fail response',
resp + self.reader._buffer)
try:
fail_code = int(resp[-6:-2], 16)
except ValueError:
raise MDCResponseError('Unexpected TLS auth fail code',
resp + self.reader._buffer)
raise MDCTLSAuthFailed(fail_code)
raise MDCResponseError('Unexpected TLS auth response',
resp + self.reader._buffer)
if self.verbose:
self.verbose('TLS authentication passed')
@property
def is_opened(self):
return self.writer is not None
@property
def is_tls_started(self):
return (self.writer and self.writer.transport.__class__.__name__ ==
'_SSLProtocolTransport')
async def send(
self,
cmd: Union[int, Tuple[int], Tuple[int, int]],
display_id: int,
data: Union[bytes, Sequence] = b''
):
cmd, subcmd = _normalize_cmd(cmd)
payload = pack_payload((cmd, subcmd), display_id, data)
if not self.is_opened:
await self.open()
assert (self.reader is not None and self.writer is not None)
self.writer.write(payload)
await wait_for(self.writer.drain(), self.timeout, 'Write timeout')
if self.verbose:
self.verbose('Sent', repr_hex(payload))
resp = await wait_for_read(self.reader, 4, self.timeout,
'Response header read timeout')
if not resp:
raise MDCResponseError('Empty response', resp)
if resp[0] != HEADER_CODE:
if (resp + self.reader._buffer) == b'MDCSTART<>':
raise MDCTLSRequired(resp + self.reader._buffer)
raise MDCResponseError('Unexpected header',
resp + self.reader._buffer)
if resp[1] != RESPONSE_CMD:
raise MDCResponseError('Unexpected cmd',
resp + self.reader._buffer)
if resp[2] != display_id:
raise MDCResponseError('Unexpected display_id',
resp + self.reader._buffer)
length = resp[3]
resp += await wait_for_read(self.reader, length + 1, self.timeout,
'Response data read timeout')
if self.verbose:
self.verbose('Recv', repr_hex(resp))
checksum = get_checksum(resp[1:-1])
if checksum != int(resp[-1]):
raise MDCResponseError('Checksum failed', resp)
ack, rcmd, data = resp[4], resp[5], resp[6:-1]
if ack not in (ACK_CODE, NAK_CODE):
raise MDCResponseError('Unexpected ACK/NAK', resp)
if subcmd and ack == ACK_CODE:
# rsubcmd is not sent on NAK
rsubcmd = data[0]
data = data[1:]
else:
rsubcmd = None
return (
ack == ACK_CODE,
(rcmd,) if rsubcmd is None else (rcmd, rsubcmd),
data
)
async def close(self):
if self.is_tls_started:
# FIX warning
# "returning true from eof_received() has no effect when using ssl"
self.writer._protocol.eof_received = lambda: None
writer = self.writer
self.reader, self.writer = None, None
writer.close()
await wait_for(writer.wait_closed(), self.timeout, 'Close timeout')
async def __aenter__(self):
if not self.is_opened:
await self.open()
return self
async def __aexit__(self, *args):
if self.is_opened:
await self.close()
def __await__(self):
return self.__aenter__().__await__()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/exceptions.py 0000644 0001751 0000200 00000001502 14736575073 021775 0 ustar 00runner docker from asyncio import TimeoutError
class MDCError(Exception):
pass
class MDCTLSRequired(Exception):
pass
class MDCTLSAuthFailed(Exception):
def __init__(self, code):
self.code = code
super().__init__(code)
def __str__(self):
if self.code == 1:
return 'Wrong pin'
elif self.code == 2:
return 'Blocked'
else:
return f'Unknown code: {self.code}'
class MDCTimeoutError(MDCError, TimeoutError):
pass
class MDCReadTimeoutError(MDCTimeoutError):
pass
class MDCResponseError(MDCError):
pass
class NAKError(MDCError):
def __init__(self, error_code):
self.error_code = error_code
super().__init__(error_code)
def __str__(self):
return f'Negative Acknowledgement [error_code {self.error_code}]'
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/fields.py 0000644 0001751 0000200 00000012364 14736575073 021072 0 ustar 00runner docker from typing import Sequence, Optional
from datetime import datetime, time
from .utils import (
parse_mdc_time, pack_mdc_time, parse_enum_bitmask, pack_bitmask,
pack_videowall_model, parse_videowall_model)
class Field:
def __init__(self, name=None):
self.name = name or self.__class__.__name__.upper()
def parse(self, data):
# returns data and cursor shift
return data, len(data)
def pack(self, value):
return [value]
class Int(Field):
range: Optional[range] = None
def __init__(self, name=None, range=None, length=1, byteorder='big'):
super().__init__(name)
self.range = range
self.length = length
self.byteorder = byteorder
def pack(self, value):
if self.range and value not in self.range:
raise ValueError('Field not in range', self.name, self.range)
return int(value).to_bytes(self.length, byteorder=self.byteorder)
def parse(self, data):
return int.from_bytes(data[:self.length], self.byteorder), self.length
class Bool(Int):
range = range(2)
def parse(self, data):
return bool(data[0]), 1
class Enum(Field):
def __init__(self, enum, name=None):
self.enum = enum
super().__init__(name or enum.__name__)
def parse(self, data):
return self.enum(data[0]), 1
def pack(self, value):
if isinstance(value, str):
value = self.enum[value]
return [self.enum(value).value]
class Str(Field):
def __init__(self, name=None, length=None):
self.length = length
super().__init__(name)
def parse(self, data):
length = self.length or len(data)
return data[:length].decode('utf8').rstrip('\x00'), length
def pack(self, value):
if self.length is not None and len(value) > self.length:
raise ValueError('Field length exceeded', len(value), self.length)
return value.encode('utf8')
class StrCoded(Field):
def __init__(self, code, name=None):
super().__init__(name)
self.code = code
def parse(self, data):
if self.code != data[0]:
raise ValueError('Expected code not matched', data[0])
length = data[1]
if not length:
return '', 2
return data[2:length + 2].decode('utf8'), length + 2
def pack(self, value):
encoded = value.encode('utf8')
return bytes([self.code, len(encoded)]) + encoded
class Time12H(Field):
def parse(self, data):
return parse_mdc_time(data[2], data[0], data[1]), 3
def pack(self, data):
day_part, hour, minute, second = pack_mdc_time(data)
return (hour, minute, day_part)
class Time(Field):
def __init__(self, name=None, seconds=False):
self.seconds = seconds
super().__init__(name)
def parse(self, data):
return (
time(data[0], data[1], data[3] if self.seconds else 0),
3 if self.seconds else 2
)
def pack(self, data):
if self.seconds:
return (data.hour, data.minute, data.second)
return (data.hour, data.minute)
class DateTime(Field):
name = 'datetime'
def __init__(self, name=None, seconds=True):
self.seconds = seconds
super().__init__(name)
def parse(self, data):
if self.seconds:
time = parse_mdc_time(data[7], data[1], data[2], data[3])
return datetime(
int.from_bytes(data[5:7], 'big'), # year
data[4], data[0], # month, day
time.hour, time.minute, time.second
), 8
time = parse_mdc_time(data[6], data[1], data[2])
return datetime(
int.from_bytes(data[4:6], 'big'), # year
data[3], data[0], # month, day
time.hour, time.minute, time.second
), 7
def pack(self, value):
day_part, hour, minute, second = pack_mdc_time(value.time())
return (
bytes([value.day, hour, minute])
+ (self.seconds and bytes([second]) or b'')
+ bytes([value.month])
+ int.to_bytes(value.year, 2, 'big') + bytes([day_part]))
class Bitmask(Enum):
def parse(self, data):
return parse_enum_bitmask(self.enum, data[0]), 1
def pack(self, data):
if not isinstance(data, Sequence):
raise ValueError('Bitmask values must be sequence')
return [pack_bitmask(data)]
class IPAddress(Field):
def parse(self, data):
return '.'.join(str(int(x)) for x in data[:4]), 4
def pack(self, data):
rv = tuple(int(x) for x in data.split('.'))
if not len(rv) == 4 or not all(0 <= x < 256 for x in rv):
raise ValueError('Invalid IP address', data)
return rv
class VideoWallModel(Field):
def parse(self, data):
return parse_videowall_model(data[0]), 1
def pack(self, data):
if isinstance(data, str):
rv = tuple(int(x) for x in data.split(','))
elif not isinstance(data, (tuple, list)):
raise TypeError('Video wall model must be Tuple[int, int] or'
'comma-separated string')
if not len(rv) == 2:
raise ValueError('Invalid video wall model', data)
return pack_videowall_model(rv)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/utils.py 0000644 0001751 0000200 00000003103 14736575073 020753 0 ustar 00runner docker from enum import Enum
from datetime import datetime
def _bit_unmask(val, length=None):
rv = tuple(reversed(tuple(int(x) for x in tuple('{0:0b}'.format(val)))))
if length and len(rv) < length:
return rv + ((0,) * (length - len(rv)))
return rv
def parse_enum_bitmask(enum, value):
"""
Returns tuple of enum values, which was set to 1 in bitmask
"""
return tuple(
enum(i)
for i, x in enumerate(
_bit_unmask(value, length=len(enum)))
if x
)
def pack_bitmask(values):
rv = 0
for val in values:
if isinstance(val, Enum):
val = val.value
rv |= (1 << val)
return rv
def parse_mdc_time(day_part, hour, minute, second=0):
"""
PM = 0x00
AM = 0x01
"""
return datetime.strptime(
f'{day_part and "AM" or "PM"} {hour} {minute} {second}',
'%p %I %M %S').time()
def pack_mdc_time(time):
time = time.strftime('%p %I %M %S').split()
return int(time[0] == 'AM'), int(time[1]), int(time[2]), int(time[3])
def repr_hex(value):
# return ' '.join(f'{x:02x}/{x}' for x in value)
return ':'.join(f'{x:02x}' for x in value)
def parse_hex(value):
return value and bytes(int(x, 16) for x in value.split(':')) or b''
def parse_videowall_model(value):
"""
Splits coordinates byte (with y, x representation) to (x, y) tuple
"""
return divmod(value, 1 << 4)[::-1]
def pack_videowall_model(value):
"""
Converts (x, y) tuple to one coordinates byte (with y, x representation)
"""
return [(value[1] * 16) + value[0]]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/samsung_mdc/version.py 0000644 0001751 0000200 00000000027 14736575073 021302 0 ustar 00runner docker __version__ = '1.15.0'
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1736112705.0172837
python-samsung-mdc-1.15.0/setup.cfg 0000644 0001751 0000200 00000000151 14736575101 016552 0 ustar 00runner docker [flake8]
max-line-length = 80
exclude = .git,__pycache__,env,venv
[egg_info]
tag_build =
tag_date = 0
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1736112699.0
python-samsung-mdc-1.15.0/setup.py 0000644 0001751 0000200 00000003545 14736575073 016465 0 ustar 00runner docker from setuptools import setup, find_packages
exec(open('samsung_mdc/version.py').read())
requires = [
'click', # tested: click >=7,<=8
'jinja2',
]
serial_requires = [
'pyserial-asyncio' # tested: pyserial==3.5; pyserial-asyncio==0.5
]
# TODO: leaving serial in default dependencies
# just not to make README and pipx usage too complicated
requires += serial_requires
test_requires = [
'pytest',
'pytest-asyncio',
'nest-asyncio',
]
setup(
name='python-samsung-mdc',
version=__version__, # noqa
description=('Samsung Multiple Display Control (MDC) '
'protocol implementation (asyncio library + CLI interface)'),
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
license='BSD-3-Clause', # https://spdx.org/licenses/BSD-3-Clause.html
license_files=['LICENSE'],
classifiers=[
'License :: OSI Approved :: BSD License',
'Intended Audience :: Developers',
'Intended Audience :: System Administrators',
'Intended Audience :: Telecommunications Industry',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Development Status :: 5 - Production/Stable',
'Operating System :: OS Independent',
'Topic :: Multimedia :: Video :: Display',
'Topic :: Home Automation',
'Topic :: Utilities',
],
python_requires='>=3.7,<4.0',
author='Victor Gavro',
author_email='vgavro@gmail.com',
url='http://github.com/vgavro/samsung-mdc',
keywords=['samsung', 'mdc'],
packages=find_packages(),
install_requires=requires,
extras_require={
'test': test_requires,
'serial': serial_requires,
'all': serial_requires,
},
entry_points={
'console_scripts': [
'samsung-mdc=samsung_mdc.cli:cli',
],
},
)