def os_open_logfile( self,  ):
     """
     py_log file is opened in the configured system editor for the user to view and edit
     may be used as/by callback from gui button.  Can be called form gt
     """
     # a_filename = self.parameters.py_path + os.path.sep + self.parameters.pylogging_fn
     AppGlobal.os_open_txt_file( self.parameters.pylogging_fn )
    def config_logger( self, ):
        """
        configure the logger in usual way using the current parameters
        ?? move to app global or in that direction
        args: zip
        ret:  the logger and side effects
              including adding logger to AppGlobal
        """
        logger = logging.getLogger( self.logger_id  )

        logger.handlers = []
        logger.setLevel( self.parameters.logging_level )     # DEBUG , INFO    WARNING    ERROR    CRITICAL

        # create the logging file handler.....
        fh = logging.FileHandler(   self.parameters.pylogging_fn )
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        fh.setFormatter( formatter )
        logger.addHandler( fh )

        msg   = "Done config_logger"
        #rint( msg )
        logger.debug( msg ) #  .debug   .info    .warn    .error
        AppGlobal.set_logger( logger )

        return logger
Beispiel #3
0
    def write_header(self, ):
        """
        what is says
        Args: Return: state change, output
        Raises: none planned, file open could fail
        """
        msg   = "write header...."
        AppGlobal.print_debug( msg )
        self.open_output_file()

        #self.fileout                = open( self.file_name, "w", encoding = "utf8", errors = 'replace' )

        columns_info  = self.builder.columns_info   # believe it is also a dict like a data dict
        columns_out   = self.builder.columns_out
        line_parts    = []
        for  ix, i_col in enumerate( columns_out ):

            i_col_info   = columns_info[i_col]
            fmt          = i_col_info["text_format"]
            col_text     = i_col_info["column_head"]

            line_parts.append( fmt.format( x =  col_text  ) )

        line    = "\t".join( line_parts )

        #rint( line, flush = True )

        self.fileout.write( line   + "\n" )
    def start_graph_live(self, a_ax):
        """
        main thread
        """
        #        print( f"start_graph_live in adapter {self.name} at {self.tcpip}" )
        #        msg    =  f"? ? start_graph_live for device: {self.name} "
        #        AppGlobal.what_thread( threading.get_ident(), msg, 20  )
        msg = "SmartPlugAdapter.start_graph_live  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=True)

        if not self.monitor:
            return

        if self.gd_time_adj is None:
            self.gd_time_adj = []
            self.gd_power = []

        #self.reset_graphing_data()   # why bother consider keeping data !!
        #if self.live_graph_lines is None:  # or use self.live_graph_ready
        if self.live_graph_ready == False:  # or use self.live_graph_ready
            self.live_graph_lines, = a_ax.plot(
                [], [], 'o', label=self.name
            )  # note unpack comma -- seem to be a list of 1 element
            #            msg    =  f"self.live_graph_lines created for device: {self.name} {type(self.live_graph_lines)}  {self.live_graph_lines}"
            #            AppGlobal.logger.info( msg )
            #            print( msg )

            #           got  >>>> why a list   self.live_graph_lines: <class 'list'>  [<matplotlib.lines.Line2D object at 0x00000274E0287AC8>]
            self.linestyle, self.colorstyle, self.markerstyle, self.widthstyle = AppGlobal.graph_live.line_style.get_next_style(
            )
            self.live_graph_ready = True
    def check_timer_done(self, ):
        """
        for now do not assume timer is running
        call from ht polling action
        """
        msg = "SmartPlugAdapter.check_timer_done  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=False)

        if not (self.timer_on):
            return

        now = time.time()
        sec_left = (self.timer_start +
                    self.time_sec) - now  # could be timer_end stored
        #        print( f"check_timer_done sec_left {sec_left} for plug: {self.name} " )   # debug
        if sec_left <= 0:
            self.off()
            self.display_msg("Timer Stop")
        else:
            self.timer_sec_left = sec_left
            a_string = self.sec_to_string(sec_left)
            # just update display
            self.display_msg(f"Timer  {a_string}")
    def update_graph_live(self, ):
        """
        call from main thread

        from graph_live but could we use our own polling  ??
        return True or False, depending on weather we were already set up

        """
        msg = "SmartPlugAdapter.update_graph_live  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=True)

        #        msg    =  f"Adapter.update_graph_live device: {self.name}  data: {self.graphing_new_data} ready: {self.live_graph_ready}"
        #        AppGlobal.logger.info( msg )

        if (not self.graphing_new_data) or (not self.live_graph_ready):
            return False

        self.live_graph_lines.set_xdata(self.gd_time_adj)
        self.live_graph_lines.set_ydata(self.gd_power)

        # not sure these need to be done on every cycle but works
        self.live_graph_lines.set_linestyle(self.linestyle)
        self.live_graph_lines.set_color(self.colorstyle)
        self.live_graph_lines.set_marker(self.markerstyle)
        self.live_graph_lines.set_linewidth(self.widthstyle)
        self.live_graph_lines.set_label(
            self.name
        )  # not currently working lacks update or.... setting legend later seemed to do it once may be enough
        self.graphing_new_data = False

        return True
