changeset 17:39c0686e6765

daemon: Change to executing from a config file. This updates daemon.py to execute based on a config file, rather than needing manual configuration. It also instructs setup.py to create an entry point for the new daemon.py main function. Configuration is specified in a TOML file whose format is documented, kind of, in the parse_config function.
author Paul Fisher <paul@pfish.zone>
date Thu, 17 Oct 2019 22:28:12 -0400
parents 770215590d80
children 9daa281d996b
files setup.py weatherlog/daemon.py
diffstat 2 files changed, 64 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/setup.py	Thu Oct 17 22:19:33 2019 -0400
+++ b/setup.py	Thu Oct 17 22:28:12 2019 -0400
@@ -2,7 +2,7 @@
 
 setuptools.setup(
     name='weatherlog',
-    version='0.1.0',
+    version='0.2.0',
     packages=setuptools.find_packages(),
     python_requires='>=3.7',
     install_requires=[
@@ -14,6 +14,12 @@
         'requests',
         'RPi.bme280',
         'smbus2',
+        'toml',
     ],
     setup_requires=['wheel'],
+    entry_points={
+        'console_scripts': [
+            'weatherlogd = weatherlog.daemon:main',
+        ]
+    }
 )
--- a/weatherlog/daemon.py	Thu Oct 17 22:19:33 2019 -0400
+++ b/weatherlog/daemon.py	Thu Oct 17 22:28:12 2019 -0400
@@ -1,8 +1,13 @@
 """Entry point to set up a temperature logging daemon."""
 
+import argparse
+import enum
 import time
 import typing as t
 
+import attr
+import toml
+
 from . import http_writer
 from . import logger
 from . import reader
@@ -12,25 +17,69 @@
 
 
 def run(
-    directory: str,
-    url: str,
-    preamble: t.Dict[str, t.Any],
+    rd: reader.Reader,
+    log: logger.BufferedLogger,
+    writer: logger.RemoteWriter,
     interval: int = DEFAULT_INTERVAL_SECS,
 ):
     """Sets up and runs a logger daemon."""
-    writer = http_writer.HTTPWriter(url, preamble)
-    log = logger.BufferedLogger(directory, writer)
     log.start()
-
-    r = reader.DHT22Reader()
     cycle = 0
     start = time.time()
     try:
         while True:
-            log.write(r.read().as_dict())
+            log.write(rd.read().as_dict())
             cycle += 1
             target = start + interval * cycle
             now = time.time()
             time.sleep(max(target - now, MIN_INTERVAL_SECS))
     finally:
         log.close()
+
+
+class SensorType(enum.Enum):
+    DHT22 = 'DHT22'
+    BME280 = 'BME280'
+
+
+@attr.s(auto_attribs=True, frozen=True, slots=True)
+class _Config:
+    # The directory to store reading data in.
+    directory: str
+    # The URL to submit readings to.
+    url: str
+    # The type of sensor to read from.
+    sensor: SensorType
+    # The authentication preamble for the URL.
+    auth: t.Dict[str, object]
+
+
+def parse_config(config_file: str):
+    with open(config_file, 'r') as infile:
+        config = toml.load(infile)
+    return _Config(
+        directory=config['directory'],
+        url=config['url'],
+        sensor=SensorType(config['sensor']['type']),
+        auth=config['auth'])
+
+
+def main(args: t.Optional[t.Sequence[str]] = None) -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument('config_file', help='TOML file to load config from.')
+    parsed = parser.parse_args(args)
+    config = parse_config(parsed.config_file)
+    writer = http_writer.HTTPWriter(config.url, config.auth)
+    log = logger.BufferedLogger(config.directory, writer)
+
+    if config.sensor is SensorType.DHT22:
+        rdr = reader.DHT22Reader()
+    elif config.sensor is SensorType.BME280:
+        rdr = reader.BME280Reader()
+    else:
+        raise AssertionError('Unknown sensor type')
+    run(rdr, log, writer)
+
+
+if __name__ == '__main__':
+    main()