diff --git a/hotcooler/tempmon/__init__.py b/hotcooler/tempmon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hotcooler/tempmon/client.py b/hotcooler/tempmon/client.py new file mode 100644 index 0000000..5a7c157 --- /dev/null +++ b/hotcooler/tempmon/client.py @@ -0,0 +1,139 @@ +""" +Tempmon Client + +Responsible for reading temperatures from probes, and recording to database +""" + +import sys +import argparse +import time +import datetime +import random +import socket +import logging + +from sqlalchemy.orm.exc import NoResultFound + +from websauna.system.devop.cmdline import init_websauna + +from hotcooler import models + + +log = logging.getLogger(__name__) + + +class TempmonClient: + """ + Linux daemon implementation of Tempmon client + """ + # must set this to a sessionmaker + Session = None + + def run(self): + """ + This method is invoked upon daemon startup. It is meant to run/loop + "forever" or until daemon stop. + """ + # maybe generate random data instead of reading from true probe + # self.dummy_probes = self.config.getbool('tempmon.client', 'dummy_probes', default=False) + self.dummy_probes = True # TODO + + # figure out which client we are + # hostname = self.config.get('tempmon.client', 'hostname', default=socket.gethostname()) + hostname = 'demo.rattail-tempmon.org' # TODO + session = self.Session() + try: + client = session.query(models.Client)\ + .filter_by(hostname=hostname)\ + .one() + except NoResultFound: + session.close() + raise RuntimeError("No tempmon client configured for hostname: {}".format(hostname)) + client_uuid = client.uuid + self.delay = client.delay or 60 + session.close() + + # main loop: take readings, pause, repeat + while True: + + session = self.Session() + + try: + client = session.query(models.Client).filter_by(uuid=client_uuid).one() + self.delay = client.delay or 60 + if client.enabled: + for probe in client.enabled_probes(): + self.take_reading(session, probe) + + except: + log.exception("Failed to read/record temperature data (but will keep trying)") + session.rollback() + + else: + # make sure we show as being online + if not client.online: + client.online = True + session.commit() + + finally: + session.close() + + time.sleep(self.delay) + + def take_reading(self, session, probe): + """ + Take a single reading and add to Rattail database. + """ + reading = models.Reading() + reading.client = probe.client + reading.probe = probe + reading.degrees_f = self.read_temp(probe) + reading.taken = datetime.datetime.utcnow() + session.add(reading) + return reading + + def read_temp(self, probe): + """ + Check for good reading, then format temperature to our liking + """ + if self.dummy_probes: + return self.random_temp(probe) + lines = self.read_temp_raw(probe) + while lines[0].strip()[-3:] != 'YES': + time.sleep(0.2) + lines = self.read_temp_raw(probe) + equals_pos = lines[1].find('t=') + if equals_pos != -1: + temp_string = lines[1][equals_pos+2:] + temp_c = float(temp_string) / 1000.0 + temp_f = temp_c * 9.0 / 5.0 + 32.0 + return round(temp_f,4) + + def read_temp_raw(self, probe): + """ + Function that gets the raw temp data + """ + with open(probe.device_path, 'rt') as therm_file: + return therm_file.readlines() + + def random_temp(self, probe): + temp = random.uniform(probe.critical_temp_min - 5, probe.critical_temp_max + 5) + return round(temp, 4) + + +def tempmon_client_command(*args): + """ + Manage the hotcooler tempmon-client daemon + """ + parser = argparse.ArgumentParser(prog='tempmon-client', + description=tempmon_client_command.__doc__.strip()) + + parser.add_argument('config_uri', help="Path to main config file") + + args = parser.parse_args(args or sys.argv[1:]) + request = init_websauna(args.config_uri, console_app=True) + session = request.dbsession + + client = TempmonClient() + client.Session = request.registry.db_session_maker + client.run() diff --git a/setup.py b/setup.py index f22d689..44bba8b 100644 --- a/setup.py +++ b/setup.py @@ -18,26 +18,27 @@ if len(sys.argv) >= 2: raise RuntimeError("It is not possible to install this package with setup.py. Use pip to install this package as instructed in Websauna tutorial.") -setup(name='hotcooler', - version='0.0', - description='hotcooler', - long_description=README + '\n\n' + CHANGES, - classifiers=[ +setup( + name='hotcooler', + version='0.0', + description='hotcooler', + long_description=README + '\n\n' + CHANGES, + classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - ], - author='', - author_email='', - url='', - keywords='web websauna pyramid', - packages=find_packages(), - include_package_data=True, - zip_safe=False, - test_suite='hotcooler', - install_requires=['websauna'], - extras_require={ + ], + author='', + author_email='', + url='', + keywords='web websauna pyramid', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + test_suite='hotcooler', + install_requires=['websauna'], + extras_require={ # Dependencies for running test suite 'test': [ "pytest", @@ -53,11 +54,14 @@ setup(name='hotcooler', # Dependencies to make releases 'dev': ['websauna[dev]'], - }, + }, - # Define where this application starts as referred by WSGI web servers - entry_points="""\ - [paste.app_factory] - main = hotcooler:main - """, - ) + entry_points = { + 'paste.app_factory': [ + 'main = hotcooler:main', + ], + 'console_scripts': [ + 'tempmon-client = hotcooler.tempmon.client:tempmon_client_command', + ], + }, +)