Beispiel #7
0
    def __init__(self, ):

        AppGlobal.parameters = self
        self.default_mode()
        self.running_on_tweaks()
        self.choose_mode()
        AppGlobal.parameter_tweaks()  # including restart
    def os_open_helpfile( self,  ):
        """
        callback from gui button

        """
        help_file            = self.parameters.help_file
        AppGlobal.os_open_help_file( help_file )
    def check_recording(self, ):
        """
        call helper thread, best or ok in main -- used by smart_plug not smart_plug_graphing
        if recording then record the data in db
        for now do not assume timer is running
        call from ht polling action
        """
        msg = "SmartPlugAdapter.check_recording  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=False)

        if not (self.recording or self.monitor):
            return

#        msg    =  f"?ht? adapter check_recording "
#        AppGlobal.what_thread( threading.get_ident(), msg, 20  )

        now = time.time()
        if now < self.next_record_time:  # or monitor time
            return

        self.last_record_time = now
        self.next_record_time = now + self.delta_t
        try:
            plug = pyHS100.SmartPlug(self.tcpip)
            plug_data = plug.get_emeter_realtime()

            # ?? could add plug state - on off,.....

            # id          = f"{self.name}{self.last_record_time}r"
            # print   ( f"record data {id}:{plug_data})" )
            rnd = "%.2f" % plug_data["power"]
            msg = f'{rnd} watts'  #!! bit of rounding would be nice here
            self.gui_tk_label_2.config(text=msg)

        except pyHS100.smartdevice.SmartDeviceException as exception:  # look up correct exception
            self.timer_on = False
            #self.record_off()  to much messaging and will throw own error
            msg = f"failed to communicate with plug - record off for: {self.name} {self.tcpip}"

            AppGlobal.gui.print_info_string(msg)
            return

        if AppGlobal.graph_live_flag and self.monitor and self.live_graph_ready:
            #            print( "check_recording" )
            self.graphing_new_data = True
            #!! normalized and unnormalize data
            self.gd_time.append(self.last_record_time)
            self.gd_time_adj.append(
                AppGlobal.graph_live.rescale_time_function(
                    self.last_record_time))  #?? make local ref??
            self.gd_power.append(plug_data["power"])

        if self.recording:
            db_data = ((self.name, self.last_record_time, "r", "?",
                        plug_data["voltage"], plug_data["current"],
                        plug_data["power"], plug_data["total"]), )
            self.insert_measurements(db_data)
 def os_open_helpfile( self,  ):
     """
     help "file" is opened in the os configured application for the file type:
     file set in parameters may be txt, pdf, or a url
     method may be used as callback from gui button
     """
     help_file            = self.parameters.help_file
     AppGlobal.os_open_help_file( help_file )
 def os_open_parmfile( self,  ):
     """
     parameter file is opened in the configured system editor for the user to view and edit
     may be used as callback from gui button
     path addition here because parameters.py is not itself a parameter
     """
     a_filename = self.parameters.py_path + os.path.sep + "parameters.py"
     AppGlobal.os_open_txt_file( a_filename )
 def polling_mt(self, ):
     """
     polling for the main thread ( gui thread )
     """
     msg = "SmartPlugAdapter.polling_mt  "
     AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                   msg=msg,
                                   main=True)
