浏览代码

README and cleanup

Lukas Angerer 5 年之前
父节点
当前提交
790390c9c9
共有 6 个文件被更改,包括 84 次插入121 次删除
  1. 7 0
      LICENSE
  2. 77 0
      README.md
  3. 0 7
      scripts/commands/CmdExit.py
  4. 0 26
      scripts/commands/CmdUpdate.py
  5. 0 0
      scripts/commands/__init__.py
  6. 0 88
      scripts/cron-section-replace.py

+ 7 - 0
LICENSE

@@ -0,0 +1,7 @@
+Copyright 2021 Lukas Angerer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 77 - 0
README.md

@@ -0,0 +1,77 @@
+# CronAlarm
+Configuration based web UI to manage _sections_ of a crontab
+
+The main application is a Blazor (server) frontend that presents an alarm selection UI based on `appsettings.json`. The .NET 5 application is meant to run in a container and communicates with the host system through a separate Python 3 Flask API that creates a thin wrapper for actually reading and writing the crontab through `crontab -l` and `crontab -`.
+
+The configuration consists of two layers:
+* The core settings that define the general application parameters
+* A secondary configuration placed in the `wwwroot/data` directory that contains the actual alert configuration
+
+# Building
+In the root of the project, simply execute
+```bash
+docker-compose build
+```
+
+This will build the application and construct a tagged container image for running it.
+
+# Running
+Still in the root of the project, run
+
+```bash
+docker-compose up
+```
+
+This runs the container and mounts the `./data` directory as `/app/wwwroot/data` which means that the `./data/settings.json` file in that directory will be used for the alert configuration.
+
+And for running the Flask API on the host - assuming that you have Python3 and Flask installed
+```bash
+./scripts/cron-api.py
+```
+
+
+
+# Configuration
+
+The base `appsettings.json` configuration defines the base URI of the Flask Python web API running on the host system. The URI must of course be resolvable from within the container that runs the Blazor frontend.
+
+```json
+{
+  "CronApi": {
+    "BaseUri": "http://192.168.42.44:5000"
+  }
+}
+```
+
+The main alert configuration is split into groups. All alerts use the same command, so that is defined on the top level of `Alerts.Command`.
+```json
+{
+  "Alerts": {
+    "Command": "/home/pi/mpc-alarm-cron.sh",
+    "Groups": [
+        // ...
+    ]
+  }
+}
+```
+
+In every alert **group** at most one alert can be selected at any given time. The group must have a `Name` and a list of `Options` where each option defines a human readable `Label` and the corresponding cron `Pattern`. When a specific option is selected, the pattern is combined with the command and will be added as a line to the crontab.
+```json
+{
+  "Name": "Weekdays",
+  "Options": [
+    {
+      "Label": "06:20 - Work",
+      "Pattern": "20 06 * * MON-FRI"
+    },
+    {
+      "Label": "08:30 - Holidays",
+      "Pattern": "30 08 * * MON-FRI"
+    },
+    {
+      "Label": "09:00 - Sleeping In",
+      "Pattern": "00 09 * * MON-FRI"
+     }
+  ]
+}
+```

+ 0 - 7
scripts/commands/CmdExit.py

@@ -1,7 +0,0 @@
-
-class CmdExit:
-    def __init__(self, config):
-        self.config = config
-    
-    def execute(self):
-        self.config["Running"] = False

+ 0 - 26
scripts/commands/CmdUpdate.py

@@ -1,26 +0,0 @@
-import os
-import re
-
-class CmdUpdate:
-    def __init__(self, config):
-        self.config = config
-    
-    def execute(self):
-        print("Updating...")
-        stream = os.popen("crontab -l")
-        crontab = stream.read()
-        result = crontab
-        begin = re.search("\s*#\s*BEGIN\(ALERTS\).*", crontab)
-        if begin:
-            fragment = ""
-            with open(self.config["CronFragment"]) as file:
-                fragment = file.read()
-            
-            result = crontab[:begin.span()[1]] + "\n"
-            result += fragment
-
-            end = re.search("\s*#\s*END\(ALERTS\).*", crontab)
-            if end:
-                result += crontab[end.span()[0]:]
-        
-        print(result)

+ 0 - 0
scripts/commands/__init__.py


+ 0 - 88
scripts/cron-section-replace.py

@@ -1,88 +0,0 @@
-#!/usr/bin/python3
-
-import os
-import errno
-import re
-from commands.CmdExit import CmdExit
-from commands.CmdUpdate import CmdUpdate
-
-DIR = os.path.dirname(os.path.realpath(__file__))
-
-CONFIG = {
-    "PipePath": os.path.join(DIR, "../data/cmd-pipe"),
-    "CronFragment": os.path.join(DIR, "../data/cron-fragment.txt"),
-    "Running": True,
-}
-
-# try:
-#     os.mkfifo(PIPE)
-# except OSError as oe:
-#     if oe.errno != errno.EEXIST:
-#         raise
-
-def execute(command):
-    if command == "EXIT":
-        global running
-        running = False
-    elif command == "UPDATE":
-        print("Updating...")
-        stream = os.popen('crontab -l')
-        crontab = stream.read()
-        result = crontab
-        begin = re.search("\s*#\s*BEGIN\(ALERTS\).*", crontab)
-        if begin:
-            result = crontab[:begin.span()[1]] + "\n"
-
-            end = re.search("\s*#\s*END\(ALERTS\).*", crontab)
-            if end:
-                result += "muahahah\ntest"
-                result += crontab[end.span()[0]:]
-        
-        print(result)
-    else:
-        print("UNKNOWN COMMAND: {0}".format(command))
-
-
-def main(config):
-    # commandPipe = ''
-    # cronFragment = ''
-    # try:
-    #     opts, args = getopt.getopt(argv,"h",["help","pipe=","cron="])
-    # except getopt.GetoptError:
-    #     print 'cron-section-replace --pipe <command-pipe> --cron <cron-fragment>'
-    #     sys.exit(2)
-    # for opt, arg in opts:
-    #     if opt in ("-h", "--help"):
-    #         print 'cron-section-replace --pipe <command-pipe> --cron <cron-fragment>'
-    #         sys.exit()
-    #     elif opt in ("--pipe"):
-    #         commandPipe = arg
-    #     elif opt in ("--cron):
-    #         cronFragment = arg
-    
-    # print(commandPipe)
-    # print(cronFragment)
-    # sys.exit()
-
-    commands = {
-        "EXIT": CmdExit(config),
-        "UPDATE": CmdUpdate(config)
-    }
-
-    
-    while config["Running"]:
-        print("Waiting for connection...")
-        with open(config["PipePath"]) as pipe:
-            print("Connection established")
-            while True:
-                cmd = pipe.read().strip()
-                if len(cmd) == 0:
-                    break
-                if cmd in commands:
-                    commands[cmd].execute()
-                else:
-                    print("UNKNOWN COMMAND: {0}".format(cmd))
-                #execute(data.strip())
-
-if __name__ == "__main__":
-   main(CONFIG)