Compare commits
4 Commits
avoid-flap
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
998315ef05 | ||
|
07e1126a58 | ||
|
a146b668f9 | ||
|
43a5402706 |
11
README.md
11
README.md
@ -1,8 +1,10 @@
|
|||||||
# Selfhost utilities
|
# Selfhost utilities
|
||||||
|
|
||||||
A collection of utilities for self hosters.
|
A collection of utilities for self hosters.
|
||||||
Every utility is in a folder with its relevant configuration and is completely separated from the other, so you can install only the ones you need.
|
Every utility is in a folder with its relevant configuration and is completely separated from the other, so you can install only the ones you need.
|
||||||
|
|
||||||
## 🚨 HEALTHCHECK
|
## 🚨 HEALTHCHECK
|
||||||
|
|
||||||
A simple server health check.
|
A simple server health check.
|
||||||
Allows to keep under control the machine vitals (cpu usage, raid status, thermals...) and alert the sysadmin in case of anomalies.
|
Allows to keep under control the machine vitals (cpu usage, raid status, thermals...) and alert the sysadmin in case of anomalies.
|
||||||
|
|
||||||
@ -18,9 +20,18 @@ Tested on Debian 11, but should run on almost any standard linux box.
|
|||||||
Please see [healthcheck documentation](healthcheck/README.md)
|
Please see [healthcheck documentation](healthcheck/README.md)
|
||||||
|
|
||||||
## 🖥 MDDCLIENT
|
## 🖥 MDDCLIENT
|
||||||
|
|
||||||
A DynDns2 client supporting multiple domains with individual API calls. Developed to allow updating multiple (sub)domains on Infomaniak dynamic DNS, that supports only one domain per request. Works with any provider supporting DynDns2 protocol.
|
A DynDns2 client supporting multiple domains with individual API calls. Developed to allow updating multiple (sub)domains on Infomaniak dynamic DNS, that supports only one domain per request. Works with any provider supporting DynDns2 protocol.
|
||||||
|
|
||||||
Please see [mddclient documentation](mddclient/README.md)
|
Please see [mddclient documentation](mddclient/README.md)
|
||||||
|
|
||||||
|
## 📟 ESP32-LCD
|
||||||
|
|
||||||
|
A status LCD for your homelab! Low cost (about 20€ in parts), simple to build (no circuit board, no components, only an LCD, a ESP32 and, if needed, a potentiometer). Connects to your local wifi and receives HTTP requests from your homelab machines, and shows them to the screen.
|
||||||
|
|
||||||
|
![ESP32-LCD prototype](images/esp32-lcd.jpg)
|
||||||
|
|
||||||
|
Please see [ESP32-LCD documentation](esp32-lcd/README.md)
|
||||||
|
|
||||||
# License
|
# License
|
||||||
This whole repository is released under GNU General Public License version 3: see http://www.gnu.org/licenses/
|
This whole repository is released under GNU General Public License version 3: see http://www.gnu.org/licenses/
|
||||||
|
@ -14,26 +14,28 @@ A status LCD for your homelab.
|
|||||||
|
|
||||||
Connect the LCD to the board:
|
Connect the LCD to the board:
|
||||||
|
|
||||||
LCD Pin ESP32 Pin
|
**LCD Pin -> ESP32 Pin**
|
||||||
____________________________________
|
|
||||||
PIN01-VSS GND
|
|
||||||
PIN02-VDD 5V
|
|
||||||
PIN03 V0 10K Pot (Middle pin)
|
|
||||||
PIN04 RS GPIO19
|
|
||||||
PIN05 RW GND
|
|
||||||
PIN06 E GPIO23
|
|
||||||
PIN07 D0 NOT USED
|
|
||||||
PIN08 D1 NOT USED
|
|
||||||
PIN09 D2 NOT USED
|
|
||||||
PIN10 D3 NOT USED
|
|
||||||
PIN11 D4 GPIO18
|
|
||||||
PIN12 D5 GPIO17
|
|
||||||
PIN13 D6 GPIO16
|
|
||||||
PIN14 D7 GPIO15
|
|
||||||
PIN15 A 5V
|
|
||||||
PIN16 K GND
|
|
||||||
|
|
||||||
Open config.h file and set display size and your wifi data.
|
- PIN01-VSS -> GND
|
||||||
|
- PIN02-VDD -> 5V
|
||||||
|
- PIN03 V0 -> 10K Pot (Middle pin)
|
||||||
|
- PIN04 RS -> GPIO19
|
||||||
|
- PIN05 RW -> GND
|
||||||
|
- PIN06 E -> GPIO23
|
||||||
|
- PIN07 D0 -> NOT USED
|
||||||
|
- PIN08 D1 -> NOT USED
|
||||||
|
- PIN09 D2 -> NOT USED
|
||||||
|
- PIN10 D3 -> NOT USED
|
||||||
|
- PIN11 D4 -> GPIO18
|
||||||
|
- PIN12 D5 -> GPIO17
|
||||||
|
- PIN13 D6 -> GPIO16
|
||||||
|
- PIN14 D7 -> GPIO15
|
||||||
|
- PIN15 A -> 5V
|
||||||
|
- PIN16 K -> GND
|
||||||
|
|
||||||
|
Connect the potentiometer lateral pins to VCC and GND. Use the potentiometer to set the screen contrast.
|
||||||
|
|
||||||
|
Open config.h file and set display size and your wifi access data.
|
||||||
|
|
||||||
Flash the code to the ESP32. If you use the Arduino ide to do it, just open the esp32-lcd.ino file with the Arduino ide and follow [this instructions](https://randomnerdtutorials.com/getting-started-with-esp32/)
|
Flash the code to the ESP32. If you use the Arduino ide to do it, just open the esp32-lcd.ino file with the Arduino ide and follow [this instructions](https://randomnerdtutorials.com/getting-started-with-esp32/)
|
||||||
|
|
||||||
@ -45,8 +47,10 @@ Restart the ESP32. The display shows "Conn to wifi..." with the WIFI name in the
|
|||||||
- Make a GET request to the same IP address with a parameter "message" containing some text
|
- Make a GET request to the same IP address with a parameter "message" containing some text
|
||||||
|
|
||||||
> Example: to make the request using CURL from command line, try something along this lines (replace the IP addr with the one shown in the display):
|
> Example: to make the request using CURL from command line, try something along this lines (replace the IP addr with the one shown in the display):
|
||||||
> curl -G http://192.168.1.78 --data-urlencode "message=Something interesting happened!"
|
> `curl -G http://192.168.1.78 --data-urlencode "message=Something interesting happened!"`
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
The ESP32 logs are written in the serial monitor at 115200 baud. Just open the Arduino ide Serial Monitor from Tools menu and look at the logs.
|
The ESP32 logs are written in the serial monitor at 115200 baud. Just open the Arduino ide Serial Monitor from Tools menu and look at the logs.
|
||||||
|
|
||||||
|
If the screen is supplied with power but not initialized (maybe due to bad contacts or non working esp32 firmware), it should show some black blocks on the first line. If you canot see those (nor any other text), first of all check the contrast using the potentiometer.
|
||||||
|
@ -80,14 +80,6 @@ NOTIFY_ALARM_END=TRUE
|
|||||||
# success result, error result, command failure. Then paste the command
|
# success result, error result, command failure. Then paste the command
|
||||||
# and regex in this config, enable the check and run to verify is working.
|
# and regex in this config, enable the check and run to verify is working.
|
||||||
|
|
||||||
#### AVOID FLAPPING ####
|
|
||||||
# For values slowly changing, like a server cabinet temperature, a lot of start/end alarm notifications
|
|
||||||
# may be generated if the value is near the limit (e.g. alarm set to 30, temperature jumping between 30.1
|
|
||||||
# and 29.9). To avoid this, a "grace" delta can be set with:
|
|
||||||
# DELTA=0.5
|
|
||||||
# In the previous example, this make the alarm fire at 30.5 and stop at 29.5.
|
|
||||||
# If not specified, the delta is 0.
|
|
||||||
|
|
||||||
|
|
||||||
[system_load_1min]
|
[system_load_1min]
|
||||||
# The system load average in the last minute
|
# The system load average in the last minute
|
||||||
|
@ -169,20 +169,13 @@ class Main:
|
|||||||
|
|
||||||
# Compare detected value with equal, not equal, more than and less values
|
# Compare detected value with equal, not equal, more than and less values
|
||||||
logging.info('detected {}'.format(detectedValue))
|
logging.info('detected {}'.format(detectedValue))
|
||||||
|
|
||||||
# String comparison
|
|
||||||
if config.alarm_string_equal and (detectedValue == config.alarm_string_equal):
|
if config.alarm_string_equal and (detectedValue == config.alarm_string_equal):
|
||||||
return 'value is "{}"'.format(detectedValue)
|
return 'value is "{}"'.format(detectedValue)
|
||||||
if config.alarm_string_not_equal and (detectedValue != config.alarm_string_not_equal):
|
if config.alarm_string_not_equal and (detectedValue != config.alarm_string_not_equal):
|
||||||
return 'value is "{}", but should be "{}"'.format(detectedValue, config.alarm_string_not_equal)
|
return 'value is "{}", but should be "{}"'.format(detectedValue, config.alarm_string_not_equal)
|
||||||
|
if config.alarm_value_equal and (locale.atof(detectedValue) == float(config.alarm_value_equal)):
|
||||||
# Numeric comparison
|
|
||||||
numeric_value = locale.atof(detectedValue)
|
|
||||||
delta_upper = numeric_value + config.delta
|
|
||||||
delta_lower = numeric_value - config.delta
|
|
||||||
if config.alarm_value_equal and (delta_lower < float(config.alarm_value_equal) or delta_upper > float(config.alarm_value_equal)):
|
|
||||||
return 'value is {}'.format(detectedValue)
|
return 'value is {}'.format(detectedValue)
|
||||||
if config.alarm_value_not_equal and (delta_lower > float(config.alarm_value_not_equal) or delta_upper < float(config.alarm_value_not_equal)):
|
if config.alarm_value_not_equal and (locale.atof(detectedValue) != float(config.alarm_value_not_equal)):
|
||||||
return 'value is {}, but should be {}'.format(detectedValue, config.alarm_value_not_equal)
|
return 'value is {}, but should be {}'.format(detectedValue, config.alarm_value_not_equal)
|
||||||
if config.alarm_value_more_than and locale.atof(detectedValue) > float(config.alarm_value_more_than):
|
if config.alarm_value_more_than and locale.atof(detectedValue) > float(config.alarm_value_more_than):
|
||||||
return 'value is {}, but should not exceed {}'.format(locale.atof(detectedValue), config.alarm_value_more_than)
|
return 'value is {}, but should not exceed {}'.format(locale.atof(detectedValue), config.alarm_value_more_than)
|
||||||
|
@ -48,13 +48,13 @@ import json
|
|||||||
|
|
||||||
|
|
||||||
NAME = 'mddclient'
|
NAME = 'mddclient'
|
||||||
VERSION = '0.1'
|
VERSION = '0.2'
|
||||||
DESCRIPTION = 'A DynamicDns client like ddclient, but supporting multiple (sub)domains'
|
DESCRIPTION = 'A DynamicDns client like ddclient, but supporting multiple (sub)domains'
|
||||||
STATUS_FILE = '/tmp/mddclient.tmp'
|
STATUS_FILE = '/tmp/mddclient.tmp'
|
||||||
CHECKIP_REQUEST_ADDR = 'http://checkip.dyndns.org'
|
CHECKIP_REQUEST_ADDR = 'http://checkip.dyndns.org'
|
||||||
CHECKIP_RESPONSE_PARSER = '<body>Current IP Address: (\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})</body>'
|
CHECKIP_RESPONSE_PARSER = '<body>Current IP Address: (\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})</body>'
|
||||||
DDCLIENT2_REQUEST_ADDR = "https://{}/nic/update?system=dyndns&hostname={}&myip={}"
|
DDCLIENT2_REQUEST_ADDR = "https://{}/nic/update?system=dyndns&hostname={}&myip={}"
|
||||||
DDCLIENT2_RESPONSE_PARSER = '^(nochg|good) (\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})$'
|
DDCLIENT2_RESPONSE_PARSER = '^(nochg|no_change|good) (\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})$'
|
||||||
USER_AGENT = 'Selfhost Utils Mddclient ' + VERSION
|
USER_AGENT = 'Selfhost Utils Mddclient ' + VERSION
|
||||||
|
|
||||||
class Main:
|
class Main:
|
||||||
@ -121,11 +121,7 @@ class Main:
|
|||||||
|
|
||||||
def getCurrentIp(self):
|
def getCurrentIp(self):
|
||||||
'''Obtains current IP from checkip.dyndns.org'''
|
'''Obtains current IP from checkip.dyndns.org'''
|
||||||
try:
|
|
||||||
response = requests.get(CHECKIP_REQUEST_ADDR)
|
response = requests.get(CHECKIP_REQUEST_ADDR)
|
||||||
except Exception as e:
|
|
||||||
self._log.error('Unable to obtain new IP addr: connection error: {}'.format(e))
|
|
||||||
return
|
|
||||||
|
|
||||||
match = re.search(CHECKIP_RESPONSE_PARSER, response.text, re.MULTILINE)
|
match = re.search(CHECKIP_RESPONSE_PARSER, response.text, re.MULTILINE)
|
||||||
if not match:
|
if not match:
|
||||||
@ -159,7 +155,7 @@ class Main:
|
|||||||
if operationResult == 'good':
|
if operationResult == 'good':
|
||||||
# Success!
|
# Success!
|
||||||
return ipAddr
|
return ipAddr
|
||||||
elif operationResult == 'nochg':
|
elif operationResult == 'nochg' or operationResult == 'no_change':
|
||||||
# Should not happen: IP didn't need update
|
# Should not happen: IP didn't need update
|
||||||
self._log.warning('Ip addres didn\'t need update: this should happen only at first run')
|
self._log.warning('Ip addres didn\'t need update: this should happen only at first run')
|
||||||
return ipAddr
|
return ipAddr
|
||||||
@ -182,7 +178,7 @@ class Main:
|
|||||||
elif operationResult == '911':
|
elif operationResult == '911':
|
||||||
raise Exception('There is a problem or scheduled maintenance on server side')
|
raise Exception('There is a problem or scheduled maintenance on server side')
|
||||||
else:
|
else:
|
||||||
raise Exception('Server returned an unknown result code: {}}'.format(operationResult))
|
raise Exception('Server returned an unknown result code: {}'.format(operationResult))
|
||||||
|
|
||||||
|
|
||||||
class Status:
|
class Status:
|
||||||
@ -268,7 +264,11 @@ if __name__ == '__main__':
|
|||||||
level = logging.WARNING
|
level = logging.WARNING
|
||||||
else:
|
else:
|
||||||
level = logging.INFO
|
level = logging.INFO
|
||||||
logging.basicConfig(level=level, format='%(asctime)s %(message)s')
|
logging.basicConfig(
|
||||||
|
format='%(asctime)s %(levelname)-8s %(message)s',
|
||||||
|
level=level,
|
||||||
|
datefmt='%Y-%m-%d %H:%M:%S'
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
main = Main(args.configFile)
|
main = Main(args.configFile)
|
||||||
|
Loading…
Reference in New Issue
Block a user