Beispiel #13
0
 def polling_ht(self, ):
     """
     polling for the helper thread
     not sure this is ever needed
     """
     msg = "GraphLive.polling_ht  "
     AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                   msg=msg,
                                   main=False)
    def polling(self):
        """
        started from gui thread as beginning of new thread
        this is an infinite loop monitoring the queue
        actions based on queue_to_helper and run_event
        application purpose is the device polling where we monitor/record the devices

        """
        #self.logger.debug(  "HealperThread.polling()  entered " )
        msg = "smart plug Helper.polling()"
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=False)

        while True:

            try:
                #                if self.last_time  + 10 < time.time():
                #                    self.last_time = time.time()
                #                    self.print_info_string(   "Time" + str( self.last_time  ) ) # + a_port )

                (action, function, function_args) = self.rec_from_queue()
                if action != "":
                    self.logger.debug("smart_plug_helper.polling() queue: " +
                                      action + " " + str(function) + " " +
                                      str(function_args))  # ?? comment out
                if action == "call":
                    #print( "ht making call" )
                    sys.stdout.flush()
                    self.controller.helper_task_active = True
                    function(*function_args)
                    self.logger.debug(
                        "smart_plug_helper.polling() return running helper loop "
                    )  # ?? comment out
                    #self.print_helper_label( "return running helper loop " )
                    self.controller.helper_task_active = False  # do we maintain this, or move to helper -- looks like not used, app global better location
                if action == "stop":
                    # seems to be working fine
                    msg = "helper got stop in the queue -- end thread with return "
                    # AppGlobal.what_thread( threading.get_ident(), msg, 50  )
                    self.controller.helper_task_active = False
                    return  # this will kill the thread --- think it is the return which should end the loop ?  or do we need a break -- we seem to keep looping
                    msg = "helper got stop in the queue -- ran thru return "
                    # AppGlobal.what_thread( threading.get_ident(), msg, 50  )

                self.device_polling()

            # must catch all exceptions if we do not want polling to stop ... but maybe we do
            except Exception as he:
                #self.logger.info( "schedule_me_helper.HelperThread threw exception from " + he.msg )        # info debug... !! also info to msg area
                msg = f"smart_plug_helper.HelperThread threw exception: {he}"
                print(f"see log: {msg}")
                self.logger.error(msg, exc_info=True)  # info debug...
            time.sleep(
                self.ht_delta_t)  # ok here since it is the main pooling loop
            #self.logger.debug(  "HealperThread.polling() done with sleep " )
        return
    def polling_ht(self, ):
        """
        polling for the helper thread
        """
        msg = "SmartPlugAdapter.polling_ht  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=False)

        self.check_timer_done()
        self.check_recording()
    def reset_graphing_data(self, ):
        """
        fine in helper thread ok in both
        """
        AppGlobal.log_if_wrong_thread(
            threading.get_ident(),
            msg="now in smart_plug_adapter.reset_graphing_data",
            main=True)
        #        print( "reset_graphing_data" )

        self.gd_time = []
        self.gd_time_adj = []
        self.gd_power = []
        #self.gd_power_adj        = []
        self.gd_energy = []
        self.gd_energy_adj = []

        self.graphing_new_data = False
    def run(self):
        """
        called when thread is started from gt its call should be considered as from ht
        """
        self.parameters.init_from_helper()

        self.scheduled_event_list = AppGlobal.scheduled_event_list
        id = threading.get_ident()

        msg = f"helper run  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=False)

        self.logger.debug(msg)

        #        print( "==============================================" )
        self.polling()  # may mach name in gui thread
    def graph_power_from_db(self, line_style=None, ax=None):
        """
        None default because required hence exception
        """
        msg = "SmartPlugAdapter.graph_power_from_db  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=True)

        line_style.get_next_style()
        time_data = self.gd_time_adj
        if ((time_data is None) or len(time_data) == 0):
            print(f"no data for {self.name}")
            return
        ax.plot(time_data,
                self.gd_power,
                linestyle=line_style.linestyle,
                marker=line_style.markerstyle,
                color=line_style.colorstyle,
                label=self.name)  # label= "Power (Watts)"
