Compare commits

...
2 Commits
4 changed files with 16 additions and 3 deletions
+2
View File
@@ -1,3 +1,5 @@
# 树莓派接入插件 v1.2.2 # 树莓派接入插件 v1.2.2
2026-10-14 网络卡片改为按 network.link 显示已连接/已断开,字段缺失显示状态未确认,与 NAS 的接口连接语义一致。页面不再把 network.internet 用作接口状态文案。Agent 的既有互联网探测和告警逻辑不修改。两项后端测试及全页三尺寸中英文主题浏览器验证通过。部署使用停服一致性数据备份 /volume1/homes/way/pigway-console-backup-synology-vpi-link-v1.2.2-20261014/data.tar.gz,目录 0700;旧树莓派插件保存在数据卷 adapter-backups/vpi-link-v1.2.2/pigway-agent。 2026-10-14 网络卡片改为按 network.link 显示已连接/已断开,字段缺失显示状态未确认,与 NAS 的接口连接语义一致。页面不再把 network.internet 用作接口状态文案。Agent 的既有互联网探测和告警逻辑不修改。两项后端测试及全页三尺寸中英文主题浏览器验证通过。部署使用停服一致性数据备份 /volume1/homes/way/pigway-console-backup-synology-vpi-link-v1.2.2-20261014/data.tar.gz,目录 0700;旧树莓派插件保存在数据卷 adapter-backups/vpi-link-v1.2.2/pigway-agent。
生产安装并启用 1.2.2,实际 UI 已使用 network.link 且不存在“互联网正常”文案;认证 API 两设备在线,实时流量和历史指标保留。验证会话删除。Tea ZIP 与 SHA256 附件下载校验一致。
+5 -2
View File
@@ -1,7 +1,7 @@
{ {
"id": "pigway-agent", "id": "pigway-agent",
"name": "PIGWay Raspberry Pi Agent", "name": "PIGWay Raspberry Pi Agent",
"version": "1.2.2", "version": "1.3.0",
"protocol_version": 1, "protocol_version": 1,
"entrypoint": "worker.py", "entrypoint": "worker.py",
"runtime": "python3", "runtime": "python3",
@@ -38,5 +38,8 @@
"contract": "console-ui-v1", "contract": "console-ui-v1",
"entrypoint": "ui/index.js" "entrypoint": "ui/index.js"
}, },
"legacy_connection_fields": true "legacy_connection_fields": true,
"collection": {
"interval_seconds": 1
}
} }
+6
View File
@@ -80,9 +80,15 @@ def collect(device):
rate = value.get('network', {}).get(field) rate = value.get('network', {}).get(field)
if isinstance(rate, (int, float)) and not isinstance(rate, bool) and math.isfinite(rate) and rate >= 0: if isinstance(rate, (int, float)) and not isinstance(rate, bool) and math.isfinite(rate) and rate >= 0:
metrics.append({'resource_id': 'host', 'key': key, 'value': rate, 'unit': 'bytes_per_second'}) metrics.append({'resource_id': 'host', 'key': key, 'value': rate, 'unit': 'bytes_per_second'})
for metric in metrics:
source = value.get('network', {}) if metric['key'].startswith('network_') else value.get('system', {})
at = source.get('observed_at') if metric['key'] != 'uptime_seconds' else value.get('timestamp')
if isinstance(at, (int, float)) and not isinstance(at, bool) and math.isfinite(at) and at > 0:
metric['observed_at'] = at
return {'schema_version': 1, 'observed_at': value.get('timestamp'), return {'schema_version': 1, 'observed_at': value.get('timestamp'),
'identity': {'reported_name': value['machine'].get('name'), 'category': 'host', 'identity': {'reported_name': value['machine'].get('name'), 'category': 'host',
'details': value.get('device', {})}, 'details': value.get('device', {})},
'collection': value.get('collection', MANIFEST['collection']),
'capabilities': MANIFEST['capabilities'], 'metrics': metrics, 'capabilities': MANIFEST['capabilities'], 'metrics': metrics,
'resources': [{'id': 'host', 'type': 'host'}], 'resources': [{'id': 'host', 'type': 'host'}],
'alerts': value.get('display', {}).get('alerts', []), 'alerts': value.get('display', {}).get('alerts', []),
+3 -1
View File
@@ -11,7 +11,7 @@ class MetricsTests(unittest.TestCase):
value = {'timestamp': 123, 'machine': {'name':'Pi'}, 'system': { value = {'timestamp': 123, 'machine': {'name':'Pi'}, 'system': {
'cpu_percent': 10, 'memory_total_bytes': 8000, 'memory_used_bytes': 4000, 'cpu_percent': 10, 'memory_total_bytes': 8000, 'memory_used_bytes': 4000,
'disk_total_bytes': 16000, 'disk_used_bytes': 5000}, 'disk_total_bytes': 16000, 'disk_used_bytes': 5000},
'uptime_seconds': 1000, 'network': {'link_speed_mbps':1000,'rx_bytes_per_second':0,'tx_bytes_per_second':1234}, 'uptime_seconds': 1000, 'network': {'observed_at':120,'link_speed_mbps':1000,'rx_bytes_per_second':0,'tx_bytes_per_second':1234},
'display': {'alerts': []}} 'display': {'alerts': []}}
with patch.object(worker,'request',return_value=value): result=worker.collect({}) with patch.object(worker,'request',return_value=value): result=worker.collect({})
values = {m['key']:m['value'] for m in result['metrics']} values = {m['key']:m['value'] for m in result['metrics']}
@@ -20,6 +20,8 @@ class MetricsTests(unittest.TestCase):
self.assertEqual(values['network_link_speed_mbps'],1000) self.assertEqual(values['network_link_speed_mbps'],1000)
self.assertEqual(values['network_rx_bytes_per_second'],0) self.assertEqual(values['network_rx_bytes_per_second'],0)
self.assertEqual(values['network_tx_bytes_per_second'],1234) self.assertEqual(values['network_tx_bytes_per_second'],1234)
self.assertEqual(next(m['observed_at'] for m in result['metrics'] if m['key']=='network_tx_bytes_per_second'),120)
self.assertEqual(result['collection']['interval_seconds'],1)
self.assertTrue(result['alerts_complete']) self.assertTrue(result['alerts_complete'])
def test_absent_or_boolean_fields_not_invented(self): def test_absent_or_boolean_fields_not_invented(self):