3131import errno
3232import logging
3333import serial
34+ import io
35+
3436try :
3537 import serial_asyncio
3638 ASYNC_IMPORTED = True
@@ -90,6 +92,7 @@ class SmartPluginWebIf():
9092if __name__ == '__main__' :
9193 logger = logging .getLogger (__name__ )
9294 logger .debug (f"init standalone { __name__ } " )
95+
9396else :
9497 logger = logging .getLogger (__name__ )
9598 logger .debug (f"init plugin component { __name__ } " )
@@ -152,6 +155,53 @@ def format_time(timedelta):
152155 elif timedelta > 0.000000001 :
153156 return f"{ timedelta * 1000000000.0 :.2f} ns"
154157
158+
159+ #
160+ # string logger
161+ #
162+
163+
164+ class StringLogger ():
165+
166+ def __init__ (self ):
167+ ### Create the logger
168+ self .logger = logging .getLogger ('sml_string_logger' )
169+ self .logger .setLevel (logging .DEBUG )
170+
171+ ### Setup the console handler with a StringIO object
172+ self .log_capture_string = io .StringIO ()
173+ self .ch = logging .StreamHandler (self .log_capture_string )
174+ self .ch .setLevel (logging .DEBUG )
175+
176+ ### Optionally add a formatter
177+ formatter = logging .Formatter ('%(levelname)s: %(message)s' )
178+ self .ch .setFormatter (formatter )
179+
180+ ### Add the console handler to the logger
181+ self .logger .addHandler (self .ch )
182+
183+ def __call__ (self ):
184+ return self .log_capture_string .getvalue ()
185+
186+ def close (self ):
187+ self .log_capture_string .close ()
188+
189+ def debug (self , * args , ** kwargs ):
190+ self .logger .debug (* args , ** kwargs )
191+
192+ def info (self , * args , ** kwargs ):
193+ self .logger .info (* args , ** kwargs )
194+
195+ def warning (self , * args , ** kwargs ):
196+ self .logger .warning (* args , ** kwargs )
197+
198+ def error (self , * args , ** kwargs ):
199+ self .logger .error (* args , ** kwargs )
200+
201+ def critical (self , * args , ** kwargs ):
202+ self .logger .critical (* args , ** kwargs )
203+
204+
155205#
156206# asyncio reader
157207#
@@ -307,7 +357,7 @@ def __init__(self, logger, config: dict):
307357 self .target = '(not set)'
308358 self .buffersize = config .get ('sml' , {'buffersize' : 1024 }).get ('buffersize' , 1024 )
309359
310- logger .debug (f"config='{ config } '" )
360+ self . logger .debug (f"config='{ config } '" )
311361
312362 def __call__ (self ) -> bytes :
313363
@@ -316,7 +366,7 @@ def __call__(self) -> bytes:
316366 #
317367 locked = self .lock .acquire (blocking = False )
318368 if not locked :
319- logger .error ('could not get lock for serial/network access. Is another scheduled/manual action still active?' )
369+ self . logger .error ('could not get lock for serial/network access. Is another scheduled/manual action still active?' )
320370 return b''
321371
322372 try : # lock release
@@ -325,7 +375,7 @@ def __call__(self) -> bytes:
325375 if not self .sock :
326376 # error already logged, just go
327377 return b''
328- logger .debug (f"time to open { self .target } : { format_time (time .time () - runtime )} " )
378+ self . logger .debug (f"time to open { self .target } : { format_time (time .time () - runtime )} " )
329379
330380 #
331381 # read data from device
@@ -334,13 +384,13 @@ def __call__(self) -> bytes:
334384 try :
335385 response = self .read ()
336386 if len (response ) == 0 :
337- logger .error ('reading data from device returned 0 bytes!' )
387+ self . logger .info ('reading data from device returned 0 bytes!' )
338388 return b''
339389 else :
340- logger .debug (f'read { len (response )} bytes' )
390+ self . logger .debug (f'read { len (response )} bytes' )
341391
342392 except Exception as e :
343- logger .error (f'reading data from { self .target } failed with error: { e } ' )
393+ self . logger .error (f'reading data from { self .target } failed with error: { e } ' )
344394
345395 except Exception :
346396 # passthrough, this is only for releasing the lock
@@ -428,41 +478,41 @@ def get_sock(self):
428478 timeout = self .timeout
429479 )
430480 if not self .serial_port == self .sock .name :
431- logger .debug (f"Asked for { self .serial_port } as serial port, but really using now { self .sock .name } " )
481+ self . logger .debug (f"Asked for { self .serial_port } as serial port, but really using now { self .sock .name } " )
432482 self .target = f'serial://{ self .sock .name } '
433483
434484 except FileNotFoundError :
435- logger .error (f"Serial port '{ self .serial_port } ' does not exist, please check your port" )
485+ self . logger .error (f"Serial port '{ self .serial_port } ' does not exist, please check your port" )
436486 return None , ''
437487 except serial .SerialException :
438488 if self .sock is None :
439489 if count < 3 :
440490 # count += 1
441- logger .error (f"Serial port '{ self .serial_port } ' could not be opened, retrying { count } /3..." )
491+ self . logger .error (f"Serial port '{ self .serial_port } ' could not be opened, retrying { count } /3..." )
442492 time .sleep (3 )
443493 continue
444494 else :
445- logger .error (f"Serial port '{ self .serial_port } ' could not be opened" )
495+ self . logger .error (f"Serial port '{ self .serial_port } ' could not be opened" )
446496 else :
447- logger .error (f"Serial port '{ self .serial_port } ' could be opened but somehow not accessed" )
497+ self . logger .error (f"Serial port '{ self .serial_port } ' could be opened but somehow not accessed" )
448498 return None , ''
449499 except OSError :
450- logger .error (f"Serial port '{ self .serial_port } ' does not exist, please check the spelling" )
500+ self . logger .error (f"Serial port '{ self .serial_port } ' does not exist, please check the spelling" )
451501 return None , ''
452502 except Exception as e :
453- logger .error (f"unforeseen error occurred: '{ e } '" )
503+ self . logger .error (f"unforeseen error occurred: '{ e } '" )
454504 return None , ''
455505
456506 if self .sock is None :
457507 if count == 3 :
458- logger .error ("retries unsuccessful, serial port could not be opened, giving up." )
508+ self . logger .error ("retries unsuccessful, serial port could not be opened, giving up." )
459509 else :
460510 # this should not happen...
461- logger .error ("retries unsuccessful or unforeseen error occurred, serial object was not initialized." )
511+ self . logger .error ("retries unsuccessful or unforeseen error occurred, serial object was not initialized." )
462512 return None , ''
463513
464514 if not self .sock .is_open :
465- logger .error (f"serial port '{ self .serial_port } ' could not be opened with given parameters, maybe wrong baudrate?" )
515+ self . logger .error (f"serial port '{ self .serial_port } ' could not be opened with given parameters, maybe wrong baudrate?" )
466516 return None , ''
467517
468518 elif self .host :
@@ -476,7 +526,7 @@ def get_sock(self):
476526 self .target = f'tcp://{ self .host } :{ self .port } '
477527
478528 else :
479- logger .error ('neither serialport nor host/port was given, no action possible.' )
529+ self . logger .error ('neither serialport nor host/port was given, no action possible.' )
480530 return None , ''
481531
482532
@@ -592,7 +642,7 @@ def parse(self, data: bytes) -> dict:
592642 return self .fp ()
593643
594644
595- def query (config ) -> dict :
645+ def query (config , logger = logger ) -> dict :
596646 """
597647 This function will
598648 1. open a serial communication line to the smartmeter
@@ -653,7 +703,11 @@ def discover(config: dict) -> bool:
653703 # reduced baud rates or changed parameters, but there would need to be
654704 # the need for this.
655705 # For now, let's see how well this works...
656- return bool (query (config ))
706+ str_log = StringLogger ()
707+ result = bool (query (config , str_log ))
708+ if not result :
709+ config ['discover_log' ] = str_log ()
710+ return result
657711
658712
659713if __name__ == '__main__' :
@@ -718,8 +772,8 @@ def discover(config: dict) -> bool:
718772 logger .info ("This is Smartmeter Plugin, SML module, running in standalone mode" )
719773 logger .info ("==================================================================" )
720774
721- result = query (config )
722-
775+ result = discover (config )
776+
723777 if not result :
724778 logger .info (f"No results from query, maybe a problem with the serial port '{ config ['serial_port' ]} ' given." )
725779 elif len (result ) > 1 :
0 commit comments