Beispiel #19
0
    def write_header(self,  ):
        """
        what is says
        Args: Return: state change, output
        Raises: none planned
        """
        lines                       = []

        # will use in other methods
        self.col_names              =  [ i_format[0] for i_format in self.table_info.format_list ]
        msg   = f"write_header()  self.col_names  {self.col_names}"
        AppGlobal.print_debug( msg )

        i_line    = f"#---------- SelectLogWriter output from {AppGlobal.controller.app_name} {AppGlobal.controller.app_version}"
        lines.append( i_line  )

        lines.append( f" self.table_info.sql = {self.table_info.sql}"  )

        i_line    = f"use_table:{self.table_info.table_name}"
        lines.append( i_line  )

        msg       = "\n".join( lines )
        AppGlobal.logger.log( AppGlobal.force_log_level, msg )
    def device_polling(self):
        """
        helper thread
        poll the devices - now just smartplugs, but fairly easily adapted to other polling
        tasks.
        so simple may put back in polling
        also extended for graphing
        """
        #        print( "device_polling" )
        msg = "HealperThread.device_polling  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=False)

        # AppGlobal.what_thread( threading.get_ident(), msg, 50  )
        now = time.time()
        for i_adapter in AppGlobal.smartplug_adapter_list:
            i_adapter.polling_ht()

        # now the live graph
#        print( f"AppGlobal.graph_live {AppGlobal.graph_live_flag}")
        if not AppGlobal.graph_live_flag:
            return
#        print( "smart_plug_helper device_polling graph_live" )
# think this test now in graph_live
        update_graph_bool = False
        for i_adapter in AppGlobal.smartplug_adapter_list:
            if i_adapter.graphing_new_data:
                update_graph_bool = True
                break
#        print( f"update_graph_bool {update_graph_bool}" )
        if not update_graph_bool:
            #            print( f"update_graph_bool {update_graph_bool}" )
            return
        AppGlobal.logger.error(
            "SmartPlugHelper.device_polling off to graph live")
        AppGlobal.graph_live.polling_ht()
Beispiel #21
0
    def polling_mt(self, ):
        """
        polling for the main thread ( gui thread )
        """
        msg = "GraphLive.polling_mt  "
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=True)
        #        AppGlobal.logger.error( msg )

        # collect flags from the individual devices
        need_redraw_flag = False
        for i_adapter in AppGlobal.smartplug_adapter_list:
            if i_adapter.update_graph_live():
                need_redraw_flag = True

        #AppGlobal.logger.error( "GraphLive.polling_mt test next" )
        if need_redraw_flag:
            AppGlobal.logger.error("GraphLive.polling_mt need redraw True")
            self.ax.relim()
            self.ax.autoscale_view()
            #We need to draw *and* flush
            AppGlobal.logger.error("GraphLive.polling_mt next draw flush")
            self.figure.canvas.draw()
            self.figure.canvas.flush_events()
            plt.legend(loc=2)  # help with update  -- seems to work
            #            AppGlobal.logger.error( "GraphLive.polling_mt polling show next" )
            plt.show(block=False)
            #            AppGlobal.logger.error( "GraphLive.polling_mtpolling post next" )

            # ------------ for tracking memory use
            self.debug_count += 1
            if self.debug_count > 50:
                AppGlobal.show_process_memory(
                    "GraphLive.polling_mt polling time to log", log_level=20)
                self.debug_count = 0
