Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add web UI notification for under voltage/throttling events #1203

Merged
merged 16 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Please check out the full story with much more detail on the [wiki](https://gith
# How do I contribute?
PiSCSI is using the <a href="https://datasift.github.io/gitflow/IntroducingGitFlow.html">Gitflow Workflow</a>. A quick overview:

- The *master* branch should always reflect the contents of the last stable release
- The *main* branch should always reflect the contents of the last stable release
- The *develop* branch should contain the latest tested & approved updates. Pull requests should be used to merge changes into develop.
- The rest of the feature branches are for developing new features
- A tag will be created for each "release". The releases will be named <year>.<month>.<release number> where the release number is incremented for each subsequent release tagged in the same calendar month. The first release of the month of January 2021 is called "21.01.01", the second one in the same month "21.01.02" and so on.
Expand Down
1 change: 1 addition & 0 deletions python/common/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
protobuf==3.19.5
requests==2.31.0
vcgencmd==0.1.1
8 changes: 8 additions & 0 deletions python/common/src/piscsi/return_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,11 @@ class ReturnCodes:
EXTRACTIMAGE_NO_FILES_SPECIFIED = 91
EXTRACTIMAGE_NO_FILES_EXTRACTED = 92
EXTRACTIMAGE_COMMAND_ERROR = 93
UNDER_VOLTAGE_DETECTED = 100
ARM_FREQUENCY_CAPPED = 101
CURRENTLY_THROTTLED = 102
SOFT_TEMPERATURE_LIMIT_ACTIVE = 103
UNDER_VOLTAGE_HAS_OCCURRED = 116
ARM_FREQUENCY_CAPPING_HAS_OCCURRED = 117
THROTTLING_HAS_OCCURRED = 118
SOFT_TEMPERATURE_LIMIT_HAS_OCCURRED = 119
36 changes: 36 additions & 0 deletions python/common/src/piscsi/sys_cmds.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
from socket import socket, gethostname, AF_INET, SOCK_DGRAM
from pathlib import Path
from platform import uname
from vcgencmd import Vcgencmd

from piscsi.return_codes import ReturnCodes
from piscsi.common_settings import SHELL_ERROR


Expand Down Expand Up @@ -263,3 +265,37 @@ def shutdown_system():
return process.returncode, process.stdout.decode("utf-8")

return process.returncode, process.stderr.decode("utf-8")

@staticmethod
def get_throttled(enabled_modes, test_modes):
"""
Takes (list) enabled_modes & (list) test_modes parameters & returns a
tuple of (str) category & (str) message.

enabled_modes is a list of modes that will be enabled for display if
they're triggered. test_modes works similarly to enabled_mode but will
ALWAYS display the modes listed for troubleshooting styling.
"""
vcgcmd = Vcgencmd()
t_states = vcgcmd.get_throttled()['breakdown']
matched_states = []

state_msgs = {
"0": ("error", ReturnCodes.UNDER_VOLTAGE_DETECTED),
"1": ("warning", ReturnCodes.ARM_FREQUENCY_CAPPED),
"2": ("error", ReturnCodes.CURRENTLY_THROTTLED),
"3": ("warning", ReturnCodes.SOFT_TEMPERATURE_LIMIT_ACTIVE),
"16": ("warning", ReturnCodes.UNDER_VOLTAGE_HAS_OCCURRED),
"17": ("warning", ReturnCodes.ARM_FREQUENCY_CAPPING_HAS_OCCURRED),
"18": ("warning", ReturnCodes.THROTTLING_HAS_OCCURRED),
"19": ("warning", ReturnCodes.SOFT_TEMPERATURE_LIMIT_HAS_OCCURRED),
}

for k in t_states:
if t_states[k] and k in enabled_modes:
matched_states.append(state_msgs[k])

for t in test_modes:
matched_states.append(state_msgs[t])

return matched_states
23 changes: 23 additions & 0 deletions python/web/mock/bin/vcgencmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash

# Info: https://www.raspberrypi.com/documentation/computers/os.html#vcgencmd
#
# Bit Hex value Meaning
# ----- ----------- ------------------------
# 0 0x1 Under-voltage detected
# 1 0x2 Arm frequency capped
# 2 0x4 Currently throttled
# 3 0x8 Soft temperature limit active
# 16 0x10000 Under-voltage has occurred
# 17 0x20000 Arm frequency capping has occurred
# 18 0x40000 Throttling has occurred
# 19 0x80000 Soft temperature limit has occurred

if [[ "$1" == "get_throttled" ]]
then
# Return 'Under-voltage detected' & 'Under-voltage has occurred'
echo "throttled=0x10001"
fi

echo "Mock does not recognize: $0 $@"
exit 1
3 changes: 2 additions & 1 deletion python/web/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ protobuf==3.20.2
requests==2.31.0
simplepam==0.1.5
flask_babel==2.0.0
ua-parser==0.16.1
ua-parser==0.16.1
vcgencmd==0.1.1
16 changes: 16 additions & 0 deletions python/web/src/return_code_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ class ReturnCodeMapper:
_("No files were extracted (existing files are skipped)"),
ReturnCodes.EXTRACTIMAGE_COMMAND_ERROR:
_("Unable to extract archive: %(error)s"),
ReturnCodes.UNDER_VOLTAGE_DETECTED:
_("Under voltage detected - Make sure to use a proper power source (2.5+ amps)."),
ReturnCodes.ARM_FREQUENCY_CAPPED:
_("ARM frequency capped - Ensure proper airflow/cooling."),
ReturnCodes.CURRENTLY_THROTTLED:
_("Currently throttled - Make sure to use a proper power source (2.5+ amps)."),
ReturnCodes.SOFT_TEMPERATURE_LIMIT_ACTIVE:
_("Soft-temperature limit active - Ensure proper airflow/cooling."),
ReturnCodes.UNDER_VOLTAGE_HAS_OCCURRED:
_("Under voltage has occurred since last reboot. Make sure to use a proper power source (2.5+ amps)."),
ReturnCodes.ARM_FREQUENCY_CAPPING_HAS_OCCURRED:
_("ARM frequency capping has occurred since last reboot. Ensure proper airflow/cooling."),
ReturnCodes.THROTTLING_HAS_OCCURRED:
_("Throttling has occurred since the last reboot. Make sure to use a proper power source (2.5+ amps)."),
ReturnCodes.SOFT_TEMPERATURE_LIMIT_HAS_OCCURRED:
_("Soft temperature limit has occurred since last reboot. Ensure proper airflow/cooling."),
}
# fmt: on

