From a7243bf2c1ac7013ca7b1465f96a7cb5d78ee41e Mon Sep 17 00:00:00 2001 From: Evan Richardson Date: Sat, 10 Feb 2024 05:00:19 +0000 Subject: [PATCH 1/4] refactor parse.py make code more readable and up to standards --- Docker/src/python/tHome/eagle/parse.py | 32 +++++++++++--------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/Docker/src/python/tHome/eagle/parse.py b/Docker/src/python/tHome/eagle/parse.py index 3a5c464..c62d6fe 100644 --- a/Docker/src/python/tHome/eagle/parse.py +++ b/Docker/src/python/tHome/eagle/parse.py @@ -1,26 +1,20 @@ -#=========================================================================== -# -# Parse XML messages into an object. -# -#=========================================================================== import defusedxml.ElementTree as ET from . import messages -#========================================================================== +def parse(xmlText): + """ + Parse XML messages into an object. -# -# <[Message]>... -# -def parse( xmlText ): - root = ET.fromstring( xmlText ) - assert( root.tag == "rainforest" ) + Args: + xmlText (str): The XML text to parse. - child = root[0] + Returns: + messages.Message: The parsed message object. + """ + root = ET.fromstring(xmlText) + assert root.tag == "rainforest" - msgClass = messages.tagMap.get( child.tag, None ) - if not msgClass: - return None + child = root[0] - return msgClass( child ) - -#========================================================================== + msgClass = messages.tagMap.get(child.tag, None) + return msgClass(child) if msgClass else None \ No newline at end of file -- 2.49.1 From 961692c938d555df1c88ea19806f12b58efd359a Mon Sep 17 00:00:00 2001 From: Evan Richardson Date: Sat, 10 Feb 2024 05:13:27 +0000 Subject: [PATCH 2/4] refactor get.py --- Docker/src/python/tHome/eagle/get.py | 146 ++++++++++----------------- 1 file changed, 52 insertions(+), 94 deletions(-) diff --git a/Docker/src/python/tHome/eagle/get.py b/Docker/src/python/tHome/eagle/get.py index 91ce6f3..c0e5682 100644 --- a/Docker/src/python/tHome/eagle/get.py +++ b/Docker/src/python/tHome/eagle/get.py @@ -1,110 +1,68 @@ from . import config from . import messages as msg -#from . import convert -#from .DeviceData import DeviceData -#from .DeviceInfo import DeviceInfo -#from .InstantDemand import InstantDemand -#from .Reading import Reading -#from .Total import Total import defusedxml.ElementTree as ET import socket -#========================================================================== def all(): - # Newlines are required - xmlCmd = "\nget_device_data\n" \ - "%s\n\n" % ( config.macAddress ) - xmlData = sendXml( xmlCmd ) + xmlCmd = "\nget_device_data\n" \ + "%s\n\n" % config.macAddress + xmlData = sendXml(xmlCmd) + xmlData = "%s" % xmlData + root = ET.fromstring(xmlData) + return msg.DeviceData(root) - # Add fake wrapper for parsing list of elements - xmlData = "%s" % xmlData - root = ET.fromstring( xmlData ) - - return DeviceData( root ) - -#========================================================================== def device(): - # Newlines are required - xmlCmd = "\nlist_devices\n\n" - xmlData = sendXml( xmlCmd ) - root = ET.fromstring( xmlData ) + xmlCmd = "\nlist_devices\n\n" + xmlData = sendXml(xmlCmd) + root = ET.fromstring(xmlData) + return msg.DeviceInfo(root) - return msg.DeviceInfo( root ) - -#========================================================================== def instant(): - # Newlines are required - xmlCmd = "\nget_instantaneous_demand\n" \ - "%s\n\n" % ( config.macAddress ) - xmlData = sendXml( xmlCmd ) - root = ET.fromstring( xmlData ) + xmlCmd = "\nget_instantaneous_demand\n" \ + "%s\n\n" % config.macAddress + xmlData = sendXml(xmlCmd) + root = ET.fromstring(xmlData) + return msg.InstantaneousDemand(root) - return msg.InstantaneousDemand( root ) +def history(start): + startHex = convert.fromTime(start) + xmlCmd = "\nget_history_data\n" \ + "%s\n%s\n" \ + "\n" % (config.macAddress, startHex) + xmlData = sendXml(xmlCmd) + root = ET.fromstring(xmlData) + return [msg.Total(child) for child in root] -#========================================================================== -def history( start ): - "start == datetime in utc" - startHex = convert.fromTime( start ) - - # Newlines are required - xmlCmd = "\nget_history_data\n" \ - "%s\n%s\n" \ - "\n" % ( config.macAddress, startHex ) - xmlData = sendXml( xmlCmd ) +def instantHistory(interval): + assert interval in ['hour', 'day', 'week'] + xmlCmd = "\nget_demand_values\n" \ + "%s\n\n" % config.macAddress + xmlData = sendXml(xmlCmd) + xmlData = "%s" % xmlData + root = ET.fromstring(xmlData) + return msg.Reading.xmlToList(root) - # Add fake wrapper for parsing list of elements - root = ET.fromstring( xmlData ) +def totalHistory(interval): + assert interval in ['day', 'week', 'month', 'year'] + xmlCmd = "\nget_summation_values\n" \ + "%s\n\n" % config.macAddress + xmlData = sendXml(xmlCmd) + xmlData = "%s" % xmlData + root = ET.fromstring(xmlData) + return msg.Reading.xmlToList(root) - return [ Total( child ) for child in root ] - -#========================================================================== -def instantHistory( interval ): - "interval = 'hour', 'day', 'week'" - assert( interval in [ 'hour', 'day', 'week' ] ) +def sendXml(xmlCmd): + sock = socket.create_connection((config.host, config.port)) + try: + sock.send(xmlCmd.encode()) - # Newlines are required - xmlCmd = "\nget_demand_values\n" \ - "%s\n\n" % ( config.macAddress ) - xmlData = sendXml( xmlCmd ) + buf = b"" + while True: + s = sock.recv(1024) + if not s: + break + buf += s + finally: + sock.close() - # Add fake wrapper for parsing list of elements - xmlData = "%s" % xmlData - root = ET.fromstring( xmlData ) - - return msg.Reading.xmlToList( root ) - -#========================================================================== -def totalHistory( interval ): - "interval = 'day', 'week', 'month', 'year'" - assert( interval in [ 'day', 'week', 'month', 'year' ] ) - - # Newlines are required - xmlCmd = "\nget_summation_values\n" \ - "%s\n\n" % ( config.macAddress ) - xmlData = sendXml( xmlCmd ) - - # Add fake wrapper for parsing list of elements - xmlData = "%s" % xmlData - root = ET.fromstring( xmlData ) - - return msg.Reading.xmlToList( root ) - -#========================================================================== -def sendXml( xmlCmd ): - sock = socket.create_connection( ( config.host, config.port ) ) - try: - sock.send( xmlCmd ) - - buf = "" - while True: - s = sock.recv( 1024 ) - if not s: - break - - buf += s - finally: - sock.close() - - return buf - -#========================================================================== + return buf.decode() \ No newline at end of file -- 2.49.1 From a35d0cbb58c80520c7c21d65995adf797458b59a Mon Sep 17 00:00:00 2001 From: Evan Richardson Date: Sat, 10 Feb 2024 05:29:27 +0000 Subject: [PATCH 3/4] Refactor config.py --- Docker/src/python/tHome/eagle/config.py | 59 ++++++++++++++----------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/Docker/src/python/tHome/eagle/config.py b/Docker/src/python/tHome/eagle/config.py index 375aec5..42645e2 100644 --- a/Docker/src/python/tHome/eagle/config.py +++ b/Docker/src/python/tHome/eagle/config.py @@ -1,36 +1,41 @@ -#=========================================================================== -# -# Config file -# -#=========================================================================== - -__doc__ = """Config file parsing. -""" +"""Config file parsing.""" from .. import util from ..util import config as C -#=========================================================================== - # Config file section name and defaults. configEntries = [ - # ( name, converter function, default value ) - C.Entry( "httpPort", int, 22042 ), - C.Entry( "mqttEnergy", str ), - C.Entry( "mqttPower", str ), - C.Entry( "logFile", util.path.expand ), - C.Entry( "logLevel", int, 20 ), # INFO - ] + # ( name, converter function, default value ) + C.Entry("httpPort", int, 22042), + C.Entry("mqttEnergy", str), + C.Entry("mqttPower", str), + C.Entry("logFile", util.path.expand), + C.Entry("logLevel", int, 20), # INFO +] -#=========================================================================== -def parse( configDir, configFile='eagle.py' ): - return C.readAndCheck( configDir, configFile, configEntries ) +def parse(configDir, configFile='eagle.py'): + """ + Parse the configuration file. -#=========================================================================== -def log( config, logFile=None ): - if not logFile: - logFile = config.logFile - - return util.log.get( "eagle", config.logLevel, logFile ) + Args: + configDir (str): The directory containing the configuration file. + configFile (str): The name of the configuration file. -#=========================================================================== + Returns: + dict: The parsed configuration. + """ + return C.readAndCheck(configDir, configFile, configEntries) + +def log(config, logFile=None): + """ + Get the logger configuration. + + Args: + config (dict): The configuration dictionary. + logFile (str): The log file path. + + Returns: + logger: The logger object. + """ + logFile = logFile or config['logFile'] + return util.log.get("eagle", config['logLevel'], logFile) \ No newline at end of file -- 2.49.1 From 3c54175ab82b811270d67bc21c749bb669027297 Mon Sep 17 00:00:00 2001 From: Evan Richardson Date: Sat, 10 Feb 2024 05:30:54 +0000 Subject: [PATCH 4/4] refactor config.py --- Docker/src/python/tHome/config.py | 114 ++++++++++++++++-------------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/Docker/src/python/tHome/config.py b/Docker/src/python/tHome/config.py index 3bc7b4e..fb49693 100644 --- a/Docker/src/python/tHome/config.py +++ b/Docker/src/python/tHome/config.py @@ -1,67 +1,75 @@ -#=========================================================================== -# -# Config parsing -# -#=========================================================================== +"""Config parsing.""" -__doc__ = """Config parsing. -""" +import os +import glob +import configparser from .util import Data -import ConfigParser -import glob -import os.path -#=========================================================================== -def parse( configDir ): - # Parse the files. Default xform makes all keys lower case so set - # it to str to stop that behavior. - p = ConfigParser.ConfigParser() - p.optionxform = str +def parse(configDir): + """ + Parse configuration files from the specified directory. - files = glob.glob( os.path.join( configDir, "*.conf" ) ) - for f in files: - p.read( f ) + Args: + configDir (str): The directory containing the configuration files. - cfg = Data( _config = p ) - for s in p.sections(): - d = Data() - for o in p.options( s ): - setattr( d, o, p.get( s, o ) ) + Returns: + Data: Parsed configuration data. + """ + config = configparser.ConfigParser() + config.optionxform = str # Prevent keys from being transformed to lowercase - setattr( cfg, s, d ) + config_files = glob.glob(os.path.join(configDir, "*.conf")) + for file in config_files: + config.read(file) - return cfg + parsed_config = Data(_config=config) + for section in config.sections(): + section_data = Data() + for option in config.options(section): + setattr(section_data, option, config.get(section, option)) + setattr(parsed_config, section, section_data) -#=========================================================================== -def update( data, secDef ): - for section, fields in secDef.iteritems(): - if not hasattr( data, section ): - setattr( data, section, Data() ) + return parsed_config - secData = data[section] - for name, convertFunc, defaultValue in fields: - if hasattr( secData, name ): - secData[name] = convertFunc( secData[name] ) +def update(data, secDef): + """ + Update configuration data with default values and type conversion. - else: - secData[name] = defaultValue + Args: + data (Data): Configuration data to be updated. + secDef (dict): Dictionary defining sections and their default values and conversion functions. + """ + for section, fields in secDef.items(): + if not hasattr(data, section): + setattr(data, section, Data()) -#=========================================================================== -def toPath( value ): - """TODO: doc - """ - if value is None: - return None - - value = str( value ) - - if "$" in value: - value = os.path.expandvars( value ) - - if "~" in value: - value = os.path.expanduser( value ) + sec_data = data[section] + for name, convert_func, default_value in fields: + if hasattr(sec_data, name): + sec_data[name] = convert_func(sec_data[name]) + else: + sec_data[name] = default_value - return value +def toPath(value): + """ + Convert a path value to its absolute path. -#=========================================================================== + Args: + value (str): The path value. + + Returns: + str: The absolute path. + """ + if value is None: + return None + + value = str(value) + + if "$" in value: + value = os.path.expandvars(value) + + if "~" in value: + value = os.path.expanduser(value) + + return value \ No newline at end of file -- 2.49.1