Beispiel #22
0
def add_data_path(  file_name ):
    """
    complete short path names by adding data_dir part  ... remove instance function of same name
    from another app may not be used here
    """
    ret  = ""  # why, will fail unless changed, just not well though out
    try:     # in case of config errors log
        ret = os.path.join( AppGlobal.parameters.data_dir, file_name )
    except Exception as exception:   # should not really catch all
        AppGlobal.print_debug(       exception.msg )
        AppGlobal.gui_write_error(   exception.msg )
        #self.os_open_text_file( self.parameters.pylogging_fn )
        msg     = "Exception building file name {AppGlobal.parameters.data_dir} and {file_name} see py_log"
        #AppGlobal.print_debug(       msg )
        AppGlobal.gui_write_error(   msg )

    return ret
 def os_open_parmxfile( self,  ):
     """
     used as callback from gui button
     """
     a_filename = self.starting_dir  + os.path.sep + self.parmeters_x + ".py"
     AppGlobal.os_open_txt_file( a_filename )
 def os_open_parmfile( self,  ):
     """
     callback from gui button
     """
     a_filename  = self.starting_dir  + os.path.sep + "parameters.py"
     AppGlobal.os_open_txt_file( a_filename )
 def os_open_logfile( self,  ):
     """
     callback from gui button
     """
     AppGlobal.os_open_txt_file( self.parameters.pylogging_fn )
Beispiel #26
0
 def os_open_parmfile( self,  ):
     """
     used as callback from gui button -- rename cb ??
     """
     a_filename = self.starting_dir  + os.path.sep + "parameters.py"   # assuming a txt file
     AppGlobal.os_open_txt_file( a_filename )
Beispiel #27
0
def make_file_writer(  builder,  ):
    """
    Purpose:
        what it says -- make a file_writer of the correct format output_format
    Args: builder -- among other things uses output_format to determine type of
          writer and file name

    Returns: a fileWriter, mutates builder
    Issues:  could use a thoughtful refactoring  --- consider dict version
    """
    output_format   = builder.output_format

    # msg     = f"make_file_writer: for format {output_format}"
    #rint( msg )

    # some are left over and need to be changed

    # AppGlobal.print_debug( msg )
    # AppGlobal.gui_write_progress( msg )

    if  output_format == "py_log":
        # fileout_name           = add_data_path( AppGlobal.parameters.pylogging_fn  )
        builder.output_name    = "not used"
        select_writer          = SelectLogWriter(  builder )

        # our use os.path.join( rpath, "temp", "zxqq.txt" ) ))

    elif output_format == "csv":
        # fileout_name      = add_data_path( "output_select.csv"  )
        builder.output_name    = AppGlobal.parameters.output_path  + f"{os.sep}output_select.csv"

        #AppGlobal.parameters.output_path
        select_writer          = SelectCSVWriter( builder )

    elif output_format == "txt":
        builder.output_name    = AppGlobal.parameters.output_path  + f"{os.sep}output_select.txt"
        select_writer          = SelectTxtWriter( builder )

    elif output_format == "yaml":  # still need to do

        builder.output_name    = AppGlobal.parameters.output_path  + f"{os.sep}output_select.yaml"
        select_writer          = SelectYamlWriter( builder )

    elif output_format == "html":
        #fileout_name      = add_data_path( "output_select.html"  )
        builder.output_name    = AppGlobal.parameters.output_path  +  f"{os.sep}output_select.html"
        select_writer          = SelectHTMLWriter( builder )

    elif output_format == "msg":
        #fileout_name      = add_data_path( "output_select.html"  )
        builder.output_name    = AppGlobal.parameters.output_path  +  f"{os.sep}output_select.msg"
        # !! why 2 was one for test resolve and fix select_writer          = SelectMessageWriter( builder )
        print( "select_writer          = SelectMsgWriter( builder )" )
        select_writer          = SelectMsgWriter( builder )

    # elif output_format == "zap":
    #     #fileout_name      = add_data_path( "output_select.html"  )
    #     builder.output_name    = AppGlobal.parameters.output_path  +  f"{os.sep}output_select.msg"
    #     select_writer          = SelectZapWriter( builder )

    else:
        msg   =  f"invalid output_format = {output_format}"
        AppGlobal.gui.display_info_string( msg )
        AppGlobal.print_debug( msg )
        raise  Exception( msg )