Expand Down
16 changes: 16 additions & 0 deletions python/web/src/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,19 @@

# Fallback theme for older browsers
TEMPLATE_THEME_LEGACY = "classic"

# Enable throttle notifications
#
# Available modes:
# "0": "Under-voltage detected"
# "1": "Arm frequency capped"
# "2": "Currently throttled"
# "3": "Soft temperature limit active"
# "16": "Under-voltage has occurred"
# "17": "Arm frequency capping has occurred"
# "18": "Throttling has occurred"
# "19": "Soft temperature limit has occurred"
THROTTLE_NOTIFY_MODES = ["0", "16"]
# Include a list of modes to be shown ALL THE TIME to be used for styling
# and formatting.
THROTTLE_TEST_MODES = []
33 changes: 33 additions & 0 deletions python/web/src/static/themes/classic/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,36 @@ summary.filename {
left: 50%;
margin-left: -27px;
}

div.throttle_notice > div {
display: grid;
align-items: center;
background-color: #efefef;
background-repeat: no-repeat;
background-position: 1rem center;
background-size: 1rem;
font-size: x-small;
font-weight: bold;
justify-content: center;
}

div.throttle_notice > div.error {
background-color: #dc3545;
align-items: center;
}

div.throttle_notice > div.warning {
background-color: #ffc107;
align-items: center;
}

div.throttle_notice > div a {
color: black;
text-decoration: none;
}

div.throttle_notice > div a:hover {
text-decoration: underline;
}


43 changes: 43 additions & 0 deletions python/web/src/static/themes/modern/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,49 @@ div.footer div.theme-change-hint a {
color: yellow;
}

/*
------------------------------------------------------------------------------
Throttle messages
------------------------------------------------------------------------------
*/

div.throttle_notice > div {
display: grid;
align-items: center;
background-color: #efefef;
background-repeat: no-repeat;
background-position: 1rem center;
background-size: 1rem;
font-size: x-small;
font-weight: bold;
}

div.throttle_notice > div.error {
background-color: var(--danger);
background-image: url("icons/error.svg");
color: #fff;
align-items: center;
}

div.throttle_notice > div.warning {
background-color: var(--warning);
background-image: url("icons/warning.svg");
align-items: center;
}

div.throttle_notice > div > span.message {
padding-left: 3rem;
}

div.throttle_notice > div a {
color: black;
text-decoration: none;
}

div.throttle_notice > div a:hover {
text-decoration: underline;
}

/*
------------------------------------------------------------------------------
Flash messages
Expand Down
12 changes: 12 additions & 0 deletions python/web/src/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ <h1>
</div>
</div>

<div class="throttle_notice">
{% if (env["throttle_status"]|length > 0) %}
{% for category, response in env["throttle_status"] %}
<div class="{{ category }}">
<span class="message" title="{{ response['msg'] }}"><a
href="https://www.raspberrypi.com/documentation/computers/configuration.html#undervoltage-warning">Potential
instability due to: {{ response['msg'] }}</a></span>
</div>
{% endfor %}
{% endif %}
</div>

<div class="flash" id="flash">
{% if get_flashed_messages(): %}
{% for category, message in get_flashed_messages(with_categories=true) %}
Expand Down
7 changes: 7 additions & 0 deletions python/web/src/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
TEMPLATE_THEMES,
TEMPLATE_THEME_DEFAULT,
TEMPLATE_THEME_LEGACY,
THROTTLE_NOTIFY_MODES,
THROTTLE_TEST_MODES,
)


Expand All @@ -88,6 +90,9 @@ def get_env_info():
else:
username = None

throttled_statuses = sys_cmd.get_throttled(
THROTTLE_NOTIFY_MODES, THROTTLE_TEST_MODES)

return {
"running_env": sys_cmd.running_env(),
"username": username,
Expand All @@ -106,6 +111,8 @@ def get_env_info():
"cd_suffixes": tuple(server_info["sccd"]),
"rm_suffixes": tuple(server_info["scrm"]),
"mo_suffixes": tuple(server_info["scmo"]),
"throttle_status":
[(s[0], ReturnCodeMapper.add_msg({"return_code":s[1]})) for s in throttled_statuses],
}


Expand Down
9 changes: 9 additions & 0 deletions python/web/tests/api/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,12 @@ def test_rename_system(env, http_client):
response_data = response.json()

assert response_data["data"]["system_name"] == old_name


def test_throttle_notification(http_client):
response = http_client.get("/")
response_data = response.json()

assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS
assert "Under voltage detected" in response_data["data"]