-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvizy_power_monitor.py
177 lines (143 loc) · 6.08 KB
/
vizy_power_monitor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import getpass
import time
import os
import signal
from datetime import datetime
from vizypowerboard import VizyPowerBoard, get_cpu_temp, DIPSWITCH_POWER_DEFAULT_ON, DIPSWITCH_1_BOOT_MODE
BRIGHTNESS = 0x30
WHITE = [BRIGHTNESS//3, BRIGHTNESS//3, BRIGHTNESS//3]
YELLOW = [BRIGHTNESS//2, BRIGHTNESS//2, 0]
SYNC_TIMEOUT = 60*5 # seconds
# Start using fan at this temperature.
TEMP_MIN = 65 # Celsius
# CPU is throttled back at 80C -- we want to try our hardest not to get there.
TEMP_MAX = 75
FAN_MIN = 1
FAN_MAX = 5
FAN_WINDOW = 30 # seconds
FAN_ATTEN = 0.25
script_dir = os.path.dirname(os.path.realpath(__file__))
os.chdir(script_dir)
logfile = open("log.txt", "a")
def log(msg):
logfile.write(f"{datetime.now().strftime('%d/%m/%Y %H:%M:%S')} {msg}\n")
logfile.flush()
log(f"Running {os.path.realpath(__file__)} as user {getpass.getuser()}")
if getpass.getuser() != "root":
log(f"ERROR: must run as root")
class PowerMonitor:
def set_dip_switches(self):
desired_dip_switches = sum([
# DIPSWITCH_EXT_BUTTON Don't use remote power button
# DIPSWITCH_MUTE_BUZZER Don't mute buzzer
# DIPSWITCH_NO_BG_LED Don't disable background LED
# DIPSWITCH_POWER_DEFAULT_OFF Don't keep power off when 12V applied
DIPSWITCH_POWER_DEFAULT_ON, # Automatically turn on when 12V applied
# DIPSWITCH_POWER_SWITCH Don't keep "last selected power state" when 12V applied
# DIPSWITCH_POWER_PLUG Don't disable power button for turning off vizy
DIPSWITCH_1_BOOT_MODE, # Just one boot mode
])
actual_dip_switches = self.v.dip_switches()
if actual_dip_switches == desired_dip_switches:
log(f"Dip switches already correct (0x{actual_dip_switches:x})")
else:
log(f"Changing dip switches to 0x{desired_dip_switches:x}")
self.v.dip_switches(desired_dip_switches)
def __init__(self):
log("Running Vizy Power Monitor...")
self.count = 0
self.last_fan_speed = 0
self.fan_speed = (0, 0)
self.avg_fan_speed = 0
self.run = True
def handler(signum, frame):
self.run = False
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)
self.v = VizyPowerBoard()
self.set_dip_switches()
# Set time using battery-backed RTC time on Vizy Power Board,
# unless it's already been set by systemd-timesyncd.
# So we set the time based on the RTC value. If we can't sync
# in the future because we don't have a network connection,
# we have the battery-backed RTC value to fall back on.
if not os.path.exists("/run/systemd/timesync/synchronized"):
log("Setting datetime from RTC")
self.v.rtc_set_system_datetime()
else:
log("datetime already set via ntp")
# Set background LED to yellow (finished booting).
self.v.led_background(*YELLOW)
every_hour_last_time = time.time() - 3600
# Poll continuously...
while self.run:
self.handle_timesync()
self.handle_fan()
self.handle_power_button()
time.sleep(1)
# Every hour, turn on IR block filter, in case it has started to drift
# If colors start looking a bit magenta or pink during the day, it could be due
# to the IR block filter not being enabled
if time.time() - every_hour_last_time >= 3600:
log("Enabling IR filter")
self.v.ir_filter(True)
every_hour_last_time = time.time()
if self.v.led_background()==YELLOW:
self.v.led_background(*WHITE)
self.v.fan(0)
log("Exiting Vizy Power Monitor")
def handle_power_button(self):
# Check power button status.
powerOff = self.v.power_off_requested()
if powerOff:
# Initate shutdown.
# Turn off background LED.
self.v.led_background(0, 0, 0)
# Flash LED red as we shut down.
self.v.led(255, 0, 0, 0, True, 15, 500, 500)
os.system('shutdown now')
self.run = False
def handle_timesync(self):
# Spend the first minutes looking for timesync update so we can update the RTC.
if self.count<SYNC_TIMEOUT:
if os.path.exists("/run/systemd/timesync/synchronized"):
# Update RTC time because it will likely be slightly more accurate.
self.v.rtc(datetime.now())
count = SYNC_TIMEOUT # We're done.
else:
self.count += 1
# We scale the fan speed based on the temperature. At TEMP_MIN, the fan turns at
# FAN_MIN. At TEMP_MAX, the fan turns at FAN_MAX.
def handle_fan(self):
temp = get_cpu_temp()
fan_speed = (temp-TEMP_MIN)/(TEMP_MAX-TEMP_MIN)*(FAN_MAX-FAN_MIN) + FAN_MIN
self.avg_fan_speed = FAN_ATTEN*fan_speed + (1-FAN_ATTEN)*self.avg_fan_speed
if self.avg_fan_speed<FAN_MIN:
fan_speed = FAN_MIN
elif self.avg_fan_speed>FAN_MAX:
fan_speed = FAN_MAX
else:
fan_speed = round(self.avg_fan_speed)
#print(temp, fan_speed, self.avg_fan_speed)
t = time.time()
# Be more responsive to increases in fan speed than decreases.
if fan_speed>self.fan_speed[0]:
self.fan_speed = (fan_speed, t)
self.set_fan(fan_speed)
self.v.fan(fan_speed)
# Only decrease fan speed if our window expires.
elif t-self.fan_speed[1]>FAN_WINDOW:
self.fan_speed = (fan_speed, t)
self.set_fan(fan_speed)
self.v.fan(fan_speed)
def set_fan(self, speed):
self.v.fan(speed)
if speed != self.last_fan_speed:
self.last_fan_speed = speed
log(f"CPU temp {get_cpu_temp():.1f}, changing fan speed to {speed}")
try:
PowerMonitor()
except Exception as e:
log(f"Received exceptiopn {e}, exiting")
finally:
log("Exiting")