Compare commits
12 Commits
master
...
convert-to
| Author | SHA1 | Date | |
|---|---|---|---|
| e8221faeb1 | |||
| 3c17eab1ed | |||
| 3755693de6 | |||
| 5937d793e2 | |||
| 0fe6aa147a | |||
| 5f366ca174 | |||
| 6725174e36 | |||
| d6f59437c2 | |||
| ecbd4942ad | |||
| 5c9c7b8359 | |||
| f079b882da | |||
| 114dc5d9de |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ tmp/
|
||||
.nrepl-port
|
||||
target/
|
||||
result
|
||||
.clj-kondo/
|
||||
|
||||
1141
deps-lock.json
1141
deps-lock.json
File diff suppressed because it is too large
Load Diff
36
deps.edn
36
deps.edn
@@ -1,31 +1,29 @@
|
||||
{
|
||||
:paths ["src"]
|
||||
:deps {
|
||||
org.clojure/clojure { :mvn/version "1.11.1" }
|
||||
org.clojure/core.async { :mvn/version "1.5.648" }
|
||||
org.clojure/tools.cli { :mvn/version "1.0.206" }
|
||||
|
||||
org.fudo/fudo-clojure {
|
||||
:git/url "https://git.fudo.org/fudo-public/fudo-clojure.git"
|
||||
:sha "c6a1ebef2e5b64d432a46ac48639c674e62b7cee"
|
||||
}
|
||||
|
||||
org.eclipse.paho/org.eclipse.paho.client.mqttv3 { :mvn/version "1.2.5" }
|
||||
camel-snake-kebab/camel-snake-kebab { :mvn/version "0.4.2" }
|
||||
;; All dependencies are now built into Babashka:
|
||||
;; - babashka.process (for shell commands)
|
||||
;; - clojure.core.async (for concurrency)
|
||||
;; - clojure.tools.cli (for CLI parsing)
|
||||
;; - cheshire.core (for JSON)
|
||||
;;
|
||||
;; This script no longer requires any external dependencies!
|
||||
}
|
||||
:aliases {
|
||||
;; For running with regular Clojure (if needed for development):
|
||||
:jvm {
|
||||
:extra-deps {
|
||||
org.clojure/clojure { :mvn/version "1.12.3" }
|
||||
org.clojure/core.async { :mvn/version "1.8.741" }
|
||||
org.clojure/tools.cli { :mvn/version "1.3.250" }
|
||||
}
|
||||
}
|
||||
:test {
|
||||
:extra-paths ["test"]
|
||||
:extra-deps {
|
||||
io.github.cognitect-labs/test-runner
|
||||
{
|
||||
:git/url "https://github.com/cognitect-labs/test-runner.git"
|
||||
:sha "dfb30dd6605cb6c0efc275e1df1736f6e90d4d73"
|
||||
}
|
||||
eftest/eftest {:mvn/version "0.6.0"}
|
||||
}
|
||||
:main-opts ["-m" "cognitect.test-runner"]
|
||||
:exec-fn cognitect.test-runner.api/test
|
||||
:main-opts ["-e" "(require '[eftest.runner :refer [find-tests run-tests]]) (run-tests (find-tests \"test\"))"]
|
||||
}
|
||||
:build { :default-ns build }
|
||||
}
|
||||
}
|
||||
|
||||
632
flake.lock
generated
632
flake.lock
generated
@@ -3,39 +3,109 @@
|
||||
"clj-nix": {
|
||||
"inputs": {
|
||||
"devshell": "devshell",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nix-fetcher-data": "nix-fetcher-data",
|
||||
"nixpkgs": [
|
||||
"fudo-clojure",
|
||||
"helpers",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1663870497,
|
||||
"narHash": "sha256-gnoyYWvZl64WBqR3tf9bKHAznEtBCHmwx7taHghH9Lw=",
|
||||
"lastModified": 1732066389,
|
||||
"narHash": "sha256-6z9KTXwDQN14vljs5USgb4Pr8BYk46Q2yhkcdNx9QEE=",
|
||||
"owner": "jlesquembre",
|
||||
"repo": "clj-nix",
|
||||
"rev": "23d9daacc80e634df078c4c6e34d592e1593d84c",
|
||||
"rev": "95f26552686259f64685569e6966cdefebb0a6a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "jlesquembre",
|
||||
"ref": "0.4.0",
|
||||
"repo": "clj-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"clj-nix_2": {
|
||||
"inputs": {
|
||||
"devshell": "devshell_2",
|
||||
"nix-fetcher-data": "nix-fetcher-data_2",
|
||||
"nixpkgs": [
|
||||
"helpers",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1732066389,
|
||||
"narHash": "sha256-6z9KTXwDQN14vljs5USgb4Pr8BYk46Q2yhkcdNx9QEE=",
|
||||
"owner": "jlesquembre",
|
||||
"repo": "clj-nix",
|
||||
"rev": "95f26552686259f64685569e6966cdefebb0a6a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "jlesquembre",
|
||||
"ref": "0.4.0",
|
||||
"repo": "clj-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"clj2nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"utils": "utils"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1727456190,
|
||||
"narHash": "sha256-aZvIrzSQQ6ATmAHeitqchAD2DTLcWV+PU8qs80fq3lA=",
|
||||
"owner": "hlolli",
|
||||
"repo": "clj2nix",
|
||||
"rev": "4a968eca5d368b5c818081333a64a3eb93e50d24",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hlolli",
|
||||
"repo": "clj2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"clj2nix_2": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"gitignore": "gitignore_2",
|
||||
"nixpkgs": "nixpkgs_4",
|
||||
"utils": "utils_4"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1727456190,
|
||||
"narHash": "sha256-aZvIrzSQQ6ATmAHeitqchAD2DTLcWV+PU8qs80fq3lA=",
|
||||
"owner": "hlolli",
|
||||
"repo": "clj2nix",
|
||||
"rev": "4a968eca5d368b5c818081333a64a3eb93e50d24",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hlolli",
|
||||
"repo": "clj2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devshell": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"fudo-clojure",
|
||||
"helpers",
|
||||
"clj-nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1658746384,
|
||||
"narHash": "sha256-CCJcoMOcXyZFrV1ag4XMTpAPjLWb4Anbv+ktXFI1ry0=",
|
||||
"lastModified": 1728330715,
|
||||
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "0ffc7937bb5e8141af03d462b468bd071eb18e1b",
|
||||
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -44,56 +114,473 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"devshell_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"helpers",
|
||||
"clj-nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1642700792,
|
||||
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
|
||||
"lastModified": 1728330715,
|
||||
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
|
||||
"repo": "devshell",
|
||||
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"repo": "devshell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1656928814,
|
||||
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
|
||||
"lastModified": 1668681692,
|
||||
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1668681692,
|
||||
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719745305,
|
||||
"narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719745305,
|
||||
"narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"fudo-clojure": {
|
||||
"inputs": {
|
||||
"helpers": "helpers",
|
||||
"nixpkgs": "nixpkgs_3",
|
||||
"utils": "utils_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1770077493,
|
||||
"narHash": "sha256-HgmKxXAcQBZ1l/olVlQWWyble8BdJdL7QgP7sJ2pXy4=",
|
||||
"owner": "fudoniten",
|
||||
"repo": "fudo-clojure",
|
||||
"rev": "107e56fa3222e9472892b0f3cad358223a91a257",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "fudoniten",
|
||||
"repo": "fudo-clojure",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"fudo-clojure",
|
||||
"helpers",
|
||||
"clj2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1660459072,
|
||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"helpers",
|
||||
"clj2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1660459072,
|
||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"helpers": {
|
||||
"inputs": {
|
||||
"clj-nix": "clj-nix",
|
||||
"clj2nix": "clj2nix",
|
||||
"nix2container": "nix2container",
|
||||
"nixpkgs": [
|
||||
"fudo-clojure",
|
||||
"nixpkgs"
|
||||
],
|
||||
"utils": "utils_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1770058348,
|
||||
"narHash": "sha256-jqtOJhEOUCjJ8RoLkfejOlUyzSAHr6YHYwGXoUSnW5c=",
|
||||
"owner": "fudoniten",
|
||||
"repo": "fudo-nix-helpers",
|
||||
"rev": "89ad3f7ca2cff121475794bde7c6e29e0e5169d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "fudoniten",
|
||||
"repo": "fudo-nix-helpers",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"helpers_2": {
|
||||
"inputs": {
|
||||
"clj-nix": "clj-nix_2",
|
||||
"clj2nix": "clj2nix_2",
|
||||
"nix2container": "nix2container_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"utils": "utils_5"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1770058348,
|
||||
"narHash": "sha256-jqtOJhEOUCjJ8RoLkfejOlUyzSAHr6YHYwGXoUSnW5c=",
|
||||
"owner": "fudoniten",
|
||||
"repo": "fudo-nix-helpers",
|
||||
"rev": "89ad3f7ca2cff121475794bde7c6e29e0e5169d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "fudoniten",
|
||||
"repo": "fudo-nix-helpers",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-fetcher-data": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": [
|
||||
"fudo-clojure",
|
||||
"helpers",
|
||||
"clj-nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1728229178,
|
||||
"narHash": "sha256-p5Fx880uBYstIsbaDYN7sECJT11oHxZQKtHgMAVblWA=",
|
||||
"owner": "jlesquembre",
|
||||
"repo": "nix-fetcher-data",
|
||||
"rev": "f3a73c34d28db49ef90fd7872a142bfe93120e55",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "jlesquembre",
|
||||
"repo": "nix-fetcher-data",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-fetcher-data_2": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_2",
|
||||
"nixpkgs": [
|
||||
"helpers",
|
||||
"clj-nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1728229178,
|
||||
"narHash": "sha256-p5Fx880uBYstIsbaDYN7sECJT11oHxZQKtHgMAVblWA=",
|
||||
"owner": "jlesquembre",
|
||||
"repo": "nix-fetcher-data",
|
||||
"rev": "f3a73c34d28db49ef90fd7872a142bfe93120e55",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "jlesquembre",
|
||||
"repo": "nix-fetcher-data",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix2container": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767430085,
|
||||
"narHash": "sha256-SiXJ6xv4pS2MDUqfj0/mmG746cGeJrMQGmoFgHLS25Y=",
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"rev": "66f4b8a47e92aa744ec43acbb5e9185078983909",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix2container_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_5"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767430085,
|
||||
"narHash": "sha256-SiXJ6xv4pS2MDUqfj0/mmG746cGeJrMQGmoFgHLS25Y=",
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"rev": "66f4b8a47e92aa744ec43acbb5e9185078983909",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1672580127,
|
||||
"narHash": "sha256-3lW3xZslREhJogoOkjeZtlBtvFMyxHku7I/9IVehhT8=",
|
||||
"lastModified": 1673785507,
|
||||
"narHash": "sha256-EPUT8yVdvJhhjhbgnFWXXd4IUPKSOmww2+z4AmOdyPI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0874168639713f547c05947c76124f78441ea46c",
|
||||
"rev": "d06d765eeac716d8f1ca80f0935fd6fc951816ad",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1717284937,
|
||||
"narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib_2": {
|
||||
"locked": {
|
||||
"lastModified": 1717284937,
|
||||
"narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1767028467,
|
||||
"narHash": "sha256-7G+2aXClSMaTY1ogpX14CAxjRsvyVzpE0GRwL71WO7g=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1cabc318c11299f07ca53e3cb719854682fe6eb3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1720691131,
|
||||
"narHash": "sha256-CWT+KN8aTPyMIx8P303gsVxUnkinIz0a/Cmasz1jyIM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a046c1202e11b62cbede5385ba64908feb7bfac4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-22.05",
|
||||
"ref": "nixos-24.05",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1673785507,
|
||||
"narHash": "sha256-EPUT8yVdvJhhjhbgnFWXXd4IUPKSOmww2+z4AmOdyPI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d06d765eeac716d8f1ca80f0935fd6fc951816ad",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1767028467,
|
||||
"narHash": "sha256-7G+2aXClSMaTY1ogpX14CAxjRsvyVzpE0GRwL71WO7g=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1cabc318c11299f07ca53e3cb719854682fe6eb3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_6": {
|
||||
"locked": {
|
||||
"lastModified": 1770464364,
|
||||
"narHash": "sha256-z5NJPSBwsLf/OfD8WTmh79tlSU8XgIbwmk6qB1/TFzY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "23d72dabcb3b12469f57b37170fcbc1789bd7457",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-25.11",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"clj-nix": "clj-nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"utils": "utils"
|
||||
"fudo-clojure": "fudo-clojure",
|
||||
"helpers": "helpers_2",
|
||||
"nixpkgs": "nixpkgs_6",
|
||||
"utils": "utils_6"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_4": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
@@ -110,6 +597,93 @@
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils_3": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils_4": {
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils_5": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils_6": {
|
||||
"inputs": {
|
||||
"systems": "systems_4"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
35
flake.nix
35
flake.nix
@@ -2,36 +2,45 @@
|
||||
description = "WallFly presence monitor.";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-22.05";
|
||||
nixpkgs.url = "nixpkgs/nixos-25.11";
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
clj-nix = {
|
||||
url = "github:jlesquembre/clj-nix";
|
||||
helpers = {
|
||||
url = "github:fudoniten/fudo-nix-helpers";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
fudo-clojure.url = "github:fudoniten/fudo-clojure";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, utils, clj-nix, ... }:
|
||||
outputs = { self, nixpkgs, utils, helpers, fudo-clojure, ... }:
|
||||
utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages."${system}";
|
||||
cljpkgs = clj-nix.packages."${system}";
|
||||
update-deps = pkgs.writeShellScriptBin "update-deps.sh" ''
|
||||
${clj-nix.packages."${system}".deps-lock}/bin/deps-lock
|
||||
'';
|
||||
gs = import nixpkgs { inherit system; };
|
||||
|
||||
cljLibs = {
|
||||
"org.fudo/fudo-clojure" =
|
||||
fudo-clojure.packages."${system}".fudo-clojure.preppedSrc;
|
||||
};
|
||||
in {
|
||||
packages = {
|
||||
wallfly = cljpkgs.mkCljBin {
|
||||
wallfly = helpers.legacyPackages."${system}".mkClojureBin {
|
||||
projectSrc = ./.;
|
||||
name = "org.fudo/wallfly";
|
||||
main-ns = "wallfly.core";
|
||||
jdkRunner = pkgs.jdk17_headless;
|
||||
primaryNamespace = "wallfly.core";
|
||||
src = ./.;
|
||||
inherit cljLibs;
|
||||
};
|
||||
};
|
||||
|
||||
defaultPackage = self.packages."${system}".wallfly;
|
||||
|
||||
devShell =
|
||||
pkgs.mkShell { buildInputs = with pkgs; [ clojure update-deps ]; };
|
||||
devShells = rec {
|
||||
default = updateDeps;
|
||||
updateDeps = pkgs.mkShell {
|
||||
buildInputs = with helpers.legacyPackages."${system}";
|
||||
[ (updateClojureDeps { deps = cljLibs; }) ];
|
||||
};
|
||||
};
|
||||
}) // {
|
||||
overlay = final: prev: {
|
||||
inherit (self.packages."${prev.system}") wallfly;
|
||||
|
||||
179
src/wallfly/core.clj
Normal file → Executable file
179
src/wallfly/core.clj
Normal file → Executable file
@@ -1,63 +1,130 @@
|
||||
#!/usr/bin/env bb
|
||||
|
||||
(ns wallfly.core
|
||||
(:require [clojure.java.shell :as shell]
|
||||
(:require [babashka.process :as process]
|
||||
[clojure.core.async :refer [chan >!! <!! go-loop timeout alt!]]
|
||||
[clojure.string :as str :refer [trim-newline]]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.string :as str]
|
||||
[cheshire.core :as json]
|
||||
[clojure.tools.cli :refer [parse-opts]]
|
||||
[clojure.pprint :refer [pprint]]
|
||||
[fudo-clojure.result :refer [success failure unwrap map-success success? error-message]]
|
||||
[camel-snake-kebab.core :refer [->SCREAMING_SNAKE_CASE]])
|
||||
(:import [org.eclipse.paho.client.mqttv3
|
||||
MqttClient
|
||||
MqttConnectOptions
|
||||
MqttMessage]
|
||||
org.eclipse.paho.client.mqttv3.persist.MemoryPersistence)
|
||||
(:gen-class))
|
||||
[clojure.pprint :refer [pprint]]))
|
||||
|
||||
(defn- exit! [status msg]
|
||||
(println msg)
|
||||
(System/exit status))
|
||||
|
||||
(defn- create-mqtt-client [broker-uri client-id mqtt-username passwd]
|
||||
(let [opts (doto (MqttConnectOptions.)
|
||||
(.setCleanSession true)
|
||||
(.setAutomaticReconnect true)
|
||||
(.setPassword (char-array passwd))
|
||||
(.setUserName mqtt-username))]
|
||||
(try
|
||||
(doto (MqttClient. broker-uri client-id (MemoryPersistence.))
|
||||
(.connect opts))
|
||||
(catch Exception e
|
||||
(exit! 1 (.getMessage e))))))
|
||||
(defn- parse-broker-uri [uri]
|
||||
"Parse MQTT broker URI like tcp://host:port or ssl://host:port"
|
||||
(let [uri-pattern #"^(tcp|ssl)://([^:]+)(?::(\d+))?$"
|
||||
[_ protocol host port] (re-matches uri-pattern uri)]
|
||||
(when-not host
|
||||
(exit! 1 (str "Invalid broker URI format: " uri)))
|
||||
{:host host
|
||||
:port (or port (if (= protocol "ssl") "8883" "1883"))
|
||||
:use-tls (= protocol "ssl")}))
|
||||
|
||||
(defn- create-mqtt-config [broker-uri mqtt-username passwd]
|
||||
"Create MQTT configuration for mosquitto_pub"
|
||||
(merge (parse-broker-uri broker-uri)
|
||||
{:username mqtt-username
|
||||
:password passwd}))
|
||||
|
||||
(defn- shell-exec [& args]
|
||||
(let [{:keys [exit out err]} (apply shell/sh args)]
|
||||
(if (= exit 0)
|
||||
(success (trim-newline out))
|
||||
(failure err { :error err :status-code exit }))))
|
||||
"Execute shell command and return {:success true :out output} or {:success false :error error}"
|
||||
(try
|
||||
(let [result (process/shell {:out :string :err :string} (str/join " " args))
|
||||
out (str/trim (:out result))]
|
||||
{:success true :out out})
|
||||
(catch Exception e
|
||||
{:success false :error (ex-message e)})))
|
||||
|
||||
(defn- get-current-session-id []
|
||||
"Get the current user's session ID from loginctl"
|
||||
(let [username (System/getenv "USER")
|
||||
result (shell-exec "loginctl" "list-sessions" "--no-legend")]
|
||||
(when (:success result)
|
||||
(->> (str/split-lines (:out result))
|
||||
(filter #(str/includes? % username))
|
||||
first
|
||||
(re-find #"^\s*(\S+)")
|
||||
second))))
|
||||
|
||||
(defn- get-idle-time-from-loginctl []
|
||||
"Get idle time in seconds using systemd-logind (works with X11 and Wayland)"
|
||||
(if-let [session-id (get-current-session-id)]
|
||||
(let [idle-hint-result (shell-exec "loginctl" "show-session" session-id "-p" "IdleHint" "--value")
|
||||
idle-since-result (shell-exec "loginctl" "show-session" session-id "-p" "IdleSinceHintMonotonic" "--value")]
|
||||
(if (and (:success idle-hint-result) (:success idle-since-result))
|
||||
(if (= "yes" (:out idle-hint-result))
|
||||
;; Session is idle - calculate how long
|
||||
(let [idle-since-usec (Long/parseLong (:out idle-since-result))
|
||||
uptime-result (shell-exec "awk" "{print $1*1000000}" "/proc/uptime")
|
||||
now-usec (when (:success uptime-result) (long (Double/parseDouble (:out uptime-result))))
|
||||
idle-usec (when now-usec (- now-usec idle-since-usec))
|
||||
idle-sec (when idle-usec (max 0 (quot idle-usec 1000000)))]
|
||||
(if idle-sec
|
||||
{:success true :out idle-sec}
|
||||
{:success false :error "Failed to calculate idle time"}))
|
||||
;; Session is not idle
|
||||
{:success true :out 0})
|
||||
{:success false :error (str "Failed to get session idle info: " (:error idle-since-result))}))
|
||||
{:success false :error "Could not determine current session"}))
|
||||
|
||||
(defn- get-idle-time-from-xprintidle []
|
||||
"Get idle time in seconds using xprintidle (X11 only, legacy fallback)"
|
||||
(let [result (shell-exec "xprintidle")]
|
||||
(if (:success result)
|
||||
{:success true :out (quot (Integer/parseInt (:out result)) 1000)}
|
||||
result)))
|
||||
|
||||
(defn- get-idle-time []
|
||||
(map-success (shell-exec "xprintidle")
|
||||
(fn [idle-str] (quot (Integer/parseInt idle-str) 1000))))
|
||||
"Get idle time in seconds.
|
||||
|
||||
Tries systemd-logind first (works with both X11 and Wayland),
|
||||
falls back to xprintidle (X11 only) if loginctl fails.
|
||||
|
||||
This ensures compatibility with modern Linux systems that use Wayland,
|
||||
while maintaining backwards compatibility with X11-only systems."
|
||||
(let [result (get-idle-time-from-loginctl)]
|
||||
(if (:success result)
|
||||
result
|
||||
(get-idle-time-from-xprintidle))))
|
||||
|
||||
(defn- get-hostname [] (unwrap (shell-exec "hostname")))
|
||||
(defn- get-hostname []
|
||||
(let [result (shell-exec "hostname")]
|
||||
(if (:success result)
|
||||
(:out result)
|
||||
(exit! 1 (str "Failed to get hostname: " (:error result))))))
|
||||
|
||||
(defn- get-fqdn [] (unwrap (shell-exec "hostname" "-f")))
|
||||
(defn- get-fqdn []
|
||||
(let [result (shell-exec "hostname" "-f")]
|
||||
(if (:success result)
|
||||
(:out result)
|
||||
(exit! 1 (str "Failed to get FQDN: " (:error result))))))
|
||||
|
||||
(defn- get-username [] (System/getenv "USER"))
|
||||
|
||||
(defn- create-message [msg retained]
|
||||
(doto (MqttMessage. (.getBytes msg))
|
||||
(.setQos 1)
|
||||
(.setRetained retained)))
|
||||
(defn- send-message [mqtt-config topic msg & {:keys [retained] :or {retained false}}]
|
||||
"Send MQTT message using mosquitto_pub"
|
||||
(let [{:keys [host port username password use-tls]} mqtt-config
|
||||
base-args ["mosquitto_pub"
|
||||
"-h" host
|
||||
"-p" (str port)
|
||||
"-u" username
|
||||
"-P" password
|
||||
"-t" topic
|
||||
"-m" msg
|
||||
"-q" "1"]
|
||||
args (if retained
|
||||
(conj base-args "-r")
|
||||
base-args)]
|
||||
(try
|
||||
(let [result @(process/process args {:out :string :err :string})]
|
||||
(when (not= 0 (:exit result))
|
||||
(exit! 1 (str "mosquitto_pub failed: " (:err result)))))
|
||||
(catch Exception e
|
||||
(exit! 1 (str "Failed to send MQTT message: " (ex-message e)))))))
|
||||
|
||||
(defn- send-message [client topic msg & {:keys [retained] :or {retained false}}]
|
||||
(try
|
||||
(.publish client topic (create-message msg retained))
|
||||
(catch Exception e
|
||||
(exit! 1 (.getMessage e)))))
|
||||
|
||||
(defn- create-reporter [client time-to-idle location user host host-device]
|
||||
(defn- create-reporter [mqtt-config time-to-idle location user host host-device]
|
||||
(let [base-topic (format "homeassistant/binary_sensor/wallfly_%s_%s"
|
||||
user host)
|
||||
presence-topic (format "%s/state" base-topic)]
|
||||
@@ -75,13 +142,13 @@
|
||||
:manufacturer "Fudo"
|
||||
:suggested_area location}}]
|
||||
(println (format "sending to %s: %s" cfg-topic (with-out-str (pprint payload))))
|
||||
(send-message client cfg-topic (json/write-str payload) :retained true)
|
||||
(send-message mqtt-config cfg-topic (json/generate-string payload) :retained true)
|
||||
;; Send one message that will persist if the host dies
|
||||
(send-message client presence-topic "OFF" :retained true))
|
||||
(send-message mqtt-config presence-topic "OFF" :retained true))
|
||||
(fn [idle-time]
|
||||
;; (emit-idle idle-time)
|
||||
(when (< idle-time time-to-idle)
|
||||
(send-message client presence-topic "ON")))))
|
||||
(send-message mqtt-config presence-topic "ON")))))
|
||||
|
||||
(defn- execute! [delay-seconds report]
|
||||
(let [stop-chan (chan)
|
||||
@@ -89,9 +156,9 @@
|
||||
(go-loop [idle-measure (get-idle-time)]
|
||||
(if (nil? idle-measure)
|
||||
nil
|
||||
(do (if (success? idle-measure)
|
||||
(report (unwrap idle-measure))
|
||||
(println (str "error reading idle time: " (error-message idle-measure))))
|
||||
(do (if (:success idle-measure)
|
||||
(report (:out idle-measure))
|
||||
(println (str "error reading idle time: " (:error idle-measure))))
|
||||
(recur (alt! (timeout delay-time) (get-idle-time)
|
||||
stop-chan nil)))))))
|
||||
|
||||
@@ -103,11 +170,15 @@
|
||||
["-t" "--time-to-idle SECONDS" "Number of seconds before considering this host idle."]
|
||||
["-d" "--delay-time SECONDS" "Time to wait before polling for idle time."]])
|
||||
|
||||
(defn- ->screaming-snake-case [s]
|
||||
"Convert kebab-case string to SCREAMING_SNAKE_CASE"
|
||||
(-> s (str/replace "-" "_") (str/upper-case)))
|
||||
|
||||
(defn- get-key [opts k]
|
||||
(if-let [opt (get opts k)]
|
||||
[k opt]
|
||||
[k (System/getenv (format "WALLFLY_%s"
|
||||
(-> k name ->SCREAMING_SNAKE_CASE)))]))
|
||||
(-> k name ->screaming-snake-case)))]))
|
||||
|
||||
(defn- get-args [keys args]
|
||||
(let [{:keys [options errors summary]} (parse-opts args cli-opts)]
|
||||
@@ -137,16 +208,18 @@
|
||||
delay-time
|
||||
mqtt-username]} (get-args required-keys args)
|
||||
catch-shutdown (chan)
|
||||
password (-> mqtt-password-file (slurp) (str/trim-newline))
|
||||
password (-> mqtt-password-file (slurp) (str/trim))
|
||||
username (get-username)
|
||||
hostname (get-hostname)
|
||||
host-device (format "wallfly-%s" (get-fqdn))
|
||||
client-id (format "wallfly-%s" (rand-str 10))
|
||||
client (create-mqtt-client mqtt-broker-uri client-id mqtt-username password)
|
||||
reporter (create-reporter client (Integer/parseInt time-to-idle) location username hostname host-device)
|
||||
mqtt-config (create-mqtt-config mqtt-broker-uri mqtt-username password)
|
||||
reporter (create-reporter mqtt-config (Integer/parseInt time-to-idle) location username hostname host-device)
|
||||
stop-chan (execute! (Integer/parseInt delay-time) reporter)]
|
||||
(.addShutdownHook (Runtime/getRuntime)
|
||||
(Thread. (fn [] (>!! catch-shutdown true))))
|
||||
(<!! catch-shutdown)
|
||||
(>!! stop-chan true)
|
||||
(System/exit 0)))
|
||||
|
||||
(when (= *file* (System/getProperty "babashka.file"))
|
||||
(apply -main *command-line-args*))
|
||||
|
||||
Reference in New Issue
Block a user