Compare commits

...
9 Commits
Author SHA1 Message Date
samw 83c94f8802 wip from deskpi? 2026-07-06 12:00:54 +00:00
samw 7cd6639283 Add logo 2023-02-10 12:38:12 +00:00
samw 3065040189 Update and format config example
Also add dhall-json for testing json/yaml conversion
2022-07-11 14:07:39 +00:00
samw 7b2a92012b Streamline configuration
Fixes triggermulti deserialization
Add 'generate-dhall' argument, which generates dhall for the config
enums.
2022-07-11 14:03:13 +00:00
samw b0c1c7d966 Add mosquitto to devshell 2022-07-11 13:39:48 +01:00
samw 96d9ca3dc8 Add dhall-lsp-server to devshell 2022-07-08 21:35:15 +01:00
samw 14e9860697 Support triggering multiple actions 2022-07-08 14:01:09 +01:00
samw d4a0a1e992 Update rust, direnv-nix 2022-07-08 13:43:17 +01:00
samw c3361915a4 Rearrange mappings/actions, swap fmt for tracing
Some mappings made more sense as actions. This clears the way for
triggering multiple actions.
2022-07-08 13:22:12 +01:00
13 changed files with 1036 additions and 529 deletions
+4
View File
@@ -1 +1,5 @@
if ! has nix_direnv_version || ! nix_direnv_version 2.1.1; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.1.1/direnvrc" "sha256-b6qJ4r34rbE23yWjMqbmu3ia2z4b2wIlZUksBke/ol0="
fi
use flake use flake
export RUST_LOG=info
Generated
+756 -437
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -4,10 +4,11 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
hidapi = "1" hidapi = "2"
rumqttc = "0.12" rumqttc = "0.12"
bitvec = "1" bitvec = "1"
serde = "1" serde = "1"
serde_dhall = "0.11" serde_dhall = "0.11"
log = "*" tracing = "0.1.35"
env_logger = "*" tracing-subscriber = "0.3.14"
chrono = "0.4.19"
+13
View File
@@ -0,0 +1,13 @@
use std::error::Error;
use std::process::Command;
fn main() -> Result<(), Box<dyn Error>> {
let hash = Command::new("git")
.arg("rev-parse")
.arg("--short")
.arg("HEAD")
.output()?
.stdout;
println!("cargo:rustc-env=VERSION={}", std::str::from_utf8(&hash)?);
Ok(())
}
+25 -15
View File
@@ -1,16 +1,26 @@
{ mqtt_servers.default = { host = "1.2.3.4", user = Some "username", pass = Some let T = ./types.dhall
"password" }
, layers.default
=
{ default_mapping = Mapping.Print "no mapping!"
, mappings =
let sometopic = "homeassistant/switch/something/set"
in { key_0 = in { mqtt_servers.default
Mapping.Trigger (Action.MQTTPub { topic = sometopic, payload = "ON" }) =
, key_1 = { host = "1.2.3.4", user = Some "username", pass = Some "password" }
Mapping.Trigger , layers.default
(Action.MQTTPub { topic = sometopic, payload = "OFF" }) =
} { default_mapping = T.Action.Print "no mapping!"
} , mappings =
} let sometopic = "homeassistant/switch/something/set"
in { key_0 =
T.Mapping.Trigger
(T.Action.MQTTPub { topic = sometopic, payload = "ON" })
, key_1 =
T.Mapping.Trigger
(T.Action.MQTTPub { topic = sometopic, payload = "OFF" })
, key_2 =
T.Mapping.TriggerMulti
[ T.Action.Print "Doing a thing"
, T.Action.MQTTPub
{ topic = "thing/foo/bar", payload = "boop" }
]
}
}
}
Generated
+50 -34
View File
@@ -6,11 +6,11 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1650900878, "lastModified": 1678957337,
"narHash": "sha256-qhNncMBSa9STnhiLfELEQpYC1L4GrYHNIzyCZ/pilsI=", "narHash": "sha256-Gw4nVbuKRdTwPngeOZQOzH/IFowmz4LryMPDiJN/ah4=",
"owner": "numtide", "owner": "numtide",
"repo": "devshell", "repo": "devshell",
"rev": "d97df53b5ddaa1cfbea7cddbd207eb2634304733", "rev": "3e0e60ab37cd0bf7ab59888f5c32499d851edb47",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -36,11 +36,11 @@
}, },
"flake-utils_2": { "flake-utils_2": {
"locked": { "locked": {
"lastModified": 1637014545, "lastModified": 1659877975,
"narHash": "sha256-26IZAc5yzlD9FlDT54io1oqG/bBoyka+FJk5guaX4x4=", "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "bba5dcc8e0b20ab664967ad83d24d64cb64ec4f4", "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -54,11 +54,11 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1651574473, "lastModified": 1679567394,
"narHash": "sha256-wQhFORvRjo8LB2hTmETmv6cbyKGDPbfWqvZ/0chnDE4=", "narHash": "sha256-ZvLuzPeARDLiQUt6zSZFGOs+HZmE+3g4QURc8mkBsfM=",
"owner": "nix-community", "owner": "nix-community",
"repo": "naersk", "repo": "naersk",
"rev": "f21309b38e1da0d61b881b6b6d41b81c1aed4e1d", "rev": "88cd22380154a2c36799fe8098888f0f59861a15",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -69,11 +69,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1643381941, "lastModified": 1677383253,
"narHash": "sha256-pHTwvnN4tTsEKkWlXQ8JMY423epos8wUOhthpwJjtpc=", "narHash": "sha256-UfpzWfSxkfXHnb4boXZNaKsAcUrZT9Hw+tao1oZxd08=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5efc8ca954272c4376ac929f4c5ffefcc20551d5", "rev": "9952d6bc395f5841262b006fbace8dd7e143b634",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -85,12 +85,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1652059086, "lastModified": 1678500271,
"narHash": "sha256-CjHSbr6LSFkN4YBdTB6+8ZQmSqhsbiXqAeQ9hQJ/gBI=", "narHash": "sha256-tRBLElf6f02HJGG0ZR7znMNFv/Uf7b2fFInpTHiHaSE=",
"owner": "NixOS", "path": "/nix/store/x00nwwv3ima5ri9q6wi7mq9vml4djjgn-source",
"repo": "nixpkgs", "rev": "5eb98948b66de29f899c7fe27ae112a47964baf8",
"rev": "934e076a441e318897aa17540f6cf7caadc69028", "type": "path"
"type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "id": "nixpkgs",
@@ -99,12 +98,11 @@
}, },
"nixpkgs_3": { "nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1652059086, "lastModified": 1678500271,
"narHash": "sha256-CjHSbr6LSFkN4YBdTB6+8ZQmSqhsbiXqAeQ9hQJ/gBI=", "narHash": "sha256-tRBLElf6f02HJGG0ZR7znMNFv/Uf7b2fFInpTHiHaSE=",
"owner": "NixOS", "path": "/nix/store/x00nwwv3ima5ri9q6wi7mq9vml4djjgn-source",
"repo": "nixpkgs", "rev": "5eb98948b66de29f899c7fe27ae112a47964baf8",
"rev": "934e076a441e318897aa17540f6cf7caadc69028", "type": "path"
"type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "id": "nixpkgs",
@@ -113,11 +111,11 @@
}, },
"nixpkgs_4": { "nixpkgs_4": {
"locked": { "locked": {
"lastModified": 1637453606, "lastModified": 1665296151,
"narHash": "sha256-Gy6cwUswft9xqsjWxFYEnx/63/qzaFUwatcbV5GF/GQ=", "narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8afc4e543663ca0a6a4f496262cd05233737e732", "rev": "14ccaaedd95a488dd7ae142757884d8e125b3363",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -142,11 +140,11 @@
"nixpkgs": "nixpkgs_4" "nixpkgs": "nixpkgs_4"
}, },
"locked": { "locked": {
"lastModified": 1652064227, "lastModified": 1681179516,
"narHash": "sha256-ZpIfELJNVzcxa6+YUr1KfbpTkHh02FASdlHaCC5/Q6w=", "narHash": "sha256-Ij/3YM5Gz5AiDKnP77ODJp2RGRRWmGTWYlfnuUT3z78=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "31db726b18ad2620fead6e7cd31b6e57d1cd061b", "rev": "9e7373ba5627ffe952f66a3e82e3a375bdc38565",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -155,13 +153,31 @@
"type": "github" "type": "github"
} }
}, },
"utils": { "systems": {
"locked": { "locked": {
"lastModified": 1649676176, "lastModified": 1681028828,
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=", "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678", "rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github" "type": "github"
}, },
"original": { "original": {
+10 -2
View File
@@ -27,18 +27,26 @@
pkgs = import nixpkgs { pkgs = import nixpkgs {
inherit system; inherit system;
overlays = [devshell.overlay (import rust-overlay)]; overlays = [devshell.overlays.default (import rust-overlay)];
}; };
in in
pkgs.mkShell { pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
# Dhall (config) stuff
dhall dhall
(rust-bin.stable.latest.default.override { dhall-json
dhall-lsp-server
# Rust!
(rust-bin.stable."1.68.1".default.override {
extensions = ["rls"]; extensions = ["rls"];
}) })
# Build requirements
pkg-config pkg-config
libusb libusb
openssl openssl
# Utils
mosquitto # So we can poke mqtt
usbutils
]; ];
}; };
formatter = pkgs.alejandra; formatter = pkgs.alejandra;
BIN
View File
Binary file not shown.
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="Layer1">
<rect x="0" y="0" width="256" height="256" style="fill:rgb(57,57,57);"/>
</g>
<g transform="matrix(0.933333,0,0,0.933333,8.53333,8.53333)">
<path d="M185.333,209C185.333,201.825 179.508,196 172.333,196L146.333,196C139.158,196 133.333,201.825 133.333,209L133.333,235C133.333,242.175 139.158,248 146.333,248L172.333,248C179.508,248 185.333,242.175 185.333,235L185.333,209ZM248,209C248,201.825 242.175,196 235,196L209,196C201.825,196 196,201.825 196,209L196,235C196,242.175 201.825,248 209,248L235,248C242.175,248 248,242.175 248,235L248,209ZM122.667,209C122.667,201.825 116.842,196 109.667,196L83.667,196C76.492,196 70.667,201.825 70.667,209L70.667,235C70.667,242.175 76.492,248 83.667,248L109.667,248C116.842,248 122.667,242.175 122.667,235L122.667,209ZM60,209C60,201.825 54.175,196 47,196L21,196C13.825,196 8,201.825 8,209L8,235C8,242.175 13.825,248 21,248L47,248C54.175,248 60,242.175 60,235L60,209ZM122.667,146.333C122.667,139.158 116.842,133.333 109.667,133.333L83.667,133.333C76.492,133.333 70.667,139.158 70.667,146.333L70.667,172.333C70.667,179.508 76.492,185.333 83.667,185.333L109.667,185.333C116.842,185.333 122.667,179.508 122.667,172.333L122.667,146.333ZM60,146.333C60,139.158 54.175,133.333 47,133.333L21,133.333C13.825,133.333 8,139.158 8,146.333L8,172.333C8,179.508 13.825,185.333 21,185.333L47,185.333C54.175,185.333 60,179.508 60,172.333L60,146.333ZM185.333,146.333C185.333,139.158 179.508,133.333 172.333,133.333L146.333,133.333C139.158,133.333 133.333,139.158 133.333,146.333L133.333,172.333C133.333,179.508 139.158,185.333 146.333,185.333L172.333,185.333C179.508,185.333 185.333,179.508 185.333,172.333L185.333,146.333ZM248,146.333C248,139.158 242.175,133.333 235,133.333L209,133.333C201.825,133.333 196,139.158 196,146.333L196,172.333C196,179.508 201.825,185.333 209,185.333L235,185.333C242.175,185.333 248,179.508 248,172.333L248,146.333ZM185.333,83.667C185.333,76.492 179.508,70.667 172.333,70.667L146.333,70.667C139.158,70.667 133.333,76.492 133.333,83.667L133.333,109.667C133.333,116.842 139.158,122.667 146.333,122.667L172.333,122.667C179.508,122.667 185.333,116.842 185.333,109.667L185.333,83.667ZM248,83.667C248,76.492 242.175,70.667 235,70.667L209,70.667C201.825,70.667 196,76.492 196,83.667L196,109.667C196,116.842 201.825,122.667 209,122.667L235,122.667C242.175,122.667 248,116.842 248,109.667L248,83.667ZM122.667,83.667C122.667,76.492 116.842,70.667 109.667,70.667L83.667,70.667C76.492,70.667 70.667,76.492 70.667,83.667L70.667,109.667C70.667,116.842 76.492,122.667 83.667,122.667L109.667,122.667C116.842,122.667 122.667,116.842 122.667,109.667L122.667,83.667ZM60,83.667C60,76.492 54.175,70.667 47,70.667L21,70.667C13.825,70.667 8,76.492 8,83.667L8,109.667C8,116.842 13.825,122.667 21,122.667L47,122.667C54.175,122.667 60,116.842 60,109.667L60,83.667ZM185.333,21C185.333,13.825 179.508,8 172.333,8L146.333,8C139.158,8 133.333,13.825 133.333,21L133.333,47C133.333,54.175 139.158,60 146.333,60L172.333,60C179.508,60 185.333,54.175 185.333,47L185.333,21ZM248,21C248,13.825 242.175,8 235,8L209,8C201.825,8 196,13.825 196,21L196,47C196,54.175 201.825,60 209,60L235,60C242.175,60 248,54.175 248,47L248,21ZM60,21C60,13.825 54.175,8 47,8L21,8C13.825,8 8,13.825 8,21L8,47C8,54.175 13.825,60 21,60L47,60C54.175,60 60,54.175 60,47L60,21ZM122.667,21C122.667,13.825 116.842,8 109.667,8L83.667,8C76.492,8 70.667,13.825 70.667,21L70.667,47C70.667,54.175 76.492,60 83.667,60L109.667,60C116.842,60 122.667,54.175 122.667,47L122.667,21Z" style="fill:url(#_Linear1);"/>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(224,224,-224,224,16,16)"><stop offset="0" style="stop-color:rgb(255,173,77);stop-opacity:1"/><stop offset="0.58" style="stop-color:rgb(174,222,199);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(137,245,255);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

+80 -14
View File
@@ -1,21 +1,78 @@
use crate::StdError;
use chrono::Utc;
use serde::Deserialize; use serde::Deserialize;
use serde_dhall::StaticType; use serde_dhall::{SimpleType, StaticType};
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::fmt;
use std::path::Path; use std::path::Path;
#[derive(Deserialize, StaticType, Debug)] #[derive(Deserialize, StaticType, Debug)]
pub enum Action { pub enum Action {
MQTTPub { topic: String, payload: String }, // Publish something (server, topic, payload) ActivateLayer(String), // Activate a layer
Print(String), // Print a string to console
MQTTPub { topic: String, payload: String }, // Publish payload on topic via MQTT
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
Action::ActivateLayer(layer) => write!(f, "ActivateLayer({})", layer),
Action::Print(p) => write!(f, "Print({})", p),
Action::MQTTPub { topic, payload } => {
write!(f, "MQTTPub(topic={},payload={})", topic, payload)
}
}
}
}
// Newtype wrapper around a Vec of actions.
// serde(transparent) and the imp StaticType below ensures we ignore the outer struct and
// transparently deserialize into the inner vec.
#[derive(Deserialize, Debug)]
#[serde(transparent)]
pub struct Actions(Vec<Action>);
impl Actions {
// Return the iterator over the inner Vec of Actions
pub fn iter(&self) -> std::slice::Iter<Action> {
self.0.iter()
}
}
impl fmt::Display for Actions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Actions(count={})", self.0.len())
}
}
impl StaticType for Actions {
fn static_type() -> SimpleType {
SimpleType::List(Box::new(Action::static_type()))
}
} }
#[derive(Deserialize, StaticType, Debug)] #[derive(Deserialize, StaticType, Debug)]
pub enum Mapping { pub enum Mapping {
NOP, // Do nothing. NOP, // Do nothing.
Passthrough, // Passthrough to the layer below Passthrough, // Passthrough to the layer below
ActivateLayer(String), // Activate a layer
Print(String), // Print a string to console
Trigger(Action), // Trigger an action Trigger(Action), // Trigger an action
TriggerMulti(Actions), // Trigger multiple actions in sequence
}
impl fmt::Display for Mapping {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match &self {
Mapping::NOP => "NOP".to_owned(),
Mapping::Passthrough => "Passthrough".to_owned(),
Mapping::Trigger(act) => format!("Trigger({})", act),
Mapping::TriggerMulti(acts) => format!("Trigger({})", acts),
},
)
}
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@@ -42,16 +99,25 @@ pub struct Config {
impl Config { impl Config {
pub fn from_file(p: &Path) -> Result<Self, Box<dyn Error>> { pub fn from_file(p: &Path) -> Result<Self, Box<dyn Error>> {
match serde_dhall::from_file(p) serde_dhall::from_file(p)
.with_builtin_type("Mapping".to_string(), Mapping::static_type())
.with_builtin_type("Action".to_string(), Action::static_type())
.parse::<Self>() .parse::<Self>()
{ .map_err(|e| e.into())
Ok(c) => Ok(c), }
Err(e) => {
println!("{}", e); pub fn generate_prelude() -> StdError<()> {
Err(Box::new(e)) let mut m = HashMap::new();
} m.insert("Action".to_string(), Action::static_type());
} m.insert("Mapping".to_string(), Mapping::static_type());
println!(
"-- Sampad Dhall types, autogenerated by git#{} on {}",
option_env!("VERSION").unwrap_or("unknown"),
Utc::now(),
);
println!(
"{{ Action = {}, Mapping = {} }}",
Action::static_type(),
Mapping::static_type()
);
Ok(())
} }
} }
+13 -1
View File
@@ -2,6 +2,8 @@ use bitvec::prelude::*;
use hidapi::{DeviceInfo, HidApi, HidDevice}; use hidapi::{DeviceInfo, HidApi, HidDevice};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::error::Error; use std::error::Error;
use std::fmt;
use tracing::{event, Level};
const HID_USAGE_PAGE: u16 = 0xFF; const HID_USAGE_PAGE: u16 = 0xFF;
const HID_USAGE: u16 = 0x1; const HID_USAGE: u16 = 0x1;
@@ -37,6 +39,16 @@ pub enum ButtonEvent {
KeyUp(u8), KeyUp(u8),
} }
impl fmt::Display for ButtonEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let vals = match *self {
Self::KeyDown(key) => ("KeyDown", key),
Self::KeyUp(key) => ("KeyUp", key),
};
write!(f, "{}(key={})", vals.0, vals.1)
}
}
type State = u16; type State = u16;
pub struct ButtonPad<'a> { pub struct ButtonPad<'a> {
@@ -67,7 +79,7 @@ impl Iterator for ButtonPad<'_> {
// Read from the device, blocking until the next report // Read from the device, blocking until the next report
let mut buf: [u8; 2] = [0; 2]; let mut buf: [u8; 2] = [0; 2];
self.dev.read(&mut buf).expect("error reading"); self.dev.read(&mut buf).expect("error reading");
log::info!("{:?}", buf); event!(Level::INFO, ?buf, "Read from device");
let new: u16 = buf[0] as u16 | (buf[1] as u16) << 8; let new: u16 = buf[0] as u16 | (buf[1] as u16) << 8;
+46 -23
View File
@@ -1,23 +1,26 @@
use env_logger::Env;
use hidapi::HidApi;
use log::info;
use rumqttc::{Client, MqttOptions, QoS};
use std::collections::HashMap;
use std::error::Error;
use std::path::Path;
use std::thread;
mod config; mod config;
mod device; mod device;
use config::{Action, Config, Mapping};
use hidapi::HidApi;
use rumqttc::{Client, MqttOptions, QoS};
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::path::Path;
use std::thread;
use tracing::{event, Level};
type StdError<T> = Result<T, Box<dyn Error>>;
struct State<'a> { struct State<'a> {
mqtt_servers: HashMap<String, Client>, mqtt_servers: HashMap<String, Client>,
conf: &'a config::Config, conf: &'a Config,
key_state: [bool; 16], key_state: [bool; 16],
} }
impl<'a> State<'a> { impl<'a> State<'a> {
fn init(c: &'a config::Config) -> Self { fn init(c: &'a Config) -> Self {
let mut s = State { let mut s = State {
mqtt_servers: HashMap::new(), mqtt_servers: HashMap::new(),
conf: &c, conf: &c,
@@ -71,10 +74,9 @@ impl<'a> State<'a> {
} }
} }
fn execute_mapping(&mut self, m: &config::Mapping) { fn execute_action(&mut self, action: &Action) {
match m { match action {
config::Mapping::Print(s) => println!("{}", s), Action::MQTTPub { topic, payload } => {
config::Mapping::Trigger(config::Action::MQTTPub { topic, payload }) => {
let mut topic = topic.to_owned(); let mut topic = topic.to_owned();
let srv = &self.conf.mqtt_servers["default"]; let srv = &self.conf.mqtt_servers["default"];
if let Some(prefix) = &srv.topic_prefix { if let Some(prefix) = &srv.topic_prefix {
@@ -84,7 +86,16 @@ impl<'a> State<'a> {
cli.publish(topic, QoS::AtLeastOnce, false, payload.as_bytes()) cli.publish(topic, QoS::AtLeastOnce, false, payload.as_bytes())
.unwrap(); .unwrap();
} }
_ => {} Action::Print(s) => println!("{}", s),
_ => event!(Level::WARN, %action, "Ignoring unimplemented action"),
}
}
fn execute_mapping(&mut self, mapping: &Mapping) {
match mapping {
Mapping::Trigger(act) => self.execute_action(act),
Mapping::TriggerMulti(acts) => acts.iter().map(|a| self.execute_action(a)).collect(),
_ => event!(Level::WARN, %mapping, "Ignoring unimplemented mapping"),
} }
} }
@@ -99,9 +110,22 @@ impl<'a> State<'a> {
} }
} }
fn main() -> Result<(), Box<dyn Error>> { fn main() -> StdError<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); let args: Vec<_> = env::args().collect();
let conf = config::Config::from_file(Path::new("./config.dhall"))?; if let [prog, args @ ..] = &args[..] {
if args == ["generate-dhall"] {
return Config::generate_prelude();
} else if args.len() > 0 {
eprintln!("Usage: {} [generate-dhall]", prog);
return Err("Invalid usage".into());
}
} else {
unreachable!();
}
tracing_subscriber::fmt::init();
event!(Level::INFO, "Starting...");
let conf = Config::from_file(Path::new("./config.dhall"))?;
let mut state = State::init(&conf); let mut state = State::init(&conf);
@@ -111,18 +135,17 @@ fn main() -> Result<(), Box<dyn Error>> {
let device = device::get_devices(&hid, true) let device = device::get_devices(&hid, true)
.get(0) .get(0)
.expect("No device found") .expect("No device found")
.open_device(&hid) .open_device(&hid)?;
.expect("Couldn't open device");
let pad = device::ButtonPad::new(&device); let pad = device::ButtonPad::new(&device);
for ev in pad { for ev in pad {
if let Ok(ev) = ev { if let Ok(ev) = ev {
info!("{:?}", ev); event!(Level::INFO, ?ev, "Got event");
state.handle_button(&ev); state.handle_button(&ev);
let mut data = state.get_led_data().to_vec(); let mut data = state.get_led_data().to_vec();
data.insert(0, 0x0); data.insert(0, 0x0);
data.insert(0, 0x0); data.insert(0, 0x0);
log::info!("{:?}", data); event!(Level::INFO, ?data, "Sending LED data");
device.write(data.as_slice())?; device.write(data.as_slice())?;
} }
} }
+22
View File
@@ -0,0 +1,22 @@
-- Sampad Dhall types, autogenerated by git#3065040 on 2022-07-12 12:53:11.933270598 UTC
{ Action =
< ActivateLayer : Text
| MQTTPub : { payload : Text, topic : Text }
| Print : Text
>
, Mapping =
< NOP
| Passthrough
| Trigger :
< ActivateLayer : Text
| MQTTPub : { payload : Text, topic : Text }
| Print : Text
>
| TriggerMulti :
List
< ActivateLayer : Text
| MQTTPub : { payload : Text, topic : Text }
| Print : Text
>
>
}