def settings(self, pname, updated=None): pinfo = __import__('plugins.' + pname, fromlist=[pname]) if pinfo.hasconfig == True: plugincfg = ConfigParser() try: plugincfg.read(pinfo.configfile) except PermissionError: handlers.criterr( "Permissions error on plugin configuration file. Please ensure you have write permissions for the directory." ) return { 'id': pname, 'name': pinfo.name, 'hasConfig': True, 'configOpts': pinfo.configopts, 'currentcfg': plugincfg, 'updated': updated } else: return { 'id': pname, 'name': pinfo.name, 'hasConfig': False, 'updated': updated }
def getCfg(): global maincfg global pluginlist global debugcfg try: configfile.read('config/dashday.cfg') except PermissionError: handlers.criterr("Permissions error on dashday.cfg. Please ensure you have write permissions for the directory.") maincfg = configfile['General'] pluginlist = configfile['Plugins']['toload'].split(',') debugcfg = configfile['Debug']
def changeTime(self, scheduleRun): mainconfig = ConfigParser() try: mainconfig.read('config/headlights.cfg') except PermissionError: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) mainconfig["Schedule"]["runat"] = scheduleRun with open('config/headlights.cfg', 'w') as headlightscfg: mainconfig.write(headlightscfg) headlights.reload() redirect('/', {'update': 'true'})
def fetchLocalFrc(stationcode, key): url = 'http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/' + stationcode + '?res=daily&key=' + key request = urllib.request.Request(url) request.add_header('User-Agent', 'Headlights Client') request.add_header('Content-Type', 'application/json') try: response = urllib.request.urlopen(request).read().decode('UTF-8') except urllib.error.HTTPError as e: if e.code == 403: handlers.criterr( 'HTTP error 403 (access denied). Check your API key and try again.' ) else: handlers.criterr( 'Unknown HTTP error. Error code ' + str(e.code) + '. Maybe the Met Office API (or your internet) is dead?') except Exception: import traceback handlers.criterr('Generic urllib exception: ' + traceback.format_exc()) weatherobj = json.loads(response) try: weather = weatherobj['SiteRep']['DV']['Location']['Period'][0]['Rep'][ 0] return weather except KeyError: handlers.criterr( 'Could not parse weather data. Sounds like the Met Office changed their API, but not their endpoint. :/' ) # oh noes! they changed their API?!?
def settings(self): mainconfig = ConfigParser() try: mainconfig.read('config/headlights.cfg') except PermissionError: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) return { 'runTime': mainconfig["Schedule"]["runat"], 'name': mainconfig["General"]["HelloMyNameIs"], 'pvend': mainconfig["General"]["Vendor"], 'pprod': mainconfig["General"]["Product"] }
def fetchRegionFrcAsText(regioncode, key): url = 'http://datapoint.metoffice.gov.uk/public/data/txt/wxfcs/regionalforecast/json/' + regioncode + '?key=' + key request = urllib.request.Request(url) request.add_header('User-Agent', 'Headlights Client') request.add_header('Content-Type', 'application/json') try: response = urllib.request.urlopen(request).read().decode( 'UTF-8') # Tries to read, will throw exception on 403 etc except urllib.error.HTTPError as e: if e.code == 403: handlers.criterr( 'HTTP error 403 (access denied). Check your API key and try again.' ) # This is probably them forgetting to put their key in (correctly) else: handlers.criterr( 'Unknown HTTP error. Error code ' + str(e.code) + '. Maybe the Met Office API (or your internet) is dead?' ) # Something very odd. except Exception: import traceback handlers.criterr('Generic urllib exception: ' + traceback.format_exc()) # Something even odder weatherobj = json.loads(response) try: shortWthrText = weatherobj['RegionalFcst']['FcstPeriods']['Period'][0][ 'Paragraph'][1][ '$'] # wahey the text! let's hope they never change the way their API returns... except KeyError: handlers.criterr( 'Could not parse weather data. Sounds like the Met Office changed their API, but not their endpoint. :/' ) # oh noes! they changed their API?!? return str(shortWthrText)
def disablePlugin(self, pid): mainconfig = ConfigParser() try: mainconfig.read('config/headlights.cfg') except PermissionError: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) pluginlist = mainconfig['Plugins']['toload'].split(',') pluginlist.remove(pid) mainconfig['Plugins']['toload'] = ','.join(pluginlist) with open('config/headlights.cfg', 'w') as headlightscfg: mainconfig.write(headlightscfg) headlights.reload() redirect('/plugins', {'disable': 'true'})
def init(): global config global wtypes global weathercfg # Load configuration and all that jazz config = ConfigParser() if os.path.isfile('config/weather.cfg'): config.read('config/weather.cfg') weathercfg = config['Info'] else: if "HEADLIGHTS_TESTMODE" in os.environ and os.environ[ 'HEADLIGHTS_TESTMODE'] == '1': try: weathercfg = { 'TextRegionCode': '514', 'ForecastLocation': '3672', 'DataPointKey': os.environ['HEADLIGHTS_DPKEY'] } except KeyError: handlers.criterr( "Incorrectly set test environment variables. Please set up Headlights correctly for testing." ) else: handlers.err( 'Cannot find weather.cfg. weather has not been loaded.') pluginloader.unload('weather') # Convert the weather types returned from the Met Office API into something vaguely sensible wtypes = { 'NA': ['questionmark.png', ')', 'Unknown'], '1': ['sunny.png', 'B', 'Sunny'], '3': ['ptlycloudy.png', 'H', 'Partly cloudy'], '5': ['mist.png', 'E', 'Misty'], '6': ['fog.png', 'F', 'Foggy'], '7': ['cloudy.png', 'Y', 'Cloudy'], '8': ['overcast.png', '%', 'Overcast'], '11': ['drizzle.png', 'Q', 'Drizzly'], '12': ['lightrain.png', 'T', 'Light rain'], '15': ['heavyrain.png', 'R', 'Heavy rain'], '18': ['sleet.png', 'M', 'Sleet'], '21': ['hail.png', 'X', 'Hail'], '24': ['lightsnow.png', 'V', 'Light snow'], '27': ['heavysnow.png', 'W', 'Heavy snow'], '30': ['thunder.png', '0', 'Thunder'] }
def start(): global headlightsjob global configfile if os.path.isfile('config/headlights.cfg'): configfile = ConfigParser() try: configfile.read('config/headlights.cfg') except Exception as e: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) else: print("Please configure Headlights!") exit() print("Headlights started at " + time.strftime("%d/%m/%y %H:%M:%S")) main.start() refresh_eink(configfile) print("Headlights completed!")
def changeGeneral(self, name, pvend, pprod, escpos=True, eink=False): if eink: eink = True mainconfig = ConfigParser() try: mainconfig.read('config/headlights.cfg') except PermissionError: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) mainconfig["General"]["HelloMyNameIs"] = name mainconfig["General"]["vendor"] = pvend mainconfig["General"]["product"] = pprod mainconfig["Output"]["eink"] = eink with open('config/headlights.cfg', 'w') as headlightscfg: mainconfig.write(headlightscfg) headlights.reload() redirect('/', {'update': 'true'})
def changeSettings(self, pname, psect, **kw): pname = unicodedata.normalize('NFKD', pname).encode('ascii', 'ignore') pinfo = __import__('plugins.' + pname, fromlist=[pname]) if pinfo.hasconfig == True: plugincfg = ConfigParser() try: plugincfg.read(pinfo.configfile) except PermissionError: handlers.criterr( "Permissions error on plugin configuration file. Please ensure you have write permissions for the directory." ) for name, value in kw.items(): plugincfg[psect][name] = value with open(pinfo.configfile, 'w') as configfile: plugincfg.write(configfile) redirect('/plugins/settings/' + pname, {'updated': 'true'}) else: redirect('/plugins/settings/' + pname, {'updated': 'false'})
def index(self, enable='', disable='', delete=''): def pclass(plugin, enabledlist): if plugin in enabledlist: return {'class': 'success'} else: return {'class': 'none'} def pHasSettings(plugin): pinfo = __import__('plugins.' + plugin, fromlist=[plugin]) if pinfo.hasconfig == True: return {} else: return { 'class': 'disabled', 'disabled': 'true' } # returns disabled to disable the settings icon on the plugins page def getplugininfo(plugin): pinfo = __import__('plugins.' + plugin, fromlist=[plugin]) return { 'name': pinfo.name, 'descrip': pinfo.description, 'version': pinfo.version, 'author': pinfo.author } mainconfig = ConfigParser() try: mainconfig.read('config/headlights.cfg') except PermissionError: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) pluginlist = mainconfig['Plugins']['toload'].split(',') return { 'enabled': pluginlist, 'all': getSubdirectories('plugins'), 'pclass': pclass, 'getpinfo': getplugininfo, 'enable': enable, 'disable': disable, 'delete': delete, 'psclass': pHasSettings }
def init(isfirst=False): global userconfig global nextRunTime global firstrun firstrun = isfirst if os.path.isfile('config/web.cfg') == True: userconfig = ConfigParser() try: userconfig.read('config/web.cfg') except PermissionError: handlers.criterr( "Permissions error on web.cfg. Please ensure you have write permissions for the directory." ) else: print( "No configuration file found. Please configure Headlights's server." ) exit() nextRunTime = "loading..."
def setup_printer(maincfg, debugcfg, logging): # Connects to the printer (unless test mode is enabled, in which case starts a dummy instance) if debugcfg['TestMode'] == "1": logging.warning( 'Headlights is in test mode. Nothing will actually be printed - you\'ll just see the output to the printer on the screen.' ) p = printer.Dummy() logging.debug("Initialized dummy printer") else: try: p = printer.Usb(int(maincfg['Vendor'], 16), int(maincfg['Product'], 16)) except (usb.core.NoBackendError, escpos.exceptions.USBNotFoundError) as e: logging.debug(e) handlers.criterr( "Could not initialize printer. Check a printer matching the vendor and product in the config file is actually connected, and relaunch Headlights." ) logging.debug("Initialized USB printer") return p
def reload(): global headlightsjob global configfile if os.path.isfile('config/headlights.cfg'): configfile = ConfigParser() try: configfile.read('config/headlights.cfg') except Exception as e: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) else: print("Please configure Headlights!") exit() schedule.clear() refresh_eink(configfile) headlightsjob = schedule.every().day.at( configfile['Schedule']['runat']).do(start) web.serv.updateScheduledRun( headlightsjob.next_run.strftime("%d/%m/%y %H:%M:%S"))
def tweet_print(self, authkey): configfile = ConfigParser() try: configfile.read('config/headlights.cfg') except PermissionError: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) maincfg = configfile['General'] debugcfg = configfile['Debug'] try: key = configfile['Web']['apiauth'] except KeyError: key = '' if key != authkey: abort(401) p = printer.setup_printer(maincfg, debugcfg, logging) p.text(request.body) p.cut() abort(200)
def init(): global config global wtypes global weathercfg # Load configuration and all that jazz config = configparser.ConfigParser() if os.path.isfile('config/weather.cfg'): config.read('config/weather.cfg') weathercfg = config['Info'] else: if "DASHDAY_TESTMODE" in os.environ and os.environ['DASHDAY_TESTMODE'] == '1': try: weathercfg = {'TextRegionCode': '514', 'ForecastLocation': '3672', 'DataPointKey': os.environ['DASHDAY_DPKEY']} except KeyError: handlers.criterr("Incorrectly set test environment variables. Please set up Dashday correctly for testing.") else: handlers.err('Cannot find weather.cfg. weather has not been loaded.') pluginloader.unload('weather') # Convert the weather types returned from the Met Office API into something vaguely sensible wtypes = {'NA' : 'questionmark.png', '1' : 'sunny.png', '3' : 'ptlycloudy.png', '5' : 'mist.png', '6' : 'fog.png', '7' : 'cloudy.png', '8' : 'overcast.png', '11' : 'drizzle.png', '12' : 'lightrain.png', '15' : 'heavyrain.png', '18' : 'sleet.png', '21' : 'hail.png', '24' : 'lightsnow.png', '27' : 'heavysnow.png', '30' : 'thunder.png' }
def fetchFrcWthrType(stationcode,key): url = 'http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/' + stationcode + '?res=daily&key=' + key request = urllib.request.Request(url) request.add_header('User-Agent','Dashday Client') request.add_header('Content-Type','application/json') try: response = urllib.request.urlopen(request).read().decode('UTF-8') except urllib.error.HTTPError as e: if e.code == 403: handlers.criterr('HTTP error 403 (access denied). Check your API key and try again.') else: handlers.criterr('Unknown HTTP error. Error code ' + str(e.code) + '. Maybe the Met Office API (or your internet) is dead?') except Exception: import traceback handlers.criterr('Generic urllib exception: ' + traceback.format_exc()) weatherobj = json.loads(response) try: weatherType = weatherobj['SiteRep']['DV']['Location']['Period'][0]['Rep'][0]['W'] except KeyError: handlers.criterr('Could not parse weather data. Sounds like the Met Office changed their API, but not their endpoint. :/') # oh noes! they changed their API?!? return(str(weatherType))
def fetchRegionFrcAsText(regioncode,key): url = 'http://datapoint.metoffice.gov.uk/public/data/txt/wxfcs/regionalforecast/json/' + regioncode + '?key=' + key request = urllib.request.Request(url) request.add_header('User-Agent','Dashday Client') request.add_header('Content-Type','application/json') try: response = urllib.request.urlopen(request).read().decode('UTF-8') # Tries to read, will throw exception on 403 etc except urllib.error.HTTPError as e: if e.code == 403: handlers.criterr('HTTP error 403 (access denied). Check your API key and try again.') # This is probably them forgetting to put their key in (correctly) else: handlers.criterr('Unknown HTTP error. Error code ' + str(e.code) + '. Maybe the Met Office API (or your internet) is dead?') # Something very odd. except Exception: import traceback handlers.criterr('Generic urllib exception: ' + traceback.format_exc()) # Something even odder weatherobj = json.loads(response) try: shortWthrText = weatherobj['RegionalFcst']['FcstPeriods']['Period'][0]['Paragraph'][1]['$'] # wahey the text! let's hope they never change the way their API returns... except KeyError: handlers.criterr('Could not parse weather data. Sounds like the Met Office changed their API, but not their endpoint. :/') # oh noes! they changed their API?!? return(str(shortWthrText))
configfile['Schedule']['runat']).do(start) web.serv.updateScheduledRun( headlightsjob.next_run.strftime("%d/%m/%y %H:%M:%S")) if __name__ == "__main__": global configfile # Load the configuration file # noinspection PyPackageRequirements if os.path.isfile('config/headlights.cfg'): configfile = ConfigParser() try: configfile.read('config/headlights.cfg') except Exception as e: handlers.criterr( "Permissions error on headlights.cfg. Please ensure you have write permissions for the directory." ) web.serv.init() # Run the web server thr = threading.Thread(target=web.serv.run) thr.start() # Runs the API thr = threading.Thread(target=web.serv.run_api) thr.start() # Runs the scheduler and all that jazz thr = threading.Thread(target=runapp) thr.start() else:
def main(): global maincfg global pluginlist global debugcfg global configfile # Let's make logging work. Formatting the log here logFormatter = logging.Formatter("%(asctime)s: %(levelname)s: %(message)s","%m/%d/%Y %I:%M:%S %p") rootLogger = logging.getLogger() rootLogger.setLevel(logging.NOTSET) # Make sure the root logger doesn't block any potential output to the fileHandler or consoleHandler # And output it to the console consoleHandler = logging.StreamHandler() consoleHandler.setFormatter(logFormatter) consoleHandler.setLevel(logging.WARNING) # Only output warnings to stdout rootLogger.addHandler(consoleHandler) # Finally, output the log to a file try: fileHandler = logging.FileHandler("dashday.log") fileHandler.setFormatter(logFormatter) fileHandler.setLevel(logging.INFO) # Output info to the log by default rootLogger.addHandler(fileHandler) except PermissionError: handlers.criterr("Permissions error on dashday.log. Please ensure you have write permissions for the directory.") # Wahey logging.info('Dashday process started') # Create a new configuration file instance configfile = configparser.ConfigParser() # Does the user even have a configuration file? if os.path.isfile('config/dashday.cfg') != True: # Check for test mode specified in the environment variables if "DASHDAY_TESTMODE" in os.environ and os.environ['DASHDAY_TESTMODE'] == '1': try: maincfg = {'HelloMyNameIs' : "TestModeUsr"} pluginlist = ['weather'] debugcfg = {'TestMode': "1", 'LogLevel': "DEBUG"} except KeyError: handlers.criterr("Incorrectly set test environment variables. Please set up Dashday correctly for testing.") logging.warning("Running in environment variable based test mode.") else: handlers.criterr('dashday.cfg not found. Please configure dashday before launch.') else: getCfg() # And let's start the logging! numeric_level = getattr(logging, debugcfg['LogLevel'].upper(), None) if not isinstance(numeric_level, int): raise ValueError('Invalid log level: {0!s}'.format(loglevel)) consoleHandler.setLevel(numeric_level) fileHandler.setLevel(numeric_level) # Connects to the printer (unless test mode is enabled, in which case starts a dummy instance) if debugcfg['TestMode'] == "1": logging.warning('Dashday is in test mode. Nothing will actually be printed - you\'ll just see the output to the printer on the screen.') p = printer.Dummy() logging.debug("Initialized dummy printer") else: try: p = printer.Usb(int(maincfg['Vendor'],16),int(maincfg['Product'],16)) except (usb.core.NoBackendError,escpos.exceptions.USBNotFoundError) as e: logging.debug(e) handlers.criterr("Could not initialize printer. Check a printer matching the vendor and product in the config file is actually connected, and relaunch Dashday.") logging.debug("Initialized USB printer") # Setup the printer for the beautiful header, and then print it p.set(align="LEFT",text_type="B",width=2,height=2) logging.debug("Set the header printing style") p.text("Hello,\n" + maincfg['HelloMyNameIs'] + '\n\n') logging.debug("Printed the header") # Time to reset the font p.set("LEFT", "A", "normal", 1, 1) logging.debug("Unset the header printing style") p.text(datetime.datetime.now().strftime("%a %d %b")) # Load all the plugins in the plugins list in the config file for plugin in pluginlist: pluginloader.init(plugin) # Print all the things pluginloader.printAllPlugins(p) # Cut the paper! Magic! p.cut() logging.debug("Ended the print and cut the paper") # If we're in test mode, print the results to the screen if debugcfg['TestMode'] == "1": print(str(p.output)) logging.debug("Printed dummy output to stdout") logging.debug("Debug logging enabled, printing to log:") # this would only appear if they did have debug logging enabled so yeah logging.debug(str(p.output)) # Call the "wahey I'm done!" handler handlers.closed()