From f7f11acafe0a404fa218c13832e32fce574ae0f6 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Fri, 2 Jan 2026 11:37:35 +0000 Subject: Adds battery monitoring to frontend --- hsm-web/Client/package-lock.json | 77 +++++++++++++++++++++++++++++++++++++++- hsm-web/Client/package.json | 1 + hsm-web/Client/src/App.vue | 11 ++++++ hsm-web/Client/src/INA226.vue | 48 +++++++++++++++++++++++++ hsm-web/Client/src/config.js | 3 ++ hsm-web/Hsm/Web.hs | 32 +++++++++++------ hsm-web/Main.hs | 6 ++-- hsm-web/hsm-web.cabal | 6 ++++ 8 files changed, 171 insertions(+), 13 deletions(-) create mode 100644 hsm-web/Client/src/INA226.vue create mode 100644 hsm-web/Client/src/config.js (limited to 'hsm-web') diff --git a/hsm-web/Client/package-lock.json b/hsm-web/Client/package-lock.json index 5324573..14e6a57 100644 --- a/hsm-web/Client/package-lock.json +++ b/hsm-web/Client/package-lock.json @@ -6,6 +6,7 @@ "": { "dependencies": { "@vue/cli": "^5.0.9", + "axios": "^1.13.2", "core-js": "^3.8.3", "vue": "^3.2.13" }, @@ -4614,6 +4615,12 @@ "retry": "0.13.1" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -4687,6 +4694,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-core": { "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", @@ -5679,6 +5697,18 @@ "node": ">=0.1.90" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -6677,6 +6707,15 @@ "node": ">=0.10.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -7099,6 +7138,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -8456,7 +8510,6 @@ "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "dev": true, "funding": [ { "type": "individual", @@ -8497,6 +8550,22 @@ "node": ">=0.10.0" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -12930,6 +12999,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/ps-list": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/ps-list/-/ps-list-7.2.0.tgz", diff --git a/hsm-web/Client/package.json b/hsm-web/Client/package.json index 65a15d8..21f7248 100644 --- a/hsm-web/Client/package.json +++ b/hsm-web/Client/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "@vue/cli": "^5.0.9", + "axios": "^1.13.2", "core-js": "^3.8.3", "vue": "^3.2.13" }, diff --git a/hsm-web/Client/src/App.vue b/hsm-web/Client/src/App.vue index 202e4d2..84fefcf 100644 --- a/hsm-web/Client/src/App.vue +++ b/hsm-web/Client/src/App.vue @@ -1,3 +1,14 @@ + + diff --git a/hsm-web/Client/src/INA226.vue b/hsm-web/Client/src/INA226.vue new file mode 100644 index 0000000..bf0141f --- /dev/null +++ b/hsm-web/Client/src/INA226.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/hsm-web/Client/src/config.js b/hsm-web/Client/src/config.js new file mode 100644 index 0000000..b8e9a98 --- /dev/null +++ b/hsm-web/Client/src/config.js @@ -0,0 +1,3 @@ +module.exports = { + api: 'http://192.168.8.170:3000' +} diff --git a/hsm-web/Hsm/Web.hs b/hsm-web/Hsm/Web.hs index ae8b1f2..4f6fb6e 100644 --- a/hsm-web/Hsm/Web.hs +++ b/hsm-web/Hsm/Web.hs @@ -8,15 +8,24 @@ module Hsm.Web ) where -import Effectful (Dispatch (Static), DispatchOf, Eff, IOE, (:>)) -import Effectful.Dispatch.Static (SideEffects (WithSideEffects), StaticRep, evalStaticRep, getStaticRep, unsafeEff) +import Data.Aeson (encode) +import Effectful (Dispatch (Static), DispatchOf, Eff, IOE, liftIO, (:>)) +import Effectful.Dispatch.Static + ( SideEffects (WithSideEffects) + , StaticRep + , evalStaticRep + , getStaticRep + , unEff + , unsafeEff + ) import Effectful.Dispatch.Static.Primitive (Env) import Effectful.Exception (finally) -import Hsm.Log (Log, Logs, Severity (Info, Trace), logMsg, makeLoggerIO) +import Hsm.INA226 (I2CINA226, INA226, readINA226State) +import Hsm.Log (Logs, Severity (Info, Trace), logMsg, makeLoggerIO) import Network.Wai.Handler.Warp (defaultSettings, setLogger) import Network.Wai.Middleware.Static (addBase, noDots, staticPolicy, (>->)) import Paths_hsm_web (getDataFileName) -import Web.Scotty (Options (settings, verbose), defaultOptions, file, get, middleware, scottyOpts) +import Web.Scotty (Options (settings, verbose), defaultOptions, file, get, middleware, raw, scottyOpts, setHeader) data Web (a :: * -> *) (b :: *) @@ -25,21 +34,24 @@ type instance DispatchOf Web = Static WithSideEffects newtype instance StaticRep Web = Web Options -server :: Options -> Env es -> IO () -server options _ = do +server :: (I2CINA226 :> es, INA226 :> es, Logs '["i2c", "ina226"] es) => Options -> Env es -> IO () +server options env = do dist <- getDataFileName "Client/dist/" scottyOpts options $ do - get "/" . file $ dist <> "index.html" middleware . staticPolicy $ noDots >-> addBase dist + get "/" . file $ dist <> "index.html" + get "/ina226" $ do + setHeader "Content-Type" "application/json" + liftIO (unEff readINA226State env) >>= raw . encode -runServer :: (Log "web" :> es, Web :> es) => Eff es () +runServer :: (I2CINA226 :> es, INA226 :> es, Logs '["i2c", "ina226", "web"] es, Web :> es) => Eff es () runServer = finally startServer stopServer where startServer = do Web options <- getStaticRep - logMsg Info "Starting scotty web server" + logMsg @"web" Info "Starting scotty web server" unsafeEff $ server options - stopServer = logMsg Info "Stopping scotty web server" + stopServer = logMsg @"web" Info "Stopping scotty web server" runWeb :: (IOE :> es, Logs '["scotty", "web"] es) => Eff (Web : es) a -> Eff es a runWeb action = do diff --git a/hsm-web/Main.hs b/hsm-web/Main.hs index 7555067..0ec780e 100644 --- a/hsm-web/Main.hs +++ b/hsm-web/Main.hs @@ -3,6 +3,8 @@ import Data.Function ((&)) import Effectful (runEff) import Hsm.Core.App (bootstrapAppNoEcho) +import Hsm.I2C (runI2C) +import Hsm.INA226 (runINA226) import Hsm.Log (Severity (Info), runLogsOpt) import Hsm.Log.Options (makeLoggerOptionParser) import Hsm.Web (runServer, runWeb) @@ -10,9 +12,9 @@ import Hsm.Web (runServer, runWeb) -- Avoids package/module qualifiers in generated code import Options.Applicative -type Logs = '["scotty", "web"] +type Logs = '["i2c", "ina226", "scotty", "web"] $(makeLoggerOptionParser @Logs "Options" "parser" 'Info) main :: IO () -main = bootstrapAppNoEcho parser "Launch HsMouse Web Server" $ \opts -> runServer & runWeb & runLogsOpt @Options @Logs opts & runEff +main = bootstrapAppNoEcho parser "Launch HsMouse Web Server" $ \opts -> runServer & runWeb & runINA226 & runI2C & runLogsOpt @Options @Logs opts & runEff diff --git a/hsm-web/hsm-web.cabal b/hsm-web/hsm-web.cabal index d3b9074..20b7db0 100644 --- a/hsm-web/hsm-web.cabal +++ b/hsm-web/hsm-web.cabal @@ -4,14 +4,17 @@ name: hsm-web version: 0.1.0.0 data-files: Client/dist/*.html + Client/dist/css/*.css Client/dist/js/*.js Client/dist/js/*.js.map library build-depends: + , aeson , base , effectful-core , effectful-plugin + , hsm-ina226 , hsm-log , scotty , wai-middleware-static @@ -27,10 +30,13 @@ library executable hsm-web build-depends: + , aeson , base , effectful-core , effectful-plugin , hsm-core + , hsm-i2c + , hsm-ina226 , hsm-log , optparse-applicative , scotty -- cgit v1.2.1