#    msg     = f"make_file_writer: return {( select_writer, fileout_name )}"
#    AppGlobal.print_debug( msg )

    return ( select_writer )
Beispiel #28
0
 def os_open_logfile(self, ):
     """
     used as/by callback from gui button.  Can be called form gt
     """
     AppGlobal.os_open_txt_file(self.parameters.pylogging_fn)
 def cb_about( self,  ):
     """
     call back for gui button
     """
     AppGlobal.about()
Beispiel #30
0
    def start_graph_live(self, ):
        """
        call from main thread
        start off a session of live graphing -- need to run in main thread
        moved from the plug
        no validation if start is ok
        no inactivation of controls .... even this should perhaps come from a checkbox


        what do we want to do:
            graph only plugs that are being monitored or recorded
            accumulate the data in a list ( list of tuples might be better than two lists or even a numpy type thing )

            ??? what is monitor, record is turned off ( keep data and graph line so far stop adding to data )
            ??? what if new adapter is turned on ( was it on earlire ?? )
            ??? export csv    export all cached data to a csv, if none just a message
        """
        msg = "GraphLive.start_graph_live"
        #        AppGlobal.what_thread( threading.get_ident(), msg, 20  )
        AppGlobal.log_if_wrong_thread(threading.get_ident(),
                                      msg=msg,
                                      main=True)

        self.rescale_time_function = lambda x: x  # default identity function
        self.set_rescale_time_function()

        #Set up plot
        self.figure, self.ax = plt.subplots(
            figsize=(self.parameters.graph_x_size,
                     self.parameters.graph_y_size))

        self.figure.canvas.mpl_connect('close_event', self.end_graph_live_evt)

        plt.legend(
            loc=2
        )  # for label to show up ?  # made small sq show up no content, too early ?
        # lets try to build the line in each adapter
        self.lines, = self.ax.plot(
            [], [], 'o', color="red", linewidth=2.5, linestyle="-"
        )  # the line is the thing to update ... can we add later, lets assume we can
        #Auto scale on unknown axis and known limits on the other
        self.ax.set_autoscaley_on(True)
        #self.ax.set_xlim(self.min_x, self.max_x)
        #Other stuff
        self.ax.grid(linestyle='-', linewidth='0.5', color='red')
        self.ax.tick_params(axis='y', labelcolor='red')

        self.ax.set_title(f"Power measured by SmartPlugs")
        self.ax.set_xlabel(
            f"Time in {self.graph_time_units} from {self.graph_time_zero}"
        )  # these have been set multiple times
        self.ax.set_ylabel(
            f"Power in watts")  # these have been set multiple times

        for i_adapter in AppGlobal.smartplug_adapter_list:
            i_adapter.start_graph_live(
                self.ax
            )  # this is a setup it does not start graphing by itself
        plt.show(block=False)
        AppGlobal.logger.error("GraphLive.start_graph_live complete")
        AppGlobal.graph_live_flag = True  # this will kick off in polling set last