From caa16c27d5d324312f975f449dcf398ded105563 Mon Sep 17 00:00:00 2001 From: Moises Martinez Date: Tue, 27 Feb 2024 18:30:45 +0000 Subject: [PATCH] Updated README and improved multimonitor support --- README.md | 142 ++++++++++++++++++++++++- ddc-mqtt/devices.py | 4 +- ddc-mqtt/rename_to_config.yml | 14 +-- ddc-mqtt/start.py | 27 +++-- hass_display_kvm.jpg | Bin 0 -> 58073 bytes simpleddc-extension/simple-ddc.h | 1 + simpleddc-extension/simpleddc-python.c | 9 +- simpleddc-extension/simpleddc.c | 35 +++++- 8 files changed, 199 insertions(+), 33 deletions(-) create mode 100644 hass_display_kvm.jpg diff --git a/README.md b/README.md index 19ba3d4..a3a458d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,139 @@ -# ddc-mqtt +# ddc-mqtt - A simple software Display Input switch for DDC-supporting monitors -Setup: +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/moimartb) -0. Connect a device where to run this to a DDC enabled port of your monitor— I use a raspberry pi with a GN95C Samsung display -1. create a config.yml (copy it from rename_to_config.yml) for your mqtt credentials and monitor setup (Use ddcutil to get inputs and codes) -2. docker-compose up -d +- Do you have a displays with multiple inputs? +- Would you like to be able to switch inputs without the need of meddling with clunky OSD menus and akwardly-positioned control 'nipples'? +- Does this happen to you even with fancy, expensive displays like the Neo G9 line from Samsung? +- Can you imagine KVM scenarios based on home assistant scenarios? + +Then this software is for you!!! + +You only need a Linux machine connected to the display, a bit of knowledge on your monitor's support of DDC capabilities and this will +create a device for your + +## Display's DDC capabilities + +First of all, you'd need some info about your display about DDC capabilities wrt display inputs. You'd need the *ddcutil* tool to figure +things out. For example: this is what my G95NC Samsung Neo G9 Reports: + +``` +$ ddcutil capabilities +Model: FALCON +MCCS version: 2.0 +Commands: + Op Code: 01 (VCP Request) + Op Code: 02 (VCP Response) + Op Code: 03 (VCP Set) + Op Code: 07 (Timing Request) + Op Code: 0C (Save Settings) + Op Code: E3 (Capabilities Reply) + Op Code: F3 (Capabilities Request) +VCP Features: + Feature: 02 (New control value) + Feature: 04 (Restore factory defaults) + Feature: 05 (Restore factory brightness/contrast defaults) + Feature: 08 (Restore color defaults) + Feature: 10 (Brightness) + Feature: 12 (Contrast) + Feature: 14 (Select color preset) + Values: + 05: 6500 K + 08: 9300 K + 0b: User 1 + 0c: User 2 + Feature: 16 (Video gain: Red) + Feature: 18 (Video gain: Green) + Feature: 1A (Video gain: Blue) + Feature: 52 (Active control) + Feature: 60 (Input Source) + Values: + 01: VGA-1 + 03: DVI-1 + 04: DVI-2 + 11: HDMI-1 + 12: HDMI-2 + 0f: DisplayPort-1 + 10: DisplayPort-2 + Feature: 62 (Audio speaker volume) + Feature: 8D (Audio Mute) + Feature: FF (Manufacturer specific feature) +``` + +We'll focus on the **Feature: 60 (Input Source)** ... I have good news and bad news: + +1. Good news: The display supports changing inputs with DDC! +2. Bad news: **The inputs and the values are, for the most part, are bullsh\*t!!** + +Because of this, we will need to figure out the actual input values for each actual input of the monitor. + +You can use **ddcutil** again to figure them out like so: + +``` +# change the last number to figure the actual values that change the input. Have in mind these are hexadecimal values you'll need to convert to integers +$ sudo ddcutil setvcp 0x60 0x01 +``` + +## Configuration + +Once you have the values for the inputs you want to control, you can fill out the config.yml inside the folder ddc-mqtt that you'd need for the software to run. For my display this would be: + +``` +mqtt: + username: #your mqtt server's username + password: #your mqtt server's password + host: 1.2.3.4 #your mqtt server's host + port: 1883 +display: + - id: 1 #display to control. if only a single monitor, this must be 1 + inputs: + HDMI1: 5 # key: value — you can change the name of the input. The codes here are as integers, not hexadecimal + HDMI2: 6 + HDMI3: 7 + DP: 15 +interval: 20 +``` + +## Setup + +You might want to use the docker container that comes with the code. If you use a Raspberry Pi you just do: + +``` +$ docker-compose up -d +``` + +And if your config is well formed, that should be it! + +If you don't use a Raspberry Pi, you might need to pay attention to the *docker-compose.yml* in the root folder as we are +forwarding the i2c devices for the ddcutil library to access. + +``` + devices: + - "/dev/i2c-20:/dev/i2c-20" + - "/dev/i2c-21:/dev/i2c-21" #these might be different in your machine +``` + +## Usage + +If your MQTT server is shared or is part of a *Home Assistant* setup, the usage is simple. You'll get a device with switches to change inputs and that's all! + +![Display KVM](https://raw.githubusercontent.com/moimart/ddc-mqtt/main/hass_display_kvm.jpg)' + +If you want to use it MQTT RAW!!! you'd have access to the entities in the following paths: + +``` +Subscribe to kikkei/display-kvm/{number_of_display}/{input_name_in_the_config}/state -> with values 'true' or 'false' to check the state +Publish 'ON' to kikkei/display-kvm/{number_of_display}/{input_name_in_the_config}/command for activation -> no need to send 'OFF'; switching is taken care of + +Example: kikkei/display-kvm/1/HDMI1/state +``` + +## Observations + +This software is polling at an interval to re-check of the state of the active input— just in case you switch it manually... + +This interval can be configured in the config.yml with the key interval. It is optional and by default is 20 seconds... + +I coded this to support multiple monitors but I have not tested it at all... contributions are very welcome! :) + +**Hope it works for you as well as it works for me!** \ No newline at end of file diff --git a/ddc-mqtt/devices.py b/ddc-mqtt/devices.py index becd4d1..220b336 100644 --- a/ddc-mqtt/devices.py +++ b/ddc-mqtt/devices.py @@ -1,6 +1,6 @@ device = { "identifiers": ["Kikkei Labs Display KVM"], - "name": "Display KVM", + "name": "Display KVM #?", "model": "Kikkei-display-kvm-0", "manufacturer": "Kikkei Labs", } @@ -8,7 +8,7 @@ device = { display_device = { "identifiers": ["Input"], - "name": "Display Input KVM", + "name": "Display Input KVM #?", "model": "Kikkei-display-kvm", "manufacturer": "Kikkei Labs", } diff --git a/ddc-mqtt/rename_to_config.yml b/ddc-mqtt/rename_to_config.yml index 8936c88..4f45c70 100644 --- a/ddc-mqtt/rename_to_config.yml +++ b/ddc-mqtt/rename_to_config.yml @@ -4,10 +4,10 @@ mqtt: host: port: 1883 display: - id: 1 - inputs: - hdmi1: 5 - hdmi2: 6 - hdmi3: 7 - dp: 15 - + - id: 1 + inputs: + HDMI1: 5 + HDMI2: 6 + HDMI3: 7 + DP: 15 +interval: 20 diff --git a/ddc-mqtt/start.py b/ddc-mqtt/start.py index 2552ced..eff7b85 100644 --- a/ddc-mqtt/start.py +++ b/ddc-mqtt/start.py @@ -23,6 +23,8 @@ class Service: config["mqtt"]["host"], config["mqtt"]["port"]) + poll_interval = 20 if "interval" not in config else config["interval"] + self.mqtt.delegate = self self.inputs = {} @@ -30,27 +32,24 @@ class Service: print(display_data) - self.inputs = { - display_data['id']: { - "switches": [] - } - } + self.inputs = {} - for input_name, input_code in display_data['inputs'].items(): - self.create_display_switch(display_data['id'],input_name,input_code) + for display in display_data: + self.inputs[display['id']] = { "switches": [] } + for input_name, input_code in display['inputs'].items(): + self.create_display_switch(display['id'],input_name,input_code) - self.timer = Timer(30, self) + self.timer = Timer(poll_interval, self) self.update_inputs_states() def update_inputs_states(self): - input_code = simpleddc.show_input() - - print(f"Input code is {input_code}") - for display_id in self.inputs.keys(): + input_code = simpleddc.show_input(int(display_id)) + + print(f"Input code is {input_code}") + for entry in self.inputs[display_id]["switches"]: if entry["code"] == input_code: - print("Found {}".format(entry["topic"])) self.mqtt.client.publish(entry["topic"], "true") entry["state"] = True else: @@ -101,7 +100,7 @@ class Service: config["state_topic"] = config["state_topic"].replace("?", input_name) device = display_device.copy() - device["name"] = device["name"].replace("#", input_name) + device["name"] = device["name"].replace("?", input_name) device["model"] = "{}-{}".format(device["model"],display_id) config["device"] = device diff --git a/hass_display_kvm.jpg b/hass_display_kvm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..71fc1114a3577a64050544152608d507a916313a GIT binary patch literal 58073 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

