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
+
+
+
+
+
+## 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) => {
diff --git a/webui/vite.config.js b/webui/vite.config.js
index f8a7dba..eb29451 100644
--- a/webui/vite.config.js
+++ b/webui/vite.config.js
@@ -15,6 +15,15 @@ export default defineConfig({
}
},
base: "/",
+ build: {
+ assetsDir: 'assets',
+ rollupOptions: {
+ input: {
+ main: fileURLToPath(new URL('./index.html', import.meta.url))
+ }
+ }
+ },
+ publicDir: 'public',
server: {
proxy: {
'/auth': { // This is the path you want to proxy
diff --git a/zarf/icon/icon.png b/zarf/icon/icon.png
new file mode 100644
index 0000000..463c72d
Binary files /dev/null and b/zarf/icon/icon.png differ
diff --git a/zarf/icon/icon.svg b/zarf/icon/icon.svg
new file mode 100644
index 0000000..0d090b0
--- /dev/null
+++ b/zarf/icon/icon.svg
@@ -0,0 +1,56 @@
+
+
+
+
diff --git a/zarf/screenshots/detail.jpg b/zarf/screenshots/detail.jpg
new file mode 100644
index 0000000..1d2cf65
Binary files /dev/null and b/zarf/screenshots/detail.jpg differ
diff --git a/zarf/screenshots/overview.jpg b/zarf/screenshots/overview.jpg
new file mode 100644
index 0000000..859a033
Binary files /dev/null and b/zarf/screenshots/overview.jpg differ