def handle_on_off(controller, outlet, pin, targetstate): currentOutletState = GPIO.input(pin) # log any change if controller.AppPrefs.outletDict[outlet].enable_log == "True": if currentOutletState != targetstate: #defs_common.logtoconsole("I need to log this! " + outlet, fg="YELLOW", bg="BLUE", style="BRIGHT") # lets record 1 or 0 in the log if currentOutletState == PIN_ON: val = 1 else: val = 0 defs_common.logprobedata(outlet + "_", val) # lets record 1 or 0 in the log if targetstate == PIN_ON: val = 1 else: val = 0 defs_common.logprobedata(outlet + "_", val) defs_common.logtoconsole( "***Logged*** Outlet Change " + "[" + outlet + "] " + str(controller.AppPrefs.outletDict[outlet].outletname) + " = " + str(val), fg="CYAN", style="BRIGHT") controller.logger.info( "***Logged*** Outlet Change " + "[" + outlet + "] " + str(controller.AppPrefs.outletDict[outlet].outletname) + " = " + str(val)) if targetstate == PIN_ON: GPIO.output(pin, False) elif targetstate == PIN_OFF: GPIO.output(pin, True)
def initialize_logger(self, output_dir, output_file, loglevel_console, loglevel_logfile): self.logger = logging.getLogger() self.logger.setLevel(logging.DEBUG) # check if log dir exists, if not create it if not os.path.exists(output_dir): defs_common.logtoconsole("Logfile directory not found") os.mkdir(output_dir) defs_common.logtoconsole("Logfile directory created: " + os.getcwd() + "/" + str(output_dir)) # create console handler and set level to info self.handler = logging.StreamHandler() self.handler.setLevel(loglevel_console) self.formatter = logging.Formatter('%(asctime)s %(message)s') self.handler.setFormatter(self.formatter) self.logger.addHandler(self.handler) # create log file handler and set level to info self.handler = logging.handlers.RotatingFileHandler(os.path.join( output_dir, output_file), maxBytes=2000000, backupCount=5) self.handler.setLevel(loglevel_logfile) self.formatter = logging.Formatter( '%(asctime)s <%(levelname)s> [%(threadName)s:%(module)s] %(message)s' ) self.handler.setFormatter(self.formatter) self.logger.addHandler(self.handler)
def __init__(self, master): defs_common.logtoconsole("Initializing ProbeWidget...", fg="YELLOW", bg="MAGENTA", style="BRIGHT") #initialize the messaging queues self.connection = pika.BlockingConnection( pika.ConnectionParameters(host='localhost')) self.channel = self.connection.channel() result = self.channel.queue_declare(exclusive=True) self.callback_queue = result.method.queue self.channel.basic_consume(self.rpc_response, no_ack=True, queue=self.callback_queue) self.probeid = StringVar() self.name = StringVar() self.probetype = StringVar() self.probeval = StringVar() self.probeval.set("----") # give a default value self.figprobe = Figure(figsize=(1, 1), dpi=100) self.aniprobe = self.figprobe.add_subplot(111, axisbg="gainsboro") self.probeframe = LabelFrame(master, text=self.name, relief=RAISED) self.probeframe.pack(fill=X, side=TOP) self.frame_probeval = LabelFrame(self.probeframe, relief=FLAT) self.frame_probeval.pack(side=LEFT) self.lbl_probeval = Label(self.frame_probeval, text="00.0", textvariable=self.probeval, relief=FLAT, font=("Helvetica", 44), padx=10) self.lbl_probeval.pack(side=TOP) self.lbl_probeSN = Label(self.frame_probeval, textvariable=self.probeid, padx=10) # self.lbl_probeSN.pack(side=TOP) # dont display this, but its used to match the probe values on update #set up mini plot #some definitions for the plots LARGE_FONT = ("Verdana", 12) style.use("ggplot") self.figprobe = Figure(figsize=(1, 1), dpi=100) self.figprobe.set_facecolor("gainsboro") self.aniprobe = self.figprobe.add_subplot(111, axisbg="gainsboro") self.canvasprobe = FigureCanvasTkAgg(self.figprobe, self.probeframe) self.canvasprobe.show() self.canvasprobe.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=True) #canvasprobe.get_tk_widget().grid(padx=10, sticky=W, row=0, column=2, columnspan=2) ani = animation.FuncAnimation(self.figprobe, self.animate_probe, interval=300000)
def __init__(self, parent, controller): defs_common.logtoconsole("Initializing Dashboard...", fg = "YELLOW", bg = "MAGENTA", style = "BRIGHT") tk.Frame.__init__(self,parent) #initialize the messaging queues self.connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) self.channel = self.connection.channel() result = self.channel.queue_declare(exclusive=True) self.callback_queue = result.method.queue self.channel.basic_consume(self.rpc_response, no_ack=True, queue=self.callback_queue) # create dictionary to hold assigned probes and outlets self.probeDict = {} self.outletDict = {} self.canvas = tk.Canvas(self, borderwidth=0) self.frame_master=Frame(self.canvas, relief=FLAT) self.frame_master.pack(side=LEFT, fill=BOTH) self.frame_left_column=LabelFrame(self.frame_master, relief = RAISED) self.frame_left_column.pack(side=LEFT, anchor=N, fill=Y) self.frame_mid_column=LabelFrame(self.frame_master, relief = RAISED) self.frame_mid_column.pack(side=LEFT, anchor=N, fill=Y) self.frame_right_column=LabelFrame(self.frame_master, relief = RAISED) self.frame_right_column.pack(side=LEFT, anchor=N) self.frameProbes = tk.Frame(self.frame_left_column, width=470) self.frameProbes.pack(side=TOP, anchor=N, fill=BOTH, expand=True) self.frameSpaceFrame = Frame(self.frame_left_column, width=470, height=1) self.frameSpaceFrame.pack(side=TOP) self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) self.canvas.configure(yscrollcommand=self.vsb.set) self.vsb.pack(side="right", fill="y") self.canvas.pack(side="left", fill=BOTH, expand=True) self.canvas.create_window((4,4), window=self.frame_master, anchor="nw", tags="self.frame_master") self.frameProbes.bind("<Configure>", self.onFrameConfigure) ## logocanvas=Canvas(self.frame_right_column,width=250,height=250) ## logocanvas.pack() ## ## self.img=PhotoImage(file="images/reefberrypi_logo2.gif") ## logocanvas.create_image(0,0,image=self.img, anchor=NW) # add probe frames to show current data and mini graphs on the GUI self.readExistingProbes() # add the feed widget self.feedFrame = cls_FeedWidget.FeedWidget(self.frame_mid_column) # now add the outlets self.readOutlets()
def uploadsettings(self, section, key, value): defs_common.logtoconsole("Request settings change: [" + str(section) + "] [" + str(key) + "] = " + str(value)) # request settings change on server request = { "rpc_req": "set_writeinifile", "section": str(section), "key": str(key), "value": str(value) } request = json.dumps(request) self.rpc_call(request, "rpc_queue")
def __init__(self): defs_common.logtoconsole("Application Start", fg="WHITE", style="BRIGHT") self.threads = [] self.queue = queue.Queue() self.threadlock = threading.Lock() self.INFLUXDB_HOST = "192.168.1.217" self.INFLUXDB_PORT = "8086" self.INFLUXDB_DBNAME = "reefberrypi" self.MQTT_BROKER_HOST = "192.168.1.217" self.MQTT_USERNAME = "******" self.MQTT_PASSWORD = "******" LOG_FILEDIR = "logs" LOG_FILENAME = "RBP_controller.log" LOGLEVEL_CONSOLE = logging.INFO # DEBUG, INFO, ERROR LOGLEVEL_LOGFILE = logging.INFO self.initialize_logger(LOG_FILEDIR, LOG_FILENAME, LOGLEVEL_CONSOLE, LOGLEVEL_LOGFILE) self.logger.info("Reefberry Pi controller startup...") # read prefs self.AppPrefs = cls_Preferences.AppPrefs(self) self.refreshPrefs = False # set up the GPIO GPIO_config.initGPIO() # dht11 temperature and humidity sensor self.dht_sensor = dht11.DHT11(pin=GPIO_config.dht11) # connect to InfluxDB self.InfluxDBclient = self.ConnectInfluxDB(self.INFLUXDB_HOST, self.INFLUXDB_PORT, self.INFLUXDB_DBNAME) # connect to MQTT broker # create new instance and assign the AppUID to it self.MQTTclient = mqtt.Client(self.AppPrefs.appuid) # self.MQTTclient.on_message=on_message #attach function to callback self.MQTTclient.username_pw_set(self.MQTT_USERNAME, self.MQTT_PASSWORD) self.MQTTclient.connect(self.MQTT_BROKER_HOST) self.MQTTclient.subscribe("reefberrypi/demo") self.threadManager()
def getProbeList(self): defs_common.logtoconsole("Request probe list for outlet control") # get setting value from server request = { "rpc_req": "get_probelist", } request = json.dumps(request) val = self.rpc_call(request, "rpc_queue") val = val.decode() val = json.loads(val) #print (val) return val
def rpc_call(self, n, queue): self.response = None self.corr_id = str(uuid.uuid4()) ## print(str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + " RPC call: " + n ## + " UID: " + self.corr_id) defs_common.logtoconsole( str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + " RPC call: " + n + " UID: " + self.corr_id, fg="GREEN", style="BRIGHT") if self.connection.is_open: defs_common.logtoconsole("Pika connection is OPEN") else: defs_common.logtoconsole("Pika connection is CLOSED") defs_common.logtoconsole("Reopen Pika connection") self.initializeConnection() self.channel.basic_publish(exchange='', routing_key=queue, properties=pika.BasicProperties( reply_to=self.callback_queue, correlation_id=self.corr_id, expiration="300000"), body=str(n)) while self.response is None: self.connection.process_data_events() return self.response
def getConnectedProbes(self): self.readExistingProbes() defs_common.logtoconsole( "Request list of connected temperature probes...") # get setting value from server request = { "rpc_req": "get_connectedtempprobes", } request = json.dumps(request) connectedtempprobes = self.controller.rpc_call(request, "rpc_queue") connectedtempprobes = connectedtempprobes.decode() connectedtempprobes = json.loads(connectedtempprobes) #print (connectedtempprobes) # we need to clear widget so we can refresh with current list # first grab current selection to it is still highlited after refresh selection = self.lst_probes.curselection() self.lst_probes.delete(0, END) addtolist = True newprobeID = "" for d in connectedtempprobes['tempprobelist']: newprobeid = d.split("/")[-1] for p in self.probeDict: if str(d.split("/")[-1]) == str(self.probeDict[p].probeid): addtolist = False break else: addtolist = True if addtolist == True: self.lst_probes.insert(END, newprobeid) # set back to what was slected try: self.lst_probes.activate(selection) self.lst_probes.selection_set(selection) except: #print("Error selecting listbox index") pass # diable the add probe button if nothing left in the list if self.lst_probes.size() == 0: self.btn_assignprobe["state"] = DISABLED else: self.btn_assignprobe["state"] = NORMAL
def sendKeepAlive(self): # periodically (like every 1 or 2 minutes) send a message to the exchange so it # knows this channel is still active and not closed due to inactivity defs_common.logtoconsole("send keep alive request: " + "PrefPage", fg="YELLOW", style="BRIGHT") request = { "rpc_req": "set_keepalive", "module": "PrefPage", } request = json.dumps(request) self.rpc_call(request, "rpc_queue") # every 2 minutes, send out a message on this channel so the exchange server knows # we are still alive and doesn't close our connection heartbeatThread = threading.Timer(120, self.sendKeepAlive) heartbeatThread.daemon = True heartbeatThread.start()
def handle_RBPstatus(self, channel): result = channel.queue_declare(exclusive=True) queue_name = result.method.queue channel.queue_bind(exchange='rbp_currentstatus', queue=queue_name) def callback(ch, method, properties, body): body = body.decode() #print(" [x] %r" % body) self.queue.put(body) channel.basic_consume(callback, queue=queue_name, no_ack=True) defs_common.logtoconsole("Listening for status updates on exchange: rbp_currentstatus") channel.start_consuming()
def select_feed_mode(mode): #DefClr = app.cget("bg") #btn_feedA.configure(bg=DefClr) #btn_feedB.configure(bg=DefClr) #btn_feedC.configure(bg=DefClr) #btn_feedD.configure(bg=DefClr) #btn_feedCancel.configure(bg=DefClr) defs_common.logtoconsole("Feed mode request: " + str(mode), fg="YELLOW", style="BRIGHT") # request outlet change on server request = { "rpc_req": "set_feedmode", "feedmode": str(mode), } request = json.dumps(request) self.rpc_call(request, "rpc_queue")
def downloadsettings(self, section, key, defaultval): defs_common.logtoconsole("Request settings vaue: [" + str(section) + "] [" + str(key) + "]") # get setting value from server request = { "rpc_req": "get_readinifile", "section": str(section), "key": str(key), "defaultval": str(defaultval) } request = json.dumps(request) val = self.rpc_call(request, "rpc_queue") val = val.decode() val = json.loads(val) val = val.get("readinifile") #print (val) return val
def processIncoming(self): """Handle all messages currently in the queue, if any.""" while self.queue.qsize( ): try: msg = self.queue.get(0) # Check contents of message and do whatever is needed. As a # simple test, print it (in real life, you would # suitably update the GUI's display in a richer fashion). #defs_common.logtoconsole("processIncoming " + str(msg)) msg = json.loads(msg) for key in msg: if key == "status_currentprobeval": curID = str(msg["status_currentprobeval"]["probeid"]) curVal = str(msg["status_currentprobeval"]["probeval"]) curName = str(msg["status_currentprobeval"]["probename"]) if curID == "dht_h": #if this is a humidity value, tack on the % sign curVal = str(curVal) + "%" self.frames[cls_DashBoard.DashBoard].updateProbeVal(curID, curVal, curName) if key == "status_currentoutletstate": #defs_common.logtoconsole(str(msg), fg="MAGENTA", bg="GREEN") self.frames[cls_DashBoard.DashBoard].updateOutletStatus(str(msg["status_currentoutletstate"]["outletid"]), str(msg["status_currentoutletstate"]["outletname"]), str(msg["status_currentoutletstate"]["outletbus"]), str(msg["status_currentoutletstate"]["control_type"]), str(msg["status_currentoutletstate"]["button_state"]), str(msg["status_currentoutletstate"]["outletstate"]), str(msg["status_currentoutletstate"]["statusmsg"])) if key == "status_feedmode": defs_common.logtoconsole(str(msg), fg="MAGENTA", bg="GREEN") feedmode = str(msg["status_feedmode"]["feedmode"]) timeremaining = str(msg["status_feedmode"]["timeremaining"]) self.frames[cls_DashBoard.DashBoard].feedFrame.updatefeedstatus(feedmode, timeremaining) except queue.Empty: # just on general principles, although we don't # expect this branch to be taken in this case pass
def rpc_call(self, n, queue): self.response = None self.corr_id = str(uuid.uuid4()) ## print(str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + " RPC call: " + n ## + " UID: " + self.corr_id) defs_common.logtoconsole("RPC call: " + n + " UID: " + self.corr_id, fg="GREEN", style="BRIGHT") self.channel.basic_publish(exchange='', routing_key=queue, properties=pika.BasicProperties( reply_to = self.callback_queue, correlation_id = self.corr_id, expiration="300000"), body=str(n)) while self.response is None: self.connection.process_data_events(time_limit=20) # if timelimit in seconds is reached, and we don't get a response, lets break out of the loop # but we must handle NONE return on the caller break return self.response
def readExistingProbes(self): # create dictionary to hold assigned temperature probes # these are probes that are already saved in config file self.probeDict = {} self.probeDict.clear() # send the command back up to the controller to handle the request defs_common.logtoconsole("Request probe list for outlet control") # get setting value from server request = { "rpc_req": "get_probelist", } request = json.dumps(request) probelist = self.controller.rpc_call(request, "rpc_queue") probelist = probelist.decode() probelist = json.loads(probelist) for tempprobe in probelist['probelist']: if tempprobe.split("_")[0] == "ds18b20": probe = ProbeClass() probe.probeid = probelist['probelist'][tempprobe][ 'probeid'].split("_")[1] probe.name = probelist['probelist'][tempprobe]['probename'] self.probeDict[probe.probeid] = probe
def select_outlet_state(self): defs_common.logtoconsole("outlet state change: " + str(self.outletid.get()) + " to " + str(self.button_state.get()), fg="YELLOW", style="BRIGHT") if self.button_state.get() == defs_common.OUTLET_OFF: self.statusmsg.set("OFF") self.lbl_outlet_status.config(foreground="RED") ## self.channel.basic_publish(exchange='', ## routing_key='outlet_change', ## properties=pika.BasicProperties(expiration='30000'), ## body=str(str(self.outletid.get()) + "," + "OFF")) ## #body=str("int_outlet_1" + "," + "OFF")) # request outlet change on server request = { "rpc_req": "set_outletoperationmode", "bus": str(self.outletbus.get()), "outletnum": str(self.outletid.get().split("_")[2]), "opmode": "off" } request = json.dumps(request) self.rpc_call(request, "rpc_queue") elif self.button_state.get() == defs_common.OUTLET_AUTO: self.statusmsg.set("AUTO") self.lbl_outlet_status.config(foreground="DARK ORANGE") ## self.channel.basic_publish(exchange='', ## routing_key='outlet_change', ## properties=pika.BasicProperties(expiration='30000'), ## body=str(str(self.outletid.get()) + "," + "AUTO")) ## #body=str("int_outlet_1" + "," + "AUTO")) request = { "rpc_req": "set_outletoperationmode", "bus": str(self.outletbus.get()), "outletnum": str(self.outletid.get().split("_")[2]), "opmode": "auto" } request = json.dumps(request) self.rpc_call(request, "rpc_queue") elif self.button_state.get() == defs_common.OUTLET_ON: self.statusmsg.set("ON") self.lbl_outlet_status.config(foreground="GREEN") ## self.channel.basic_publish(exchange='', ## routing_key='outlet_change', ## properties=pika.BasicProperties(expiration='30000'), ## body=str(str(self.outletid.get()) + "," + "ON")) ## #body=str("int_outlet_1" + "," + "ON")) request = { "rpc_req": "set_outletoperationmode", "bus": str(self.outletbus.get()), "outletnum": str(self.outletid.get().split("_")[2]), "opmode": "on" } request = json.dumps(request) self.rpc_call(request, "rpc_queue") else: self.lbl_outlet_status.config(text="UNKNOWN", foreground="BLACK") ## selection = "Select outlet option " + self.lbl_outlet_status.cget("text") ## print(Fore.YELLOW + Style.BRIGHT + datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ## " " + selection + Style.RESET_ALL) self.outlet_freezeupdate.set(True) defs_common.logtoconsole("Freeze Update: " + str(self.outletid.get() + " " + str(self.outlet_freezeupdate.get())), fg="CYAN")
def __init__(self, master): defs_common.logtoconsole("Initializing OutletWidget...", fg="YELLOW", bg="MAGENTA", style="BRIGHT") self.outletid = StringVar() # id of this outlet ex: int_outlet_1 self.outletname = StringVar() # user defined name of outlet self.outletbus = StringVar() # int or ext bus self.control_type = StringVar( ) # control scheme of outlet ex: skimmer, lights, always... self.button_state = IntVar( ) # button state ON (3), OFF (1), or AUTO (2) self.outletstate = StringVar() # is the outlet currently on or off self.statusmsg = StringVar( ) # short status message to display above buttons self.outlet_freezeupdate = BooleanVar() # set initial value... self.statusmsg.set("waiting...") self.outlet_freezeupdate.set(True) self.initializeConnection() ## #initialize the messaging queues ## self.connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', heartbeat_interval=5)) ## self.channel = self.connection.channel() ## ## result = self.channel.queue_declare(exclusive=True) ## self.callback_queue = result.method.queue ## ## self.channel.basic_consume(self.rpc_response, no_ack=True, ## queue=self.callback_queue) # frame for internal outlet 1 control self.frame_outlet = LabelFrame(master, text="waiting...", relief=RAISED) self.frame_outlet.pack(fill=X, side=TOP) self.frame_outlet_spacer = tk.LabelFrame(self.frame_outlet, relief=tk.FLAT) self.frame_outlet_spacer.pack(fill=X, side=TOP) self.img_cfg16 = PhotoImage(file="images/settings-16.png") self.btn_cfg_outlet = Button( self.frame_outlet_spacer, text="edit", image=self.img_cfg16, relief=FLAT, command=lambda: self.configureOutlet(master)) self.btn_cfg_outlet.pack(side=LEFT, anchor=W) self.lbl_outlet_status = Label(self.frame_outlet_spacer, text="waiting...", relief=FLAT, textvariable=self.statusmsg) self.lbl_outlet_status.pack(side=TOP, anchor=E) self.rdo_outlet_off = Radiobutton(self.frame_outlet, text="Off", variable=self.button_state, value=1, command=self.select_outlet_state, indicatoron=0) self.rdo_outlet_off.pack(side=LEFT, expand=1, fill=X) self.rdo_outlet_auto = Radiobutton(self.frame_outlet, text="Auto", variable=self.button_state, value=2, command=self.select_outlet_state, indicatoron=0) self.rdo_outlet_auto.pack(side=LEFT, expand=1, fill=X) self.rdo_outlet_on = Radiobutton(self.frame_outlet, text="On", variable=self.button_state, value=3, command=self.select_outlet_state, indicatoron=0) self.rdo_outlet_on.pack(side=LEFT, expand=1, fill=X) self.sendKeepAlive()
def __init__(self, parent, controller, ChannelID, channelName): tk.Frame.__init__(self, parent) defs_common.logtoconsole("Initializing CalibPH...", fg="YELLOW", bg="MAGENTA", style="BRIGHT") self.controller = controller self.parent = parent self.ChannelID = ChannelID self.ChannelName = channelName # registering validation command #self.vldt_ifnum_cmd = (self.register(self.ValidateIfNum),'%s', '%S') self.isCalRunning = False self.calType = "none" self.calPoints = [] self.currentDVlistCounter = 0 self.currentDVlist = [] strHeadLabel = "Channel " + str(self.ChannelID) + " [" + str( self.ChannelName) + "]" headlabel = tk.Label(self, text=strHeadLabel, font=LARGE_FONT) headlabel.grid(row=0, column=0, pady=10, sticky=W, columnspan=3) lbl_calPoint = headlabel = tk.Label(self, text="Cal Point") lbl_calPoint.grid(row=1, column=0, padx=10) lbl_refVal = headlabel = tk.Label(self, text="PH Reference") lbl_refVal.grid(row=1, column=1, padx=10) lbl_digVal = headlabel = tk.Label(self, text="Digital Value") lbl_digVal.grid(row=1, column=2, padx=10) # we want to underline the header, so: # clone the font, set the underline attribute, # and assign it to our widget f = font.Font(lbl_calPoint, lbl_calPoint.cget("font")) f.configure(underline=True) lbl_calPoint.configure(font=f) lbl_refVal.configure(font=f) lbl_digVal.configure(font=f) # read values from config file # Low Val lbl_pointLow = tk.Label(self, text="Low") lbl_pointLow.grid(row=2, column=0) lbl_phrefLow = tk.Label(self, text="4.0") lbl_phrefLow.grid(row=2, column=1) strval = "ch" + str(ChannelID) + "_ph_low" val = self.controller.controller.controller.controller.downloadsettings( "mcp3008", strval, "900") self.lbl_low_val = tk.Label(self, text=val) self.lbl_low_val.grid(row=2, column=2) btn_LowCalStart = Button(self, text="Start Low Calibration", command=lambda: self.startCalLoop('low')) btn_LowCalStart.grid(row=2, column=3, sticky=EW) # Med Val lbl_pointMed = tk.Label(self, text="Mid") lbl_pointMed.grid(row=3, column=0) lbl_phrefMed = tk.Label(self, text="7.0") lbl_phrefMed.grid(row=3, column=1) strval = "ch" + str(ChannelID) + "_ph_med" val = self.controller.controller.controller.controller.downloadsettings( "mcp3008", strval, "800") self.lbl_med_val = tk.Label(self, text=val) self.lbl_med_val.grid(row=3, column=2) btn_MedCalStart = Button(self, text="Start Mid Calibration", command=lambda: self.startCalLoop('med')) btn_MedCalStart.grid(row=3, column=3, sticky=EW) # High Val lbl_pointHigh = tk.Label(self, text="High") lbl_pointHigh.grid(row=4, column=0) lbl_phrefHigh = tk.Label(self, text="10.0") lbl_phrefHigh.grid(row=4, column=1) strval = "ch" + str(ChannelID) + "_ph_high" val = self.controller.controller.controller.controller.downloadsettings( "mcp3008", strval, "700") self.lbl_high_val = tk.Label(self, text=val) self.lbl_high_val.grid(row=4, column=2) btn_HighCalStart = Button(self, text="Start High Calibration", command=lambda: self.startCalLoop('high')) btn_HighCalStart.grid(row=4, column=3, sticky=EW) # Show current PH and DV val frame_LiveData = LabelFrame(self, text="Live Data") frame_LiveData.grid(row=5, column=0, columnspan=4, sticky=EW) lbl_curPH = tk.Label(frame_LiveData, text="Current PH:") lbl_curPH.grid(row=5, column=0, sticky=E, columnspan=2) self.lbl_curPHval = tk.Label(frame_LiveData, text="waiting...") self.lbl_curPHval.grid(row=5, column=2, sticky=W, padx=20) lbl_curDV = tk.Label(frame_LiveData, text="Current Digital Value:") lbl_curDV.grid(row=6, column=0, sticky=E, columnspan=2) self.lbl_curDVval = tk.Label(frame_LiveData, text="waiting...") self.lbl_curDVval.grid(row=6, column=2, sticky=W, padx=20, pady=10) # calibration data frame_CalData = LabelFrame(self, text="Calibration Data") frame_CalData.grid(row=7, column=0, columnspan=4, sticky=EW) # cal label self.calLabel = Label(frame_CalData, text=" ", font=LARGE_FONT_BOLD) self.calLabel.grid(row=0, column=0, columnspan=4, padx=5, pady=4, sticky=W) # progress bar self.calprogress = ttk.Progressbar(frame_CalData, orient='horizontal', mode='determinate', maximum=NUMCALPOINTS, length=450) self.calprogress.grid(row=1, column=0, sticky=EW, pady=10, padx=5, columnspan=6) # data points plot style.use("ggplot") self.figprobe = Figure(figsize=(6, 2.5), dpi=100) self.figprobe.set_facecolor("gainsboro") self.aniprobe = self.figprobe.add_subplot(111) self.aniprobe.set_title("Data Points") self.canvasprobe = FigureCanvasTkAgg(self.figprobe, frame_CalData) self.canvasprobe.show() self.canvasprobe.get_tk_widget().grid(sticky=EW, row=2, column=2, columnspan=4) # histogram plot ## self.fighist, self.axhist = plt.subplots() ## plt.title("Histogram") self.fighist = Figure(figsize=(6, 2.5), dpi=100) self.axhist = self.fighist.add_subplot(111) self.axhist.set_title("Histogram") self.fighist.set_facecolor("gainsboro") self.fighist.set_size_inches(6, 2.5) self.fighist.set_dpi(100) self.axhist.axes.tick_params(axis='x', labelsize=8) self.axhist.axes.tick_params(axis='y', labelsize=8) self.axhist.axes.set_xlim([0, 1023]) self.canvashist = FigureCanvasTkAgg(self.fighist, frame_CalData) self.canvashist.show() self.canvashist.get_tk_widget().grid(sticky=EW, row=3, column=2, columnspan=4) ani = animation.FuncAnimation(self.figprobe, self.animate_probe, interval=1000) # scrolled textbox for calibration values self.txt_calPoints = tkst.ScrolledText(frame_CalData, width=15, height=10, wrap='word') self.txt_calPoints.grid(row=2, column=0, sticky=NSEW, padx=5, columnspan=2) # results area self.frame_Results = LabelFrame(frame_CalData, text="Results") self.frame_Results.grid(row=3, column=0, sticky=NSEW, columnspan=2) self.lbl_num_Samples = Label(self.frame_Results, text="Num. Samples:") self.lbl_num_Samples.grid(row=0, column=0, sticky=E) self.lbl_num_SamplesVal = Label(self.frame_Results, text="", width=10) self.lbl_num_SamplesVal.grid(row=0, column=1, sticky=EW) self.lbl_resultMin = Label(self.frame_Results, text="Min:") self.lbl_resultMin.grid(row=1, column=0, sticky=E) self.lbl_resultMinVal = Label(self.frame_Results, text="", width=10) self.lbl_resultMinVal.grid(row=1, column=1, sticky=EW) self.lbl_resultMax = Label(self.frame_Results, text="Max:") self.lbl_resultMax.grid(row=2, column=0, sticky=E) self.lbl_resultMaxVal = Label(self.frame_Results, text="", width=10) self.lbl_resultMaxVal.grid(row=2, column=1, sticky=EW) self.lbl_resultMean = Label(self.frame_Results, text="Mean:") self.lbl_resultMean.grid(row=3, column=0, sticky=E) self.lbl_resultMeanVal = Label(self.frame_Results, text="", width=10) self.lbl_resultMeanVal.grid(row=3, column=1, sticky=EW) self.lbl_resultStdDev = Label(self.frame_Results, text="Std. Deviation:") self.lbl_resultStdDev.grid(row=4, column=0, sticky=E) self.lbl_resultStdDevVal = Label(self.frame_Results, text="", width=10) self.lbl_resultStdDevVal.grid(row=4, column=1, sticky=EW) self.lbl_resultCalval = Label(self.frame_Results, text="Calibration Value:") self.lbl_resultCalval.grid(row=5, column=0, sticky=E) self.lbl_resultCalvalVal = Label(self.frame_Results, text="", width=10, font=LARGE_FONT_BOLD) self.lbl_resultCalvalVal.grid(row=5, column=1, sticky=EW, pady=20) self.btn_ApplyCal = Button(self.frame_Results, text="Apply Calibration Value", command=self.ApplyCal) self.btn_ApplyCal.grid(row=6, column=0, columnspan=2, sticky=EW) self.currentADCLoop(self.ChannelID)
def endApplication(self): defs_common.logtoconsole("Exiting application") self.running = 0
def __init__(self, master, queue, endCommand): self.queue = queue #tk.Tk.__init__(self, *args, **kwargs) defs_common.logtoconsole("Application startup...") #self.iconbitmap('@images/reefberrypi_logo.xbm') master.wm_title("Reefberry Pi") #set minimum size of the window master.minsize(400,400) master.geometry("800x680") #self.toolbar = cls_Toolbar.Toolbar() #create a menubar master.menubar = Menu(master) # create a pulldown menu, and add it to the menu bar master.filemenu = Menu(master.menubar, tearoff=0) #master.filemenu.add_command(label="Exit", command=master.quit) master.filemenu.add_command(label="Quit", command=self.on_closing) master.menubar.add_cascade(label="File", menu=master.filemenu) # display the menu master.config(menu=master.menubar) ######################### #create toolbar frame self.frame_toolbar = tk.LabelFrame(master, relief = tk.FLAT) self.frame_toolbar.pack(side=tk.TOP, fill=tk.X) self.img_dashboard = PhotoImage(file="images/dashboard-64.png") self.btn_DashBoard = ttk.Button(self.frame_toolbar, text="Dashboard", image=self.img_dashboard, compound=TOP, command=lambda: self.show_frame(cls_DashBoard.DashBoard)) self.btn_DashBoard.pack(side=LEFT) self.img_graph = PhotoImage(file="images/line-chart-64.png") self.btn_GraphPage = ttk.Button(self.frame_toolbar, text="Graphs", image=self.img_graph, compound=TOP, command=lambda: self.show_frame(cls_GraphPage.GraphPage)) self.btn_GraphPage.pack(side=LEFT) self.img_prefs = PhotoImage(file="images/gears-64.png") self.btn_prefs = ttk.Button(self.frame_toolbar, text="Settings", image=self.img_prefs, compound=TOP, command=lambda: self.show_frame(cls_PrefPage.PrefPage)) self.btn_prefs.pack(side=LEFT) self.img_about = PhotoImage(file="images/reefberrypi_logo-64.png") self.btn_About = ttk.Button(self.frame_toolbar, text="About", image=self.img_about, compound=TOP, command=lambda: self.show_frame(cls_SplashPage.SplashPage)) self.btn_About.pack(side=LEFT) # create statusbar statusbar = StatusBar(master) statusbar.pack(side=BOTTOM, fill=X) #statusbar.set("connected to server") ######################### #self.sizegrip = ttk.Sizegrip(master) #self.sizegrip.pack(side="right", anchor="s") container = tk.Frame(master) container.pack(side="top", fill="both", expand = True) container.grid_rowconfigure(0, weight=1) container.grid_columnconfigure(0, weight=1) self.frames = {} for F in (cls_DashBoard.DashBoard, cls_GraphPage.GraphPage, cls_PrefPage.PrefPage, cls_SplashPage.SplashPage): frame = F(container, self) self.frames[F] = frame frame.grid(row=0, column=0, sticky="nsew") self.show_frame(cls_DashBoard.DashBoard)
def apploop(self): while True: #defs_common.logtoconsole("app loop") ########################################################################################## # read ds18b20 temperature sensor # # we support multiple probes, so work from the probe dictionary and get data # for each ########################################################################################## # read data from the temperature probes if (int(round(time.time() * 1000)) - self.AppPrefs.ds18b20_SamplingTimeSeed ) > self.AppPrefs.ds18b20_SamplingInterval: for p in self.AppPrefs.tempProbeDict: try: timestamp = datetime.now() dstempC = float( ds18b20.read_temp( self.AppPrefs.tempProbeDict[p].probeid, "C")) dstempF = defs_common.convertCtoF(float(dstempC)) dstempF = float(dstempF) tempData = str(dstempC) + "," + str(dstempF) if str(self.AppPrefs.temperaturescale) == str( defs_common.SCALE_F): broadcasttemp = str("%.1f" % dstempF) else: broadcasttemp = str("%.1f" % dstempC) #self.tempProbeDict[p].lastLogTime = self.ds18b20_LastLogTimeDict # json_body = [ # { # "measurement": "probevalues", # "tags": { # "appuid": self.AppPrefs.appuid, # "probeid": "ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid # }, # "time": datetime.utcnow(), # "fields": { # "value": float(dstempC), # } # } # ] if (int(round(time.time() * 1000)) - int(self.AppPrefs.tempProbeDict[p].lastLogTime) ) > self.AppPrefs.ds18b20_LogInterval: # log and broadcast temperature value defs_common.logprobedata( "ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid + "_", tempData) defs_common.logtoconsole( "***Logged*** [ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid + "] " + self.AppPrefs.tempProbeDict[p].name + str(" = {:.1f}".format(dstempC)) + " C | " + str("{:.1f}".format(dstempF)) + " F", fg="CYAN", style="BRIGHT") self.logger.info( str("***Logged*** [ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid + "] " + self.AppPrefs.tempProbeDict[p].name + str(" = {:.1f}".format(dstempC)) + " C | " + str("{:.1f}".format(dstempF))) + " F") # log to InfluxDB # self.WriteDataInfluxDB(json_body) self.WriteProbeDataInfluxDB( "ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid, dstempC) #self.broadcastProbeStatus("ds18b20", "ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid, str(broadcasttemp), self.AppPrefs.tempProbeDict[p].name) #self.ds18b20_LastLogTimeDict = int(round(time.time()*1000)) self.AppPrefs.tempProbeDict[p].lastLogTime = int( round(time.time() * 1000)) #self.AppPrefs.tempProbeDict[p].lastTemperature = dstempC self.AppPrefs.tempProbeDict[ p].lastTemperature = broadcasttemp else: self.logger.info( str("[ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid + "] " + self.AppPrefs.tempProbeDict[p].name + str(" = {:.1f}".format(dstempC)) + " C | " + str("{:.1f}".format(dstempF))) + " F") # broadcast temperature value # self.MQTTclient.publish("reefberrypi/demo",str(dstempC)) self.broadcastProbeStatus( "ds18b20", "ds18b20_" + str(self.AppPrefs.tempProbeDict[p].probeid), str(broadcasttemp), self.AppPrefs.tempProbeDict[p].name) self.AppPrefs.tempProbeDict[ p].lastTemperature = broadcasttemp except Exception as e: defs_common.logtoconsole( str("<<<Error>>> Can not read ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid + " temperature data!"), fg="WHITE", bg="RED", style="BRIGHT") self.logger.error( "<<<Error>>> Can not read ds18b20_" + self.AppPrefs.tempProbeDict[p].probeid + " temperature data!") self.logger.error(e) # record the new sampling time self.AppPrefs.ds18b20_SamplingTimeSeed = int( round(time.time() * 1000)) # convert time to milliseconds ################################################################################################################ # read dht11 temperature and humidity sensor # # these sensors are slow to refresh and should not be read more # than once every second or two (ie: dht_SamplingInterval = 3000ms or 5000ms for 3s or 5s) would be safe ################################################################################################################ if self.AppPrefs.DHT_Sensor.get("enabled") == "True": if (int(round(time.time() * 1000)) - self.AppPrefs.DHT_Sensor.get("dht11_samplingtimeseed") ) > int( self.AppPrefs.DHT_Sensor.get( "dht11_samplinginterval")): # let's read the dht11 temp and humidity data result = self.dht_sensor.read() if result.is_valid(): temp_c = result.temperature temp_f = defs_common.convertCtoF(float(temp_c)) temp_f = float(temp_f) hum = result.humidity timestamp = datetime.now() if str(self.AppPrefs.temperaturescale) == str( defs_common.SCALE_F): broadcasttemp = str("%.1f" % temp_f) else: broadcasttemp = str("%.1f" % temp_c) if (int(round(time.time() * 1000)) - self.AppPrefs. DHT_Sensor.get("dht11_lastlogtime")) > int( self.AppPrefs.DHT_Sensor.get( "dht11_loginterval")): tempData = str( "{:.1f}".format(temp_c)) + "," + str(temp_f) # log and broadcast temperature value defs_common.logprobedata("dht_t_", tempData) defs_common.logtoconsole( "***Logged*** [dht_t] " + self.AppPrefs.DHT_Sensor.get( "temperature_name") + " = %.1f C" % temp_c + " | %.1f F" % temp_f, fg="CYAN", style="BRIGHT") self.logger.info( str("***Logged*** [dht_t] " + self.AppPrefs.DHT_Sensor.get( "temperature_name") + " = %.1f C" % temp_c + " | %.1f F" % temp_f)) self.broadcastProbeStatus( "dht", "dht_t", str(broadcasttemp), self.AppPrefs.DHT_Sensor.get( "temperature_name")) # json_body = [ # { # "measurement": "probevalues", # "tags": { # "appuid": self.AppPrefs.appuid, # "probeid": "dht_t" # }, # "time": datetime.utcnow(), # "fields": { # "value": float(temp_c), # } # } # ] # log to InfluxDB # self.WriteDataInfluxDB(json_body) self.WriteProbeDataInfluxDB("dht_t", temp_c) # log and broadcast humidity value defs_common.logprobedata("dht_h_", "{:.0f}".format(hum)) defs_common.logtoconsole( "***Logged*** [dht_h] " + self.AppPrefs.DHT_Sensor.get("humidity_name") + " = %d %%" % hum, fg="CYAN", style="BRIGHT") self.logger.info( str("***Logged*** [dht_h] " + self.AppPrefs.DHT_Sensor.get( "humidity_name") + " = %d %%" % hum)) self.broadcastProbeStatus( "dht", "dht_h", str(hum), self.AppPrefs.DHT_Sensor.get("humidity_name")) # json_body = [ # { # "measurement": "probevalues", # "tags": { # "appuid": self.AppPrefs.appuid, # "probeid": "dht_h" # }, # "time": datetime.utcnow(), # "fields": { # "value": float(hum), # } # } # ] # log to InfluxDB # self.WriteDataInfluxDB(json_body) self.WriteProbeDataInfluxDB("dht_h", hum) self.AppPrefs.DHT_Sensor[ "dht11_lastlogtime"] = int( round(time.time() * 1000)) else: self.logger.info( str("[dht_t] " + self.AppPrefs.DHT_Sensor.get( "temperature_name") + " = %.1f C" % temp_c + " | %.1f F" % temp_f)) self.logger.info( str("[dht_h] " + self.AppPrefs.DHT_Sensor.get( "humidity_name") + " = %d %%" % hum)) # broadcast humidity value self.broadcastProbeStatus( "dht", "dht_h", str(hum), self.AppPrefs.DHT_Sensor.get("humidity_name")) # broadcast temperature value self.broadcastProbeStatus( "dht", "dht_t", str(broadcasttemp), self.AppPrefs.DHT_Sensor.get( "temperature_name")) # record the new sampling time self.AppPrefs.DHT_Sensor[ "dht11_samplingtimeseed"] = int( round(time.time() * 1000)) # convert time to milliseconds ########################################################################################## # read each of the 8 channels on the mcp3008 # channels (0-7) ########################################################################################## # only read the data at every ph_SamplingInterval (ie: 500ms or 1000ms) if (int(round(time.time() * 1000)) - self.AppPrefs.dv_SamplingTimeSeed ) > self.AppPrefs.dv_SamplingInterval: # for x in range (0,8): for ch in self.AppPrefs.mcp3008Dict: if self.AppPrefs.mcp3008Dict[ch].ch_enabled == "True": #defs_common.logtoconsole(str(self.mcp3008Dict[ch].ch_num) + " " + str(self.mcp3008Dict[ch].ch_name) + " " + str(self.mcp3008Dict[ch].ch_enabled) + " " + str(len(self.mcp3008Dict[ch].ch_dvlist))) dv = mcp3008.readadc( int(self.AppPrefs.mcp3008Dict[ch].ch_num), GPIO_config.SPICLK, GPIO_config.SPIMOSI, GPIO_config.SPIMISO, GPIO_config.SPICS) self.AppPrefs.mcp3008Dict[ch].ch_dvlist.append(dv) #self.logger.info(str(self.mcp3008Dict[ch].ch_num) + " " + str(self.mcp3008Dict[ch].ch_name) + " " + str(self.mcp3008Dict[ch].ch_dvlist)) # once we hit our desired sample size of ph_numsamples (ie: 120) # then calculate the average value if len(self.AppPrefs.mcp3008Dict[ch].ch_dvlist) >= int( self.AppPrefs.mcp3008Dict[ch].ch_numsamples): # The probes may pick up noise and read very high or # very low values that we know are not good values. We are going to use numpy # to calculate the standard deviation and remove the outlying data that is # Sigma standard deviations away from the mean. This way these outliers # do not affect our results self.logger.info( "mcp3008 ch" + str(self.AppPrefs.mcp3008Dict[ch].ch_num) + " raw data " + str(self.AppPrefs.mcp3008Dict[ch].ch_name) + " " + str(self.AppPrefs.mcp3008Dict[ch].ch_dvlist)) dv_FilteredCounts = numpy.array( self.AppPrefs.mcp3008Dict[ch].ch_dvlist) dv_FilteredMean = numpy.mean(dv_FilteredCounts, axis=0) dv_FlteredSD = numpy.std(dv_FilteredCounts, axis=0) dv_dvlistfiltered = [ x for x in dv_FilteredCounts if (x > dv_FilteredMean - float(self.AppPrefs.mcp3008Dict[ch].ch_sigma) * dv_FlteredSD) ] dv_dvlistfiltered = [ x for x in dv_dvlistfiltered if (x < dv_FilteredMean + float(self.AppPrefs.mcp3008Dict[ch].ch_sigma) * dv_FlteredSD) ] self.logger.info( "mcp3008 ch" + str(self.AppPrefs.mcp3008Dict[ch].ch_num) + " filtered " + str(self.AppPrefs.mcp3008Dict[ch].ch_name) + " " + str(dv_dvlistfiltered)) # calculate the average of our filtered list try: dv_AvgCountsFiltered = int( sum(dv_dvlistfiltered) / len(dv_dvlistfiltered)) # delete this line print("{:.2f}".format(dv_AvgCountsFiltered)) except: # need to revisit this error handling. Exception thrown when all dv_AvgCountsFiltered = 1 # values were 1023 print("Error collecting data") # self.mcp3008Dict[ch].ch_dvlist.clear() ## delete this line if self.AppPrefs.mcp3008Dict[ch].ch_type == "pH": # bug, somtimes value is coming back high, like really high, like 22.0. this is an impossible # value since max ph is 14. need to figure this out later, but for now, lets log this val to aid in # debugging orgval = dv_AvgCountsFiltered # convert digital value to ph lowCal = self.AppPrefs.mcp3008Dict[ch].ch_ph_low medCal = self.AppPrefs.mcp3008Dict[ch].ch_ph_med highCal = self.AppPrefs.mcp3008Dict[ch].ch_ph_high dv_AvgCountsFiltered = ph_sensor.dv2ph( dv_AvgCountsFiltered, ch, lowCal, medCal, highCal) dv_AvgCountsFiltered = float( "{:.2f}".format(dv_AvgCountsFiltered)) if dv_AvgCountsFiltered > 14: self.logger.error("Invalid PH value: " + str(dv_AvgCountsFiltered) + " " + str(orgval) + " " + str(dv_dvlistfiltered)) defs_common.logtoconsole( "Invalid PH value: " + str(dv_AvgCountsFiltered) + " " + str(orgval) + " " + str(dv_dvlistfiltered), fg="RED", style="BRIGHT") # if enough time has passed (ph_LogInterval) then log the data to file # otherwise just print it to console timestamp = datetime.now() if (int(round(time.time() * 1000)) - self.AppPrefs.mcp3008Dict[ch].LastLogTime ) > self.AppPrefs.dv_LogInterval: # sometimes a high value, like 22.4 gets recorded, i need to fix this, but for now don't log that # if ph_AvgFiltered < 14.0: #RBP_commons.logprobedata(config['logs']['ph_log_prefix'], "{:.2f}".format(ph_AvgFiltered)) # log data to InfluxDB self.WriteProbeDataInfluxDB( "mcp3008_ch" + str(self.AppPrefs.mcp3008Dict[ch].ch_num), "{:.2f}".format(dv_AvgCountsFiltered)) defs_common.logprobedata( "mcp3008_ch" + str(self.AppPrefs.mcp3008Dict[ch].ch_num) + "_", "{:.2f}".format(dv_AvgCountsFiltered)) print( timestamp.strftime(Fore.CYAN + Style.BRIGHT + "%Y-%m-%d %H:%M:%S") + " ***Logged*** dv = " + "{:.2f}".format(dv_AvgCountsFiltered) + Style.RESET_ALL) self.AppPrefs.mcp3008Dict[ch].LastLogTime = int( round(time.time() * 1000)) else: print( timestamp.strftime("%Y-%m-%d %H:%M:%S") + " dv = " + "{:.2f}".format(dv_AvgCountsFiltered)) self.broadcastProbeStatus( "mcp3008", "mcp3008_ch" + str(self.AppPrefs.mcp3008Dict[ch].ch_num), str(dv_AvgCountsFiltered), str(self.AppPrefs.mcp3008Dict[ch].ch_name)) self.AppPrefs.mcp3008Dict[ch].lastValue = str( dv_AvgCountsFiltered) # clear the list so we can populate it with new data for the next data set self.AppPrefs.mcp3008Dict[ch].ch_dvlist.clear() # record the new sampling time self.AppPrefs.dv_SamplingTimeSeed = int( round(time.time() * 1000)) # convert time to milliseconds ########################################################################################## # pause to slow down the loop, otherwise CPU usage spikes as program is busy waiting ########################################################################################## time.sleep(.5)
def __init__(self, master): defs_common.logtoconsole("Initializing FeedWidget...", fg="YELLOW", bg="MAGENTA", style="BRIGHT") ## #initialize the messaging queues ## connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) ## channel = connection.channel() #initialize the messaging queues self.initializeConnection() ## self.connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) ## self.channel = self.connection.channel() ## ## result = self.channel.queue_declare(exclusive=True) ## self.callback_queue = result.method.queue ## ## self.channel.basic_consume(self.rpc_response, no_ack=True, ## queue=self.callback_queue) #queue for posting outlet changes #channel.queue_declare(queue='outlet_change') # frame for feed timers self.frame_feedtimers = LabelFrame(master, text="Feed Mode", relief=RAISED) self.frame_feedtimers.pack(fill=X, side=TOP) self.frame_feed_spacer = tk.LabelFrame(self.frame_feedtimers, relief=tk.FLAT) self.frame_feed_spacer.pack(fill=X, side=TOP) self.img_cfg16 = PhotoImage(file="images/settings-16.png") self.btn_cfg_feed = Button( self.frame_feed_spacer, text="edit", image=self.img_cfg16, relief=FLAT, command=lambda: self.configureFeedmode(master)) self.btn_cfg_feed.pack(side=LEFT, anchor=W) self.lbl_feedtimers_status = Label(self.frame_feed_spacer, text=" ", relief=FLAT) self.lbl_feedtimers_status.pack(side=TOP, anchor=E) self.btn_feedA = Button(self.frame_feedtimers, text="A", width=2, command=lambda: select_feed_mode("A")) self.btn_feedA.pack(side=LEFT, padx=2) self.btn_feedB = Button(self.frame_feedtimers, text="B", width=2, command=lambda: select_feed_mode("B")) self.btn_feedB.pack(side=LEFT, padx=2) self.btn_feedC = Button(self.frame_feedtimers, text="C", width=2, command=lambda: select_feed_mode("C")) self.btn_feedC.pack(side=LEFT, padx=2) self.btn_feedD = Button(self.frame_feedtimers, text="D", width=2, command=lambda: select_feed_mode("D")) self.btn_feedD.pack(side=LEFT, padx=2) self.btn_feedCancel = Button( self.frame_feedtimers, text="Cancel", width=6, command=lambda: select_feed_mode("CANCEL")) self.btn_feedCancel.pack(side=RIGHT, anchor=E, padx=2) self.sendKeepAlive() def select_feed_mode(mode): #DefClr = app.cget("bg") #btn_feedA.configure(bg=DefClr) #btn_feedB.configure(bg=DefClr) #btn_feedC.configure(bg=DefClr) #btn_feedD.configure(bg=DefClr) #btn_feedCancel.configure(bg=DefClr) defs_common.logtoconsole("Feed mode request: " + str(mode), fg="YELLOW", style="BRIGHT") # request outlet change on server request = { "rpc_req": "set_feedmode", "feedmode": str(mode), } request = json.dumps(request) self.rpc_call(request, "rpc_queue")