W5G2rXdc^JF{xLW4o;@93EP**J+a39|qQ^ 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 TBm4hX4xjgb2HJqoe`o#&6h@{! literal 0 HcmV?d00001 diff --git a/simpleddc-extension/simple-ddc.h b/simpleddc-extension/simple-ddc.h index dd6b97a..d7103f8 100644 --- a/simpleddc-extension/simple-ddc.h +++ b/simpleddc-extension/simple-ddc.h @@ -7,6 +7,7 @@ #include DDCA_Display_Handle * open_first_display_by_dlist(); +DDCA_Display_Handle * open_display_by_dlist(unsigned int display); DDCA_Status switch_input(DDCA_Display_Handle* handle, uint8_t input); uint8_t show_any_value( DDCA_Display_Handle dh, diff --git a/simpleddc-extension/simpleddc-python.c b/simpleddc-extension/simpleddc-python.c index 57b32ec..d580f01 100644 --- a/simpleddc-extension/simpleddc-python.c +++ b/simpleddc-extension/simpleddc-python.c @@ -19,9 +19,16 @@ static PyObject* switch_to_input(PyObject* self, PyObject* args) { } static PyObject* show_input(PyObject* self, PyObject* args) { - DDCA_Display_Handle* handle = open_first_display_by_dlist(); + uint8_t* display = malloc(sizeof(uint8_t)); + + if (!PyArg_ParseTuple(args, "i", display)) + *display = 0; + + DDCA_Display_Handle* handle = open_display_by_dlist(*display); int result = show_any_value(handle,DDCA_NON_TABLE_VCP_VALUE, 0x60); ddca_close_display(handle); + + free(display); return PyLong_FromLong(result); } diff --git a/simpleddc-extension/simpleddc.c b/simpleddc-extension/simpleddc.c index 2a89722..b4a2743 100644 --- a/simpleddc-extension/simpleddc.c +++ b/simpleddc-extension/simpleddc.c @@ -8,7 +8,8 @@ ddca_rc_desc(status_code)); \ } while(0) -DDCA_Display_Handle * open_first_display_by_dlist() { +DDCA_Display_Handle * open_first_display_by_dlist() +{ printf("Check for monitors using ddca_get_displays()...\n"); DDCA_Display_Handle dh = NULL; @@ -20,13 +21,39 @@ DDCA_Display_Handle * open_first_display_by_dlist() { if (dlist->ct == 0) { printf(" No DDC capable displays found\n"); - } - else { + } else { DDCA_Display_Info * dinf = &dlist->info[0]; DDCA_Display_Ref * dref = dinf->dref; printf("Opening display %s\n", dinf->model_name); printf("Model: %s\n", dinf->model_name); - //printf("Model: %s\n", dinf->mmid.model_name); + + DDCA_Status rc = ddca_open_display2(dref, false, &dh); + if (rc != 0) { + DDC_ERRMSG("ddca_open_display2", rc); + } + } + ddca_free_display_info_list(dlist); + return dh; +} + +DDCA_Display_Handle * open_display_by_dlist(unsigned int display) +{ + DDCA_Display_Handle dh = NULL; + + // Inquire about detected monitors. + DDCA_Display_Info_List* dlist = NULL; + ddca_get_display_info_list2( + false, // don't include invalid displays + &dlist); + + if (dlist->ct == 0 || dlist->ct < display) { + printf(" No DDC capable displays found\n"); + } else { + DDCA_Display_Info * dinf = &dlist->info[display - 1]; + DDCA_Display_Ref * dref = dinf->dref; + printf("Opening display %s\n", dinf->model_name); + printf("Model: %s\n", dinf->model_name); + DDCA_Status rc = ddca_open_display2(dref, false, &dh); if (rc != 0) { DDC_ERRMSG("ddca_open_display2", rc);