v0.9
Complete README, Blocked queries graph, Lightsoff mode
This commit is contained in:
parent
841e320fe8
commit
47824edc51
140
README.md
140
README.md
@ -1,15 +1,145 @@
|
|||||||
# BlockyGrapher
|
# BlockyGrapher v0.9b
|
||||||
Query grapher for Blocky DNS Server
|
![Example setup using this software](./images/displaydemo.jpg)
|
||||||
|
Physical display query grapher for Blocky DNS Server.
|
||||||
|
|
||||||
|
I2C SSD1306 Displays with resolution 128x64 and 128x32 are supported.
|
||||||
|
Note that this software in beta, and akward bugs may occur.
|
||||||
|
## Preparation
|
||||||
|
### Prometheus Metrics endpoint
|
||||||
|
Open your Blocky DNS Server configuration. Include HTTP server port and enable Prometheus metrics endpoint.
|
||||||
|
```
|
||||||
|
ports:
|
||||||
|
dns: 53
|
||||||
|
http: 4500
|
||||||
|
|
||||||
|
prometheus:
|
||||||
|
enable: true
|
||||||
|
path: /metrics
|
||||||
|
```
|
||||||
|
Restart server. Open your HTTP port in browser and head into metrics. You shoud see something like this:
|
||||||
|
|
||||||
|
![Prometheus Metrics endpoint](./images/endpoint.png)
|
||||||
|
|
||||||
|
You should not open HTTP port to the public (Unless you know what you are doing). Open your config again and edit the ports:
|
||||||
|
```
|
||||||
|
ports:
|
||||||
|
dns: 53
|
||||||
|
http: 127.0.0.1:4500
|
||||||
|
```
|
||||||
|
Restart server again.
|
||||||
|
### Display Installation
|
||||||
|
Both supported SSD1306 displays has 4 I2C pins that we need to connect - VCC, GND, SCL/SCK, SDA.
|
||||||
|
|
||||||
|
![SSD1306 128x64/128x32 I2C Displays](./images/ssd1306.jpg)
|
||||||
|
|
||||||
|
In this example we will look at Orange PI Zero 3.
|
||||||
|
|
||||||
|
![Orange PI Zero 3 pinout](./images/zero3pinout.png)
|
||||||
|
|
||||||
|
We need I2C pins here. We also need 5V (Which is VCC) pin and GND. Connect these to the display.
|
||||||
|
|
||||||
|
![Connection](./images/zero3pinoutcut.png)
|
||||||
|
|
||||||
|
### Enabling I2C support in SoC Board settings
|
||||||
|
On most SoC Boards you need to enable I2C devices support.
|
||||||
|
In this example, we have Orange PI4 LTS with Armbian OS.
|
||||||
|
We need to open armbian configuration utility `sudo armbian-config` and proceed to System > Hardware.
|
||||||
|
Note that Orange PI4 LTS has RK3399 SoC. We need to select RK3399 I2C entries. Apply the changes and reboot.
|
||||||
|
|
||||||
|
![Armbian Config GUI](./images/rk3399i2c.png)
|
||||||
|
|
||||||
|
In some cases GUI Hardware configuration tool may not be available (Like Orange PI Zero 3 with DietPI OS). In that case you need to edit startup environment file in `/boot` directory.
|
||||||
|
```bash
|
||||||
|
~$ nano /boot/dietpiEnv.txt
|
||||||
|
|
||||||
|
rootdev=UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||||
|
rootfstype=ext4
|
||||||
|
consoleargs=console=ttyS0,115200 console=tty1
|
||||||
|
usbstoragequirks=
|
||||||
|
extraargs=net.ifnames=0
|
||||||
|
docker_optimizations=off
|
||||||
|
overlay_path=allwinner
|
||||||
|
overlay_prefix=sun50i-h616
|
||||||
|
# We need to insert hardware I2C name in overlays. In case of Orange PI Zero 3 this is i2c3.
|
||||||
|
overlays=i2c3
|
||||||
|
user_overlays=
|
||||||
|
```
|
||||||
|
Save and reboot.
|
||||||
|
|
||||||
## Install python packages
|
## Install python packages
|
||||||
Debian-based:
|
Debian-based:
|
||||||
```bash
|
```bash
|
||||||
# install system packages
|
# install system packages
|
||||||
|
apt update
|
||||||
apt install i2c-tools python3-dev python3-pip python3-numpy libfreetype6-dev libjpeg-dev build-essential
|
apt install i2c-tools python3-dev python3-pip python3-numpy libfreetype6-dev libjpeg-dev build-essential
|
||||||
# Give user permission to use i2c interface (Replace user with your name). Remember that you need re-login to user after this (You can reboot too if it didn't work for some reason).
|
# Give user permission to use i2c interface (Replace user with your username). Remember that you need re-login to user after this (You can reboot too if it didn't work for some reason).
|
||||||
usermod -a -G i2c user
|
usermod -a -G i2c user
|
||||||
# install python packages
|
# install python packages
|
||||||
pip install -r requirements.txt --break-system-packages
|
pip install -r requirements.txt --break-system-packages
|
||||||
```
|
```
|
||||||
## Finding right port
|
## Finding right port/address and starting the program
|
||||||
|
*In most cases pysical I2C port can differ from system I2C port binding!* It's better to double-check the ports before proceeding.
|
||||||
|
|
||||||
`i2cdetect -l` can help to find port you need to use.
|
`i2cdetect -l` can help to find port you need to use.
|
||||||
Schematics for your adapter/board can also help.
|
```
|
||||||
|
~$ sudo i2cdetect -l
|
||||||
|
i2c-0 i2c mv64xxx_i2c adapter I2C adapter
|
||||||
|
i2c-1 i2c DesignWare HDMI I2C adapter
|
||||||
|
i2c-2 i2c mv64xxx_i2c adapter I2C adapter
|
||||||
|
```
|
||||||
|
In this example we can see that ether port 0 or port 2 is needed. Let's pick port 2.
|
||||||
|
SSD1306 128x64 and 128x32 displays usually have 0x3C/0x30 address.
|
||||||
|
```
|
||||||
|
~$ sudo i2cdetect -y 2
|
||||||
|
0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||||
|
00: -- -- -- -- -- -- -- --
|
||||||
|
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||||
|
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||||
|
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
|
||||||
|
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||||
|
```
|
||||||
|
We found our display! Now we can start the grapher.
|
||||||
|
```
|
||||||
|
python3 dnsmonitor.py --i2c-port 2 --i2c-address 0x3C
|
||||||
|
```
|
||||||
|
Note that during first successful startup, you will need to edit newly created `config.ini` configuration file.
|
||||||
|
```
|
||||||
|
[F] [16:54:12] config.ini does not exist. Please edit newly created file and start the program.
|
||||||
|
```
|
||||||
|
You can see all available startup options using `python3 dnsmonitor.py -h`.
|
||||||
|
## Cofiguration
|
||||||
|
Unedited `config.ini` will look like this:
|
||||||
|
```
|
||||||
|
[source]
|
||||||
|
url = http://127.0.0.1:4500/metrics
|
||||||
|
[appearance]
|
||||||
|
points = 14
|
||||||
|
pointsDistance = 10
|
||||||
|
dots = True
|
||||||
|
[lightsoff]
|
||||||
|
enabled = False
|
||||||
|
start = 23
|
||||||
|
stop = 06
|
||||||
|
```
|
||||||
|
### `[source]`
|
||||||
|
`url` - Contains Prometeus endpoint URL.
|
||||||
|
### `[appearence]`
|
||||||
|
`points` - Amount of points on the graph. Each point pepresents each hour passed. Note that points can can render out of display bounds.
|
||||||
|
|
||||||
|
`pointsDistance` - Distance between the points.
|
||||||
|
|
||||||
|
`dots` - Renders small dots on each hour. Improves graph readability.
|
||||||
|
### `[lightsoff]`
|
||||||
|
`enabled` - Enables Lights OFF mode. During set up period in `start` and `stop` values, turns display off completely. May be useful during the nighttime, where *you are sleeping and not paying attention at all* (OLED displays are VERY bright during night as well).
|
||||||
|
|
||||||
|
`start` - Hour, when Lights OFF mode starts.
|
||||||
|
|
||||||
|
`stop` - Hour, when Lights OFF mode ends.
|
||||||
|
## Install as service (Autorun)
|
||||||
|
### TODO
|
||||||
|
## Credits
|
||||||
|
[Terminus Font](https://files.ax86.net/terminus-ttf/) - Font used by numbers in graph.
|
||||||
|
|
||||||
|
[Font Awesome V4](https://fontawesome.com/v4/) - Icons
|
||||||
|
|
||||||
|
[luma.oled](https://github.com/rm-hull/luma.oled) - SSD1306 Display Driver
|
||||||
|
130
dnsmonitor.py
130
dnsmonitor.py
@ -71,12 +71,13 @@ def get_device(actual_args=None):
|
|||||||
def sigterm_handler(signum, frame):
|
def sigterm_handler(signum, frame):
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def scalepoints(points):
|
def scalepoints(points, blpoints):
|
||||||
if device.height == 32:
|
if device.height == 32:
|
||||||
h = 31
|
h = 31
|
||||||
else:
|
else:
|
||||||
h = 46
|
h = 46
|
||||||
sclpoint = [0] * len(points)
|
sclpoint = [0] * len(points)
|
||||||
|
blsclpoint = [0] * len(blpoints)
|
||||||
scl = 1
|
scl = 1
|
||||||
maxpnt = 0
|
maxpnt = 0
|
||||||
i = 0
|
i = 0
|
||||||
@ -90,10 +91,14 @@ def scalepoints(points):
|
|||||||
while i <= len(points) -1:
|
while i <= len(points) -1:
|
||||||
sclpoint[i] = int(points[i] / scl)
|
sclpoint[i] = int(points[i] / scl)
|
||||||
i += 1
|
i += 1
|
||||||
|
i = 0
|
||||||
|
while i <= len(blpoints) -1:
|
||||||
|
blsclpoint[i] = int(blpoints[i] / scl)
|
||||||
|
i += 1
|
||||||
maxcanv = h * scl
|
maxcanv = h * scl
|
||||||
return sclpoint, maxcanv
|
return sclpoint, blsclpoint, maxcanv
|
||||||
|
|
||||||
def movepoints(points, insertq, restarts, insertr):
|
def movepoints(points, insertq, blpoints, insertbl, restarts, insertr):
|
||||||
i = 1
|
i = 1
|
||||||
tmp = points[0]
|
tmp = points[0]
|
||||||
tmp2 = 0
|
tmp2 = 0
|
||||||
@ -118,7 +123,21 @@ def movepoints(points, insertq, restarts, insertr):
|
|||||||
restarts[i] = tmp3
|
restarts[i] = tmp3
|
||||||
tmp3 = tmp4
|
tmp3 = tmp4
|
||||||
i += 1
|
i += 1
|
||||||
return points, restarts
|
|
||||||
|
i = 1
|
||||||
|
tmp = blpoints[0]
|
||||||
|
tmp2 = 0
|
||||||
|
blpoints[0] = insertbl
|
||||||
|
while i < len(blpoints):
|
||||||
|
if i == len(blpoints) - 1:
|
||||||
|
blpoints[i] = tmp
|
||||||
|
else:
|
||||||
|
tmp2 = blpoints[i]
|
||||||
|
blpoints[i] = tmp
|
||||||
|
tmp = tmp2
|
||||||
|
i += 1
|
||||||
|
i = 1
|
||||||
|
return points, blpoints, restarts
|
||||||
|
|
||||||
def extractor(prev, flaunch, url):
|
def extractor(prev, flaunch, url):
|
||||||
try:
|
try:
|
||||||
@ -141,7 +160,7 @@ def extractor(prev, flaunch, url):
|
|||||||
else:
|
else:
|
||||||
return 0, -1
|
return 0, -1
|
||||||
except:
|
except:
|
||||||
cliout("Extraction failed.", 2)
|
cliout("Extraction (Q) failed.", 2)
|
||||||
if flaunch == True:
|
if flaunch == True:
|
||||||
return -1
|
return -1
|
||||||
else:
|
else:
|
||||||
@ -151,7 +170,38 @@ def extractor(prev, flaunch, url):
|
|||||||
else:
|
else:
|
||||||
return count, count - prev
|
return count, count - prev
|
||||||
|
|
||||||
def drawout(sclpo, maxcanv, failture, restart, distance, dots):
|
def blextractor(prev, flaunch, url):
|
||||||
|
try:
|
||||||
|
data = str(urllib.request.urlopen(url).read()).split("\\n")
|
||||||
|
count = 0
|
||||||
|
i = 0
|
||||||
|
while data[i] != "# TYPE blocky_response_total counter":
|
||||||
|
i += 1
|
||||||
|
i += 1
|
||||||
|
while True:
|
||||||
|
if "blocky_response_total{reason=\"CACHED\",response_code=\"NOERROR\",response_type=\"CACHED\"}" in data[i]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
count += int(re.sub('blocky_response_total{.*?} ', '', data[i]))
|
||||||
|
i += 1
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
cliout(f"Connection to server failed: {e.reason}", 2)
|
||||||
|
if flaunch == True:
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
return 0, -1
|
||||||
|
except:
|
||||||
|
cliout("Extraction (BL) failed.", 2)
|
||||||
|
if flaunch == True:
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
return 0, -1
|
||||||
|
if flaunch == True:
|
||||||
|
return count
|
||||||
|
else:
|
||||||
|
return count, count - prev
|
||||||
|
|
||||||
|
def drawout(sclpo, sclbl, maxcanv, failture, restart, distance, dots):
|
||||||
match device.height:
|
match device.height:
|
||||||
case 32:
|
case 32:
|
||||||
# todo: backport
|
# todo: backport
|
||||||
@ -173,7 +223,23 @@ def drawout(sclpo, maxcanv, failture, restart, distance, dots):
|
|||||||
draw.text((device.width - 30, 0), "\uf021", font=icons, fill="white")
|
draw.text((device.width - 30, 0), "\uf021", font=icons, fill="white")
|
||||||
case 64:
|
case 64:
|
||||||
with canvas(device) as draw:
|
with canvas(device) as draw:
|
||||||
# Graph
|
# Blocked (solid) Graph
|
||||||
|
|
||||||
|
currpoint = 1
|
||||||
|
targetpx = 128 - distance
|
||||||
|
while currpoint < len(sclbl):
|
||||||
|
if (sclbl[currpoint - 1] < sclbl[currpoint]):
|
||||||
|
draw.polygon([(targetpx + distance, 47 - sclbl[currpoint - 1]), (targetpx, 47 - sclbl[currpoint]), (targetpx, 47 - sclbl[currpoint - 1])], fill="white")
|
||||||
|
#cliout(f"RectDBG (less): X1: {targetpx}, Y1: {47 - sclbl[currpoint - 1]}, X2:{targetpx + distance}, Y2: 47",4)
|
||||||
|
draw.rectangle((targetpx, 47 - sclbl[currpoint - 1], targetpx + distance, 47), fill="white")
|
||||||
|
else:
|
||||||
|
draw.polygon([(targetpx + distance, 47 - sclbl[currpoint - 1]), (targetpx + distance, 47 - sclbl[currpoint]), (targetpx, 47 - sclbl[currpoint])], fill="white")
|
||||||
|
#cliout(f"RectDBG (more): X1: {targetpx}, Y1: {47 - sclbl[currpoint]}, X1: {targetpx + distance}, Y2: 47", 4)
|
||||||
|
draw.rectangle((targetpx, 47 - sclbl[currpoint], targetpx + distance, 47), fill="white")
|
||||||
|
currpoint += 1
|
||||||
|
targetpx -= distance
|
||||||
|
|
||||||
|
# Main Graph
|
||||||
currpoint = 1
|
currpoint = 1
|
||||||
targetpx = 128 - distance
|
targetpx = 128 - distance
|
||||||
while currpoint < len(sclpo):
|
while currpoint < len(sclpo):
|
||||||
@ -201,16 +267,17 @@ def drawout(sclpo, maxcanv, failture, restart, distance, dots):
|
|||||||
with canvas(device) as draw:
|
with canvas(device) as draw:
|
||||||
draw.text((0,0), "Display not supported", font=smafont, fill="white")
|
draw.text((0,0), "Display not supported", font=smafont, fill="white")
|
||||||
|
|
||||||
def getdata(oldtotal, url):
|
def getdata(oldtotal, oldbltotal, url):
|
||||||
total, query = extractor(oldtotal, False, url)
|
total, query = extractor(oldtotal, False, url)
|
||||||
|
bltotal, blquery = blextractor(oldbltotal, False, url)
|
||||||
# Server lifetime counter | Difference | Err? | Restarted?
|
# Server lifetime counter | Difference | Err? | Restarted?
|
||||||
if query == -1:
|
if query == -1:
|
||||||
cliout("Extractor finished with error.", 1)
|
cliout("Extractor finished with error.", 1)
|
||||||
return oldtotal, 0, True, False
|
return oldtotal, 0, bltotal, 0, True, False
|
||||||
if total < oldtotal:
|
if total < oldtotal:
|
||||||
cliout("Server restart was detected.", 1)
|
cliout("Server restart was detected.", 1)
|
||||||
return total, 0, False, True
|
return total, 0, bltotal, 0, False, True
|
||||||
return total, query, False, False
|
return total, query, bltotal, blquery, False, False
|
||||||
|
|
||||||
def cliout(data, code):
|
def cliout(data, code):
|
||||||
time = datetime.datetime.now().strftime("%H:%M:%S")
|
time = datetime.datetime.now().strftime("%H:%M:%S")
|
||||||
@ -223,11 +290,13 @@ def cliout(data, code):
|
|||||||
codes = "E"
|
codes = "E"
|
||||||
case 3:
|
case 3:
|
||||||
codes = "F"
|
codes = "F"
|
||||||
|
case 4:
|
||||||
|
codes = "V"
|
||||||
print(f"[{codes}] [{time}] {data}")
|
print(f"[{codes}] [{time}] {data}")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
cliout("Blocky Graph Monitor for OLED v0.8b", 0)
|
cliout("Blocky Graph Monitor for OLED v0.9b", 0)
|
||||||
cliout("(C) Nikopol 2024", 0)
|
cliout("Nikopol 2024", 0)
|
||||||
cliout("Init...", 0)
|
cliout("Init...", 0)
|
||||||
|
|
||||||
# vars spagetti
|
# vars spagetti
|
||||||
@ -241,7 +310,10 @@ def main():
|
|||||||
restart = False
|
restart = False
|
||||||
rstate = False
|
rstate = False
|
||||||
dots = False
|
dots = False
|
||||||
device.contrast(0)
|
lightsoff = False
|
||||||
|
start = 22
|
||||||
|
stop = 6
|
||||||
|
device.contrast(1)
|
||||||
|
|
||||||
|
|
||||||
# Load config
|
# Load config
|
||||||
@ -249,12 +321,12 @@ def main():
|
|||||||
filedata = open("config.ini").readlines()
|
filedata = open("config.ini").readlines()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
f = open("config.ini", "x")
|
f = open("config.ini", "x")
|
||||||
f.write("[source]\nurl = http://127.0.0.1:4500/metrics\n[appearance]\npoints = 14\npointsDistance = 10")
|
f.write("[source]\nurl = http://127.0.0.1:4500/metrics\n[appearance]\npoints = 14\npointsDistance = 10\ndots = True\n[lightsoff]\nenabled = False\nstart = 23\nstop = 06")
|
||||||
f.close()
|
f.close()
|
||||||
cliout("config.ini does not exist. Please edit newly created file and start the program.", 3)
|
cliout("config.ini does not exist. Please edit newly created file and start the program.", 3)
|
||||||
exit()
|
exit()
|
||||||
except:
|
except:
|
||||||
cliout("Failed to read config file. Are permissions correct?", 3)
|
cliout("Failed to read/write config file. Are permissions correct?", 3)
|
||||||
exit()
|
exit()
|
||||||
while i < len(filedata):
|
while i < len(filedata):
|
||||||
if "url = " in filedata[i]:
|
if "url = " in filedata[i]:
|
||||||
@ -265,13 +337,21 @@ def main():
|
|||||||
distance = int(filedata[i][17:])
|
distance = int(filedata[i][17:])
|
||||||
if "dots = True" in filedata[i]:
|
if "dots = True" in filedata[i]:
|
||||||
dots = True
|
dots = True
|
||||||
|
if "enabled = True" in filedata[i]:
|
||||||
|
lightsoff = True
|
||||||
|
if "start = " in filedata[i]:
|
||||||
|
start = int(filedata[i][8:])
|
||||||
|
if "stop = " in filedata[i]:
|
||||||
|
stop = int(filedata[i][7:])
|
||||||
i += 1
|
i += 1
|
||||||
i = 0
|
i = 0
|
||||||
cliout("Config load ok", 0)
|
cliout("Config load ok", 0)
|
||||||
|
|
||||||
pointsmap = [0] * pointscount
|
pointsmap = [0] * pointscount
|
||||||
|
blockedmap = [0] * len(pointsmap)
|
||||||
restartmap = [True] * len(pointsmap)
|
restartmap = [True] * len(pointsmap)
|
||||||
total = extractor(0, True, url)
|
total = extractor(0, True, url)
|
||||||
|
bltotal = blextractor(0, True, url)
|
||||||
|
|
||||||
# Show fail on boot time if occurs.
|
# Show fail on boot time if occurs.
|
||||||
if total == -1:
|
if total == -1:
|
||||||
@ -281,21 +361,24 @@ def main():
|
|||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
today_time = now.strftime("%H:%M")
|
today_time = now.strftime("%H:%M")
|
||||||
hrs = now.strftime("%H")
|
hrs = now.strftime("%H")
|
||||||
|
#cliout(f"start is {start}", 1)
|
||||||
if today_time != today_last_time:
|
if today_time != today_last_time:
|
||||||
#if 1:
|
#if 1:
|
||||||
today_last_time = today_time
|
today_last_time = today_time
|
||||||
if hrs != last_hrs:
|
if hrs != last_hrs:
|
||||||
#if 1:
|
#if 1:
|
||||||
|
if (int(hrs) < stop and int(hrs) >= start and lightsoff == True):
|
||||||
|
cliout(f"Lightsoff is enabled and within time range. Screen will be black this hour.", 0)
|
||||||
last_hrs = hrs
|
last_hrs = hrs
|
||||||
# Grab fresh data
|
# Grab fresh data
|
||||||
cliout("Extracting data...", 0)
|
cliout("Extracting data...", 0)
|
||||||
total, query, failture, rstate = getdata(total, url)
|
total, query, bltotal, blquery, failture, rstate = getdata(total, bltotal, url)
|
||||||
#print(total)
|
#print(total)
|
||||||
#print(query)
|
#print(query)
|
||||||
# Now write it to (raw) array and move older data
|
# Now write it to (raw) array and move older data
|
||||||
pointsmap, restartmap = movepoints(pointsmap, query, restartmap, rstate)
|
pointsmap, blockedmap, restartmap = movepoints(pointsmap, query, blockedmap, blquery, restartmap, rstate)
|
||||||
# ...and scale it to screen size
|
# ...and scale it to screen size
|
||||||
sclpo, maxcanv = scalepoints(pointsmap)
|
sclpo, sclbl, maxcanv = scalepoints(pointsmap, blockedmap)
|
||||||
# also write restart map
|
# also write restart map
|
||||||
restart = False
|
restart = False
|
||||||
while i < len(restartmap):
|
while i < len(restartmap):
|
||||||
@ -303,8 +386,13 @@ def main():
|
|||||||
restart = True
|
restart = True
|
||||||
i += 1
|
i += 1
|
||||||
i = 0
|
i = 0
|
||||||
cliout(f"Done. Current: {query}", 0)
|
cliout(f"Done. Current: {query}, Blocked: {blquery}", 0)
|
||||||
drawout(sclpo, maxcanv, failture, restart, distance, dots)
|
if (int(hrs) < stop and int(hrs) >= start and lightsoff == True):
|
||||||
|
with canvas(device) as draw:
|
||||||
|
# :troll:
|
||||||
|
hrs = hrs
|
||||||
|
else:
|
||||||
|
drawout(sclpo, sclbl, maxcanv, failture, restart, distance, dots)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
BIN
images/displaydemo.jpg
Normal file
BIN
images/displaydemo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 136 KiB |
BIN
images/endpoint.png
Normal file
BIN
images/endpoint.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
images/rk3399i2c.png
Normal file
BIN
images/rk3399i2c.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
images/ssd1306.jpg
Normal file
BIN
images/ssd1306.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 260 KiB |
BIN
images/zero3pinout.png
Normal file
BIN
images/zero3pinout.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 156 KiB |
BIN
images/zero3pinoutcut.png
Normal file
BIN
images/zero3pinoutcut.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Loading…
Reference in New Issue
Block a user