comparison weatherlog/daemon.py @ 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 c01f9929ae38
children 7117db65715e
comparison
equal deleted inserted replaced
16:770215590d80 17:39c0686e6765
1 """Entry point to set up a temperature logging daemon.""" 1 """Entry point to set up a temperature logging daemon."""
2 2
3 import argparse
4 import enum
3 import time 5 import time
4 import typing as t 6 import typing as t
7
8 import attr
9 import toml
5 10
6 from . import http_writer 11 from . import http_writer
7 from . import logger 12 from . import logger
8 from . import reader 13 from . import reader
9 14
10 DEFAULT_INTERVAL_SECS = 60 15 DEFAULT_INTERVAL_SECS = 60
11 MIN_INTERVAL_SECS = 5 16 MIN_INTERVAL_SECS = 5
12 17
13 18
14 def run( 19 def run(
15 directory: str, 20 rd: reader.Reader,
16 url: str, 21 log: logger.BufferedLogger,
17 preamble: t.Dict[str, t.Any], 22 writer: logger.RemoteWriter,
18 interval: int = DEFAULT_INTERVAL_SECS, 23 interval: int = DEFAULT_INTERVAL_SECS,
19 ): 24 ):
20 """Sets up and runs a logger daemon.""" 25 """Sets up and runs a logger daemon."""
21 writer = http_writer.HTTPWriter(url, preamble)
22 log = logger.BufferedLogger(directory, writer)
23 log.start() 26 log.start()
24
25 r = reader.DHT22Reader()
26 cycle = 0 27 cycle = 0
27 start = time.time() 28 start = time.time()
28 try: 29 try:
29 while True: 30 while True:
30 log.write(r.read().as_dict()) 31 log.write(rd.read().as_dict())
31 cycle += 1 32 cycle += 1
32 target = start + interval * cycle 33 target = start + interval * cycle
33 now = time.time() 34 now = time.time()
34 time.sleep(max(target - now, MIN_INTERVAL_SECS)) 35 time.sleep(max(target - now, MIN_INTERVAL_SECS))
35 finally: 36 finally:
36 log.close() 37 log.close()
38
39
40 class SensorType(enum.Enum):
41 DHT22 = 'DHT22'
42 BME280 = 'BME280'
43
44
45 @attr.s(auto_attribs=True, frozen=True, slots=True)
46 class _Config:
47 # The directory to store reading data in.
48 directory: str
49 # The URL to submit readings to.
50 url: str
51 # The type of sensor to read from.
52 sensor: SensorType
53 # The authentication preamble for the URL.
54 auth: t.Dict[str, object]
55
56
57 def parse_config(config_file: str):
58 with open(config_file, 'r') as infile:
59 config = toml.load(infile)
60 return _Config(
61 directory=config['directory'],
62 url=config['url'],
63 sensor=SensorType(config['sensor']['type']),
64 auth=config['auth'])
65
66
67 def main(args: t.Optional[t.Sequence[str]] = None) -> None:
68 parser = argparse.ArgumentParser()
69 parser.add_argument('config_file', help='TOML file to load config from.')
70 parsed = parser.parse_args(args)
71 config = parse_config(parsed.config_file)
72 writer = http_writer.HTTPWriter(config.url, config.auth)
73 log = logger.BufferedLogger(config.directory, writer)
74
75 if config.sensor is SensorType.DHT22:
76 rdr = reader.DHT22Reader()
77 elif config.sensor is SensorType.BME280:
78 rdr = reader.BME280Reader()
79 else:
80 raise AssertionError('Unknown sensor type')
81 run(rdr, log, writer)
82
83
84 if __name__ == '__main__':
85 main()