matrix2mqtt: init
This commit is contained in:
parent
b955549fe5
commit
10e1181c0b
10 changed files with 1258 additions and 0 deletions
|
@ -25,6 +25,7 @@ syntax: regexp
|
||||||
^go/trains/.*/start.sh$
|
^go/trains/.*/start.sh$
|
||||||
^go/trains/.*/lukegb-trains.json$
|
^go/trains/.*/lukegb-trains.json$
|
||||||
^py/icalfilter/config/.*$
|
^py/icalfilter/config/.*$
|
||||||
|
^rust/.*/target/.*$
|
||||||
^(.+/)?result(-([a-z]+|[0-9]+))?$
|
^(.+/)?result(-([a-z]+|[0-9]+))?$
|
||||||
|
|
||||||
syntax: glob
|
syntax: glob
|
||||||
|
|
|
@ -38,6 +38,7 @@ let
|
||||||
};
|
};
|
||||||
factorio = depot.ops.factorio;
|
factorio = depot.ops.factorio;
|
||||||
home-manager = depot.ops.home-manager-ext.built;
|
home-manager = depot.ops.home-manager-ext.built;
|
||||||
|
rust = depot.rust;
|
||||||
};
|
};
|
||||||
aarch64-linux = builtins.removeAttrs x86_64-linux [ "home-manager" "pkg-authentik" "web-barf" ] // {
|
aarch64-linux = builtins.removeAttrs x86_64-linux [ "home-manager" "pkg-authentik" "web-barf" ] // {
|
||||||
pkgs = builtins.removeAttrs x86_64-linux.pkgs [ "lutris" "plex-pass" "sheepshaver" "fr24feed" "javaws-env" "copybara" ];
|
pkgs = builtins.removeAttrs x86_64-linux.pkgs [ "lutris" "plex-pass" "sheepshaver" "fr24feed" "javaws-env" "copybara" ];
|
||||||
|
|
|
@ -22,6 +22,7 @@ in fix (self:
|
||||||
web = import ./web ch;
|
web = import ./web ch;
|
||||||
go = import ./go ch;
|
go = import ./go ch;
|
||||||
py = import ./py ch;
|
py = import ./py ch;
|
||||||
|
rust = import ./rust ch;
|
||||||
|
|
||||||
version = import ./version.nix ch;
|
version = import ./version.nix ch;
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ in {
|
||||||
homeassistant = {
|
homeassistant = {
|
||||||
password = "homeassistant";
|
password = "homeassistant";
|
||||||
acl = [
|
acl = [
|
||||||
|
"readwrite matrix2mqtt/#"
|
||||||
"readwrite zigbee2mqtt/#"
|
"readwrite zigbee2mqtt/#"
|
||||||
"readwrite homeassistant/#"
|
"readwrite homeassistant/#"
|
||||||
];
|
];
|
||||||
|
|
9
rust/default.nix
Normal file
9
rust/default.nix
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
args:
|
||||||
|
|
||||||
|
{
|
||||||
|
matrix2mqtt = import ./matrix2mqtt args;
|
||||||
|
}
|
702
rust/matrix2mqtt/Cargo.lock
generated
Normal file
702
rust/matrix2mqtt/Cargo.lock
generated
Normal file
|
@ -0,0 +1,702 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.24.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.74"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flume"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.31.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.161"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matrix2mqtt"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"rumqttc",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.36.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-probe"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.88"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom",
|
||||||
|
"libc",
|
||||||
|
"spin",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rumqttc"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1568e15fab2d546f940ed3a21f48bbbd1c494c90c99c4481339364a497f94a9"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"flume",
|
||||||
|
"futures-util",
|
||||||
|
"log",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"rustls-webpki",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.22.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-native-certs"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
|
||||||
|
dependencies = [
|
||||||
|
"openssl-probe",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.102.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schannel"
|
||||||
|
version = "0.1.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework"
|
||||||
|
version = "2.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"core-foundation",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"security-framework-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework-sys"
|
||||||
|
version = "2.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.40.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
9
rust/matrix2mqtt/Cargo.toml
Normal file
9
rust/matrix2mqtt/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "matrix2mqtt"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
rumqttc = "0.24.0"
|
||||||
|
futures = "0.3.31"
|
7
rust/matrix2mqtt/default.nix
Normal file
7
rust/matrix2mqtt/default.nix
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
|
||||||
|
depot.pkgs.callPackage ./package.nix { }
|
21
rust/matrix2mqtt/package.nix
Normal file
21
rust/matrix2mqtt/package.nix
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ lib, rustPlatform }:
|
||||||
|
|
||||||
|
rustPlatform.buildRustPackage rec {
|
||||||
|
pname = "matrix2mqtt";
|
||||||
|
version = "unstable";
|
||||||
|
|
||||||
|
src = lib.fileset.toSource {
|
||||||
|
root = ./.;
|
||||||
|
fileset = lib.fileset.unions [
|
||||||
|
./Cargo.toml
|
||||||
|
./Cargo.lock
|
||||||
|
./src
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
cargoHash = "sha256-0R6hN5cNlj8+UzZ9oM4f74UGGCS1AKlDgHaQyKq/y0Q=";
|
||||||
|
}
|
506
rust/matrix2mqtt/src/main.rs
Normal file
506
rust/matrix2mqtt/src/main.rs
Normal file
|
@ -0,0 +1,506 @@
|
||||||
|
use futures::future;
|
||||||
|
use futures::FutureExt;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::io::AsyncBufReadExt;
|
||||||
|
use tokio::io::AsyncRead;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use tokio::io::BufReader;
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
use tokio::select;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::task::JoinSet;
|
||||||
|
|
||||||
|
async fn wait_for_line<R: AsyncRead + Unpin>(
|
||||||
|
reader: &mut BufReader<R>,
|
||||||
|
want: &str,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
loop {
|
||||||
|
let mut line = String::new();
|
||||||
|
reader.read_line(&mut line).await?;
|
||||||
|
if line == want {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum MatrixCommand {
|
||||||
|
GetCurrentMappings,
|
||||||
|
SetOutput(MatrixDestination, MatrixSource),
|
||||||
|
SetOutputCEC(MatrixDestination, bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
enum MatrixSource {
|
||||||
|
HDMI1,
|
||||||
|
HDMI2,
|
||||||
|
HDMI3,
|
||||||
|
HDMI4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MatrixSource {
|
||||||
|
fn matrix_name(self: &MatrixSource) -> &'static str {
|
||||||
|
match self {
|
||||||
|
MatrixSource::HDMI1 => "hdmiin1",
|
||||||
|
MatrixSource::HDMI2 => "hdmiin2",
|
||||||
|
MatrixSource::HDMI3 => "hdmiin3",
|
||||||
|
MatrixSource::HDMI4 => "hdmiin4",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn friendly_name(self: &MatrixSource) -> &'static str {
|
||||||
|
match self {
|
||||||
|
MatrixSource::HDMI1 => "SHIELD",
|
||||||
|
MatrixSource::HDMI2 => "totoro",
|
||||||
|
MatrixSource::HDMI3 => "PS5",
|
||||||
|
MatrixSource::HDMI4 => "Mac Mini",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct ParseMatrixPortError;
|
||||||
|
|
||||||
|
impl fmt::Display for ParseMatrixPortError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "ParseMatrixPortError")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for ParseMatrixPortError {}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct UnexpectedMatrixDestinationError;
|
||||||
|
|
||||||
|
impl fmt::Display for UnexpectedMatrixDestinationError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "UnexpectedMatrixDestinationError")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for UnexpectedMatrixDestinationError {}
|
||||||
|
|
||||||
|
impl FromStr for MatrixSource {
|
||||||
|
type Err = ParseMatrixPortError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"hdmiin1" => Ok(MatrixSource::HDMI1),
|
||||||
|
"hdmiin2" => Ok(MatrixSource::HDMI2),
|
||||||
|
"hdmiin3" => Ok(MatrixSource::HDMI3),
|
||||||
|
"hdmiin4" => Ok(MatrixSource::HDMI4),
|
||||||
|
_ => Err(ParseMatrixPortError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
enum MatrixDestination {
|
||||||
|
HDMI1,
|
||||||
|
HDMI2,
|
||||||
|
HDMI3,
|
||||||
|
HDMI4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MatrixDestination {
|
||||||
|
fn matrix_name(self: &MatrixDestination) -> &'static str {
|
||||||
|
match self {
|
||||||
|
MatrixDestination::HDMI1 => "hdmiout1",
|
||||||
|
MatrixDestination::HDMI2 => "hdmiout2",
|
||||||
|
MatrixDestination::HDMI3 => "hdmiout3",
|
||||||
|
MatrixDestination::HDMI4 => "hdmiout4",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(self: &MatrixDestination) -> u8 {
|
||||||
|
match self {
|
||||||
|
MatrixDestination::HDMI1 => 1,
|
||||||
|
MatrixDestination::HDMI2 => 2,
|
||||||
|
MatrixDestination::HDMI3 => 3,
|
||||||
|
MatrixDestination::HDMI4 => 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn friendly_name(self: &MatrixDestination) -> &'static str {
|
||||||
|
match self {
|
||||||
|
MatrixDestination::HDMI1 => "Projector",
|
||||||
|
MatrixDestination::HDMI2 => "Sound Bar",
|
||||||
|
MatrixDestination::HDMI3 => "Desk Monitor",
|
||||||
|
MatrixDestination::HDMI4 => "unused",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for MatrixDestination {
|
||||||
|
type Err = ParseMatrixPortError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"hdmiout1" => Ok(MatrixDestination::HDMI1),
|
||||||
|
"hdmiout2" => Ok(MatrixDestination::HDMI2),
|
||||||
|
"hdmiout3" => Ok(MatrixDestination::HDMI3),
|
||||||
|
"hdmiout4" => Ok(MatrixDestination::HDMI4),
|
||||||
|
_ => Err(ParseMatrixPortError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct MatrixMappings {
|
||||||
|
hdmiout1: MatrixSource,
|
||||||
|
hdmiout2: MatrixSource,
|
||||||
|
hdmiout3: MatrixSource,
|
||||||
|
hdmiout4: MatrixSource,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum MatrixResponse {
|
||||||
|
CurrentMappings(MatrixMappings),
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_single_mapping(
|
||||||
|
reader: &mut BufReader<TcpStream>,
|
||||||
|
expected: MatrixDestination,
|
||||||
|
) -> Result<MatrixSource, Box<dyn Error + Send + Sync>> {
|
||||||
|
let mut line = String::new();
|
||||||
|
reader.read_line(&mut line).await?;
|
||||||
|
let splitter = line
|
||||||
|
.strip_prefix("MP ")
|
||||||
|
.and_then(|s| Some(s.split_whitespace()))
|
||||||
|
.ok_or(ParseMatrixPortError)?
|
||||||
|
.collect::<Vec<&str>>();
|
||||||
|
if splitter.len() != 2 {
|
||||||
|
return Err(Box::new(ParseMatrixPortError));
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = MatrixSource::from_str(splitter[0])?;
|
||||||
|
let dst = MatrixDestination::from_str(splitter[1])?;
|
||||||
|
if dst != expected {
|
||||||
|
return Err(Box::new(UnexpectedMatrixDestinationError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn query_mappings(
|
||||||
|
reader: &mut BufReader<TcpStream>,
|
||||||
|
response_tx: &mut broadcast::Sender<MatrixResponse>,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
reader.write_all(b"GET MP ALL\r\n").await?;
|
||||||
|
|
||||||
|
let mappings = MatrixMappings {
|
||||||
|
hdmiout1: read_single_mapping(reader, MatrixDestination::HDMI1).await?,
|
||||||
|
hdmiout2: read_single_mapping(reader, MatrixDestination::HDMI2).await?,
|
||||||
|
hdmiout3: read_single_mapping(reader, MatrixDestination::HDMI3).await?,
|
||||||
|
hdmiout4: read_single_mapping(reader, MatrixDestination::HDMI4).await?,
|
||||||
|
};
|
||||||
|
response_tx.send(MatrixResponse::CurrentMappings(mappings))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_matrix_telnet(
|
||||||
|
socket: TcpStream,
|
||||||
|
command_rx: &mut mpsc::Receiver<MatrixCommand>,
|
||||||
|
response_tx: &mut broadcast::Sender<MatrixResponse>,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
let mut stream = BufReader::new(socket);
|
||||||
|
|
||||||
|
// Wait for the hello.
|
||||||
|
println!("awaiting hello");
|
||||||
|
wait_for_line(&mut stream, "Welcome to the 4KMX44-H2 Matrix!\r\n").await?;
|
||||||
|
println!("matrix ready!");
|
||||||
|
|
||||||
|
while let Some(msg) = command_rx.recv().await {
|
||||||
|
println!("got command {:#?}", msg);
|
||||||
|
match msg {
|
||||||
|
MatrixCommand::GetCurrentMappings => {
|
||||||
|
query_mappings(&mut stream, response_tx).await?;
|
||||||
|
}
|
||||||
|
MatrixCommand::SetOutput(destination, source) => {
|
||||||
|
stream
|
||||||
|
.write_all(
|
||||||
|
format!(
|
||||||
|
"SET SW {} {}\r\n",
|
||||||
|
source.matrix_name(),
|
||||||
|
destination.matrix_name()
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let expect = format!(
|
||||||
|
"SW {} {}\r\n",
|
||||||
|
source.matrix_name(),
|
||||||
|
destination.matrix_name()
|
||||||
|
);
|
||||||
|
wait_for_line(&mut stream, &expect).await?;
|
||||||
|
query_mappings(&mut stream, response_tx).await?;
|
||||||
|
}
|
||||||
|
MatrixCommand::SetOutputCEC(destination, on_or_off) => {
|
||||||
|
let on_or_off_str = if on_or_off { "ON" } else { "OFF" };
|
||||||
|
stream
|
||||||
|
.write_all(
|
||||||
|
format!(
|
||||||
|
"SET CEC_PWR {} {}\r\n",
|
||||||
|
destination.matrix_name(),
|
||||||
|
on_or_off_str,
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let expect = format!(
|
||||||
|
"CEC_PWR {} {}\r\n",
|
||||||
|
destination.matrix_name(),
|
||||||
|
on_or_off_str,
|
||||||
|
);
|
||||||
|
wait_for_line(&mut stream, &expect).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("process terminating...");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
const TOPIC_PREFIX: &str = "matrix2mqtt/lukes-bedroom";
|
||||||
|
|
||||||
|
async fn handle_publish(
|
||||||
|
p: &rumqttc::Publish,
|
||||||
|
matrix_command_tx: &mut mpsc::Sender<MatrixCommand>,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
if let Some(destination_str) = p
|
||||||
|
.topic
|
||||||
|
.strip_prefix(TOPIC_PREFIX)
|
||||||
|
.and_then(|topic| topic.strip_prefix("/"))
|
||||||
|
.and_then(|topic| topic.strip_suffix("/cec/set"))
|
||||||
|
{
|
||||||
|
let destination = MatrixDestination::from_str(destination_str)?;
|
||||||
|
match std::str::from_utf8(&p.payload)? {
|
||||||
|
"ON" => {
|
||||||
|
println!("asked to turn {:#?} on", destination);
|
||||||
|
matrix_command_tx
|
||||||
|
.send(MatrixCommand::SetOutputCEC(destination, true))
|
||||||
|
.await?;
|
||||||
|
},
|
||||||
|
"OFF" => {
|
||||||
|
println!("asked to turn {:#?} off", destination);
|
||||||
|
matrix_command_tx
|
||||||
|
.send(MatrixCommand::SetOutputCEC(destination, false))
|
||||||
|
.await?;
|
||||||
|
},
|
||||||
|
s => {
|
||||||
|
println!("asked to set {:#?} to unknown value {}", destination, s)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if let Some(destination_str) = p
|
||||||
|
.topic
|
||||||
|
.strip_prefix(TOPIC_PREFIX)
|
||||||
|
.and_then(|topic| topic.strip_prefix("/"))
|
||||||
|
.and_then(|topic| topic.strip_suffix("/set"))
|
||||||
|
{
|
||||||
|
let destination = MatrixDestination::from_str(destination_str)?;
|
||||||
|
let source_str = std::str::from_utf8(&p.payload)?;
|
||||||
|
let source = MatrixSource::from_str(source_str)?;
|
||||||
|
println!("asked to set {:#?} to {:#?}", destination, source);
|
||||||
|
matrix_command_tx
|
||||||
|
.send(MatrixCommand::SetOutput(destination, source))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn publish_config(
|
||||||
|
client: &rumqttc::AsyncClient,
|
||||||
|
destination: MatrixDestination,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
client
|
||||||
|
.publish(
|
||||||
|
format!(
|
||||||
|
"homeassistant/select/matrix_{}/config",
|
||||||
|
destination.matrix_name()
|
||||||
|
),
|
||||||
|
rumqttc::QoS::AtLeastOnce,
|
||||||
|
true,
|
||||||
|
format!(
|
||||||
|
r#"{{
|
||||||
|
"name": "HDMI {index}: {friendly_name}",
|
||||||
|
"device": {{
|
||||||
|
"identifiers": ["192.168.1.200"],
|
||||||
|
"name": "HDMI Matrix"
|
||||||
|
}},
|
||||||
|
"device_class": "select",
|
||||||
|
"state_topic": "{topic_prefix}/{matrix_name}/get",
|
||||||
|
"command_topic": "{topic_prefix}/{matrix_name}/set",
|
||||||
|
"icon": "mdi:hdmi-port",
|
||||||
|
"entity_category": "config",
|
||||||
|
"unique_id": "{topic_prefix}/{matrix_name}",
|
||||||
|
"retain": false,
|
||||||
|
"options": [
|
||||||
|
"{option1_friendly}",
|
||||||
|
"{option2_friendly}",
|
||||||
|
"{option3_friendly}",
|
||||||
|
"{option4_friendly}"
|
||||||
|
],
|
||||||
|
"value_template": "{{% set lookup = {{'hdmiin1': '{option1_friendly}', 'hdmiin2': '{option2_friendly}', 'hdmiin3': '{option3_friendly}', 'hdmiin4': '{option4_friendly}'}} %}}{{{{ lookup[value] }}}}",
|
||||||
|
"command_template": "{{% set lookup = {{'{option1_friendly}': 'hdmiin1', '{option2_friendly}': 'hdmiin2', '{option3_friendly}': 'hdmiin3', '{option4_friendly}': 'hdmiin4'}} %}}{{{{ lookup[value] }}}}"
|
||||||
|
}}"#,
|
||||||
|
index = destination.index(),
|
||||||
|
friendly_name = destination.friendly_name(),
|
||||||
|
topic_prefix = TOPIC_PREFIX,
|
||||||
|
matrix_name = destination.matrix_name(),
|
||||||
|
option1_friendly = MatrixSource::HDMI1.friendly_name(),
|
||||||
|
option2_friendly = MatrixSource::HDMI2.friendly_name(),
|
||||||
|
option3_friendly = MatrixSource::HDMI3.friendly_name(),
|
||||||
|
option4_friendly = MatrixSource::HDMI4.friendly_name(),
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
client
|
||||||
|
.publish(
|
||||||
|
format!(
|
||||||
|
"homeassistant/switch/matrix_{}_cec/config",
|
||||||
|
destination.matrix_name()
|
||||||
|
),
|
||||||
|
rumqttc::QoS::AtLeastOnce,
|
||||||
|
true,
|
||||||
|
format!(
|
||||||
|
r#"{{
|
||||||
|
"name": "HDMI {index}: {friendly_name} - CEC Power",
|
||||||
|
"device": {{
|
||||||
|
"identifiers": ["192.168.1.200"],
|
||||||
|
"name": "HDMI Matrix"
|
||||||
|
}},
|
||||||
|
"device_class": "switch",
|
||||||
|
"command_topic": "{topic_prefix}/{matrix_name}/cec/set",
|
||||||
|
"state_topic": "{topic_prefix}/{matrix_name}/cec/get",
|
||||||
|
"unique_id": "{topic_prefix}/{matrix_name}/cec",
|
||||||
|
"retain": false
|
||||||
|
}}"#,
|
||||||
|
index = destination.index(),
|
||||||
|
friendly_name = destination.friendly_name(),
|
||||||
|
topic_prefix = TOPIC_PREFIX,
|
||||||
|
matrix_name = destination.matrix_name(),
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn select_all<I, T, E>(futures: Vec<I>) -> Result<(), E>
|
||||||
|
where
|
||||||
|
I: futures::Future<Output = Result<T, E>> + Unpin,
|
||||||
|
{
|
||||||
|
let mut fvec = futures;
|
||||||
|
while fvec.len() > 0 {
|
||||||
|
let (item_resolved, _, remaining_futures) = future::select_all(fvec).await;
|
||||||
|
item_resolved?;
|
||||||
|
fvec = remaining_futures;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
let mut set = JoinSet::new();
|
||||||
|
|
||||||
|
let (matrix_command_tx, mut matrix_command_rx) = mpsc::channel(32);
|
||||||
|
let (mut matrix_response_tx, mut matrix_response_rx) = broadcast::channel(32);
|
||||||
|
|
||||||
|
let mut mqtt_matrix_command_tx = matrix_command_tx.clone();
|
||||||
|
set.spawn(async move {
|
||||||
|
let mut mqttoptions = rumqttc::MqttOptions::new("matrix2mqtt", "localhost", 1883);
|
||||||
|
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
||||||
|
mqttoptions.set_credentials("matrix2mqtt", "matrix2mqtt");
|
||||||
|
|
||||||
|
let (client, mut connection) = rumqttc::AsyncClient::new(mqttoptions, 10);
|
||||||
|
|
||||||
|
select_all(vec![
|
||||||
|
client.subscribe(format!("{}/+/set", TOPIC_PREFIX), rumqttc::QoS::AtLeastOnce).boxed(),
|
||||||
|
client.subscribe(format!("{}/+/cec/set", TOPIC_PREFIX), rumqttc::QoS::AtLeastOnce).boxed(),
|
||||||
|
]).await?;
|
||||||
|
|
||||||
|
// Publish Home Assistant discovery messages.
|
||||||
|
println!("publishing Home Assistant discovery messages");
|
||||||
|
select_all(vec![
|
||||||
|
publish_config(&client, MatrixDestination::HDMI1).boxed(),
|
||||||
|
publish_config(&client, MatrixDestination::HDMI2).boxed(),
|
||||||
|
publish_config(&client, MatrixDestination::HDMI3).boxed(),
|
||||||
|
publish_config(&client, MatrixDestination::HDMI4).boxed(),
|
||||||
|
]).await?;
|
||||||
|
println!("done with HA startup!");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
select! {
|
||||||
|
notification = connection.poll() => {
|
||||||
|
match notification? {
|
||||||
|
rumqttc::Event::Incoming(rumqttc::Incoming::Publish(p)) => {
|
||||||
|
match handle_publish(&p, &mut mqtt_matrix_command_tx).await {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => println!("failed to handle event {:#?}: {}", p, e),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok(matrix_response) = matrix_response_rx.recv() => {
|
||||||
|
match matrix_response {
|
||||||
|
MatrixResponse::CurrentMappings(mappings) => {
|
||||||
|
println!("publishing mappings {:#?} to MQTT", mappings);
|
||||||
|
select_all(vec![
|
||||||
|
client.publish(format!("{}/hdmiout1/get", TOPIC_PREFIX), rumqttc::QoS::AtLeastOnce, true, mappings.hdmiout1.matrix_name()).boxed(),
|
||||||
|
client.publish(format!("{}/hdmiout2/get", TOPIC_PREFIX), rumqttc::QoS::AtLeastOnce, true, mappings.hdmiout2.matrix_name()).boxed(),
|
||||||
|
client.publish(format!("{}/hdmiout3/get", TOPIC_PREFIX), rumqttc::QoS::AtLeastOnce, true, mappings.hdmiout3.matrix_name()).boxed(),
|
||||||
|
client.publish(format!("{}/hdmiout4/get", TOPIC_PREFIX), rumqttc::QoS::AtLeastOnce, true, mappings.hdmiout4.matrix_name()).boxed(),
|
||||||
|
]).await?;
|
||||||
|
println!("published mappings to MQTT");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
set.spawn(async move {
|
||||||
|
let matrix_socket = TcpStream::connect("192.168.1.200:23").await?;
|
||||||
|
handle_matrix_telnet(
|
||||||
|
matrix_socket,
|
||||||
|
&mut matrix_command_rx,
|
||||||
|
&mut matrix_response_tx,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
});
|
||||||
|
|
||||||
|
let ticker_matrix_command_tx = matrix_command_tx.clone();
|
||||||
|
set.spawn(async move {
|
||||||
|
let mut interval = tokio::time::interval(Duration::from_secs(10));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
ticker_matrix_command_tx
|
||||||
|
.send(MatrixCommand::GetCurrentMappings)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Seed MQTT with the current mapping state.
|
||||||
|
matrix_command_tx
|
||||||
|
.send(MatrixCommand::GetCurrentMappings)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Bail as soon as we get a failure.
|
||||||
|
while let Some(result) = set.join_next().await {
|
||||||
|
let _ = result?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in a new issue