diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 7c55f6a7..c00380f3 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3.1.0 - name: Spell-Checking uses: codespell-project/actions-codespell@master diff --git a/.github/workflows/editorconfig-checker.yml b/.github/workflows/editorconfig-checker.yml index b21598ba..08b8659e 100644 --- a/.github/workflows/editorconfig-checker.yml +++ b/.github/workflows/editorconfig-checker.yml @@ -9,6 +9,6 @@ jobs: name: editorconfig-checker runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.0.2 + - uses: actions/checkout@v3.1.0 - uses: editorconfig-checker/action-editorconfig-checker@main # current tag v1.0.0 is really out-of-date - run: editorconfig-checker diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..d08d3732 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,25 @@ +name: Mark stale issues + +on: + schedule: + - cron: "0 8 * * *" + workflow_dispatch: + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + + steps: + - uses: actions/stale@v6.0.1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 30 + days-before-close: 5 + stale-issue-message: "This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days." + stale-issue-label: "stale" + exempt-issue-labels: "Bug, Bugfix in progress, Fixed in next release, Internal, Never stale" + exempt-all-issue-assignees: true + operations-per-run: 300 + close-issue-reason: "not_planned" diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml index 27d966d2..f689ae36 100644 --- a/.github/workflows/sync-back-to-dev.yml +++ b/.github/workflows/sync-back-to-dev.yml @@ -11,7 +11,7 @@ jobs: name: Syncing branches steps: - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3.1.0 - name: Opening pull request id: pull uses: tretuna/sync-branches@1.4.0 @@ -20,7 +20,7 @@ jobs: FROM_BRANCH: 'master' TO_BRANCH: 'development' - name: Label the pull request to ignore for release note generation - uses: actions-ecosystem/action-add-labels@v1.1.0 + uses: actions-ecosystem/action-add-labels@v1.1.3 with: labels: internal repo: ${{ github.repository }} diff --git a/padd.sh b/padd.sh index 7c2fa867..8dacef4b 100755 --- a/padd.sh +++ b/padd.sh @@ -18,7 +18,7 @@ LC_NUMERIC=C ############################################ VARIABLES ############################################# # VERSION -padd_version="v3.8.1" +padd_version="v3.9.0" # LastChecks LastCheckVersionInformation=$(date +%s) @@ -71,8 +71,8 @@ mini_status_dns_down="${check_box_bad} DNS off!" mini_status_unknown="${check_box_question} Status unknown" # REGULAR STATUS -full_status_ok="${check_box_good} System is healthy." -full_status_update="${check_box_info} Updates are available." +full_status_ok="${check_box_good} System is healthy" +full_status_update="${check_box_info} Updates are available" full_status_hot="${check_box_bad} System is hot!" full_status_off="${check_box_bad} Pi-hole is offline" full_status_ftl_down="${check_box_info} FTL is down!" @@ -80,17 +80,17 @@ full_status_dns_down="${check_box_bad} DNS is off!" full_status_unknown="${check_box_question} Status unknown!" # MEGA STATUS -mega_status_ok="${check_box_good} Your system is healthy." -mega_status_update="${check_box_info} Updates are available." +mega_status_ok="${check_box_good} Your system is healthy" +mega_status_update="${check_box_info} Updates are available" mega_status_hot="${check_box_bad} Your system is hot!" -mega_status_off="${check_box_bad} Pi-hole is offline." -mega_status_ftl_down="${check_box_info} FTLDNS service is not running." +mega_status_off="${check_box_bad} Pi-hole is offline!" +mega_status_ftl_down="${check_box_info} FTLDNS service is not running!" mega_status_dns_down="${check_box_bad} Pi-hole's DNS server is off!" -mega_status_unknown="${check_box_question} Unable to determine Pi-hole status." +mega_status_unknown="${check_box_question} Unable to determine Pi-hole status!" # TINY STATUS -tiny_status_ok="${check_box_good} System is healthy." -tiny_status_update="${check_box_info} Updates are available." +tiny_status_ok="${check_box_good} System is healthy" +tiny_status_update="${check_box_info} Updates are available" tiny_status_hot="${check_box_bad} System is hot!" tiny_status_off="${check_box_bad} Pi-hole is offline" tiny_status_ftl_down="${check_box_info} FTL is down!" @@ -159,54 +159,16 @@ GetSummaryInformation() { else top_client="${top_client_raw}" fi - - if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then - ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 10 "color") - elif [ "$1" = "mini" ]; then - ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 20 "color") - - if [ ${#latest_blocked} -gt 30 ]; then - latest_blocked=$(echo "$latest_blocked" | cut -c1-27)"..." - fi - - if [ ${#top_blocked} -gt 30 ]; then - top_blocked=$(echo "$top_blocked" | cut -c1-27)"..." - fi - elif [ "$1" = "tiny" ]; then - ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 30 "color") - - if [ ${#latest_blocked} -gt 38 ]; then - latest_blocked=$(echo "$latest_blocked" | cut -c1-38)"..." - fi - - if [ ${#top_blocked} -gt 38 ]; then - top_blocked=$(echo "$top_blocked" | cut -c1-38)"..." - fi - - if [ ${#top_domain} -gt 38 ]; then - top_domain=$(echo "$top_domain" | cut -c1-38)"..." - fi - - if [ ${#top_client} -gt 38 ]; then - top_client=$(echo "$top_client" | cut -c1-38)"..." - fi - elif [ "$1" = "regular" ] || [ "$1" = "slim" ]; then - ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 40 "color") - else - ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 30 "color") - fi } GetSystemInformation() { # System uptime - if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then - system_uptime=$(uptime | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/){if ($9=="min") {d=$6;m=$8} else {d=$6;h=$8;m=$9}} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours"}') - else - system_uptime=$(uptime | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/){if ($9=="min") {d=$6;m=$8} else {d=$6;h=$8;m=$9}} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes"}') - fi + system_uptime_raw=$(uptime) # CPU temperature - if [ -f /sys/class/thermal/thermal_zone0/temp ]; then + if [ -d "/sys/devices/platform/coretemp.0/hwmon/" ]; then + cpu=$(cat "$(find /sys/devices/platform/coretemp.0/hwmon/ -maxdepth 2 -name "temp1_input" 2>/dev/null | head -1)") + elif [ -f /sys/class/thermal/thermal_zone0/temp ]; then cpu=$(cat /sys/class/thermal/thermal_zone0/temp) elif [ -f /sys/class/hwmon/hwmon0/temp1_input ]; then cpu=$(cat /sys/class/hwmon/hwmon0/temp1_input) @@ -219,7 +181,6 @@ GetSystemInformation() { temperature="$(printf %.1f "$(echo "${cpu}" | awk '{print $1 * 9 / 5000 + 32}')")°F" elif [ "${TEMPERATUREUNIT}" = "K" ]; then temperature="$(printf %.1f "$(echo "${cpu}" | awk '{print $1 / 1000 + 273.15}')")°K" - # Addresses Issue 1: https://github.com/jpmck/PAD/issues/1 else temperature="$(printf %.1f "$(echo "${cpu}" | awk '{print $1 / 1000}')")°C" fi @@ -254,24 +215,56 @@ GetSystemInformation() { memory_percent=$(awk '/MemTotal:/{total=$2} /MemFree:/{free=$2} /Buffers:/{buffers=$2} /^Cached:/{cached=$2} END {printf "%.1f", (total-free-buffers-cached)*100/total}' '/proc/meminfo') memory_heatmap=$(HeatmapGenerator "${memory_percent}") - # Bar generations - if [ "$1" = "mini" ]; then - cpu_bar=$(BarGenerator "${cpu_percent}" 20) - memory_bar=$(BarGenerator "${memory_percent}" 20) - elif [ "$1" = "tiny" ]; then - cpu_bar=$(BarGenerator "${cpu_percent}" 7) - memory_bar=$(BarGenerator "${memory_percent}" 7) - else - cpu_bar=$(BarGenerator "${cpu_percent}" 10) - memory_bar=$(BarGenerator "${memory_percent}" 10) - fi - # Device model - if [ -f /sys/firmware/devicetree/base/model ]; then + if [ -f /sys/devices/virtual/dmi/id/product_name ] || [ -f /sys/devices/virtual/dmi/id/product_family ]; then # Get model, remove possible null byte + product_name=$(tr -d '\0' < /sys/devices/virtual/dmi/id/product_name) + product_family=$(tr -d '\0' < /sys/devices/virtual/dmi/id/product_family) + sys_model="$(echo "$product_name" | grep "$product_family")" + + # If product_family is not contained in product_name, both are shown + if [ -z "$sys_model" ]; then + sys_model="${product_family} ${product_name}" + fi + elif [ -f /sys/firmware/devicetree/base/model ]; then sys_model=$(tr -d '\0' < /sys/firmware/devicetree/base/model) - else - sys_model="" + elif [ -f /sys/devices/virtual/dmi/id/board_vendor ] || [ -f /sys/devices/virtual/dmi/id/board_name ]; then + sys_model=$(tr -d '\0' < /sys/devices/virtual/dmi/id/board_vendor) + sys_model="$sys_model $(tr -d '\0' < /sys/devices/virtual/dmi/id/board_name)" + elif [ -f /tmp/sysinfo/model ]; then + sys_model=$(tr -d '\0' < /tmp/sysinfo/model) + fi + + # Cleaning device model from useless OEM information + sys_model=${sys_model#"To be filled by O.E.M."} + sys_model=${sys_model%"To be filled by O.E.M."} + sys_model=${sys_model#"To Be Filled*"} + sys_model=${sys_model%"To Be Filled*"} + sys_model=${sys_model#"OEM*"} + sys_model=${sys_model%"OEM*"} + sys_model=${sys_model#"Not Applicable"} + sys_model=${sys_model%"Not Applicable"} + sys_model=${sys_model#"System Product Name"} + sys_model=${sys_model%"System Product Name"} + sys_model=${sys_model#"System Version"} + sys_model=${sys_model%"System Version"} + sys_model=${sys_model#"Undefined"} + sys_model=${sys_model%"Undefined"} + sys_model=${sys_model#"Default string"} + sys_model=${sys_model%"Default string"} + sys_model=${sys_model#"Not Specified"} + sys_model=${sys_model%"Not Specified"} + sys_model=${sys_model#"Type1ProductConfigId"} + sys_model=${sys_model%"Type1ProductConfigId"} + sys_model=${sys_model#"INVALID"} + sys_model=${sys_model%"INVALID"} + sys_model=${sys_model#"All Series"} + sys_model=${sys_model%"All Series"} + sys_model=${sys_model#"�"} + sys_model=${sys_model%"�"} + + if [ -z "$sys_model" ]; then + sys_model="Unknown" fi } @@ -307,7 +300,6 @@ GetNetworkInformation() { # Get hostname and gateway pi_hostname=$(hostname) - pi_gateway=$(ip r | grep 'default' | awk '{print $3}') full_hostname=${pi_hostname} # does the Pi-hole have a domain set? @@ -336,15 +328,7 @@ GetNetworkInformation() { # if there's only one DNS server if [ ${dns_count} -eq 1 ]; then - if [ "${PIHOLE_DNS_1}" = "127.0.0.1#5053" ]; then - dns_information="1 server (Cloudflared)" - elif [ "${PIHOLE_DNS_1}" = "${pi_gateway}#53" ]; then - dns_information="1 server (gateway)" - else dns_information="1 server" - fi - elif [ ${dns_count} -gt 8 ]; then - dns_information="8+ servers" else dns_information="${dns_count} servers" fi @@ -374,7 +358,6 @@ GetNetworkInformation() { dhcp_check_box=${check_box_bad} # if the DHCP Router variable isn't set - # Issue 3: https://github.com/jpmck/PADD/issues/3 if [ -z ${DHCP_ROUTER+x} ]; then DHCP_ROUTER=$(GetFTLData "gateway" | awk '{ printf $1 }') fi @@ -479,75 +462,90 @@ GetPiholeInformation() { GetVersionInformation() { # Check if version status has been saved - core_version=$(pihole -v -p | awk '{print $4}' | tr -d '[:alpha:]') - core_version_latest=$(pihole -v -p | awk '{print $(NF)}' | tr -d ')') - - # if core_version is something else then x.xx or x.xx.xxx set it to N/A - if ! echo "${core_version}" | grep -qE '^[0-9]+([.][0-9]+){1,2}$' || [ "${core_version_latest}" = "ERROR" ]; then - core_version="N/A" - core_version_heatmap=${yellow_text} + # all info is sourced from /etc/pihole/versions + + # Gather CORE version information... + # Extract vx.xx or vx.xx.xxx version + CORE_VERSION="$(echo "${CORE_VERSION}" | grep -oE '^v[0-9]+([.][0-9]+){1,2}')" + if [ "${CORE_BRANCH}" = "master" ]; then + if [ "${CORE_HASH}" = "${GITHUB_CORE_HASH}" ]; then + # up-to-date + core_version_heatmap=${green_text} + else + #out-of-date + out_of_date_flag="true" + core_version_heatmap=${red_text} + fi else - # remove the leading "v" from core_version_latest - core_version_latest=$(echo "${core_version_latest}" | tr -d '\r\n[:alpha:]') - # is core up-to-date? - if [ "${core_version}" != "${core_version_latest}" ]; then - out_of_date_flag="true" + # Custom branch + if [ -z "${CORE_BRANCH}" ]; then + # Branch name is empty, something went wrong core_version_heatmap=${red_text} + CORE_VERSION="?" else - core_version_heatmap=${green_text} + core_version_heatmap=${yellow_text} + # shorten common branch names (fix/, tweak/, new/) + # use the first 7 characters of the branch name as version + CORE_VERSION="$(printf '%s' "$CORE_BRANCH" | sed 's/fix\//f\//;s/new\//n\//;s/tweak\//t\//' | cut -c 1-7)" fi - # add leading "v" to version number - core_version="v${core_version}" fi # Gather web version information... + # Extract vx.xx or vx.xx.xxx version if [ "$INSTALL_WEB_INTERFACE" = true ]; then - web_version=$(pihole -v -a | awk '{print $4}' | tr -d '[:alpha:]') - web_version_latest=$(pihole -v -a | awk '{print $(NF)}' | tr -d ')') - - # if web_version is something else then x.xx or x.xx.xxx set it to N/A - if ! echo "${web_version}" | grep -qE '^[0-9]+([.][0-9]+){1,2}$' || [ "${web_version_latest}" = "ERROR" ]; then - web_version="N/A" - web_version_heatmap=${yellow_text} + WEB_VERSION="$(echo "${WEB_VERSION}" | grep -oE '^v[0-9]+([.][0-9]+){1,2}')" + if [ "${WEB_BRANCH}" = "master" ]; then + if [ "${WEB_HASH}" = "${GITHUB_WEB_HASH}" ]; then + # up-to-date + web_version_heatmap=${green_text} + else + #out-of-date + out_of_date_flag="true" + web_version_heatmap=${red_text} + fi else - # remove the leading "v" from web_version_latest - web_version_latest=$(echo "${web_version_latest}" | tr -d '\r\n[:alpha:]') - # is web up-to-date? - if [ "${web_version}" != "${web_version_latest}" ]; then - out_of_date_flag="true" + # Custom branch + if [ -z "${WEB_BRANCH}" ]; then + # Branch name is empty, something went wrong web_version_heatmap=${red_text} + WEB_VERSION="?" else - web_version_heatmap=${green_text} + web_version_heatmap=${yellow_text} + # shorten common branch names (fix/, tweak/, new/) + # use the first 7 characters of the branch name as version + WEB_VERSION="$(printf '%s' "$WEB_BRANCH" | sed 's/fix\//f\//;s/new\//n\//;s/tweak\//t\//' | cut -c 1-7)" fi - # add leading "v" to version number - web_version="v${web_version}" fi else # Web interface not installed - web_version="N/A" + WEB_VERSION="N/A" web_version_heatmap=${yellow_text} fi # Gather FTL version information... - ftl_version=$(pihole -v -f | awk '{print $4}' | tr -d '[:alpha:]') - ftl_version_latest=$(pihole -v -f | awk '{print $(NF)}' | tr -d ')') - - # if ftl_version is something else then x.xx or x.xx.xxx set it to N/A - if ! echo "${ftl_version}" | grep -qE '^[0-9]+([.][0-9]+){1,2}$' || [ "${ftl_version_latest}" = "ERROR" ]; then - ftl_version="N/A" - ftl_version_heatmap=${yellow_text} + # Extract vx.xx or vx.xx.xxx version + FTL_VERSION="$(echo "${FTL_VERSION}" | grep -oE '^v[0-9]+([.][0-9]+){1,2}')" + if [ "${FTL_BRANCH}" = "master" ]; then + if [ "${FTL_HASH}" = "${GITHUB_FTL_HASH}" ]; then + # up-to-date + ftl_version_heatmap=${green_text} + else + #out-of-date + out_of_date_flag="true" + ftl_version_heatmap=${red_text} + fi else - # remove the leading "v" from ftl_version_latest - ftl_version_latest=$(echo "${ftl_version_latest}" | tr -d '\r\n[:alpha:]') - # is ftl up-to-date? - if [ "${ftl_version}" != "${ftl_version_latest}" ]; then - out_of_date_flag="true" + # Custom branch + if [ -z "${FTL_BRANCH}" ]; then + # Branch name is empty, something went wrong ftl_version_heatmap=${red_text} + FTL_VERSION="?" else - ftl_version_heatmap=${green_text} + ftl_version_heatmap=${yellow_text} + # shorten common branch names (fix/, tweak/, new/) + # use the first 7 characters of the branch name as version + FTL_VERSION="$(printf '%s' "$FTL_BRANCH" | sed 's/fix\//f\//;s/new\//n\//;s/tweak\//t\//' | cut -c 1-7)" fi - # add leading "v" to version number - ftl_version="v${ftl_version}" fi # PADD version information... @@ -599,6 +597,63 @@ GetVersionInformation() { fi } +GenerateSizeDependendOutput() { + if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then + ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 10 "color") + elif [ "$1" = "mini" ]; then + ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 20 "color") + + if [ ${#latest_blocked} -gt 30 ]; then + latest_blocked=$(echo "$latest_blocked" | cut -c1-27)"..." + fi + + if [ ${#top_blocked} -gt 30 ]; then + top_blocked=$(echo "$top_blocked" | cut -c1-27)"..." + fi + elif [ "$1" = "tiny" ]; then + ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 30 "color") + + if [ ${#latest_blocked} -gt 38 ]; then + latest_blocked=$(echo "$latest_blocked" | cut -c1-38)"..." + fi + + if [ ${#top_blocked} -gt 38 ]; then + top_blocked=$(echo "$top_blocked" | cut -c1-38)"..." + fi + + if [ ${#top_domain} -gt 38 ]; then + top_domain=$(echo "$top_domain" | cut -c1-38)"..." + fi + + if [ ${#top_client} -gt 38 ]; then + top_client=$(echo "$top_client" | cut -c1-38)"..." + fi + elif [ "$1" = "regular" ] || [ "$1" = "slim" ]; then + ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 40 "color") + else + ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 30 "color") + fi + + # System uptime + if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then + system_uptime=$(echo "${system_uptime_raw}" | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/){if ($9=="min") {d=$6;m=$8} else {d=$6;h=$8;m=$9}} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours"}') + else + system_uptime=$(echo "${system_uptime_raw}" | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/){if ($9=="min") {d=$6;m=$8} else {d=$6;h=$8;m=$9}} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes"}') + fi + + # Bar generations + if [ "$1" = "mini" ]; then + cpu_bar=$(BarGenerator "${cpu_percent}" 20) + memory_bar=$(BarGenerator "${memory_percent}" 20) + elif [ "$1" = "tiny" ]; then + cpu_bar=$(BarGenerator "${cpu_percent}" 7) + memory_bar=$(BarGenerator "${memory_percent}" 7) + else + cpu_bar=$(BarGenerator "${cpu_percent}" 10) + memory_bar=$(BarGenerator "${memory_percent}" 10) + fi +} + ############################################# PRINTERS ############################################# PrintLogo() { @@ -612,185 +667,201 @@ PrintLogo() { elif [ "$1" = "mini" ]; then printf "%s${clear_line}\n${clear_line}\n" "${padd_text}${dim_text}mini${reset_text} ${mini_status}" elif [ "$1" = "tiny" ]; then - printf "%s${clear_line}\n" "${padd_text}${dim_text}tiny${reset_text} Pi-hole® ${core_version_heatmap}${core_version}${reset_text}, Web ${web_version_heatmap}${web_version}${reset_text}, FTL ${ftl_version_heatmap}${ftl_version}${reset_text}" + printf "%s${clear_line}\n" "${padd_text}${dim_text}tiny${reset_text} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}" printf "%s${clear_line}\n" " PADD ${padd_version_heatmap}${padd_version}${reset_text} ${tiny_status}${reset_text}" elif [ "$1" = "slim" ]; then printf "%s${clear_line}\n${clear_line}\n" "${padd_text}${dim_text}slim${reset_text} ${full_status}" elif [ "$1" = "regular" ] || [ "$1" = "slim" ]; then printf "%s${clear_line}\n" "${padd_logo_1}" - printf "%s${clear_line}\n" "${padd_logo_2}Pi-hole® ${core_version_heatmap}${core_version}${reset_text}, Web ${web_version_heatmap}${web_version}${reset_text}, FTL ${ftl_version_heatmap}${ftl_version}${reset_text}" + printf "%s${clear_line}\n" "${padd_logo_2}Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}" printf "%s${clear_line}\n${clear_line}\n" "${padd_logo_3}PADD ${padd_version_heatmap}${padd_version}${reset_text} ${full_status}${reset_text}" # normal or not defined else printf "%s${clear_line}\n" "${padd_logo_retro_1}" - printf "%s${clear_line}\n" "${padd_logo_retro_2} Pi-hole® ${core_version_heatmap}${core_version}${reset_text}, Web ${web_version_heatmap}${web_version}${reset_text}, FTL ${ftl_version_heatmap}${ftl_version}${reset_text}, PADD ${padd_version_heatmap}${padd_version}${reset_text}" + printf "%s${clear_line}\n" "${padd_logo_retro_2} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}, PADD ${padd_version_heatmap}${padd_version}${reset_text}" printf "%s${clear_line}\n${clear_line}\n" "${padd_logo_retro_3} ${pihole_check_box} Core ${ftl_check_box} FTL ${mega_status}${reset_text}" fi } PrintDashboard() { + # Clear the screen and move cursor to (0,0). + # This mimics the 'clear' command. + # https://vt100.net/docs/vt510-rm/ED.html + # https://vt100.net/docs/vt510-rm/CUP.html + # E3 extension `\e[3J` to clear the scrollback buffer (see 'man clear') + + printf '\e[H\e[2J\e[3J' + + # adds the y-offset + moveYOffset + if [ "$1" = "pico" ]; then # pico is a screen at least 20x10 (columns x lines) - printf "%s${clear_line}\n" "p${padd_text} ${pico_status}" - printf "%s${clear_line}\n" "${bold_text}PI-HOLE ============${reset_text}" - printf "%s${clear_line}\n" " [${ads_blocked_bar}] ${ads_percentage_today}%" - printf "%s${clear_line}\n" " ${ads_blocked_today} / ${dns_queries_today}" - printf "%s${clear_line}\n" "${bold_text}NETWORK ============${reset_text}" - printf "%s${clear_line}\n" " Hst: ${pi_hostname}" - printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}" - printf "%s${clear_line}\n" " DHCP ${dhcp_check_box} IPv6 ${dhcp_ipv6_check_box}" - printf "%s${clear_line}\n" "${bold_text}CPU ================${reset_text}" - printf "%s${clear_line}" " [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%" + moveXOffset; printf "%s${clear_line}\n" "p${padd_text} ${pico_status}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ============${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " [${ads_blocked_bar}] ${ads_percentage_today}%" + moveXOffset; printf "%s${clear_line}\n" " ${ads_blocked_today} / ${dns_queries_today}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ============${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " Hst: ${pi_hostname}" + moveXOffset; printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}" + moveXOffset; printf "%s${clear_line}\n" " DHCP ${dhcp_check_box} IPv6 ${dhcp_ipv6_check_box}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}CPU ================${reset_text}" + moveXOffset; printf "%s${clear_line}" " [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%" elif [ "$1" = "nano" ]; then # nano is a screen at least 24x12 (columns x lines) - printf "%s${clear_line}\n" "n${padd_text} ${mini_status}" - printf "%s${clear_line}\n" "${bold_text}PI-HOLE ================${reset_text}" - printf "%s${clear_line}\n" " Up: ${pihole_check_box} FTL: ${ftl_check_box}" - printf "%s${clear_line}\n" " Blk: [${ads_blocked_bar}] ${ads_percentage_today}%" - printf "%s${clear_line}\n" " Blk: ${ads_blocked_today} / ${dns_queries_today}" - printf "%s${clear_line}\n" "${bold_text}NETWORK ================${reset_text}" - printf "%s${clear_line}\n" " Host: ${pi_hostname}" - printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}" - printf "%s${clear_line}\n" " DHCP: ${dhcp_check_box} IPv6: ${dhcp_ipv6_check_box}" - printf "%s${clear_line}\n" "${bold_text}SYSTEM =================${reset_text}" - printf "%s${clear_line}\n" " Up: ${system_uptime}" - printf "%s${clear_line}" " CPU: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%" + moveXOffset; printf "%s${clear_line}\n" "n${padd_text} ${mini_status}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ================${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " Up: ${pihole_check_box} FTL: ${ftl_check_box}" + moveXOffset; printf "%s${clear_line}\n" " Blk: [${ads_blocked_bar}] ${ads_percentage_today}%" + moveXOffset; printf "%s${clear_line}\n" " Blk: ${ads_blocked_today} / ${dns_queries_today}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ================${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " Host: ${pi_hostname}" + moveXOffset; printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}" + moveXOffset; printf "%s${clear_line}\n" " DHCP: ${dhcp_check_box} IPv6: ${dhcp_ipv6_check_box}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =================${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " Up: ${system_uptime}" + moveXOffset; printf "%s${clear_line}" " CPU: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%" elif [ "$1" = "micro" ]; then # micro is a screen at least 30x16 (columns x lines) - printf "%s${clear_line}\n" "µ${padd_text} ${mini_status}" - printf "%s${clear_line}\n" "" - printf "%s${clear_line}\n" "${bold_text}PI-HOLE ======================${reset_text}" - printf "%s${clear_line}\n" " Status: ${pihole_check_box} FTL: ${ftl_check_box}" - printf "%s${clear_line}\n" "${bold_text}STATS ========================${reset_text}" - printf "%s${clear_line}\n" " Blckng: ${domains_being_blocked} domains" - printf "%s${clear_line}\n" " Piholed: [${ads_blocked_bar}] ${ads_percentage_today}%" - printf "%s${clear_line}\n" " Piholed: ${ads_blocked_today} / ${dns_queries_today}" - printf "%s${clear_line}\n" "${bold_text}NETWORK ======================${reset_text}" - printf "%s${clear_line}\n" " Host: ${full_hostname}" - printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}" - printf "%s${clear_line}\n" " DHCP: ${dhcp_check_box} IPv6: ${dhcp_ipv6_check_box}" - printf "%s${clear_line}\n" "${bold_text}SYSTEM =======================${reset_text}" - printf "%s${clear_line}\n" " Uptime: ${system_uptime}" - printf "%s${clear_line}\n" " Load: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%" - printf "%s${clear_line}" " Memory: [${memory_heatmap}${memory_bar}${reset_text}] ${memory_percent}%" + moveXOffset; printf "%s${clear_line}\n" "µ${padd_text} ${mini_status}" + moveXOffset; printf "%s${clear_line}\n" "" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ======================${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " Status: ${pihole_check_box} FTL: ${ftl_check_box}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS ========================${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " Blckng: ${domains_being_blocked} domains" + moveXOffset; printf "%s${clear_line}\n" " Piholed: [${ads_blocked_bar}] ${ads_percentage_today}%" + moveXOffset; printf "%s${clear_line}\n" " Piholed: ${ads_blocked_today} / ${dns_queries_today}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ======================${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " Host: ${full_hostname}" + moveXOffset; printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}" + moveXOffset; printf "%s${clear_line}\n" " DHCP: ${dhcp_check_box} IPv6: ${dhcp_ipv6_check_box}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =======================${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " Uptime: ${system_uptime}" + moveXOffset; printf "%s${clear_line}\n" " Load: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%" + moveXOffset; printf "%s${clear_line}" " Memory: [${memory_heatmap}${memory_bar}${reset_text}] ${memory_percent}%" elif [ "$1" = "mini" ]; then # mini is a screen at least 40x18 (columns x lines) - printf "%s${clear_line}\n" "${padd_text}${dim_text}mini${reset_text} ${mini_status}" - printf "%s${clear_line}\n" "" - printf "%s${clear_line}\n" "${bold_text}PI-HOLE ================================${reset_text}" - printf " %-9s${pihole_heatmap}%-10s${reset_text} %-9s${ftl_heatmap}%-10s${reset_text}${clear_line}\n" "Status:" "${pihole_status}" "FTL:" "${ftl_status}" - printf "%s${clear_line}\n" "${bold_text}STATS ==================================${reset_text}" - printf " %-9s%-29s${clear_line}\n" "Blckng:" "${domains_being_blocked} domains" - printf " %-9s[%-20s] %-5s${clear_line}\n" "Piholed:" "${ads_blocked_bar}" "${ads_percentage_today}%" - printf " %-9s%-29s${clear_line}\n" "Piholed:" "${ads_blocked_today} out of ${dns_queries_today}" - printf " %-9s%-29s${clear_line}\n" "Latest:" "${latest_blocked}" + moveXOffset; printf "%s${clear_line}\n" "${padd_text}${dim_text}mini${reset_text} ${mini_status}" + moveXOffset; printf "%s${clear_line}\n" "" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ================================${reset_text}" + moveXOffset; printf " %-9s${pihole_heatmap}%-10s${reset_text} %-9s${ftl_heatmap}%-10s${reset_text}${clear_line}\n" "Status:" "${pihole_status}" "FTL:" "${ftl_status}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS ==================================${reset_text}" + moveXOffset; printf " %-9s%-29s${clear_line}\n" "Blckng:" "${domains_being_blocked} domains" + moveXOffset; printf " %-9s[%-20s] %-5s${clear_line}\n" "Piholed:" "${ads_blocked_bar}" "${ads_percentage_today}%" + moveXOffset; printf " %-9s%-29s${clear_line}\n" "Piholed:" "${ads_blocked_today} out of ${dns_queries_today}" + moveXOffset; printf " %-9s%-29s${clear_line}\n" "Latest:" "${latest_blocked}" if [ "${DHCP_ACTIVE}" != "true" ]; then - printf " %-9s%-29s${clear_line}\n" "Top Ad:" "${top_blocked}" + moveXOffset; printf " %-9s%-29s${clear_line}\n" "Top Ad:" "${top_blocked}" fi - printf "%s${clear_line}\n" "${bold_text}NETWORK ================================${reset_text}" - printf " %-9s%-15s%-4s%-11s${clear_line}\n" "Host:" "${full_hostname}" "IP:" "${pi_ip4_addr}" - printf " %-9s%-8s %-4s%-5s %-4s%-5s${clear_line}\n" "Iface:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}" - printf " %-9s%-10s${clear_line}\n" "DNS:" "${dns_information}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ================================${reset_text}" + moveXOffset; printf " %-9s%-16s%-5s%-9s${clear_line}\n" "Host:" "${full_hostname}" "DNS:" "${dns_information}" + moveXOffset; printf " %-9s%s${clear_line}\n" "IP:" "${pi_ip4_addr} (${iface_name})" + moveXOffset; printf " %-9s%-4s%-12s%-4s%-5s${clear_line}\n" "Traffic:" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}" if [ "${DHCP_ACTIVE}" = "true" ]; then - printf " %-9s${dhcp_heatmap}%-10s${reset_text} %-9s${dhcp_ipv6_heatmap}%-10s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status} + moveXOffset; printf " %-9s${dhcp_heatmap}%-10s${reset_text} %-9s${dhcp_ipv6_heatmap}%-10s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status} fi - printf "%s${clear_line}\n" "${bold_text}SYSTEM =================================${reset_text}" - printf " %-9s%-29s${clear_line}\n" "Uptime:" "${system_uptime}" - printf "%s${clear_line}\n" " Load: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%" - printf "%s${clear_line}" " Memory: [${memory_heatmap}${memory_bar}${reset_text}] ${memory_percent}%" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =================================${reset_text}" + moveXOffset; printf " %-9s%-29s${clear_line}\n" "Uptime:" "${system_uptime}" + moveXOffset; printf "%s${clear_line}\n" " Load: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%" + moveXOffset; printf "%s${clear_line}" " Memory: [${memory_heatmap}${memory_bar}${reset_text}] ${memory_percent}%" elif [ "$1" = "tiny" ]; then # tiny is a screen at least 53x20 (columns x lines) - printf "%s${clear_line}\n" "${padd_text}${dim_text}tiny${reset_text} Pi-hole® ${core_version_heatmap}${core_version}${reset_text}, Web ${web_version_heatmap}${web_version}${reset_text}, FTL ${ftl_version_heatmap}${ftl_version}${reset_text}" - printf "%s${clear_line}\n" " PADD ${padd_version_heatmap}${padd_version}${reset_text} ${tiny_status}${reset_text}" - printf "%s${clear_line}\n" "${bold_text}PI-HOLE ============================================${reset_text}" - printf " %-10s${pihole_heatmap}%-16s${reset_text} %-8s${ftl_heatmap}%-10s${reset_text}${clear_line}\n" "Status:" "${pihole_status}" "FTL:" "${ftl_status}" - printf "%s${clear_line}\n" "${bold_text}STATS ==============================================${reset_text}" - printf " %-10s%-29s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains" - printf " %-10s[%-30s] %-5s${clear_line}\n" "Pi-holed:" "${ads_blocked_bar}" "${ads_percentage_today}%" - printf " %-10s%-39s${clear_line}\n" "Pi-holed:" "${ads_blocked_today} out of ${dns_queries_today}" - printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}" - printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}" + moveXOffset; printf "%s${clear_line}\n" "${padd_text}${dim_text}tiny${reset_text} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " PADD ${padd_version_heatmap}${padd_version}${reset_text} ${tiny_status}${reset_text}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ============================================${reset_text}" + moveXOffset; printf " %-10s${pihole_heatmap}%-16s${reset_text} %-8s${ftl_heatmap}%-10s${reset_text}${clear_line}\n" "Status:" "${pihole_status}" "FTL:" "${ftl_status}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS ==============================================${reset_text}" + moveXOffset; printf " %-10s%-29s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains" + moveXOffset; printf " %-10s[%-30s] %-5s${clear_line}\n" "Pi-holed:" "${ads_blocked_bar}" "${ads_percentage_today}%" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Pi-holed:" "${ads_blocked_today} out of ${dns_queries_today}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}" if [ "${DHCP_ACTIVE}" != "true" ]; then - printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}" - printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}" fi - printf "%s${clear_line}\n" "${bold_text}NETWORK ============================================${reset_text}" - printf " %-10s%-16s %-8s%-16s${clear_line}\n" "Hostname:" "${full_hostname}" "IP: " "${pi_ip4_addr}" - printf " %-10s%-16s %-4s%-5s %-4s%-5s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}" - printf " %-10s%-16s %-8s${dnssec_heatmap}%-16s${reset_text}${clear_line}\n" "DNS:" "${dns_information}" "DNSSEC:" "${dnssec_status}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ============================================${reset_text}" + moveXOffset; printf " %-10s%-16s %-8s%-16s${clear_line}\n" "Hostname:" "${full_hostname}" "IP: " "${pi_ip4_addr}" + moveXOffset; printf " %-10s%-16s %-4s%-7s %-4s%-5s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}" + moveXOffset; printf " %-10s%-16s %-8s${dnssec_heatmap}%-16s${reset_text}${clear_line}\n" "DNS:" "${dns_information}" "DNSSEC:" "${dnssec_status}" if [ "${DHCP_ACTIVE}" = "true" ]; then - printf " %-10s${dhcp_heatmap}%-16s${reset_text} %-8s${dhcp_ipv6_heatmap}%-10s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status} - printf "%s${clear_line}\n" "${dhcp_info}" + moveXOffset; printf " %-10s${dhcp_heatmap}%-16s${reset_text} %-8s${dhcp_ipv6_heatmap}%-10s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status} + moveXOffset; printf "%s${clear_line}\n" "${dhcp_info}" fi - printf "%s${clear_line}\n" "${bold_text}SYSTEM =============================================${reset_text}" - printf " %-10s%-29s${clear_line}\n" "Uptime:" "${system_uptime}" - printf " %-10s${temp_heatmap}%-17s${reset_text} %-8s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-4s${reset_text}${clear_line}\n" "CPU Temp:" "${temperature}" "Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}" - printf " %-10s[${memory_heatmap}%-7s${reset_text}] %-6s %-8s[${cpu_load_1_heatmap}%-7s${reset_text}] %-5s${clear_line}" "Memory:" "${memory_bar}" "${memory_percent}%" "CPU:" "${cpu_bar}" "${cpu_percent}%" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =============================================${reset_text}" + moveXOffset; printf " %-10s%-29s${clear_line}\n" "Uptime:" "${system_uptime}" + moveXOffset; printf " %-10s${temp_heatmap}%-17s${reset_text} %-8s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-4s${reset_text}${clear_line}\n" "CPU Temp:" "${temperature}" "Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}" + moveXOffset; printf " %-10s[${memory_heatmap}%-7s${reset_text}] %-6s %-8s[${cpu_load_1_heatmap}%-7s${reset_text}] %-5s${clear_line}" "Memory:" "${memory_bar}" "${memory_percent}%" "CPU:" "${cpu_bar}" "${cpu_percent}%" elif [ "$1" = "regular" ] || [ "$1" = "slim" ]; then # slim is a screen with at least 60 columns and exactly 21 lines # regular is a screen at least 60x22 (columns x lines) if [ "$1" = "slim" ]; then - printf "%s${clear_line}\n" "${padd_text}${dim_text}slim${reset_text} ${full_status}" - printf "%s${clear_line}\n" "" + moveXOffset; printf "%s${clear_line}\n" "${padd_text}${dim_text}slim${reset_text} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}" + moveXOffset; printf "%s${clear_line}\n" " PADD ${padd_version_heatmap}${padd_version}${reset_text} ${full_status}${reset_text}" + moveXOffset; printf "%s${clear_line}\n" "" else - printf "%s${clear_line}\n" "${padd_logo_1}" - printf "%s${clear_line}\n" "${padd_logo_2}Pi-hole® ${core_version_heatmap}${core_version}${reset_text}, Web ${web_version_heatmap}${web_version}${reset_text}, FTL ${ftl_version_heatmap}${ftl_version}${reset_text}" - printf "%s${clear_line}\n" "${padd_logo_3}PADD ${padd_version_heatmap}${padd_version}${reset_text} ${full_status}${reset_text}" - printf "%s${clear_line}\n" "" + moveXOffset; printf "%s${clear_line}\n" "${padd_logo_1}" + moveXOffset; printf "%s${clear_line}\n" "${padd_logo_2}Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}" + moveXOffset; printf "%s${clear_line}\n" "${padd_logo_3}PADD ${padd_version_heatmap}${padd_version}${reset_text} ${full_status}${reset_text}" + moveXOffset; printf "%s${clear_line}\n" "" fi - printf "%s${clear_line}\n" "${bold_text}PI-HOLE ===================================================${reset_text}" - printf " %-10s${pihole_heatmap}%-19s${reset_text} %-10s${ftl_heatmap}%-19s${reset_text}${clear_line}\n" "Status:" "${pihole_status}" "FTL:" "${ftl_status}" - printf "%s${clear_line}\n" "${bold_text}STATS =====================================================${reset_text}" - printf " %-10s%-49s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains" - printf " %-10s[%-40s] %-5s${clear_line}\n" "Pi-holed:" "${ads_blocked_bar}" "${ads_percentage_today}%" - printf " %-10s%-49s${clear_line}\n" "Pi-holed:" "${ads_blocked_today} out of ${dns_queries_today} queries" - printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}" - printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ===================================================${reset_text}" + moveXOffset; printf " %-10s${pihole_heatmap}%-19s${reset_text} %-10s${ftl_heatmap}%-19s${reset_text}${clear_line}\n" "Status:" "${pihole_status}" "FTL:" "${ftl_status}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS =====================================================${reset_text}" + moveXOffset; printf " %-10s%-49s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains" + moveXOffset; printf " %-10s[%-40s] %-5s${clear_line}\n" "Pi-holed:" "${ads_blocked_bar}" "${ads_percentage_today}%" + moveXOffset; printf " %-10s%-49s${clear_line}\n" "Pi-holed:" "${ads_blocked_today} out of ${dns_queries_today} queries" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}" if [ "${DHCP_ACTIVE}" != "true" ]; then - printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}" - printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}" fi - printf "%s${clear_line}\n" "${bold_text}NETWORK ===================================================${reset_text}" - printf " %-10s%-15s %-4s%-17s%-6s%s${clear_line}\n" "Hostname:" "${full_hostname}" "IP:" "${pi_ip4_addr}" "IPv6:" "${ipv6_check_box}" - printf " %-10s%-15s %-4s%-5s %-4s%-5s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}" - printf " %-10s%-15s %-10s${dnssec_heatmap}%-19s${reset_text}${clear_line}\n" "DNS:" "${dns_information}" "DNSSEC:" "${dnssec_status}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ===================================================${reset_text}" + moveXOffset; printf " %-10s%-15s %-4s%-17s%-6s%s${clear_line}\n" "Hostname:" "${full_hostname}" "IP:" "${pi_ip4_addr}" "IPv6:" "${ipv6_check_box}" + moveXOffset; printf " %-10s%-15s %-4s%-17s%-4s%s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}" + moveXOffset; printf " %-10s%-15s %-10s${dnssec_heatmap}%-19s${reset_text}${clear_line}\n" "DNS:" "${dns_information}" "DNSSEC:" "${dnssec_status}" if [ "${DHCP_ACTIVE}" = "true" ]; then - printf " %-10s${dhcp_heatmap}%-15s${reset_text} %-10s${dhcp_ipv6_heatmap}%-19s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status} - printf "%s${clear_line}\n" "${dhcp_info}" + moveXOffset; printf " %-10s${dhcp_heatmap}%-15s${reset_text} %-10s${dhcp_ipv6_heatmap}%-19s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status} + moveXOffset; printf "%s${clear_line}\n" "${dhcp_info}" fi - printf "%s${clear_line}\n" "${bold_text}SYSTEM ====================================================${reset_text}" - printf " %-10s%-39s${clear_line}\n" "Uptime:" "${system_uptime}" - printf " %-10s${temp_heatmap}%-21s${reset_text}%-10s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-4s${reset_text}${clear_line}\n" "CPU Temp:" "${temperature}" "CPU Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}" - printf " %-10s[${memory_heatmap}%-10s${reset_text}] %-6s %-10s[${cpu_load_1_heatmap}%-10s${reset_text}] %-5s${clear_line}" "Memory:" "${memory_bar}" "${memory_percent}%" "CPU Load:" "${cpu_bar}" "${cpu_percent}%" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM ====================================================${reset_text}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Uptime:" "${system_uptime}" + moveXOffset; printf " %-10s${temp_heatmap}%-21s${reset_text}%-10s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-4s${reset_text}${clear_line}\n" "CPU Temp:" "${temperature}" "CPU Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}" + moveXOffset; printf " %-10s[${memory_heatmap}%-10s${reset_text}] %-6s %-10s[${cpu_load_1_heatmap}%-10s${reset_text}] %-5s${clear_line}" "Memory:" "${memory_bar}" "${memory_percent}%" "CPU Load:" "${cpu_bar}" "${cpu_percent}%" else # ${padd_size} = mega # mega is a screen with at least 80 columns and 26 lines - printf "%s${clear_line}\n" "${padd_logo_retro_1}" - printf "%s${clear_line}\n" "${padd_logo_retro_2} Pi-hole® ${core_version_heatmap}${core_version}${reset_text}, Web ${web_version_heatmap}${web_version}${reset_text}, FTL ${ftl_version_heatmap}${ftl_version}${reset_text}, PADD ${padd_version_heatmap}${padd_version}${reset_text}" - printf "%s${clear_line}\n" "${padd_logo_retro_3} ${pihole_check_box} Core ${ftl_check_box} FTL ${mega_status}${reset_text}" - printf "%s${clear_line}\n" "" - printf "%s${clear_line}\n" "${bold_text}STATS =========================================================================${reset_text}" - printf " %-10s%-19s %-10s[%-40s] %-5s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains" "Piholed:" "${ads_blocked_bar}" "${ads_percentage_today}%" - printf " %-10s%-30s%-29s${clear_line}\n" "Clients:" "${clients}" " ${ads_blocked_today} out of ${dns_queries_today} queries" - printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}" - printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}" - printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}" - printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}" - printf "%s${clear_line}\n" "${bold_text}FTL ===========================================================================${reset_text}" - printf " %-10s%-9s %-10s%-9s %-10s%-9s${clear_line}\n" "PID:" "${ftlPID}" "CPU Use:" "${ftl_cpu}%" "Mem. Use:" "${ftl_mem_percentage}%" - printf " %-10s%-69s${clear_line}\n" "DNSCache:" "${cache_inserts} insertions, ${cache_deletes} deletions, ${cache_size} total entries" - printf "%s${clear_line}\n" "${bold_text}NETWORK =======================================================================${reset_text}" - printf " %-10s%-19s${clear_line}\n" "Hostname:" "${full_hostname}" - printf " %-10s%-15s %-4s%-9s %-4s%-9s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}" - printf " %-6s%-19s %-10s%-29s${clear_line}\n" "IPv4:" "${pi_ip4_addr}" "IPv6:" "${pi_ip6_addr}" - printf "%s${clear_line}\n" "${bold_text}DNS ==========================DHCP=============================================${reset_text}" - printf " %-10s%-19s %-6s${dhcp_heatmap}%-19s${reset_text}${clear_line}\n" "Servers:" "${dns_information}" "DHCP:" "${dhcp_status}" - printf " %-10s${dnssec_heatmap}%-19s${reset_text} %-10s${conditional_forwarding_heatmap}%-9s${reset_text}${clear_line}\n" "DNSSEC:" "${dnssec_status}" "IPv6 Spt:" "${dhcp_ipv6_status}" - printf " %-10s${conditional_forwarding_heatmap}%-19s${reset_text}%s${clear_line}\n" "CdFwding:" "${conditional_forwarding_status}" "${dhcp_info}" - printf "%s${clear_line}\n" "${bold_text}SYSTEM ========================================================================${reset_text}" - printf " %-10s%-39s${clear_line}\n" "Device:" "${sys_model}" - printf " %-10s%-39s %-10s[${memory_heatmap}%-10s${reset_text}] %-6s${clear_line}\n" "Uptime:" "${system_uptime}" "Memory:" "${memory_bar}" "${memory_percent}%" - printf " %-10s${temp_heatmap}%-10s${reset_text} %-10s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-7s${reset_text} %-10s[${memory_heatmap}%-10s${reset_text}] %-6s${clear_line}" "CPU Temp:" "${temperature}" "CPU Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}" "CPU Load:" "${cpu_bar}" "${cpu_percent}%" + moveXOffset; printf "%s${clear_line}\n" "${padd_logo_retro_1}" + moveXOffset; printf "%s${clear_line}\n" "${padd_logo_retro_2} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}, PADD ${padd_version_heatmap}${padd_version}${reset_text}" + moveXOffset; printf "%s${clear_line}\n" "${padd_logo_retro_3} ${pihole_check_box} Core ${ftl_check_box} FTL ${mega_status}${reset_text}" + moveXOffset; printf "%s${clear_line}\n" "" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS =========================================================================${reset_text}" + moveXOffset; printf " %-10s%-19s %-10s[%-40s] %-5s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains" "Piholed:" "${ads_blocked_bar}" "${ads_percentage_today}%" + moveXOffset; printf " %-10s%-30s%-29s${clear_line}\n" "Clients:" "${clients}" " ${ads_blocked_today} out of ${dns_queries_today} queries" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}FTL ===========================================================================${reset_text}" + moveXOffset; printf " %-10s%-9s %-10s%-9s %-10s%-9s${clear_line}\n" "PID:" "${ftlPID}" "CPU Use:" "${ftl_cpu}%" "Mem. Use:" "${ftl_mem_percentage}%" + moveXOffset; printf " %-10s%-69s${clear_line}\n" "DNSCache:" "${cache_inserts} insertions, ${cache_deletes} deletions, ${cache_size} total entries" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK =======================================================================${reset_text}" + moveXOffset; printf " %-10s%-19s${clear_line}\n" "Hostname:" "${full_hostname}" + moveXOffset; printf " %-10s%-15s %-4s%-9s %-4s%-9s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}" + moveXOffset; printf " %-6s%-19s %-10s%-29s${clear_line}\n" "IPv4:" "${pi_ip4_addr}" "IPv6:" "${pi_ip6_addr}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}DNS ==========================DHCP=============================================${reset_text}" + moveXOffset; printf " %-10s%-19s %-6s${dhcp_heatmap}%-19s${reset_text}${clear_line}\n" "Servers:" "${dns_information}" "DHCP:" "${dhcp_status}" + moveXOffset; printf " %-10s${dnssec_heatmap}%-19s${reset_text} %-10s${dhcp_ipv6_heatmap}%-9s${reset_text}${clear_line}\n" "DNSSEC:" "${dnssec_status}" "IPv6 Spt:" "${dhcp_ipv6_status}" + moveXOffset; printf " %-10s${conditional_forwarding_heatmap}%-19s${reset_text}%s${clear_line}\n" "CdFwding:" "${conditional_forwarding_status}" "${dhcp_info}" + moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM ========================================================================${reset_text}" + moveXOffset; printf " %-10s%-39s${clear_line}\n" "Device:" "${sys_model}" + moveXOffset; printf " %-10s%-39s %-10s[${memory_heatmap}%-10s${reset_text}] %-6s${clear_line}\n" "Uptime:" "${system_uptime}" "Memory:" "${memory_bar}" "${memory_percent}%" + moveXOffset; printf " %-10s${temp_heatmap}%-10s${reset_text} %-10s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-7s${reset_text} %-10s[${memory_heatmap}%-10s${reset_text}] %-6s${clear_line}" "CPU Temp:" "${temperature}" "CPU Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}" "CPU Load:" "${cpu_bar}" "${cpu_percent}%" fi + + # Clear to end of screen (below the drawn dashboard) + # https://vt100.net/docs/vt510-rm/ED.html + printf '\e[0J' } ############################################# HELPERS ############################################## @@ -854,102 +925,84 @@ BarGenerator() { echo "$out" } -# Checks the size of the screen and sets the value of padd_size +# Checks the size of the screen and sets the value of $padd_size SizeChecker(){ - # Below Pico. Gives you nothing... - if [ "$console_width" -lt "20" ] || [ "$console_height" -lt "10" ]; then - # Nothing is this small, sorry - clear - printf "%b" "${check_box_bad} Error!\n PADD isn't\n for ants!\n" - exit 1 - # Below Nano. Gives you Pico. - elif [ "$console_width" -lt "24" ] || [ "$console_height" -lt "12" ]; then - padd_size="pico" - # Below Micro, Gives you Nano. - elif [ "$console_width" -lt "30" ] || [ "$console_height" -lt "16" ]; then - padd_size="nano" - # Below Mini. Gives you Micro. - elif [ "$console_width" -lt "40" ] || [ "$console_height" -lt "18" ]; then - padd_size="micro" - # Below Tiny. Gives you Mini. - elif [ "$console_width" -lt "53" ] || [ "$console_height" -lt "20" ]; then - padd_size="mini" - # Below Slim. Gives you Tiny. - elif [ "$console_width" -lt "60" ] || [ "$console_height" -lt "21" ]; then - padd_size="tiny" - # Below Regular. Gives you Slim. - elif [ "$console_width" -lt "80" ] || [ "$console_height" -lt "26" ]; then - if [ "$console_height" -lt "22" ]; then - padd_size="slim" + # adding a tiny delay here to to give the kernel a bit time to + # report new sizes correctly after a terminal resize + # this reduces "flickering" of GenerateSizeDependendOutput() items + # after a terminal re-size + sleep 0.1 + console_height=$(stty size | awk '{ print $1 }') + console_width=$(stty size | awk '{ print $2 }') + + # Mega + if [ "$console_width" -ge "80" ] && [ "$console_height" -ge "26" ]; then + padd_size="mega" + width=80 + height=26 + # Below Mega. Gives you Regular. + elif [ "$console_width" -ge "60" ] && [ "$console_height" -ge "22" ]; then + padd_size="regular" + width=60 + height=22 + # Below Regular. Gives you Slim. + elif [ "$console_width" -ge "60" ] && [ "$console_height" -ge "21" ]; then + padd_size="slim" + width=60 + height=21 + # Below Slim. Gives you Tiny. + elif [ "$console_width" -ge "53" ] && [ "$console_height" -ge "20" ]; then + padd_size="tiny" + width=53 + height=20 + # Below Tiny. Gives you Mini. + elif [ "$console_width" -ge "40" ] && [ "$console_height" -ge "18" ]; then + padd_size="mini" + width=40 + height=18 + # Below Mini. Gives you Micro. + elif [ "$console_width" -ge "30" ] && [ "$console_height" -ge "16" ]; then + padd_size="micro" + width=30 + height=16 + # Below Micro, Gives you Nano. + elif [ "$console_width" -ge "24" ] && [ "$console_height" -ge "12" ]; then + padd_size="nano" + width=24 + height=12 + # Below Nano. Gives you Pico. + elif [ "$console_width" -ge "20" ] && [ "$console_height" -ge "10" ]; then + padd_size="pico" + width=20 + height=10 + # Below Pico. Gives you nothing... else - padd_size="regular" + # Nothing is this small, sorry + printf "%b" "${check_box_bad} Error!\n PADD isn't\n for ants!\n" + exit 1 fi - # Mega - else - padd_size="mega" - fi -} -CheckConnectivity() { - local connectivity connection_attempts wait_timer + # Center the output (default position) + xOffset="$(( (console_width - width) / 2 ))" + yOffset="$(( (console_height - height) / 2 ))" - connectivity="false" - connection_attempts=1 - wait_timer=1 + # If the user sets an offset option, use it. + if [ -n "$xOffOrig" ]; then + xOffset=$xOffOrig - while [ $connection_attempts -lt 9 ]; do - - if nc -zw1 google.com 443 2>/dev/null; then - if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then - echo "Attempt #${connection_attempts} passed..." - elif [ "$1" = "mini" ]; then - echo "Attempt ${connection_attempts} passed." - else - echo " - Attempt ${connection_attempts} passed... " - fi - - connectivity="true" - connection_attempts=11 - else - connection_attempts=$((connection_attempts+1)) - local inner_wait_timer - inner_wait_timer=$((wait_timer*1)) - - # echo "$wait_timer = $inner_wait_timer" - while [ $inner_wait_timer -gt 0 ]; do - if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then - printf "%b" "Attempt #${connection_attempts} failed...\r" - elif [ "$1" = "mini" ] || [ "$1" = "tiny" ]; then - printf "%b" "- Attempt ${connection_attempts} failed, wait ${inner_wait_timer} \r" - else - printf "%b" " - Attempt ${connection_attempts} failed... waiting ${inner_wait_timer} seconds... \r" - fi - sleep 1 - inner_wait_timer=$((inner_wait_timer-1)) - done - - # echo -ne "Attempt $connection_attempts failed... waiting $wait_timer seconds...\r" - # sleep $wait_timer - wait_timer=$((wait_timer*2)) + # Limit the offset to avoid breaks + xMaxOffset=$((console_width - width)) + if [ "$xOffset" -gt "$xMaxOffset" ]; then + xOffset="$xMaxOffset" fi + fi + if [ -n "$yOffOrig" ]; then + yOffset=$yOffOrig - done - - if [ "$connectivity" = "false" ]; then - if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then - echo "Check failed..." - elif [ "$1" = "mini" ] || [ "$1" = "tiny" ]; then - echo "- Connectivity check failed." - else - echo " - Connectivity check failed..." - fi - else - if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then - echo "Check passed..." - elif [ "$1" = "mini" ] || [ "$1" = "tiny" ]; then - echo "- Connectivity check passed." - else - echo " - Connectivity check passed..." + # Limit the offset to avoid breaks + yMaxOffset=$((console_height - height)) + if [ "$yOffset" -gt "$yMaxOffset" ]; then + yOffset="$yMaxOffset" fi fi } @@ -982,6 +1035,27 @@ getFTLAPIPort(){ } +moveYOffset(){ + # moves the cursor yOffset-times down + # https://vt100.net/docs/vt510-rm/CUD.html + # this needs to be guarded, because if the amount is 0, it is adjusted to 1 + # https://terminalguide.namepad.de/seq/csi_cb/ + + if [ "${yOffset}" -gt 0 ]; then + printf '\e[%sB' "${yOffset}" + fi +} + +moveXOffset(){ + # moves the cursor xOffset-times to the right + # https://vt100.net/docs/vt510-rm/CUF.html + # this needs to be guarded, because if the amount is 0, it is adjusted to 1 + # https://terminalguide.namepad.de/seq/csi_cb/ + + if [ "${xOffset}" -gt 0 ]; then + printf '\e[%sC' "${xOffset}" + fi +} ########################################## MAIN FUNCTIONS ########################################## OutputJSON() { @@ -993,87 +1067,89 @@ StartupRoutine(){ # Get config variables . /etc/pihole/setupVars.conf + # Clear the screen and move cursor to (0,0). + # This mimics the 'clear' command. + # https://vt100.net/docs/vt510-rm/ED.html + # https://vt100.net/docs/vt510-rm/CUP.html + # E3 extension `\e[3J` to clear the scrollback buffer see 'man clear' + printf '\e[H\e[2J\e[3J' + + # adds the y-offset + moveYOffset + + # Get versions information + . /etc/pihole/versions + if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then - PrintLogo "$1" - printf "%b" "START-UP ===========\n" - printf "%b" "Checking connection.\n" - CheckConnectivity "$1" - printf "%b" "Starting PADD...\n" + moveXOffset; PrintLogo "$1" + moveXOffset; printf "%b" "START-UP ===========\n" - printf "%b" " [■·········] 10%\r" + moveXOffset; printf "%b" " [■·········] 10%\r" # Check for updates - printf "%b" " [■■········] 20%\r" - printf "%b" " [■■■·······] 30%\r" + moveXOffset; printf "%b" " [■■········] 20%\r" + moveXOffset; printf "%b" " [■■■·······] 30%\r" # Get our information for the first time - printf "%b" " [■■■■······] 40%\r" - GetSystemInformation "$1" - printf "%b" " [■■■■■·····] 50%\r" - GetSummaryInformation "$1" - printf "%b" " [■■■■■■····] 60%\r" - GetPiholeInformation "$1" - printf "%b" " [■■■■■■■···] 70%\r" - GetNetworkInformation "$1" - printf "%b" " [■■■■■■■■··] 80%\r" - GetVersionInformation "$1" - printf "%b" " [■■■■■■■■■·] 90%\r" - printf "%b" " [■■■■■■■■■■] 100%\n" + moveXOffset; printf "%b" " [■■■■······] 40%\r" + GetSystemInformation + moveXOffset; printf "%b" " [■■■■■·····] 50%\r" + GetSummaryInformation + moveXOffset; printf "%b" " [■■■■■■····] 60%\r" + GetPiholeInformation + moveXOffset; printf "%b" " [■■■■■■■···] 70%\r" + GetNetworkInformation + moveXOffset; printf "%b" " [■■■■■■■■··] 80%\r" + GetVersionInformation + moveXOffset; printf "%b" " [■■■■■■■■■·] 90%\r" + moveXOffset; printf "%b" " [■■■■■■■■■■] 100%\n" elif [ "$1" = "mini" ]; then - PrintLogo "$1" - echo "START UP =====================" - echo "Checking connectivity." - CheckConnectivity "$1" - - echo "Starting PADD." + moveXOffset; PrintLogo "$1" + moveXOffset; echo "START UP =====================" # Get our information for the first time - echo "- Gathering system info." - GetSystemInformation "mini" - echo "- Gathering Pi-hole info." - GetPiholeInformation "mini" - GetSummaryInformation "mini" - echo "- Gathering network info." - GetNetworkInformation "mini" - echo "- Gathering version info." - GetVersionInformation "mini" - echo " - Core $core_version, Web $web_version" - echo " - FTL $ftl_version, PADD $padd_version" - echo " - $version_status" + moveXOffset; echo "- Gathering system info." + GetSystemInformation + moveXOffset; echo "- Gathering Pi-hole info." + GetPiholeInformation + GetSummaryInformation + moveXOffset; echo "- Gathering network info." + GetNetworkInformation + moveXOffset; echo "- Gathering version info." + GetVersionInformation + moveXOffset; echo " - Core $CORE_VERSION, Web $WEB_VERSION" + moveXOffset; echo " - FTL $FTL_VERSION, PADD $padd_version" + moveXOffset; echo " - $version_status" else - printf "%b" "${padd_logo_retro_1}\n" - printf "%b" "${padd_logo_retro_2}Pi-hole® Ad Detection Display\n" - printf "%b" "${padd_logo_retro_3}A client for Pi-hole\n\n" + moveXOffset; printf "%b" "${padd_logo_retro_1}\n" + moveXOffset; printf "%b" "${padd_logo_retro_2}Pi-hole® Ad Detection Display\n" + moveXOffset; printf "%b" "${padd_logo_retro_3}A client for Pi-hole\n\n" if [ "$1" = "tiny" ]; then - echo "START UP ============================================" + moveXOffset; echo "START UP ============================================" else - echo "START UP ===================================================" + moveXOffset; echo "START UP ===================================================" fi - printf "%b" "- Checking internet connection...\n" - CheckConnectivity "$1" - # Get our information for the first time - echo "- Gathering system information..." - GetSystemInformation "$1" - echo "- Gathering Pi-hole information..." - GetSummaryInformation "$1" - GetPiholeInformation "$1" - echo "- Gathering network information..." - GetNetworkInformation "$1" - echo "- Gathering version information..." - GetVersionInformation "$1" - echo " - Pi-hole Core $core_version" - echo " - Web Admin $web_version" - echo " - FTL $ftl_version" - echo " - PADD $padd_version" - echo " - $version_status" + moveXOffset; echo "- Gathering system information..." + GetSystemInformation + moveXOffset; echo "- Gathering Pi-hole information..." + GetSummaryInformation + GetPiholeInformation + moveXOffset; echo "- Gathering network information..." + GetNetworkInformation + moveXOffset; echo "- Gathering version information..." + GetVersionInformation + moveXOffset; echo " - Pi-hole Core $CORE_VERSION" + moveXOffset; echo " - Web Admin $WEB_VERSION" + moveXOffset; echo " - FTL $FTL_VERSION" + moveXOffset; echo " - PADD $padd_version" + moveXOffset; echo " - $version_status" fi - printf "%s" "- Starting in " - + moveXOffset; printf "%s" "- Starting in " for i in 3 2 1 do printf "%s..." "$i" @@ -1082,35 +1158,29 @@ StartupRoutine(){ } NormalPADD() { - while true; do - console_width=$(tput cols) - console_height=$(tput lines) + # Trap the window resize signal (handle window resize events) + trap 'TerminalResize' WINCH - # Sizing Checks - SizeChecker - - # Clear to end of screen (below the drawn dashboard) - tput ed + while true; do - # Move the cursor to top left of console to redraw - tput cup 0 0 + # Generate output that depends on the terminal size + # e.g. Heatmap and barchart + GenerateSizeDependendOutput ${padd_size} # Output everything to the screen PrintDashboard ${padd_size} - # Clear to end of screen (below the drawn dashboard) - tput ed - - # Reset status indicator (can be overwritten by one of the GetXXXXInformation) - pico_status=${pico_status_ok} - mini_status=${mini_status_ok} - tiny_status=${tiny_status_ok} - full_status=${full_status_ok} - mega_status=${mega_status_ok} - # Sleep for 5 seconds - sleep 5 + # sending sleep in the background and wait for it + # this way the TerminalResize trap can kill the sleep + # and force a instant re-draw of the dashboard + # https://stackoverflow.com/questions/32041674/linux-how-to-kill-sleep + # + # saving the PID of the background sleep process to kill it on exit and resize + sleep 5 & + sleepPID=$! + wait $! # Start getting our information for next round now=$(date +%s) @@ -1118,31 +1188,32 @@ NormalPADD() { # Get uptime, CPU load, temp, etc. every 5 seconds if [ $((now - LastCheckSystemInformation)) -ge 5 ]; then . /etc/pihole/setupVars.conf - GetSystemInformation ${padd_size} + GetSystemInformation LastCheckSystemInformation="${now}" fi # Get cache info, last ad domain, blocking percentage, etc. every 5 seconds if [ $((now - LastCheckSummaryInformation)) -ge 5 ]; then - GetSummaryInformation ${padd_size} + GetSummaryInformation LastCheckSummaryInformation="${now}" fi # Get FTL status every 5 seconds if [ $((now - LastCheckPiholeInformation)) -ge 5 ]; then - GetPiholeInformation ${padd_size} + GetPiholeInformation LastCheckPiholeInformation="${now}" fi # Get IPv4 address, DNS servers, DNSSEC, hostname, DHCP status, interface traffic, etc. every 30 seconds if [ $((now - LastCheckNetworkInformation)) -ge 30 ]; then - GetNetworkInformation ${padd_size} + GetNetworkInformation LastCheckNetworkInformation="${now}" fi # Get Pi-hole components and PADD version information once every 24 hours if [ $((now - LastCheckVersionInformation)) -ge 86400 ]; then - GetVersionInformation ${padd_size} + . /etc/pihole/versions + GetVersionInformation LastCheckVersionInformation="${now}" fi @@ -1150,42 +1221,73 @@ NormalPADD() { } DisplayHelp() { - cat << EOM -::: PADD displays stats about your piHole! + cat << EOM + +::: PADD displays stats about your Pi-hole! ::: ::: Note: If no option is passed, then stats are displayed on screen, updated every 5 seconds ::: ::: Options: -::: -j, --json output stats as JSON formatted string -::: -h, --help display this help text +::: -xoff [num] set the x-offset, reference is the upper left corner, disables auto-centering +::: -yoff [num] set the y-offset, reference is the upper left corner, disables auto-centering +::: -j, --json output stats as JSON formatted string and exit +::: -h, --help display this help text + EOM exit 0 } -if [ $# = 0 ]; then - # Turns off the cursor - # (From Pull request #8 https://github.com/jpmck/PADD/pull/8) - setterm -cursor off - trap "{ setterm -cursor on ; echo "" ; exit 0 ; }" INT TERM EXIT +CleanExit(){ + # save the return code of the script + err=$? + #clear the line + printf '\e[0K\n' - clear + # Show the cursor + # https://vt100.net/docs/vt510-rm/DECTCEM.html + printf '\e[?25h' - console_width=$(tput cols) - console_height=$(tput lines) + # if background sleep is running, kill it + # http://mywiki.wooledge.org/SignalTrap#When_is_the_signal_handled.3F + kill $sleepPID > /dev/null 2>&1 - SizeChecker + exit $err # exit the script with saved $? +} + +TerminalResize(){ + # if a terminal resize is trapped, check the new terminal size and + # kill the sleep function within NormalPADD() to trigger redrawing + # of the Dashboard + SizeChecker + kill $sleepPID > /dev/null 2>&1 +} + +main(){ + # Hiding the cursor. + # https://vt100.net/docs/vt510-rm/DECTCEM.html + printf '\e[?25l' - StartupRoutine ${padd_size} + # Trap on exit + trap 'CleanExit' INT TERM EXIT - # Run PADD - clear - NormalPADD -fi + SizeChecker + + StartupRoutine ${padd_size} -for var in "$@"; do - case "$var" in - "-j" | "--json" ) OutputJSON;; - "-h" | "--help" ) DisplayHelp;; - * ) exit 1;; + # Run PADD + NormalPADD +} + +# Process all options (if present) +while [ "$#" -gt 0 ]; do + case "$1" in + "-j" | "--json" ) OutputJSON; exit 0;; + "-h" | "--help" ) DisplayHelp; exit 0;; + "-xoff" ) xOffset="$2"; xOffOrig="$2"; shift;; + "-yoff" ) yOffset="$2"; yOffOrig="$2"; shift;; + * ) DisplayHelp; exit 1;; esac + shift done + +main