diff --git a/Makefile b/Makefile index bce1eaa..ff20859 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ SHELL := /bin/bash +COMMIT_SHA_SHORT ?= $(shell git rev-parse --short=12 HEAD) +PWD_DIR := ${CURDIR} default: help; mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) @@ -7,6 +9,82 @@ ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) # ====================================================================================== +##@ Build +build: ## builds the web UI and packages it (use VERSION=v1.2.3 or defaults to commit-SNAPSHOT) + @echo "Building web UI..." + @cd webui && make build + @echo "Creating package..." + @mkdir -p dist + @VERSION=$${VERSION:-$(COMMIT_SHA_SHORT)-SNAPSHOT} && \ + PACKAGE_NAME="nanoSmart-$$VERSION" && \ + mkdir -p "dist/$$PACKAGE_NAME/cron" && \ + mkdir -p "dist/$$PACKAGE_NAME/webui" && \ + cp -r webui/dist/assets "dist/$$PACKAGE_NAME/webui/" && \ + cp -r webui/dist/icons "dist/$$PACKAGE_NAME/webui/" && \ + cp webui/dist/index.html "dist/$$PACKAGE_NAME/webui/" && \ + cp -r cron/smart_monitor.conf "dist/$$PACKAGE_NAME/cron/smart_monitor.conf" && \ + cp -r cron/smart_monitor.sh "dist/$$PACKAGE_NAME/cron/smart_monitor.sh" && \ + cp LICENSE "dist/$$PACKAGE_NAME/" && \ + cp README.md "dist/$$PACKAGE_NAME/" && \ + cd dist && zip -r "$$PACKAGE_NAME.zip" "$$PACKAGE_NAME/" && \ + rm -rf "$$PACKAGE_NAME" && \ + echo "Package created: dist/$$PACKAGE_NAME.zip" && \ + echo "Package size: $$(du -h "$$PACKAGE_NAME.zip" | cut -f1)" + +clean: ## clean the build environment + @rm -rf ./dist + +##@ Release + +check_env: # check for needed envs +ifndef GITHUB_TOKEN + $(error GITHUB_TOKEN is undefined, create one with repo permissions here: https://github.com/settings/tokens/new?scopes=repo,write%3Apackages ) +endif + @[ "${version}" ] || ( echo ">> version is not set, usage: make release version=\"v1.2.3\" "; exit 1 ) + @which jq > /dev/null || ( echo ">> jq is not installed. Please install jq for JSON parsing."; exit 1 ) + +.PHONY: check-git-clean +check-git-clean: # check if git repo is clen + @git diff --quiet + +.PHONY: check-branch +check-branch: + @current_branch=$$(git symbolic-ref --short HEAD) && \ + if [ "$$current_branch" != "main" ]; then \ + echo "Error: You are on branch '$$current_branch'. Please switch to 'main'."; \ + exit 1; \ + fi + + +#release: check_env check-branch check-git-clean ## release a new version, call with version="v1.2.3", make sure to have valid GH token +release: check_env check-branch check-git-clean ## release a new version, call with version="v1.2.3", make sure to have valid GH token + @[ "${version}" ] || ( echo ">> version is not set, usage: make release version=\"v1.2.3\" "; exit 1 ) + @echo "Building release package for version $(version)..." + @VERSION=$(version) make build + @echo "Creating GitHub release..." + @git tag -d $(version) || true + @git tag -a $(version) -m "Release version: $(version)" + @git push --delete origin $(version) || true + @git push origin $(version) || true + @echo "Creating GitHub release..." + @RELEASE_ID=$$(curl -s -H "Authorization: token $(GITHUB_TOKEN)" \ + -H "Accept: application/vnd.github.v3+json" \ + -X POST \ + -d '{"tag_name":"$(version)","name":"Release $(version)","body":"Release version $(version)","draft":false,"prerelease":false}' \ + "https://api.github.com/repos/ansible-autobott/nanoSmart/releases" | \ + jq -r '.id') && \ + if [ "$$RELEASE_ID" = "null" ] || [ -z "$$RELEASE_ID" ]; then \ + echo "Error: Could not create release for tag $(version)"; \ + exit 1; \ + fi && \ + echo "Release created with ID: $$RELEASE_ID" && \ + echo "Uploading release assets..." && \ + curl -s -H "Authorization: token $(GITHUB_TOKEN)" \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Content-Type: application/zip" \ + --data-binary @dist/nanoSmart-$(version).zip \ + "https://uploads.github.com/repos/ansible-autobott/nanoSmart/releases/$$RELEASE_ID/assets?name=nanoSmart-$(version).zip" && \ + echo "Release $(version) created and asset uploaded successfully!" ##@ Help diff --git a/README.md b/README.md index f6a5311..1372e16 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,13 @@ # nanoSmart micro UI for displaying SMART data + + +![Overview](zarf/screenshots/overview.jpg) +![Detail](zarf/screenshots/detail.jpg) + +## How it works + +nanoSmart is composed of 2 components: + +a) a cron script that generates static json files to a specific location +b) an SPA that can consume the static json and represent relevant information \ No newline at end of file diff --git a/webui/index.html b/webui/index.html index ff91160..7642558 100644 --- a/webui/index.html +++ b/webui/index.html @@ -5,21 +5,21 @@ - - - - + + + + - - + + - + - + nano Smart diff --git a/webui/sampledata/nvme1n1_smart.json b/webui/sampledata/nvme1n1_smart.json new file mode 100644 index 0000000..c60be11 --- /dev/null +++ b/webui/sampledata/nvme1n1_smart.json @@ -0,0 +1,87 @@ +{ + "device": "/dev/nvme1n1", + "timestamp": 1755340242, + "smart_data": { + "device_info": { + "json_format_version": [1, 0], + "smartctl": { + "version": [7, 3], + "svn_revision": "5338", + "platform_info": "x86_64-linux-6.1.0-37-amd64", + "build_info": "(local build)", + "argv": ["smartctl", "-i", "-j", "/dev/nvme1n1"], + "exit_status": 0 + }, + "local_time": { + "time_t": 1755340242, + "asctime": "Sat Aug 16 12:30:42 2025 CEST" + }, + "device": { + "name": "/dev/nvme1n1", + "info_name": "/dev/nvme1n1", + "type": "nvme", + "protocol": "NVMe" + }, + "model_name": "INTEL SSDPE2KX080T8", + "serial_number": "INTEL987654321", + "firmware_version": "REDACTED", + "nvme_total_capacity": 8001563222016, + "nvme_unallocated_capacity": 0, + "nvme_controller_id": 1, + "nvme_ieee_oui_identifier": 6083300, + "nvme_number_of_namespaces": 128, + "nvme_pci_vendor": { + "id": 32902, + "subsystem_id": 32902 + }, + "nvme_version": { + "string": "1.2", + "value": 66048 + }, + "smart_support": { + "available": true, + "enabled": true + } + }, + "smart_attributes": { + "json_format_version": [1, 0], + "smartctl": { + "version": [7, 3], + "svn_revision": "5338", + "platform_info": "x86_64-linux-6.1.0-37-amd64", + "build_info": "(local build)", + "argv": ["smartctl", "-A", "-j", "/dev/nvme1n1"], + "exit_status": 0 + }, + "nvme_smart_health_information_log": { + "critical_warning": 1, + "temperature": 75, + "available_spare": 85, + "available_spare_threshold": 10, + "percentage_used": 85, + "data_units_read": 20126802, + "data_units_written": 184919244, + "host_reads": 134106494, + "host_writes": 4922206599, + "controller_busy_time": 165, + "power_cycles": 22, + "power_on_hours": 45678, + "unsafe_shutdowns": 8, + "media_errors": 2, + "num_err_log_entries": 5, + "warning_temp_time": 120, + "critical_comp_time": 0 + } + }, + "smart_health": { + "smart_status": { + "nvme": { + "value": 1 + }, + "passed": false + } + }, + "smart_errors": {}, + "smart_selftest": {} + } +} \ No newline at end of file diff --git a/webui/sampledata/sda_smart.json b/webui/sampledata/sda_smart.json new file mode 100644 index 0000000..d737467 --- /dev/null +++ b/webui/sampledata/sda_smart.json @@ -0,0 +1,467 @@ +{ + "device": "/dev/sda", + "timestamp": 1755340242, + "smart_data": { + "device_info": { + "json_format_version": [1, 0], + "smartctl": { + "version": [7, 3], + "svn_revision": "5338", + "platform_info": "x86_64-linux-6.1.0-37-amd64", + "build_info": "(local build)", + "argv": ["smartctl", "-i", "-j", "/dev/sda"], + "exit_status": 0 + }, + "local_time": { + "time_t": 1755340242, + "asctime": "Sat Aug 16 12:30:42 2025 CEST" + }, + "device": { + "name": "/dev/sda", + "info_name": "/dev/sda", + "type": "sat", + "protocol": "SATA" + }, + "model_name": "INTEL SSDSC2BB240G4", + "serial_number": "INTEL123456789", + "firmware_version": "XCV10132", + "user_capacity": { + "blocks": 468862128, + "bytes": 240057809920 + }, + "logical_block_size": 512, + "smart_support": { + "available": true, + "enabled": true + } + }, + "smart_attributes": { + "json_format_version": [1, 0], + "smartctl": { + "version": [7, 3], + "svn_revision": "5338", + "platform_info": "x86_64-linux-6.1.0-37-amd64", + "build_info": "(local build)", + "argv": ["smartctl", "-A", "-j", "/dev/sda"], + "exit_status": 0 + }, + "table": [ + { + "id": 5, + "name": "Reallocated_Sector_Ct", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 9, + "name": "Power_On_Hours", + "value": 85, + "worst": 85, + "thresh": 0, + "raw": {"value": 69412, "string": "69412"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 12, + "name": "Power_Cycle_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 55, "string": "55"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 170, + "name": "Available_Reservd_Space", + "value": 100, + "worst": 100, + "thresh": 10, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 171, + "name": "Program_Fail_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 172, + "name": "Erase_Fail_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 173, + "name": "Wear_Leveling_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 174, + "name": "Unexpected_Power_Loss_Ct", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 3, "string": "3"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 184, + "name": "End-to-End_Error", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 187, + "name": "Reported_Uncorrect", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 194, + "name": "Temperature_Celsius", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 35, "string": "35"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": false, + "string": "-O---- " + } + }, + { + "id": 195, + "name": "Hardware_ECC_Recovered", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 199, + "name": "UDMA_CRC_Error_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 202, + "name": "Data_Address_Mark_Errs", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 225, + "name": "Load_Cycle_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 226, + "name": "Load_In_Time", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 227, + "name": "Torque_Amplification_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 228, + "name": "Power-Off_Retract_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 232, + "name": "Available_Reservd_Space", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 233, + "name": "Media_Wearout_Indicator", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 234, + "name": "Average_Erase_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 235, + "name": "Max_Erase_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 241, + "name": "Total_LBAs_Written", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 242, + "name": "Total_LBAs_Read", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + } + ] + }, + "smart_health": { + "smart_status": { + "passed": true + } + }, + "smart_errors": {}, + "smart_selftest": {} + } +} \ No newline at end of file diff --git a/webui/sampledata/sdb_smart.json b/webui/sampledata/sdb_smart.json new file mode 100644 index 0000000..881004d --- /dev/null +++ b/webui/sampledata/sdb_smart.json @@ -0,0 +1,315 @@ +{ + "device": "/dev/sdb", + "timestamp": 1755340242, + "smart_data": { + "device_info": { + "json_format_version": [1, 0], + "smartctl": { + "version": [7, 3], + "svn_revision": "5338", + "platform_info": "x86_64-linux-6.1.0-37-amd64", + "build_info": "(local build)", + "argv": ["smartctl", "-i", "-j", "/dev/sdb"], + "exit_status": 0 + }, + "local_time": { + "time_t": 1755340242, + "asctime": "Sat Aug 16 12:30:42 2025 CEST" + }, + "device": { + "name": "/dev/sdb", + "info_name": "/dev/sdb", + "type": "sat", + "protocol": "SATA" + }, + "model_name": "SEAGATE ST373453LC", + "serial_number": "SEAGATE123456", + "firmware_version": "DX10", + "user_capacity": { + "blocks": 143374650, + "bytes": 73407820800 + }, + "logical_block_size": 512, + "rotation_rate": 15015, + "smart_support": { + "available": true, + "enabled": true + } + }, + "smart_attributes": { + "json_format_version": [1, 0], + "smartctl": { + "version": [7, 3], + "svn_revision": "5338", + "platform_info": "x86_64-linux-6.1.0-37-amd64", + "build_info": "(local build)", + "argv": ["smartctl", "-A", "-j", "/dev/sdb"], + "exit_status": 0 + }, + "table": [ + { + "id": 1, + "name": "Raw_Read_Error_Rate", + "value": 100, + "worst": 100, + "thresh": 16, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": true, + "event_count": false, + "auto_keep": false, + "string": "PO-R-- " + } + }, + { + "id": 2, + "name": "Throughput_Performance", + "value": 136, + "worst": 136, + "thresh": 54, + "raw": {"value": 108, "string": "108"}, + "flags": { + "prefailure": true, + "updated_online": false, + "performance": true, + "error_rate": false, + "event_count": false, + "auto_keep": false, + "string": "POS--- " + } + }, + { + "id": 3, + "name": "Spin_Up_Time", + "value": 134, + "worst": 134, + "thresh": 24, + "raw": {"value": 222, "string": "222 (Average 222)"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": true, + "error_rate": false, + "event_count": false, + "auto_keep": false, + "string": "POS--- " + } + }, + { + "id": 4, + "name": "Start_Stop_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 12, "string": "12"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 5, + "name": "Reallocated_Sector_Ct", + "value": 100, + "worst": 100, + "thresh": 36, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 7, + "name": "Seek_Error_Rate", + "value": 100, + "worst": 100, + "thresh": 30, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": true, + "event_count": false, + "auto_keep": false, + "string": "PO-R-- " + } + }, + { + "id": 9, + "name": "Power_On_Hours", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 102880, "string": "102880"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 10, + "name": "Spin_Retry_Count", + "value": 100, + "worst": 100, + "thresh": 97, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 12, + "name": "Power_Cycle_Count", + "value": 100, + "worst": 100, + "thresh": 20, + "raw": {"value": 12, "string": "12"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 194, + "name": "Temperature_Celsius", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 33, "string": "33"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": false, + "string": "-O---- " + } + }, + { + "id": 195, + "name": "Hardware_ECC_Recovered", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 197, + "name": "Current_Pending_Sector", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 198, + "name": "Offline_Uncorrectable", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 199, + "name": "UDMA_CRC_Error_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 200, + "name": "Multi_Zone_Error_Rate", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": true, + "event_count": false, + "auto_keep": false, + "string": "PO-R-- " + } + } + ] + }, + "smart_health": { + "smart_status": { + "passed": true + } + }, + "smart_errors": {}, + "smart_selftest": {} + } +} \ No newline at end of file diff --git a/webui/sampledata/sdc_smart.json b/webui/sampledata/sdc_smart.json new file mode 100644 index 0000000..d8f58bc --- /dev/null +++ b/webui/sampledata/sdc_smart.json @@ -0,0 +1,315 @@ +{ + "device": "/dev/sdc", + "timestamp": 1755340242, + "smart_data": { + "device_info": { + "json_format_version": [1, 0], + "smartctl": { + "version": [7, 3], + "svn_revision": "5338", + "platform_info": "x86_64-linux-6.1.0-37-amd64", + "build_info": "(local build)", + "argv": ["smartctl", "-i", "-j", "/dev/sdc"], + "exit_status": 0 + }, + "local_time": { + "time_t": 1755340242, + "asctime": "Sat Aug 16 12:30:42 2025 CEST" + }, + "device": { + "name": "/dev/sdc", + "info_name": "/dev/sdc", + "type": "scsi", + "protocol": "SCSI" + }, + "model_name": "HGST HUS724020ALA640", + "serial_number": "HGST123456789", + "firmware_version": "A21D", + "user_capacity": { + "blocks": 3907029168, + "bytes": 2000398934016 + }, + "logical_block_size": 512, + "rotation_rate": 7200, + "smart_support": { + "available": true, + "enabled": true + } + }, + "smart_attributes": { + "json_format_version": [1, 0], + "smartctl": { + "version": [7, 3], + "svn_revision": "5338", + "platform_info": "x86_64-linux-6.1.0-37-amd64", + "build_info": "(local build)", + "argv": ["smartctl", "-A", "-j", "/dev/sdc"], + "exit_status": 0 + }, + "table": [ + { + "id": 1, + "name": "Raw_Read_Error_Rate", + "value": 100, + "worst": 100, + "thresh": 16, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": true, + "event_count": false, + "auto_keep": false, + "string": "PO-R-- " + } + }, + { + "id": 2, + "name": "Throughput_Performance", + "value": 136, + "worst": 136, + "thresh": 54, + "raw": {"value": 108, "string": "108"}, + "flags": { + "prefailure": true, + "updated_online": false, + "performance": true, + "error_rate": false, + "event_count": false, + "auto_keep": false, + "string": "POS--- " + } + }, + { + "id": 3, + "name": "Spin_Up_Time", + "value": 134, + "worst": 134, + "thresh": 24, + "raw": {"value": 222, "string": "222 (Average 222)"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": true, + "error_rate": false, + "event_count": false, + "auto_keep": false, + "string": "POS--- " + } + }, + { + "id": 4, + "name": "Start_Stop_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 8, "string": "8"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 5, + "name": "Reallocated_Sector_Ct", + "value": 100, + "worst": 100, + "thresh": 36, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 7, + "name": "Seek_Error_Rate", + "value": 100, + "worst": 100, + "thresh": 30, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": true, + "event_count": false, + "auto_keep": false, + "string": "PO-R-- " + } + }, + { + "id": 9, + "name": "Power_On_Hours", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 8760, "string": "8760"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 10, + "name": "Spin_Retry_Count", + "value": 100, + "worst": 100, + "thresh": 97, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 12, + "name": "Power_Cycle_Count", + "value": 100, + "worst": 100, + "thresh": 20, + "raw": {"value": 8, "string": "8"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 194, + "name": "Temperature_Celsius", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 28, "string": "28"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": false, + "string": "-O---- " + } + }, + { + "id": 195, + "name": "Hardware_ECC_Recovered", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 197, + "name": "Current_Pending_Sector", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 198, + "name": "Offline_Uncorrectable", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "PO--CK " + } + }, + { + "id": 199, + "name": "UDMA_CRC_Error_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true, + "string": "-O--CK " + } + }, + { + "id": 200, + "name": "Multi_Zone_Error_Rate", + "value": 100, + "worst": 100, + "thresh": 0, + "raw": {"value": 0, "string": "0"}, + "flags": { + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": true, + "event_count": false, + "auto_keep": false, + "string": "PO-R-- " + } + } + ] + }, + "smart_health": { + "smart_status": { + "passed": true + } + }, + "smart_errors": {}, + "smart_selftest": {} + } +} \ No newline at end of file diff --git a/webui/src/assets/icons/android-chrome-144x144.png b/webui/src/assets/icons/android-chrome-144x144.png new file mode 100644 index 0000000..f44f6c2 Binary files /dev/null and b/webui/src/assets/icons/android-chrome-144x144.png differ diff --git a/webui/src/assets/icons/android-chrome-192x192.png b/webui/src/assets/icons/android-chrome-192x192.png new file mode 100644 index 0000000..4f069c2 Binary files /dev/null and b/webui/src/assets/icons/android-chrome-192x192.png differ diff --git a/webui/src/assets/icons/android-chrome-512x512.png b/webui/src/assets/icons/android-chrome-512x512.png new file mode 100644 index 0000000..9665c48 Binary files /dev/null and b/webui/src/assets/icons/android-chrome-512x512.png differ diff --git a/webui/src/assets/icons/apple-touch-icon-144x144.png b/webui/src/assets/icons/apple-touch-icon-144x144.png new file mode 100644 index 0000000..dd9c833 Binary files /dev/null and b/webui/src/assets/icons/apple-touch-icon-144x144.png differ diff --git a/webui/src/assets/icons/apple-touch-icon.png b/webui/src/assets/icons/apple-touch-icon.png new file mode 100644 index 0000000..6aca016 Binary files /dev/null and b/webui/src/assets/icons/apple-touch-icon.png differ diff --git a/webui/src/assets/icons/favicon-16x16.png b/webui/src/assets/icons/favicon-16x16.png new file mode 100644 index 0000000..508bc4a Binary files /dev/null and b/webui/src/assets/icons/favicon-16x16.png differ diff --git a/webui/src/assets/icons/favicon-32x32.png b/webui/src/assets/icons/favicon-32x32.png new file mode 100644 index 0000000..788795f Binary files /dev/null and b/webui/src/assets/icons/favicon-32x32.png differ diff --git a/webui/src/assets/icons/favicon-48x48.png b/webui/src/assets/icons/favicon-48x48.png new file mode 100644 index 0000000..833fa1b Binary files /dev/null and b/webui/src/assets/icons/favicon-48x48.png differ diff --git a/webui/src/assets/icons/favicon.ico b/webui/src/assets/icons/favicon.ico new file mode 100644 index 0000000..385c3c8 Binary files /dev/null and b/webui/src/assets/icons/favicon.ico differ diff --git a/webui/src/assets/icons/icon.png b/webui/src/assets/icons/icon.png new file mode 100644 index 0000000..463c72d Binary files /dev/null and b/webui/src/assets/icons/icon.png differ diff --git a/webui/src/assets/icons/site.webmanifest b/webui/src/assets/icons/site.webmanifest new file mode 100644 index 0000000..908e4c2 --- /dev/null +++ b/webui/src/assets/icons/site.webmanifest @@ -0,0 +1,46 @@ +{ + "name": "nano Smart", + "short_name": "nanoSmart", + "description": "SMART Monitoring Tool for Storage Devices", + "start_url": "/", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#3b82f6", + "icons": [ + { + "src": "/favicon-16x16.png", + "sizes": "16x16", + "type": "image/png" + }, + { + "src": "/favicon-32x32.png", + "sizes": "32x32", + "type": "image/png" + }, + { + "src": "/favicon-48x48.png", + "sizes": "48x48", + "type": "image/png" + }, + { + "src": "/apple-touch-icon-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "/apple-touch-icon.png", + "sizes": "180x180", + "type": "image/png" + }, + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} \ No newline at end of file diff --git a/webui/src/views/OverviewView.vue b/webui/src/views/OverviewView.vue index 7832762..4b01a31 100644 --- a/webui/src/views/OverviewView.vue +++ b/webui/src/views/OverviewView.vue @@ -58,7 +58,7 @@ const viewDetails = (device) => {