Posts: 6,817
Threads: 20
Joined: Feb 2020
The way I used grid was a hack to make the code look more like the guizero code. Had I started with tkinter I would probably have the button helper function set the grid position and set any padding.
import tkinter as tk
path = ""
class RelayButton(tk.Button):
"""A button that toggles a relay"""
def __init__(self, parent, url="", on_image=None, off_image=None, **kwargs):
super().__init__(parent, image=off_image, command=self.toggle)
self.url = url
self._on = False
self.on_image = on_image
self.off_image = off_image
@property
def on(self):
"""Return state of the relay"""
return self._on
@on.setter
def on(self, on):
"""Set state of the relay"""
self._on = on
if on:
# requests.post(f"{self.url}cm?cmnd=Power On")
self["image"] = self.on_image
else:
# requests.post(f"{self.url}cm?cmnd=Power Off")
self["image"] = self.off_image
def toggle(self):
"""Toggle state of the relay"""
self.on = not self.on
def new_button(name, url, parent, row, column, **kwargs):
"""Convenience functon that creates a RelayButton"""
on_image = tk.PhotoImage(file=f"{path}{name}_on.png")
off_image = tk.PhotoImage(file=f"{path}{name}_off.png")
button = RelayButton(parent, url, on_image, off_image, **kwargs)
button.grid(row=row, column=column, padx=5)
return button
def main():
app = tk.Tk()
buttons = [
new_button('living_room', "http://192.168.0.101/", app, 0, 0),
new_button('kitchen', "http://192.168.0.113/", app, 0, 1))
]
for b in buttons:
b.on = False
app.mainloop()
if __name__ == "__main__":
main()
Posts: 31
Threads: 7
Joined: Oct 2021
Thanks deanhystad... this is working great. My search continues for getting the actual status of the relays. Thanks again for your time on this, I really appreciate it.
Posts: 31
Threads: 7
Joined: Oct 2021
Hi deanhystad (or other great helpers available). With this code you supplied, at what portion of it can I add things like labels? For instance I want to add a clock to my window. I also plan to add things like door/window sensor status. When I attempt to add a label to the code, it does not show up. Thanks.
Posts: 6,817
Threads: 20
Joined: Feb 2020
How are you adding the label? Post some code that demonstrates failing to put a label in a window.
Posts: 31
Threads: 7
Joined: Oct 2021
Jan-17-2022, 11:07 PM
(This post was last modified: Jan-17-2022, 11:07 PM by Nu2Python.)
Ok, so I got it to show up finally. I think it must have been hiding behind the button (oops!). Now what I want to do is update the label info with the information sent back from the Sonoff switch. As you can see here, I put in a 'def getTemp()' and in 'def main()' I started with adding a button to get the request, which works. I would rather just have it update the label on open with that information and refresh every few minutes.
** I just edited the post here. I did get the clock to show up on the screen. Here is my complete code now. I just need to figure out the label updating for getting the temperature reading.
import tkinter as tk
from tkinter import *
from tkinter import messagebox
import requests
import http.client
import time
path = ""
SONOFF_Temp = 'http://192.168.0.113/cm?cmnd=status+10'
sonoff_url = 'NOT_INIT'
class RelayButton(tk.Button):
"""A button that toggles a relay"""
def __init__(self, parent, url="", on_image=None, off_image=None, **kwargs):
super().__init__(parent, image=off_image, command=self.toggle)
self.url = url
self._on = False
self.on_image = on_image
self.off_image = off_image
self.label = Label(text="", fg="Red", font=("Helvetica", 18))
self.label.place(x=600,y=20)
self.update_clock()
@property
def on(self):
"""Return state of the relay"""
return self._on
@on.setter
def on(self, on):
"""Set state of the relay"""
self._on = on
if on:
requests.post(f"{self.url}cm?cmnd=Power On")
self["image"] = self.on_image
else:
requests.post(f"{self.url}cm?cmnd=Power Off")
self["image"] = self.off_image
def toggle(self):
"""Toggle state of the relay"""
self.on = not self.on
def update_clock(self):
now = time.strftime("%H:%M:%S")
self.label.configure(text=now)
self.after(1000, self.update_clock)
def new_button(name, url, parent, row, column, **kwargs):
"""Convenience functon that creates a RelayButton"""
on_image = tk.PhotoImage(file=f"{path}{name}_on.png")
off_image = tk.PhotoImage(file=f"{path}{name}_off.png")
button = RelayButton(parent, url, on_image, off_image, **kwargs)
button.grid(row=row, column=column, padx=10, pady=10)
return button
def getTemp():
sonoff_url = SONOFF_Temp
sonR1 = requests.get(sonoff_url)
messagebox.showinfo("Temp", sonR1.text)
def main():
app = tk.Tk()
app.title('Home Relay Controls')
tempbtn = Button(app, text='GET TEMP', command=getTemp)
tempbtn.grid(row=1, column=2, padx=(250, 20))
buttons = [
new_button('living_room', "http://192.168.0.101/", app, 0, 0),
new_button('front_porch', "http://192.168.0.113/", app, 0, 1)
]
for b in buttons:
b.on = False
app.mainloop()
if __name__ == "__main__":
main()
Posts: 6,817
Threads: 20
Joined: Feb 2020
Use tkinter .after(milliseconds, func) to call a function sometime in the future.
Posts: 31
Threads: 7
Joined: Oct 2021
Jan-18-2022, 12:15 AM
(This post was last modified: Jan-18-2022, 12:17 AM by Nu2Python.)
(Jan-17-2022, 11:20 PM)deanhystad Wrote: Use tkinter .after(milliseconds, func) to call a function sometime in the future.
I got this formatted pretty well so far... Now I am trying to format the string to eliminate all the parts I don't want. This is the string that I want to get only the temperature portion from.
{"StatusSNS":{"Time":"2022-01-18T01:10:45","DS18B20":{"Id":"0316471BECFF","Temperature":70.8},"TempUnit":"F"}}
Posts: 6,817
Threads: 20
Joined: Feb 2020
Jan-18-2022, 02:36 AM
(This post was last modified: Jan-18-2022, 02:36 AM by deanhystad.)
This is a dictionary as a json string. You could convert the string to a dictionary using json.loads(). Then you could use the TempUnit and also the Time. The structure is kind of awkward.
import json
x = json.loads('{"StatusSNS":{"Time":"2022-01-18T01:10:45","DS18B20":{"Id":"0316471BECFF","Temperature":70.8},"TempUnit":"F"}}')
status = x["StatusSNS"]
timestamp = status["Time"]
device_names = list(status.keys())[1:-1]
temp_units = status["TempUnit"]
for name in device_names:
device = status[name]
print(f'ID={name}, Temperature={device["Temperature"]} {temp_units}')
Posts: 31
Threads: 7
Joined: Oct 2021
[quote="deanhystad" pid='152246' dateline='1642473379']
This is a dictionary as a json string. You could convert the string to a dictionary using json.loads(). Then you could use the TempUnit and also the Time. The structure is kind of awkward.
I must have something wrong here in 'def getTemp' I get "device_names" is not defined?
import tkinter as tk
from tkinter import *
from tkinter import messagebox
import requests
import http.client
import time
import json
path = ""
SONOFF_Temp = 'http://192.168.0.113/cm?cmnd=status+10'
sonoff_url = 'NOT_INIT'
class RelayButton(tk.Button):
"""A button that toggles a relay"""
def __init__(self, parent, url="", on_image=None, off_image=None, **kwargs):
super().__init__(parent, image=off_image, command=self.toggle)
self.url = url
self._on = False
self.on_image = on_image
self.off_image = off_image
self.label = Label(text="", fg="Black", font=("Helvetica", 18))
self.label.place(x=10,y=20)
self.update_clock()
self.templabel = Label(text="temp info", fg="Black", font=("Helvetica", 8))
self.templabel.place(x=10, y=60)
@property
def on(self):
"""Return state of the relay"""
return self._on
@on.setter
def on(self, on):
"""Set state of the relay"""
self._on = on
if on:
requests.post(f"{self.url}cm?cmnd=Power On")
self["image"] = self.on_image
else:
requests.post(f"{self.url}cm?cmnd=Power Off")
self["image"] = self.off_image
def toggle(self):
"""Toggle state of the relay"""
self.on = not self.on
def update_clock(self):
now = time.strftime("%I:%M:%S")
self.label.configure(text=now)
self.after(1000, self.update_clock)
self.after(5000, self.getTemp)
def getTemp(self):
sonoff_url = SONOFF_Temp
sonR1 = requests.get(sonoff_url)
x = json.loads(sonR1.text)
status = x["StatusSNS"]
timestamp = status["Time"]
device_names = list(status.keys())[1:-1]
temp_units = status["TempUnit"]
for name in device_names:
device = status[name]
print(f'ID={name}, Temperature={device["Temperature"]} {temp_units}')
#self.templabel.configure(text=newstr["Temperature"])
def new_button(name, url, parent, row, column, **kwargs):
"""Convenience functon that creates a RelayButton"""
on_image = tk.PhotoImage(file=f"{path}{name}_on.png")
off_image = tk.PhotoImage(file=f"{path}{name}_off.png")
button = RelayButton(parent, url, on_image, off_image, **kwargs)
button.grid(row=row, column=column, padx=50, pady=110)
return button
def main():
app = tk.Tk()
app.title('Home Relay Controls')
buttons = [
new_button('living_room', "http://192.168.0.101/", app, 0, 0),
new_button('front_porch', "http://192.168.0.113/", app, 0, 1)
]
for b in buttons:
b.on = False
app.mainloop()
if __name__ == "__main__":
main()
Posts: 6,817
Threads: 20
Joined: Feb 2020
Indentation in line 64 pushes the for loop outside the scope of the getTemp() method.
Look st your update clock method. How often is getTemp called. Think before you automatically say "Once every 5 seconds"
|