diff --git a/flake.lock b/flake.lock index fbc46f6..658be24 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "locked": { "lastModified": 1659877975, @@ -15,54 +31,79 @@ "type": "github" } }, - "flake-utils_2": { + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1644229661, - "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "hercules-ci", + "repo": "gitignore.nix", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1661626419, - "narHash": "sha256-CjdPtdwH7I5Es4SjdCGuNfeulIyaM1LP0dXGWi4dyuQ=", + "lastModified": 1735531152, + "narHash": "sha256-As8I+ebItDKtboWgDXYZSIjGlKeqiLBvjxsQHUmAf1Q=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a5e05d62460ff0b7627559a8b55ab421041ebf0a", + "rev": "3ffbbdbac0566a0977da3d2657b89cbcfe9a173b", "type": "github" }, "original": { "owner": "NixOS", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1735286948, + "narHash": "sha256-JMRV2RI58nV1UqLXqm+lcea1/dr92fYjWU5S+Rz3fmE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "31ac92f9628682b294026f0860e14587a09ffb4b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, "pre-commit-hooks": { "inputs": { - "flake-utils": "flake-utils_2", + "flake-compat": "flake-compat", + "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" - ] + ], + "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1660830093, - "narHash": "sha256-HUhx3a82C7bgp2REdGFeHJdhEAzMGCk3V8xIvfBqg1I=", + "lastModified": 1734797603, + "narHash": "sha256-ulZN7ps8nBV31SE+dwkDvKIzvN6hroRY8sYOT0w+E28=", "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "8cb8ea5f1c7bc2984f460587fddd5f2e558f6eb8", + "repo": "git-hooks.nix", + "rev": "f0f0dc4920a903c3e08f5bdb9246bb572fcae498", "type": "github" }, "original": { "owner": "cachix", - "repo": "pre-commit-hooks.nix", + "repo": "git-hooks.nix", "type": "github" } }, diff --git a/flake.nix b/flake.nix index bab44d0..2c9124a 100644 --- a/flake.nix +++ b/flake.nix @@ -2,25 +2,27 @@ description = "Tankerkönig-to-MQTT gateway"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; flake-utils.url = "github:numtide/flake-utils"; - pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix"; + pre-commit-hooks.url = "github:cachix/git-hooks.nix"; pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, nixpkgs, flake-utils, pre-commit-hooks }: flake-utils.lib.eachSystem [ "x86_64-linux" ] (system: let + ghcv = "ghc96"; pkgs = nixpkgs.legacyPackages.${system}; overlay = final: prev: { tk2mqtt = final.callCabal2nix "tk2mqtt" ./. { }; }; - haskellPackages = pkgs.haskell.packages.ghc924.extend overlay; - in { + haskellPackages = pkgs.haskell.packages.${ghcv}.extend overlay; + in + { packages.default = haskellPackages.tk2mqtt; apps = { @@ -37,13 +39,14 @@ checks = { pre-commit-check = pre-commit-hooks.lib.${system}.run { src = ./.; - settings = { ormolu.defaultExtensions = [ "GHC2021" ]; }; - tools.fourmolu = haskellPackages.fourmolu; hooks = { - nixfmt.enable = true; - fourmolu.enable = true; + nixpkgs-fmt.enable = true; hpack.enable = true; hlint.enable = true; + ormolu = { + enable = true; + settings.defaultExtensions = [ "GHC2021" ]; + }; doctest = { enable = false; name = "Run documentation tests"; @@ -64,14 +67,15 @@ nativeBuildInputs = with pkgs; [ haskellPackages.haskell-language-server - haskellPackages.fourmolu + # haskellPackages.fourmolu haskellPackages.hspec-discover haskellPackages.doctest cabal-install ghcid - nixfmt + nixfmt-rfc-style hpack hlint + yaml-language-server ]; }; }); diff --git a/package.yaml b/package.yaml index ab369cd..73e33c4 100644 --- a/package.yaml +++ b/package.yaml @@ -15,14 +15,18 @@ dependencies: - directory - containers - net-mqtt + - network-uri - req + - effectful - aeson - text -# - bytestring + - bytestring + - process ghc-options: - -Wall - -fdefer-typed-holes + - -threaded default-extensions: - BlockArguments diff --git a/src/TK2MQTT.hs b/src/TK2MQTT.hs index 1fbaeb8..f8603ad 100644 --- a/src/TK2MQTT.hs +++ b/src/TK2MQTT.hs @@ -1,26 +1,40 @@ -{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} -module TK2MQTT ( - runTK2MQTT, - defaultMain, -) where +module TK2MQTT + ( runTK2MQTT, + defaultMain, + ) +where +import Control.Exception (catch) +import Control.Monad (void) +import Control.Monad.IO.Class +import Data.Aeson +import Network.HTTP.Req +import Network.MQTT.Client qualified as MQTT +import Network.URI (URI, parseURI) import System.Environment qualified as Env import System.IO -import Control.Exception qualified as Exception -import System.IO.Error qualified as IOError +newtype APIKey = APIKey String -runTK2MQTT :: IO () -runTK2MQTT = do - putStrLn "Hello" +newtype StationID = StationID String -eitherToError :: Show a => Either a b -> IO b -eitherToError = either (Exception.throwIO . IOError.userError . show) return +runTK2MQTT :: URI -> APIKey -> StationID -> IO () +runTK2MQTT uri (APIKey apikey) (StationID stationId) = do + mc <- MQTT.connectURI MQTT.mqttConfig uri + result <- runReq defaultHttpConfig $ do + r <- req GET (https "creativecommons.tankerkoenig.de" /: "json" /: "prices.php") NoReqBody jsonResponse ("apikey" =: apikey <> "ids" =: stationId) + pure (responseBody r :: Value) + void $ liftIO $ MQTT.publish mc "tmp/fuel/diesel" (encode result) True + void $ liftIO $ print $ encode result handleError :: IOError -> IO () handleError e = do hPutStrLn stderr $ "I ran into an issue: " <> show e defaultMain :: IO () -defaultMain = Exception.catch runTK2MQTT handleError +defaultMain = do + [apikey, stationId] <- Env.getArgs + let (Just uri) = parseURI "mqtt://hm.felis-halfmoon.ts.net" + runTK2MQTT uri (APIKey apikey) (StationID stationId) `catch` handleError diff --git a/test/spec/TK2MQTTSpec.hs b/test/spec/TK2MQTTSpec.hs index 66ed07b..2e3ddce 100644 --- a/test/spec/TK2MQTTSpec.hs +++ b/test/spec/TK2MQTTSpec.hs @@ -1,12 +1,8 @@ module TK2MQTTSpec (spec) where -import HCat.Internal - +import TK2MQTT.Internal import Test.Hspec import Test.Hspec.QuickCheck (prop) spec :: Spec -spec = do - describe "parseArgs" do - it "returns the filename if given a single argument" do - True `shouldBe` True +spec = pure () diff --git a/tk2mqtt.cabal b/tk2mqtt.cabal new file mode 100644 index 0000000..0ad3370 --- /dev/null +++ b/tk2mqtt.cabal @@ -0,0 +1,135 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.36.1. +-- +-- see: https://github.com/sol/hpack + +name: tk2mqtt +version: 0.0.1.0 +description: Tankerkönig-to-MQTT gateway +author: Alexander Kobjolke +maintainer: alex@jakalx.net +copyright: Alexander Kobjolke 2023 +license: MIT +build-type: Simple +extra-source-files: + README.org + +library + exposed-modules: + TK2MQTT + TK2MQTT.Internal + other-modules: + Paths_tk2mqtt + hs-source-dirs: + src + default-extensions: + BlockArguments + OverloadedStrings + ImportQualifiedPost + ghc-options: -Wall -fdefer-typed-holes -threaded + build-depends: + aeson + , base >=4.13 && <5 + , bytestring + , containers + , directory + , effectful + , net-mqtt + , network-uri + , process + , req + , text + , time + default-language: GHC2021 + +executable tk2mqtt + main-is: tk2mqtt.hs + other-modules: + Paths_tk2mqtt + hs-source-dirs: + app + default-extensions: + BlockArguments + OverloadedStrings + ImportQualifiedPost + ghc-options: -Wall -fdefer-typed-holes -threaded + build-depends: + aeson + , base >=4.13 && <5 + , bytestring + , containers + , directory + , effectful + , net-mqtt + , network-uri + , process + , req + , text + , time + , tk2mqtt + default-language: GHC2021 + +test-suite doctest + type: exitcode-stdio-1.0 + main-is: Doctest.hs + other-modules: + Paths_tk2mqtt + hs-source-dirs: + test/doctest + default-extensions: + BlockArguments + OverloadedStrings + ImportQualifiedPost + ghc-options: -Wall -fdefer-typed-holes -threaded + build-tool-depends: + doctest:doctest + build-depends: + aeson + , base >=4.13 && <5 + , bytestring + , containers + , directory + , effectful + , net-mqtt + , network-uri + , process + , req + , text + , time + default-language: Haskell2010 + +test-suite spec + type: exitcode-stdio-1.0 + main-is: Spec.hs + other-modules: + TK2MQTTSpec + Paths_tk2mqtt + hs-source-dirs: + test/spec + default-extensions: + BlockArguments + OverloadedStrings + ImportQualifiedPost + ghc-options: -Wall -fdefer-typed-holes -threaded + cpp-options: -DTEST + build-tool-depends: + hspec-discover:hspec-discover + build-depends: + QuickCheck + , aeson + , base >=4.13 && <5 + , bytestring + , containers + , directory + , effectful + , hspec + , net-mqtt + , network-uri + , process + , quickcheck-instances + , req + , text + , time + , tk2mqtt + default-language: GHC2021