1 Commits

Author SHA1 Message Date
github-actions[bot]
2c7d6b8dba chore: bump version to 0.1.9 [skip ci] 2026-06-24 13:54:49 +00:00
6 changed files with 66 additions and 24 deletions

View File

@@ -8,8 +8,7 @@ from django.utils import timezone
AMS_INFO_TO_TYPE = { AMS_INFO_TO_TYPE = {
"1001": "AMS", "1001": "AMS",
"1003": "AMS 2 Pro", "1003": "AMS 2 Pro",
"1104": "AMS HT", # observed on production H2C (last 4 of info code 11001104) "2104": "AMS HT",
"2104": "AMS HT", # observed in dev capture (last 4 of info code 11002104)
} }
AMS_TYPE_CHOICES = [ AMS_TYPE_CHOICES = [

View File

@@ -113,14 +113,29 @@
border-color: currentColor; border-color: currentColor;
} }
/* Grouped AMS unit panels — Bootstrap row/col handles sizing; /* Grouped AMS unit panels — wide (multi-slot) units stack one per row,
multi-slot units (AMS/AMS 2 Pro) are col-12, single-slot (AMS HT) are col-lg-3. */ compact (single-slot, e.g. AMS HT) units flow side-by-side and wrap. */
.ams-groups {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.ams-group { .ams-group {
border-radius: 0.5rem; border-radius: 0.5rem;
padding: 0.75rem; padding: 0.75rem;
border: 1px solid var(--ams-group-border-color); border: 1px solid var(--ams-group-border-color);
} }
.ams-group--wide {
flex: 1 1 100%;
}
.ams-group--compact {
flex: 0 1 auto;
min-width: 220px;
}
.ams-badge-bg-ams { .ams-badge-bg-ams {
background-color: color-mix(in srgb, var(--ams-badge-ams) 8%, transparent); background-color: color-mix(in srgb, var(--ams-badge-ams) 8%, transparent);
border-left: 3px solid var(--ams-badge-ams); border-left: 3px solid var(--ams-badge-ams);

View File

@@ -164,6 +164,37 @@
</div> </div>
{% endif %} {% endif %}
<!-- AMS Status Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5>AMS Status</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<strong>Temperature:</strong>
{% if stats.ams_temp %}
{{ stats.ams_temp|floatformat:1 }}&deg;C
{% else %}
N/A
{% endif %}
</div>
<div class="col-md-4">
<strong>Humidity:</strong>
{% if stats.ams_humidity %}
{{ stats.ams_humidity }}%
{% else %}
N/A
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Filaments Section --> <!-- Filaments Section -->
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">
@@ -181,9 +212,9 @@
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
<div class="row g-3 ams-groups"> <div class="ams-groups">
{% for group in stats.ams_groups %} {% for group in stats.ams_groups %}
<div class="{% if group.filaments|length > 1 %}col-12{% else %}col-12 col-md-6 col-lg-3{% endif %} ams-group ams-badge-bg-{{ group.ams_type|slugify }}" data-ams-unit-id="{{ group.unit_id }}"> <div class="ams-group ams-badge-bg-{{ group.ams_type|slugify }} {% if group.filaments|length > 1 %}ams-group--wide{% else %}ams-group--compact{% endif %}" data-ams-unit-id="{{ group.unit_id }}">
<div class="ams-group-header d-flex justify-content-between align-items-center mb-2"> <div class="ams-group-header d-flex justify-content-between align-items-center mb-2">
<strong class="small">{{ group.label }}</strong> <strong class="small">{{ group.label }}</strong>
{% if group.humidity is not None or group.temp is not None %} {% if group.humidity is not None or group.temp is not None %}

View File

@@ -159,30 +159,27 @@ class PrinterDashboardView(LoginRequiredMixin, TemplateView):
except Exception: except Exception:
filaments_list = [] filaments_list = []
# Build a lookup from unit_id → AMS unit metadata (humidity, temp, info code) # Distinct AMS units represented in this snapshot, for the unit
# first so we can enrich blank ams_type values derived from old snapshots. # filter/badges in the template. Sort numeric unit ids first
units_meta = { # (AMS / AMS 2 Pro), HT (id 128 / bit 0x80 set) last.
u.get('unit_id'): u for u in (latest_metric.ams_units or [])
}
# Distinct AMS units in this snapshot. ams_type stored on FilamentSnapshot
# may be blank for rows written before the multi-AMS deploy — fall back to
# re-deriving from the unit's info code so labels always show correctly.
from .models import ams_type_from_info as _ams_type_from_info
seen_units = {} seen_units = {}
for f in filaments_list: for f in filaments_list:
uid = f.get('ams_unit_id') uid = f.get('ams_unit_id')
if uid is not None and uid not in seen_units: if uid is not None and uid not in seen_units:
label = f.get('ams_type') or '' seen_units[uid] = f.get('ams_type') or ''
if not label:
unit_meta = units_meta.get(str(uid), {})
label = _ams_type_from_info(unit_meta.get('info', ''))
seen_units[uid] = label
ams_units_list = [ ams_units_list = [
{'ams_unit_id': uid, 'ams_type': label} {'ams_unit_id': uid, 'ams_type': label}
for uid, label in sorted(seen_units.items()) for uid, label in sorted(seen_units.items())
] ]
# Group trays by physical AMS unit for the panel-style dashboard layout —
# one tinted panel per unit, full-width for multi-slot units (AMS/AMS 2 Pro),
# compact for single-slot units (AMS HT) so several can flow side-by-side.
# Filaments with ams_unit_id=None (pre-multi-AMS rows) fall into a single
# unlabelled group so they still render rather than being silently dropped.
units_meta = {
u.get('unit_id'): u for u in (latest_metric.ams_units or [])
}
ams_groups = [] ams_groups = []
ungrouped = [f for f in filaments_list if f.get('ams_unit_id') is None] ungrouped = [f for f in filaments_list if f.get('ams_unit_id') is None]
if ungrouped: if ungrouped:

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "bambu-run" name = "bambu-run"
version = "0.1.8" version = "0.1.9"
description = "Django reusable app for Bambu Lab 3D printer monitoring and filament inventory management" description = "Django reusable app for Bambu Lab 3D printer monitoring and filament inventory management"
readme = "README.md" readme = "README.md"
license = {text = "MIT"} license = {text = "MIT"}

View File

@@ -157,8 +157,8 @@ def test_dashboard_renders_wide_and_compact_panels(logged_in_client):
) )
html = resp.content.decode() html = resp.content.decode()
assert "col-12 ams-group" in html # wide group: col-12 only assert "ams-group--wide" in html
assert "col-lg-3 ams-group" in html # compact group: col-lg-3 assert "ams-group--compact" in html
assert "AMS 2 Pro (Unit 0)" in html assert "AMS 2 Pro (Unit 0)" in html
assert "AMS HT (Unit 128)" in html assert "AMS HT (Unit 128)" in html