From b51cbd93a4651041013220357b1166370c7866be Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Fri, 15 Jan 2021 17:48:36 +0000 Subject: [PATCH 01/17] feat(kramer_p3000): initial commit --- drivers/kramer/switches/protocol3000.cr | 101 +++++++++++++++++++ drivers/kramer/switches/protocol3000_spec.cr | 3 + 2 files changed, 104 insertions(+) create mode 100644 drivers/kramer/switches/protocol3000.cr create mode 100644 drivers/kramer/switches/protocol3000_spec.cr diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr new file mode 100644 index 00000000000..2ff196b7d61 --- /dev/null +++ b/drivers/kramer/switches/protocol3000.cr @@ -0,0 +1,101 @@ +# Documentation: https://aca.im/driver_docs/Kramer/protocol_3000_2.10_user.pdf + +class Kramer::Switcher::Protocol3000 < PlaceOS::Driver + # Discovery Information + tcp_port 23 + descriptive_name "Kramer Protocol 3000 Switcher" + generic_name :Switcher + + @device_id : String? = nil + @destination : String? = nil + @login_level : String? = nil + @password : String? = nil + + DELIMITER = "\x0D\x0A" + + def on_load + transport.tokenizer = Tokenizer.new(DELIMITER) + on_update + end + + def on_update + @device_id = setting?(String, :kramer_id) + @destination = "#{@device_id}@" if @device_id + @login_level = setting?(String, :kramer_login) + @password = setting?(String, :kramer_password) if @login_level + + state + end + + def connected + state + + schedule.every(1.minute) do + logger.debug { "-- Kramer Maintaining Connection" } + do_send("MODEL?", priority: 0) # Low priority poll to maintain connection + end + end + + def disconnected + schedule.clear + end + + # Get current state of the switcher + private def state + protocol_handshake + login + get_machine_info + end + + def switch_video(input : String | Int32, output : Array(String)) + do_send(CMDS["switch_video"], build_switch_data({input => output})) + end + + private def do_send(command, *args, **options) + cmd = args.empty? ? "##{@destination}#{command}\r" : "##{@destination}#{command} #{args.join(',')}\r" + + logger.debug { "Kramer sending: #{cmd}" } + send(cmd, **options) + end + + def received(data, task) + task.try &.success + end + + CMDS = { + "info" => "INFO-IO?", + "login" => "LOGIN", + "route" => "ROUTE", + "switch" => "AV", + "switch_audio" => "AUD", + "switch_video" => "VID", + "audio_mute" => "MUTE", + "video_mute" => "VMUTE", + "help" => "HELP", + "model" => "MODEL?" + } + CMDS.merge!(CMDS.invert) + + private def build_switch_data(map : Hash(String | Int32, Array(String))) + data = String.build do |str| + map.each do |input, outputs| + str << outputs.join { |output| "#{input}>#{output}," } + end + end + data[0..-2] # Remove the comma at the end + end + + private def protocol_handshake + do_send("", priority: 99) + end + + private def login + if @login_level && (pass = @password) + do_send(CMDS["login"], pass, priority: 99) + end + end + + def get_machine_info + do_send(CMDS["info"], priority: 99) + end +end diff --git a/drivers/kramer/switches/protocol3000_spec.cr b/drivers/kramer/switches/protocol3000_spec.cr new file mode 100644 index 00000000000..b43b12904c2 --- /dev/null +++ b/drivers/kramer/switches/protocol3000_spec.cr @@ -0,0 +1,3 @@ +DriverSpecs.mock_driver "Kramer::Switcher::Protocol3000" do + exec(:get_machine_info) +end From 4e8930c835eb044eece1d0b4d1721cf0d3fb2eda Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Tue, 19 Jan 2021 10:43:45 +0000 Subject: [PATCH 02/17] feat(p3000): port switch_audio --- drivers/kramer/switches/protocol3000.cr | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index 2ff196b7d61..fb5c51f5c2c 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -51,6 +51,29 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver do_send(CMDS["switch_video"], build_switch_data({input => output})) end + def switch_audio(input : String | Int32, output : Array(String)) + do_send(CMDS["switch_video"], build_switch_data({input => output})) + end + + enum RouteType + Video = 1 + Audio = 2 + USB = 3 + AudioVideo = 12 + VideoUSB = 13 + AudioVideoUSB = 123 + end + # def route(map, type = :audio_video) + # map.each do |input, outputs| + # input = input.to_s if input.is_a?(Symbol) + # input = input.to_i if input.is_a?(String) + + # outputs.each do |output| + # do_send(CMDS[:route], ROUTE_TYPES[type], output, input) + # end + # end + # end + private def do_send(command, *args, **options) cmd = args.empty? ? "##{@destination}#{command}\r" : "##{@destination}#{command} #{args.join(',')}\r" From 21019e1553f4969d5b4d153f1ccf92caea2f2b23 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Tue, 19 Jan 2021 13:06:48 +0000 Subject: [PATCH 03/17] feat(p3000): port over mutes --- drivers/kramer/switches/protocol3000.cr | 54 +++++++++++++++++++------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index fb5c51f5c2c..739c4292602 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -1,6 +1,10 @@ +require "placeos-driver/interface/muteable" + # Documentation: https://aca.im/driver_docs/Kramer/protocol_3000_2.10_user.pdf class Kramer::Switcher::Protocol3000 < PlaceOS::Driver + include Interface::Muteable + # Discovery Information tcp_port 23 descriptive_name "Kramer Protocol 3000 Switcher" @@ -63,25 +67,42 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver VideoUSB = 13 AudioVideoUSB = 123 end - # def route(map, type = :audio_video) - # map.each do |input, outputs| - # input = input.to_s if input.is_a?(Symbol) - # input = input.to_i if input.is_a?(String) + private def route(map : Hash(String | Int32, Array(String)), type : RouteType = RouteType::AudioVideo) + map.each do |input, outputs| + outputs.each do |output| + do_send(CMDS["route"], type.value, output, input) + end + end + end - # outputs.each do |output| - # do_send(CMDS[:route], ROUTE_TYPES[type], output, input) - # end - # end - # end + def mute( + state : Bool = true, + index : Int32 | String = 0, + layer : MuteLayer = MuteLayer::AudioVideo + ) + mute_video(index, state) if layer.video? || layer.audio_video? + mute_audio(index, state) if layer.audio? || layer.audio_video? + end - private def do_send(command, *args, **options) - cmd = args.empty? ? "##{@destination}#{command}\r" : "##{@destination}#{command} #{args.join(',')}\r" + def mute_video(index : Int32 | String = 0, state : Bool = true) + do_send(CMDS["video_mute"], index, state ? 1 : 0) + end - logger.debug { "Kramer sending: #{cmd}" } - send(cmd, **options) + def mute_audio(index : Int32 | String = 0, state : Bool = true) + do_send(CMDS["audio_mute"], index, state ? 1 : 0) + end + + def help + do_send(CMDS["help"]) + end + + def model + do_send(CMDS["model"]) end def received(data, task) + data = String.new(data[0..-3]) # Remove delimiter "\x0D\x0A" + logger.debug { "Kramer sent #{data}" } task.try &.success end @@ -121,4 +142,11 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver def get_machine_info do_send(CMDS["info"], priority: 99) end + + private def do_send(command, *args, **options) + cmd = args.empty? ? "##{@destination}#{command}\r" : "##{@destination}#{command} #{args.join(',')}\r" + + logger.debug { "Kramer sending: #{cmd}" } + send(cmd, **options) + end end From 6019aa870c1df2f35aaf219f132cfd9ef660e50f Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Tue, 19 Jan 2021 16:46:42 +0000 Subject: [PATCH 04/17] fix(p3000): only allow String for input --- drivers/kramer/switches/protocol3000.cr | 68 +++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index 739c4292602..ae0f9bcd9e3 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -51,11 +51,11 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver get_machine_info end - def switch_video(input : String | Int32, output : Array(String)) + def switch_video(input : String, output : Array(String)) do_send(CMDS["switch_video"], build_switch_data({input => output})) end - def switch_audio(input : String | Int32, output : Array(String)) + def switch_audio(input : String, output : Array(String)) do_send(CMDS["switch_video"], build_switch_data({input => output})) end @@ -67,7 +67,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver VideoUSB = 13 AudioVideoUSB = 123 end - private def route(map : Hash(String | Int32, Array(String)), type : RouteType = RouteType::AudioVideo) + def route(map : Hash(String, Array(String)), type : RouteType = RouteType::AudioVideo) map.each do |input, outputs| outputs.each do |output| do_send(CMDS["route"], type.value, output, input) @@ -103,6 +103,66 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver def received(data, task) data = String.new(data[0..-3]) # Remove delimiter "\x0D\x0A" logger.debug { "Kramer sent #{data}" } + + # Extract and check the machine number if we've defined it + components = data.split('@') + return if components.size > 1 && @device_id && components[0] != @device_id + + data = components[-1].strip + components = data.split(/\s+|,/) + + cmd = components[0] + args = components[1..-1] + args.pop if args[-1] == "OK" + + if cmd == "OK" + return task.try &.success + elsif cmd[0..2] == "ERR" || args[0][0..2] == "ERR" + if cmd[0..2] == "ERR" + error = cmd[3..-1] + errfor = nil + else + error = args[0][3..-1] + errfor = " on #{cmd}" + end + self[:last_error] = error + return task.try &.abort("Kramer command error #{error}#{errfor}") + end + + logger.debug { "Kramer cmd: #{cmd}, args: #{args}" } + + case c = CMDS[cmd] + when "info" + self[:video_inputs] = args[1].to_i + self[:video_outputs] = args[3].to_i + when "route" + # response looks like ~01@ROUTE 12,1,4 OK + layer = args[0].to_i + dest = args[1].to_i + src = args[2].to_i + self["#{RouteType.from_value(layer)}#{dest}"] = src + when "switch", "switch_audio", "switch_video" + # return string like "in>out,in>out,in>out OK" + case c + when "switch_audio" then type = "audio" + when "switch_video" then type = "video" + else type = "av" end + + args.each do |map| + inout = map.split('>') + self["#{type}#{inout[1]}"] = inout[0].to_i + end + when "audio_mute" + # Response looks like: ~01@VMUTE 1,0 OK + output = args[0] + mute = args[1] + self["audio#{output}_muted"] = mute[0] == '1' + when "video_mute" + output = args[0] + mute = args[1] + self["video#{output}_muted"] = mute[0] == '1' + end + task.try &.success end @@ -120,7 +180,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver } CMDS.merge!(CMDS.invert) - private def build_switch_data(map : Hash(String | Int32, Array(String))) + def build_switch_data(map : Hash(String, Array(String))) data = String.build do |str| map.each do |input, outputs| str << outputs.join { |output| "#{input}>#{output}," } From 54a2a04076427965bf3ec117f82ef3fc76316d9d Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Wed, 20 Jan 2021 10:51:34 +0000 Subject: [PATCH 05/17] feat(p3000): add debugging --- drivers/kramer/switches/protocol3000.cr | 8 ++++++-- drivers/kramer/switches/protocol3000_spec.cr | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index ae0f9bcd9e3..7f6fcbbf883 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -111,10 +111,16 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver data = components[-1].strip components = data.split(/\s+|,/) + pp "Got here" + cmd = components[0] args = components[1..-1] args.pop if args[-1] == "OK" + pp "Got here2" + pp "Kramer cmd: #{cmd}, args: #{args}" + logger.debug { "Kramer cmd: #{cmd}, args: #{args}" } + if cmd == "OK" return task.try &.success elsif cmd[0..2] == "ERR" || args[0][0..2] == "ERR" @@ -129,8 +135,6 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver return task.try &.abort("Kramer command error #{error}#{errfor}") end - logger.debug { "Kramer cmd: #{cmd}, args: #{args}" } - case c = CMDS[cmd] when "info" self[:video_inputs] = args[1].to_i diff --git a/drivers/kramer/switches/protocol3000_spec.cr b/drivers/kramer/switches/protocol3000_spec.cr index b43b12904c2..8d24fb61527 100644 --- a/drivers/kramer/switches/protocol3000_spec.cr +++ b/drivers/kramer/switches/protocol3000_spec.cr @@ -1,3 +1,13 @@ DriverSpecs.mock_driver "Kramer::Switcher::Protocol3000" do + settings({ + kramer_id: "01" + }) + + # protocol_handshake + should_send("#\r") + responds("~01@ OK\x0D\x0A") + should_send("#INFO-IO?\r") + responds("~01@ OK\x0D\x0A") + exec(:get_machine_info) end From 936079de5d70fd6c51e175464490fc99597fb150 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 28 Jan 2021 13:13:44 +0000 Subject: [PATCH 06/17] fix(p3000): get basic specs passing --- drivers/kramer/switches/protocol3000.cr | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index 7f6fcbbf883..d6e05b61376 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -101,7 +101,8 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver end def received(data, task) - data = String.new(data[0..-3]) # Remove delimiter "\x0D\x0A" + # Remoe initialiser `~` and delimiter "\x0D\x0A" + data = String.new(data[1..-3]) logger.debug { "Kramer sent #{data}" } # Extract and check the machine number if we've defined it @@ -111,14 +112,10 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver data = components[-1].strip components = data.split(/\s+|,/) - pp "Got here" - cmd = components[0] args = components[1..-1] - args.pop if args[-1] == "OK" + args.pop if args[-1]? == "OK" - pp "Got here2" - pp "Kramer cmd: #{cmd}, args: #{args}" logger.debug { "Kramer cmd: #{cmd}, args: #{args}" } if cmd == "OK" From 72218a10ea9a961a8c73b25f720b2a52af9c5323 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 28 Jan 2021 13:30:12 +0000 Subject: [PATCH 07/17] feat(p3000): add specs for on_load/on_update --- drivers/kramer/switches/protocol3000.cr | 7 +++--- drivers/kramer/switches/protocol3000_spec.cr | 24 ++++++++++++++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index d6e05b61376..da87eb3a823 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -12,7 +12,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver @device_id : String? = nil @destination : String? = nil - @login_level : String? = nil + @login_level : Bool? = nil @password : String? = nil DELIMITER = "\x0D\x0A" @@ -25,7 +25,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver def on_update @device_id = setting?(String, :kramer_id) @destination = "#{@device_id}@" if @device_id - @login_level = setting?(String, :kramer_login) + @login_level = setting?(Bool, :kramer_login) @password = setting?(String, :kramer_password) if @login_level state @@ -200,7 +200,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver end end - def get_machine_info + private def get_machine_info do_send(CMDS["info"], priority: 99) end @@ -208,6 +208,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver cmd = args.empty? ? "##{@destination}#{command}\r" : "##{@destination}#{command} #{args.join(',')}\r" logger.debug { "Kramer sending: #{cmd}" } + pp "Kramer sending: #{cmd}" send(cmd, **options) end end diff --git a/drivers/kramer/switches/protocol3000_spec.cr b/drivers/kramer/switches/protocol3000_spec.cr index 8d24fb61527..df05f74cb92 100644 --- a/drivers/kramer/switches/protocol3000_spec.cr +++ b/drivers/kramer/switches/protocol3000_spec.cr @@ -1,13 +1,27 @@ DriverSpecs.mock_driver "Kramer::Switcher::Protocol3000" do - settings({ - kramer_id: "01" - }) - + # on_load + # state # protocol_handshake should_send("#\r") responds("~01@ OK\x0D\x0A") + # get_machine_info should_send("#INFO-IO?\r") responds("~01@ OK\x0D\x0A") - exec(:get_machine_info) + settings({ + kramer_id: "01", + kramer_login: true, + kramer_password: "pass" + }) + # on_update + # state + # protocol_handshake + should_send("#01@\r") + responds("~01@ OK\x0D\x0A") + # login + should_send("#01@LOGIN pass\r") + responds("~01@ OK\x0D\x0A") + # get_machine_info + should_send("#01@INFO-IO?\r") + responds("~01@ OK\x0D\x0A") end From 99287926950af1460d8c9f3f790f658efca71689 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 28 Jan 2021 18:08:22 +0000 Subject: [PATCH 08/17] fix(p3000): use Int32 for all inputs and outputs --- drivers/kramer/switches/protocol3000.cr | 13 +++++++------ drivers/kramer/switches/protocol3000_spec.cr | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index da87eb3a823..8c6376fcb3e 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -51,12 +51,12 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver get_machine_info end - def switch_video(input : String, output : Array(String)) + def switch_video(input : Int32, output : Array(Int32)) do_send(CMDS["switch_video"], build_switch_data({input => output})) end - def switch_audio(input : String, output : Array(String)) - do_send(CMDS["switch_video"], build_switch_data({input => output})) + def switch_audio(input : Int32, output : Array(Int32)) + do_send(CMDS["switch_audio"], build_switch_data({input => output})) end enum RouteType @@ -67,7 +67,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver VideoUSB = 13 AudioVideoUSB = 123 end - def route(map : Hash(String, Array(String)), type : RouteType = RouteType::AudioVideo) + def route(map : Hash(Int32, Array(Int32)), type : RouteType = RouteType::AudioVideo) map.each do |input, outputs| outputs.each do |output| do_send(CMDS["route"], type.value, output, input) @@ -101,7 +101,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver end def received(data, task) - # Remoe initialiser `~` and delimiter "\x0D\x0A" + # Remove initialiser `~` and delimiter "\x0D\x0A" data = String.new(data[1..-3]) logger.debug { "Kramer sent #{data}" } @@ -151,6 +151,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver args.each do |map| inout = map.split('>') + logger.debug { "inout is #{inout}" } self["#{type}#{inout[1]}"] = inout[0].to_i end when "audio_mute" @@ -181,7 +182,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver } CMDS.merge!(CMDS.invert) - def build_switch_data(map : Hash(String, Array(String))) + private def build_switch_data(map : Hash(Int32, Array(Int32))) data = String.build do |str| map.each do |input, outputs| str << outputs.join { |output| "#{input}>#{output}," } diff --git a/drivers/kramer/switches/protocol3000_spec.cr b/drivers/kramer/switches/protocol3000_spec.cr index df05f74cb92..862db33fef2 100644 --- a/drivers/kramer/switches/protocol3000_spec.cr +++ b/drivers/kramer/switches/protocol3000_spec.cr @@ -24,4 +24,20 @@ DriverSpecs.mock_driver "Kramer::Switcher::Protocol3000" do # get_machine_info should_send("#01@INFO-IO?\r") responds("~01@ OK\x0D\x0A") + + exec(:switch_video, 1, [1, 2, 3]) + should_send("#01@VID 1>1,1>2,1>3\r") + responds("~01@VID 1>1,1>2,1>3 OK\x0D\x0A") + + exec(:switch_audio, 2, [1, 2, 3]) + should_send("#01@AUD 2>1,2>2,2>3\r") + responds("~01@AUD 2>1,2>2,2>3 OK\x0D\x0A") + + exec(:route, {1 => [1, 2, 3]}) + should_send("#01@ROUTE 12,1,1\r") + responds("~01@ROUTE 12,1,1 OK\x0D\x0A") + should_send("#01@ROUTE 12,2,1\r") + responds("~01@ROUTE 12,2,1 OK\x0D\x0A") + should_send("#01@ROUTE 12,3,1\r") + responds("~01@ROUTE 12,3,1 OK\x0D\x0A") end From 0eee721fb571aba163b50882baaffc8c83c70c6c Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 28 Jan 2021 18:38:26 +0000 Subject: [PATCH 09/17] feat(p3000): add more tests --- drivers/kramer/switches/protocol3000.cr | 2 +- drivers/kramer/switches/protocol3000_spec.cr | 31 +++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index 8c6376fcb3e..7b2c1983567 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -141,7 +141,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver layer = args[0].to_i dest = args[1].to_i src = args[2].to_i - self["#{RouteType.from_value(layer)}#{dest}"] = src + self["#{RouteType.from_value(layer).to_s.underscore}#{dest}"] = src when "switch", "switch_audio", "switch_video" # return string like "in>out,in>out,in>out OK" case c diff --git a/drivers/kramer/switches/protocol3000_spec.cr b/drivers/kramer/switches/protocol3000_spec.cr index 862db33fef2..2918558ef1e 100644 --- a/drivers/kramer/switches/protocol3000_spec.cr +++ b/drivers/kramer/switches/protocol3000_spec.cr @@ -28,16 +28,33 @@ DriverSpecs.mock_driver "Kramer::Switcher::Protocol3000" do exec(:switch_video, 1, [1, 2, 3]) should_send("#01@VID 1>1,1>2,1>3\r") responds("~01@VID 1>1,1>2,1>3 OK\x0D\x0A") + status[:video1].should eq(1) + status[:video2].should eq(1) + status[:video3].should eq(1) exec(:switch_audio, 2, [1, 2, 3]) should_send("#01@AUD 2>1,2>2,2>3\r") responds("~01@AUD 2>1,2>2,2>3 OK\x0D\x0A") + status[:audio1].should eq(2) + status[:audio2].should eq(2) + status[:audio3].should eq(2) - exec(:route, {1 => [1, 2, 3]}) - should_send("#01@ROUTE 12,1,1\r") - responds("~01@ROUTE 12,1,1 OK\x0D\x0A") - should_send("#01@ROUTE 12,2,1\r") - responds("~01@ROUTE 12,2,1 OK\x0D\x0A") - should_send("#01@ROUTE 12,3,1\r") - responds("~01@ROUTE 12,3,1 OK\x0D\x0A") + exec(:route, {3 => [1, 2, 3]}) + should_send("#01@ROUTE 12,1,3\r") + responds("~01@ROUTE 12,1,3 OK\x0D\x0A") + should_send("#01@ROUTE 12,2,3\r") + responds("~01@ROUTE 12,2,3 OK\x0D\x0A") + should_send("#01@ROUTE 12,3,3\r") + responds("~01@ROUTE 12,3,3 OK\x0D\x0A") + status[:audio_video1].should eq(3) + status[:audio_video2].should eq(3) + status[:audio_video3].should eq(3) + + exec(:mute, true, 6) + should_send("#01@VMUTE 6,1\r") + responds("#01@VMUTE 6,1 OK\x0D\x0A") + status[:video6_muted].should eq(true) + should_send("#01@MUTE 6,1\r") + responds("#01@MUTE 6,1 OK\x0D\x0A") + status[:audio6_muted].should eq(true) end From d5f12d4c1493512202b11d9a72a7fa2926ebd354 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Tue, 9 Feb 2021 16:22:30 +0000 Subject: [PATCH 10/17] feat(kramer_hdmi): initial commit --- drivers/kramer/switches/vs_hdmi.cr | 88 +++++++++++++++++++++++++ drivers/kramer/switches/vs_hdmi_spec.cr | 2 + 2 files changed, 90 insertions(+) create mode 100644 drivers/kramer/switches/vs_hdmi.cr create mode 100644 drivers/kramer/switches/vs_hdmi_spec.cr diff --git a/drivers/kramer/switches/vs_hdmi.cr b/drivers/kramer/switches/vs_hdmi.cr new file mode 100644 index 00000000000..da5d9222ba7 --- /dev/null +++ b/drivers/kramer/switches/vs_hdmi.cr @@ -0,0 +1,88 @@ +# Documentation: https://aca.im/driver_docs/Kramer/Kramer%20protocol%202000%20v0.51.pdf + +class Kramer::Switcher::VsHdmi < PlaceOS::Driver + # Discovery Information + tcp_port 23 + descriptive_name "Kramer Protocol 2000 Switcher" + generic_name :Switcher + + @limits_known : Bool = false + + def on_load + queue.delay = 150.milliseconds + queue.wait = false + end + + def connected + get_machine_type + end + + private def get_machine_type + # id com, video + command = Bytes[62, 0x81, 0x81, 0xFF] + send(command, name: :inputs) # num inputs + command[1] = 0x82 + send(command, name: :outputs) # num outputs + end + + enum Command + ResetVideo = 0 + SwitchVideo = 1 + StatusVideo = 5 + DefineMachine = 62 + IdentifyMachine = 61 + end + + def switch(map : Hash(Int32, Array(Int32))) + # instr, inp, outp, machine number + # Switch video + command = Bytes[1, 0x80, 0x80, 0xFF] + + map.each do |input, outputs| + outputs.each do |output| + command[1] += input + command[2] += output + outname = "video#{output}" + send(command, name: outname) + self[outname] = input + end + end + end + + def received(data, task) + logger.debug { "Kramer sent 0x#{data.hexstring}" } + + return if data[0] & 0b1000000 == 0 # Check we are the destination + + data[1] = data[1] & 0b1111111 # input + data[2] = data[2] & 0b1111111 # output + + case Command.from_value(data[0] & 0b111111) + when .define_machine? + if data[1] == 1 + self[:video_inputs] = data[2] + elsif data[1] == 2 + self[:video_outputs] = data[2] + end + @limits_known = true # Set here in case unsupported + when .status_video? + if data[2] == 0 # Then data[1] has been applied to all the outputs + logger.debug { "Kramer switched #{data[1]} -> All" } + + (1..self[:video_outputs].as_i).each { |i| self["video#{i}"] = data[1] } + else + self["video#{data[2]}"] = data[1] + + logger.debug { "Kramer switched #{data[1]} -> #{data[2]}" } + + # As we may not know the max number of inputs if get_machine_type didn't work + self[:video_inputs] = data[1] if data[1] > self[:video_inputs].as_i + self[:video_outputs] = data[2] if data[2] > self[:video_outputs].as_i + end + when .identify_machine? + logger.debug { "Kramer switcher protocol #{data[1]}.#{data[2]}" } + end + + task.try &.success + end +end diff --git a/drivers/kramer/switches/vs_hdmi_spec.cr b/drivers/kramer/switches/vs_hdmi_spec.cr new file mode 100644 index 00000000000..8aa414e9130 --- /dev/null +++ b/drivers/kramer/switches/vs_hdmi_spec.cr @@ -0,0 +1,2 @@ +DriverSpecs.mock_driver "Kramer::Switcher::VsHdmi" do +end From e94fdd0cb87f27aafa9eeced12c764a18505cad1 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Wed, 10 Feb 2021 13:31:19 +0000 Subject: [PATCH 11/17] feat(kramer_hdmi): decode docs for basic specs --- drivers/kramer/switches/vs_hdmi.cr | 40 +++++++++++++------------ drivers/kramer/switches/vs_hdmi_spec.cr | 10 +++++++ 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/kramer/switches/vs_hdmi.cr b/drivers/kramer/switches/vs_hdmi.cr index da5d9222ba7..94d9f4cbdf2 100644 --- a/drivers/kramer/switches/vs_hdmi.cr +++ b/drivers/kramer/switches/vs_hdmi.cr @@ -18,10 +18,10 @@ class Kramer::Switcher::VsHdmi < PlaceOS::Driver end private def get_machine_type - # id com, video + # id com, video command = Bytes[62, 0x81, 0x81, 0xFF] send(command, name: :inputs) # num inputs - command[1] = 0x82 + command = Bytes[62, 0x82, 0x81, 0xFF] send(command, name: :outputs) # num outputs end @@ -52,35 +52,37 @@ class Kramer::Switcher::VsHdmi < PlaceOS::Driver def received(data, task) logger.debug { "Kramer sent 0x#{data.hexstring}" } - return if data[0] & 0b1000000 == 0 # Check we are the destination + # Check if we are the destination + return unless data[0].bit(6) + input = data[1] & 0b111_111 + output = data[2] & 0b111_111 + command = Command.from_value(data[0] & 0b111_111) + pp "command is #{command}" - data[1] = data[1] & 0b1111111 # input - data[2] = data[2] & 0b1111111 # output - - case Command.from_value(data[0] & 0b111111) + case command when .define_machine? - if data[1] == 1 - self[:video_inputs] = data[2] - elsif data[1] == 2 - self[:video_outputs] = data[2] + if input == 1 + self[:video_inputs] = output + elsif input == 2 + self[:video_outputs] = output end @limits_known = true # Set here in case unsupported when .status_video? - if data[2] == 0 # Then data[1] has been applied to all the outputs - logger.debug { "Kramer switched #{data[1]} -> All" } + if output == 0 # Then input has been applied to all the outputs + logger.debug { "Kramer switched #{input} -> All" } - (1..self[:video_outputs].as_i).each { |i| self["video#{i}"] = data[1] } + (1..self[:video_outputs].as_i).each { |i| self["video#{i}"] = input } else - self["video#{data[2]}"] = data[1] + self["video#{output}"] = input - logger.debug { "Kramer switched #{data[1]} -> #{data[2]}" } + logger.debug { "Kramer switched #{input} -> #{output}" } # As we may not know the max number of inputs if get_machine_type didn't work - self[:video_inputs] = data[1] if data[1] > self[:video_inputs].as_i - self[:video_outputs] = data[2] if data[2] > self[:video_outputs].as_i + self[:video_inputs] = input if input > self[:video_inputs].as_i + self[:video_outputs] = output if output > self[:video_outputs].as_i end when .identify_machine? - logger.debug { "Kramer switcher protocol #{data[1]}.#{data[2]}" } + logger.debug { "Kramer switcher protocol #{input}.#{output}" } end task.try &.success diff --git a/drivers/kramer/switches/vs_hdmi_spec.cr b/drivers/kramer/switches/vs_hdmi_spec.cr index 8aa414e9130..3ee9ac6b094 100644 --- a/drivers/kramer/switches/vs_hdmi_spec.cr +++ b/drivers/kramer/switches/vs_hdmi_spec.cr @@ -1,2 +1,12 @@ DriverSpecs.mock_driver "Kramer::Switcher::VsHdmi" do + # connected + # get_machine_type + # number of inputs + should_send(Bytes[62, 0x81, 0x81, 0xFF]) + responds(Bytes[0x7E, 0x81, 0b11, 0x82]) + status[:video_inputs].should eq(3) + # number of outputs + should_send(Bytes[62, 0x82, 0x81, 0xFF]) + responds(Bytes[0x7E, 0x82, 0x90, 0x82]) + status[:video_outputs].should eq(16) end From 343d3db8df2d8bb04c7e44f9e80a275414121534 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Wed, 10 Feb 2021 13:32:44 +0000 Subject: [PATCH 12/17] fix(kramer_hdmi): check response destination properly --- drivers/kramer/switches/vs_hdmi.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/kramer/switches/vs_hdmi.cr b/drivers/kramer/switches/vs_hdmi.cr index 94d9f4cbdf2..67c75be03e1 100644 --- a/drivers/kramer/switches/vs_hdmi.cr +++ b/drivers/kramer/switches/vs_hdmi.cr @@ -52,8 +52,8 @@ class Kramer::Switcher::VsHdmi < PlaceOS::Driver def received(data, task) logger.debug { "Kramer sent 0x#{data.hexstring}" } - # Check if we are the destination - return unless data[0].bit(6) + # Only process response if we are the destination + return unless data[0].bit(6) == 1 input = data[1] & 0b111_111 output = data[2] & 0b111_111 command = Command.from_value(data[0] & 0b111_111) From 0b3406c54ac1f9a62efb78ca7439ac21bb585507 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Wed, 10 Feb 2021 13:54:26 +0000 Subject: [PATCH 13/17] feat(kramer_hdmi): add specs for switch_video --- drivers/kramer/switches/vs_hdmi.cr | 16 ++++------------ drivers/kramer/switches/vs_hdmi_spec.cr | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/drivers/kramer/switches/vs_hdmi.cr b/drivers/kramer/switches/vs_hdmi.cr index 67c75be03e1..9f661237fae 100644 --- a/drivers/kramer/switches/vs_hdmi.cr +++ b/drivers/kramer/switches/vs_hdmi.cr @@ -6,8 +6,6 @@ class Kramer::Switcher::VsHdmi < PlaceOS::Driver descriptive_name "Kramer Protocol 2000 Switcher" generic_name :Switcher - @limits_known : Bool = false - def on_load queue.delay = 150.milliseconds queue.wait = false @@ -18,11 +16,10 @@ class Kramer::Switcher::VsHdmi < PlaceOS::Driver end private def get_machine_type - # id com, video command = Bytes[62, 0x81, 0x81, 0xFF] - send(command, name: :inputs) # num inputs + send(command, name: :inputs) # no. video inputs command = Bytes[62, 0x82, 0x81, 0xFF] - send(command, name: :outputs) # num outputs + send(command, name: :outputs) # no. of video outputs end enum Command @@ -33,9 +30,7 @@ class Kramer::Switcher::VsHdmi < PlaceOS::Driver IdentifyMachine = 61 end - def switch(map : Hash(Int32, Array(Int32))) - # instr, inp, outp, machine number - # Switch video + def switch_video(map : Hash(Int32, Array(Int32))) command = Bytes[1, 0x80, 0x80, 0xFF] map.each do |input, outputs| @@ -56,17 +51,14 @@ class Kramer::Switcher::VsHdmi < PlaceOS::Driver return unless data[0].bit(6) == 1 input = data[1] & 0b111_111 output = data[2] & 0b111_111 - command = Command.from_value(data[0] & 0b111_111) - pp "command is #{command}" - case command + case Command.from_value(data[0] & 0b111_111) when .define_machine? if input == 1 self[:video_inputs] = output elsif input == 2 self[:video_outputs] = output end - @limits_known = true # Set here in case unsupported when .status_video? if output == 0 # Then input has been applied to all the outputs logger.debug { "Kramer switched #{input} -> All" } diff --git a/drivers/kramer/switches/vs_hdmi_spec.cr b/drivers/kramer/switches/vs_hdmi_spec.cr index 3ee9ac6b094..1dba5ba5caf 100644 --- a/drivers/kramer/switches/vs_hdmi_spec.cr +++ b/drivers/kramer/switches/vs_hdmi_spec.cr @@ -1,12 +1,28 @@ DriverSpecs.mock_driver "Kramer::Switcher::VsHdmi" do # connected # get_machine_type - # number of inputs + # no. of video inputs should_send(Bytes[62, 0x81, 0x81, 0xFF]) - responds(Bytes[0x7E, 0x81, 0b11, 0x82]) + responds(Bytes[0x7E, 0x81, 0b1000_0011, 0x82]) status[:video_inputs].should eq(3) - # number of outputs + # no. of video outputs should_send(Bytes[62, 0x82, 0x81, 0xFF]) responds(Bytes[0x7E, 0x82, 0x90, 0x82]) status[:video_outputs].should eq(16) + + exec(:switch_video, { + 5 => [8] + }) + should_send(Bytes[1, 0x85, 0x88, 0xFF]) + status[:video8].should eq(5) + + exec(:switch_video, { + 1 => [2,3], + 4 => [5,6] + }) + should_send(Bytes[1, 138, 144, 0xFF]) + status[:video2].should eq(1) + status[:video3].should eq(1) + status[:video5].should eq(4) + status[:video6].should eq(4) end From f1f4434c542915faf5969284425040971822df2d Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Wed, 10 Feb 2021 13:58:18 +0000 Subject: [PATCH 14/17] feat(kramer_hdmi): add version response in spec --- drivers/kramer/switches/vs_hdmi_spec.cr | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/kramer/switches/vs_hdmi_spec.cr b/drivers/kramer/switches/vs_hdmi_spec.cr index 1dba5ba5caf..a08d31ee34d 100644 --- a/drivers/kramer/switches/vs_hdmi_spec.cr +++ b/drivers/kramer/switches/vs_hdmi_spec.cr @@ -25,4 +25,7 @@ DriverSpecs.mock_driver "Kramer::Switcher::VsHdmi" do status[:video3].should eq(1) status[:video5].should eq(4) status[:video6].should eq(4) + + # Command::IdentifyMachine version response + transmit(Bytes[0x7D, 0x83, 0x85, 0x81]) end From 3b0843a6a75e000ea886b30e65bfa43a6973b93d Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Wed, 10 Feb 2021 14:00:08 +0000 Subject: [PATCH 15/17] chore(kramer): run crystal tool format --- drivers/kramer/switches/protocol3000.cr | 34 +++++++++++--------- drivers/kramer/switches/protocol3000_spec.cr | 6 ++-- drivers/kramer/switches/vs_hdmi.cr | 8 ++--- drivers/kramer/switches/vs_hdmi_spec.cr | 6 ++-- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index 7b2c1983567..81bf4992139 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -60,13 +60,14 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver end enum RouteType - Video = 1 - Audio = 2 - USB = 3 - AudioVideo = 12 - VideoUSB = 13 + Video = 1 + Audio = 2 + USB = 3 + AudioVideo = 12 + VideoUSB = 13 AudioVideoUSB = 123 end + def route(map : Hash(Int32, Array(Int32)), type : RouteType = RouteType::AudioVideo) map.each do |input, outputs| outputs.each do |output| @@ -134,8 +135,8 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver case c = CMDS[cmd] when "info" - self[:video_inputs] = args[1].to_i - self[:video_outputs] = args[3].to_i + self[:video_inputs] = args[1].to_i + self[:video_outputs] = args[3].to_i when "route" # response looks like ~01@ROUTE 12,1,4 OK layer = args[0].to_i @@ -147,7 +148,8 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver case c when "switch_audio" then type = "audio" when "switch_video" then type = "video" - else type = "av" end + else type = "av" + end args.each do |map| inout = map.split('>') @@ -169,16 +171,16 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver end CMDS = { - "info" => "INFO-IO?", - "login" => "LOGIN", - "route" => "ROUTE", - "switch" => "AV", + "info" => "INFO-IO?", + "login" => "LOGIN", + "route" => "ROUTE", + "switch" => "AV", "switch_audio" => "AUD", "switch_video" => "VID", - "audio_mute" => "MUTE", - "video_mute" => "VMUTE", - "help" => "HELP", - "model" => "MODEL?" + "audio_mute" => "MUTE", + "video_mute" => "VMUTE", + "help" => "HELP", + "model" => "MODEL?", } CMDS.merge!(CMDS.invert) diff --git a/drivers/kramer/switches/protocol3000_spec.cr b/drivers/kramer/switches/protocol3000_spec.cr index 2918558ef1e..dffb34b87ae 100644 --- a/drivers/kramer/switches/protocol3000_spec.cr +++ b/drivers/kramer/switches/protocol3000_spec.cr @@ -9,9 +9,9 @@ DriverSpecs.mock_driver "Kramer::Switcher::Protocol3000" do responds("~01@ OK\x0D\x0A") settings({ - kramer_id: "01", - kramer_login: true, - kramer_password: "pass" + kramer_id: "01", + kramer_login: true, + kramer_password: "pass", }) # on_update # state diff --git a/drivers/kramer/switches/vs_hdmi.cr b/drivers/kramer/switches/vs_hdmi.cr index 9f661237fae..ca9ad988e5d 100644 --- a/drivers/kramer/switches/vs_hdmi.cr +++ b/drivers/kramer/switches/vs_hdmi.cr @@ -23,10 +23,10 @@ class Kramer::Switcher::VsHdmi < PlaceOS::Driver end enum Command - ResetVideo = 0 - SwitchVideo = 1 - StatusVideo = 5 - DefineMachine = 62 + ResetVideo = 0 + SwitchVideo = 1 + StatusVideo = 5 + DefineMachine = 62 IdentifyMachine = 61 end diff --git a/drivers/kramer/switches/vs_hdmi_spec.cr b/drivers/kramer/switches/vs_hdmi_spec.cr index a08d31ee34d..01eaecaa89d 100644 --- a/drivers/kramer/switches/vs_hdmi_spec.cr +++ b/drivers/kramer/switches/vs_hdmi_spec.cr @@ -11,14 +11,14 @@ DriverSpecs.mock_driver "Kramer::Switcher::VsHdmi" do status[:video_outputs].should eq(16) exec(:switch_video, { - 5 => [8] + 5 => [8], }) should_send(Bytes[1, 0x85, 0x88, 0xFF]) status[:video8].should eq(5) exec(:switch_video, { - 1 => [2,3], - 4 => [5,6] + 1 => [2, 3], + 4 => [5, 6], }) should_send(Bytes[1, 138, 144, 0xFF]) status[:video2].should eq(1) From 6b840a29bfbefad4107669c4b2c73808b0bb6ff6 Mon Sep 17 00:00:00 2001 From: yarah123 <101484139+yarah123@users.noreply.github.com> Date: Thu, 12 May 2022 16:16:23 +0300 Subject: [PATCH 16/17] Update protocol3000.cr (#325) On line 2, I added the 'require' statement to 'include' the switchable interface on line 55. --- drivers/kramer/switches/protocol3000.cr | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index 81bf4992139..6e7f8725f5a 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -1,4 +1,5 @@ require "placeos-driver/interface/muteable" +require "placeos-driver/interface/switchable" # Documentation: https://aca.im/driver_docs/Kramer/protocol_3000_2.10_user.pdf @@ -51,6 +52,8 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver get_machine_info end + include Interface::InputSelection(Input) + def switch_video(input : Int32, output : Array(Int32)) do_send(CMDS["switch_video"], build_switch_data({input => output})) end From 1961c24968e03e1b3c2195df87841e3bd61c7158 Mon Sep 17 00:00:00 2001 From: Giorgi Kavrelishvili Date: Tue, 9 May 2023 11:04:29 +0400 Subject: [PATCH 17/17] Add the switcher --- drivers/amx/svsi/virtual_switcher.cr | 25 +++++++++++++------------ drivers/kramer/switches/protocol3000.cr | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/drivers/amx/svsi/virtual_switcher.cr b/drivers/amx/svsi/virtual_switcher.cr index 384f7a581d2..1feb81b78b0 100644 --- a/drivers/amx/svsi/virtual_switcher.cr +++ b/drivers/amx/svsi/virtual_switcher.cr @@ -18,18 +18,19 @@ class Amx::Svsi::VirtualSwitcher < PlaceOS::Driver decoders.each(&.switch_to(input)) end - def switch(map : FullSwitch | SelectiveSwitch) - case map - when FullSwitch - connect(map) { |mod, stream| mod.switch_to(stream) } - when SelectiveSwitch - map.each do |layer, inouts| - next unless layer = SwitchLayer.parse?(layer) - connect(inouts) do |mod, stream| - mod.switch_audio(stream) if layer.audio? - mod.switch_video(stream) if layer.video? - end - end +def switch(map : Hash(Input, Array(Output)), layer : SwitchLayer? = nil) + extron_layer = case layer + in Nil, .all? then MatrixLayer::All + in .audio? then MatrixLayer::Aud + in .video? then MatrixLayer::Vid + in .data?, .data2? + logger.debug { "layer #{layer} not available on extron matrix" } + return + end + if map.size == 1 && map.first_value.size == 1 + switch_one(map.first_key, map.first_value.first, extron_layer) + else + switch_map(map, extron_layer) end end diff --git a/drivers/kramer/switches/protocol3000.cr b/drivers/kramer/switches/protocol3000.cr index 6e7f8725f5a..353c27ca822 100644 --- a/drivers/kramer/switches/protocol3000.cr +++ b/drivers/kramer/switches/protocol3000.cr @@ -1,3 +1,4 @@ +require "placeos-driver" require "placeos-driver/interface/muteable" require "placeos-driver/interface/switchable" @@ -5,6 +6,7 @@ require "placeos-driver/interface/switchable" class Kramer::Switcher::Protocol3000 < PlaceOS::Driver include Interface::Muteable + include Interface::Switchable(Int32, Int32) # Discovery Information tcp_port 23 @@ -53,7 +55,7 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver end include Interface::InputSelection(Input) - + def switch_video(input : Int32, output : Array(Int32)) do_send(CMDS["switch_video"], build_switch_data({input => output})) end @@ -62,6 +64,21 @@ class Kramer::Switcher::Protocol3000 < PlaceOS::Driver do_send(CMDS["switch_audio"], build_switch_data({input => output})) end + def switch(map : Hash(Input, Array(Output)), layer : SwitchLayer? = nil) + case layer + in Nil, .all? + switch_video(map.first_key, map.first_value) + switch_audio(map.first_key, map.first_value) + in .video? + switch_video(map.first_key, map.first_value) + in .audio? + switch_audio(map.first_key, map.first_value) + in .data?, .data2? + logger.debug { "layer #{layer} not available on extron matrix" } + return + end + end + enum RouteType Video = 1 Audio = 2