@alteriom/mqtt-schema 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -60
- package/dist/cjs/schema_data.d.ts +181 -0
- package/dist/cjs/schema_data.js +214 -1
- package/dist/cjs/schemas/ota/ota-manifest.schema.json +187 -0
- package/dist/esm/schema_data.js +213 -0
- package/dist/esm/schemas/ota/ota-manifest.schema.json +187 -0
- package/package.json +22 -3
- package/schemas/control_response.schema.json +15 -0
- package/schemas/envelope.schema.json +16 -0
- package/schemas/firmware_status.schema.json +17 -0
- package/schemas/gateway_info.schema.json +21 -0
- package/schemas/gateway_metrics.schema.json +26 -0
- package/schemas/mqtt_v1_bundle.json +14 -0
- package/schemas/ota/ota-manifest.schema.json +187 -0
- package/schemas/sensor_data.schema.json +33 -0
- package/schemas/sensor_heartbeat.schema.json +14 -0
- package/schemas/sensor_status.schema.json +14 -0
- package/schemas/validation_rules.md +44 -0
@@ -0,0 +1,187 @@
|
|
1
|
+
{
|
2
|
+
"$id": "https://schemas.alteriom.com/ota/ota-manifest.schema.json",
|
3
|
+
"title": "Alteriom OTA Firmware Manifest",
|
4
|
+
"description": "Schema for Alteriom OTA firmware manifests supporting both rich and minimal variants",
|
5
|
+
"type": "object",
|
6
|
+
"oneOf": [
|
7
|
+
{
|
8
|
+
"title": "Rich Manifest",
|
9
|
+
"description": "Rich manifest format with environment, branch, and manifests object",
|
10
|
+
"type": "object",
|
11
|
+
"properties": {
|
12
|
+
"environment": {
|
13
|
+
"type": "string",
|
14
|
+
"description": "Target environment (e.g., universal-sensor)"
|
15
|
+
},
|
16
|
+
"branch": {
|
17
|
+
"type": "string",
|
18
|
+
"description": "Source control branch the build originated from"
|
19
|
+
},
|
20
|
+
"manifests": {
|
21
|
+
"type": "object",
|
22
|
+
"description": "Build variants keyed by type (dev, prod, etc.)",
|
23
|
+
"patternProperties": {
|
24
|
+
"^[a-z][a-z0-9-]*$": {
|
25
|
+
"$ref": "#/$defs/richEntry"
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"additionalProperties": false,
|
29
|
+
"minProperties": 1
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"required": ["environment", "branch", "manifests"],
|
33
|
+
"additionalProperties": true
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"title": "Minimal Environment Map",
|
37
|
+
"description": "Minimal manifest format as environment -> channels mapping",
|
38
|
+
"type": "object",
|
39
|
+
"patternProperties": {
|
40
|
+
"^[a-z][a-z0-9-]*$": {
|
41
|
+
"type": "object",
|
42
|
+
"description": "Environment entry with channel mappings",
|
43
|
+
"patternProperties": {
|
44
|
+
"^[a-z][a-z0-9-]*$": {
|
45
|
+
"$ref": "#/$defs/minimalChannel"
|
46
|
+
}
|
47
|
+
},
|
48
|
+
"additionalProperties": false,
|
49
|
+
"minProperties": 1
|
50
|
+
}
|
51
|
+
},
|
52
|
+
"additionalProperties": false,
|
53
|
+
"minProperties": 1
|
54
|
+
}
|
55
|
+
],
|
56
|
+
"$defs": {
|
57
|
+
"richEntry": {
|
58
|
+
"title": "Rich Build Entry",
|
59
|
+
"description": "Rich manifest build entry (dev or prod)",
|
60
|
+
"type": "object",
|
61
|
+
"properties": {
|
62
|
+
"build_type": {
|
63
|
+
"type": "string",
|
64
|
+
"enum": ["dev", "prod"],
|
65
|
+
"description": "Build type identifier"
|
66
|
+
},
|
67
|
+
"file": {
|
68
|
+
"type": "string",
|
69
|
+
"description": "Firmware binary filename"
|
70
|
+
},
|
71
|
+
"size": {
|
72
|
+
"type": "integer",
|
73
|
+
"minimum": 1,
|
74
|
+
"description": "Total firmware size in bytes"
|
75
|
+
},
|
76
|
+
"sha256": {
|
77
|
+
"type": "string",
|
78
|
+
"pattern": "^[a-f0-9]{64}$",
|
79
|
+
"description": "SHA256 hash of the full firmware binary (lowercase hex)"
|
80
|
+
},
|
81
|
+
"firmware_version": {
|
82
|
+
"type": "string",
|
83
|
+
"description": "Semantic or build version string"
|
84
|
+
},
|
85
|
+
"built": {
|
86
|
+
"type": "string",
|
87
|
+
"format": "date-time",
|
88
|
+
"description": "ISO8601 timestamp when built"
|
89
|
+
},
|
90
|
+
"ota_url": {
|
91
|
+
"type": "string",
|
92
|
+
"format": "uri",
|
93
|
+
"description": "Absolute or relative URL to fetch the firmware"
|
94
|
+
},
|
95
|
+
"chunk_size": {
|
96
|
+
"type": "integer",
|
97
|
+
"minimum": 1,
|
98
|
+
"description": "Size of each chunk except possibly the last"
|
99
|
+
},
|
100
|
+
"chunks": {
|
101
|
+
"description": "Either structured chunk objects or array of SHA256 strings",
|
102
|
+
"oneOf": [
|
103
|
+
{
|
104
|
+
"type": "array",
|
105
|
+
"items": {
|
106
|
+
"$ref": "#/$defs/chunkObject"
|
107
|
+
},
|
108
|
+
"description": "Array of structured chunk objects with metadata"
|
109
|
+
},
|
110
|
+
{
|
111
|
+
"type": "array",
|
112
|
+
"items": {
|
113
|
+
"type": "string",
|
114
|
+
"pattern": "^[a-f0-9]{64}$",
|
115
|
+
"description": "SHA256 hash of chunk (lowercase hex)"
|
116
|
+
},
|
117
|
+
"description": "Array of SHA256 strings for chunks"
|
118
|
+
}
|
119
|
+
]
|
120
|
+
}
|
121
|
+
},
|
122
|
+
"required": ["build_type", "file", "size", "sha256", "firmware_version", "built", "ota_url"],
|
123
|
+
"additionalProperties": true
|
124
|
+
},
|
125
|
+
"chunkObject": {
|
126
|
+
"title": "OTA Chunk Object",
|
127
|
+
"description": "Structured chunk metadata with offset and size",
|
128
|
+
"type": "object",
|
129
|
+
"properties": {
|
130
|
+
"index": {
|
131
|
+
"type": "integer",
|
132
|
+
"minimum": 0,
|
133
|
+
"description": "0-based sequential chunk index"
|
134
|
+
},
|
135
|
+
"offset": {
|
136
|
+
"type": "integer",
|
137
|
+
"minimum": 0,
|
138
|
+
"description": "Byte offset within firmware binary"
|
139
|
+
},
|
140
|
+
"size": {
|
141
|
+
"type": "integer",
|
142
|
+
"minimum": 1,
|
143
|
+
"description": "Chunk size in bytes"
|
144
|
+
},
|
145
|
+
"sha256": {
|
146
|
+
"type": "string",
|
147
|
+
"pattern": "^[a-f0-9]{64}$",
|
148
|
+
"description": "SHA256 hash of the chunk (lowercase hex)"
|
149
|
+
}
|
150
|
+
},
|
151
|
+
"required": ["index", "offset", "size", "sha256"],
|
152
|
+
"additionalProperties": true
|
153
|
+
},
|
154
|
+
"minimalChannel": {
|
155
|
+
"title": "Minimal Channel Entry",
|
156
|
+
"description": "Minimal manifest channel entry",
|
157
|
+
"type": "object",
|
158
|
+
"properties": {
|
159
|
+
"file": {
|
160
|
+
"type": "string",
|
161
|
+
"description": "Firmware binary filename"
|
162
|
+
},
|
163
|
+
"size": {
|
164
|
+
"type": "integer",
|
165
|
+
"minimum": 1,
|
166
|
+
"description": "Total firmware size in bytes"
|
167
|
+
},
|
168
|
+
"sha256": {
|
169
|
+
"type": "string",
|
170
|
+
"pattern": "^[a-f0-9]{64}$",
|
171
|
+
"description": "SHA256 hash of the firmware binary (lowercase hex)"
|
172
|
+
},
|
173
|
+
"version": {
|
174
|
+
"type": "string",
|
175
|
+
"description": "Firmware version string"
|
176
|
+
},
|
177
|
+
"timestamp": {
|
178
|
+
"type": "string",
|
179
|
+
"format": "date-time",
|
180
|
+
"description": "ISO8601 timestamp"
|
181
|
+
}
|
182
|
+
},
|
183
|
+
"required": ["file", "size", "sha256", "version", "timestamp"],
|
184
|
+
"additionalProperties": true
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@alteriom/mqtt-schema",
|
3
|
-
"version": "0.3.
|
3
|
+
"version": "0.3.2",
|
4
4
|
"description": "Alteriom MQTT v1 schemas, TypeScript types, and validation helpers for web integration",
|
5
5
|
"license": "MIT",
|
6
6
|
"author": "Alteriom Development Team",
|
@@ -42,6 +42,11 @@
|
|
42
42
|
"./schemas/ota/ota-manifest.schema.json": {
|
43
43
|
"default": "./schemas/ota/ota-manifest.schema.json"
|
44
44
|
},
|
45
|
+
"./ota-manifest": {
|
46
|
+
"import": "./schemas/ota/ota-manifest.schema.json",
|
47
|
+
"require": "./schemas/ota/ota-manifest.schema.json",
|
48
|
+
"default": "./schemas/ota/ota-manifest.schema.json"
|
49
|
+
},
|
45
50
|
"./types/ota-manifest": {
|
46
51
|
"types": "./types/ota-manifest.d.ts",
|
47
52
|
"default": "./types/ota-manifest.d.ts"
|
@@ -61,6 +66,9 @@
|
|
61
66
|
],
|
62
67
|
"types/ota-manifest": [
|
63
68
|
"types/ota-manifest.d.ts"
|
69
|
+
],
|
70
|
+
"ota-manifest": [
|
71
|
+
"schemas/ota/ota-manifest.schema.json"
|
64
72
|
]
|
65
73
|
}
|
66
74
|
},
|
@@ -79,24 +87,35 @@
|
|
79
87
|
"node": ">=16.0.0"
|
80
88
|
},
|
81
89
|
"scripts": {
|
90
|
+
"// Build Scripts": "====================",
|
82
91
|
"prebuild": "npm run clean && node scripts/copy-schemas.cjs",
|
83
92
|
"build:cjs": "tsc -p tsconfig.json",
|
84
93
|
"build:esm": "tsc -p tsconfig.esm.json && node scripts/post-esm.cjs",
|
85
94
|
"build": "npm run build:cjs && npm run build:esm && node scripts/copy-schemas.cjs",
|
86
95
|
"clean": "rimraf dist schemas",
|
87
96
|
"prepare": "npm run build",
|
88
|
-
|
97
|
+
|
98
|
+
"// Testing & Validation": "====================",
|
99
|
+
"lint": "echo 'No linter configured - consider adding ESLint for future releases'",
|
89
100
|
"test": "node test/validate-fixtures.cjs",
|
101
|
+
"test:ota": "node scripts/validate-ota-manifest.js",
|
90
102
|
"verify:schemas": "node scripts/check-schema-sync.cjs",
|
91
103
|
"verify:release": "node scripts/check-release-integrity.cjs",
|
92
104
|
"verify": "npm run verify:schemas && npm run verify:release && npm test",
|
105
|
+
"verify:all": "node scripts/verify_all.cjs",
|
106
|
+
|
107
|
+
"// Release Management": "====================",
|
93
108
|
"release:prepare": "node scripts/release-prepare.cjs",
|
94
109
|
"wiki:generate": "node scripts/generate-wiki.cjs",
|
110
|
+
|
111
|
+
"// Repository Metadata": "====================",
|
95
112
|
"metadata:report": "alteriom-metadata report --org-tag alteriom || echo 'metadata report failed'",
|
96
113
|
"metadata:validate": "alteriom-metadata validate --org-tag alteriom || echo 'metadata validate failed'",
|
97
114
|
"metadata:apply": "alteriom-metadata apply --org-tag alteriom",
|
98
115
|
"metadata:apply:dry": "alteriom-metadata dry-run --org-tag alteriom",
|
99
|
-
|
116
|
+
|
117
|
+
"// Legacy Aliases": "====================",
|
118
|
+
"validate:ota": "npm run test:ota"
|
100
119
|
},
|
101
120
|
"keywords": [
|
102
121
|
"alteriom",
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
3
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/control_response.schema.json",
|
4
|
+
"title": "Control / Command Response v1",
|
5
|
+
"allOf": [{"$ref": "envelope.schema.json"}],
|
6
|
+
"type": "object",
|
7
|
+
"required": ["status"],
|
8
|
+
"properties": {
|
9
|
+
"command": {"type": "string"},
|
10
|
+
"status": {"type": "string", "enum": ["ok", "error"]},
|
11
|
+
"message": {"type": "string"},
|
12
|
+
"result": {"type": ["object", "array", "string", "number", "boolean", "null"]}
|
13
|
+
},
|
14
|
+
"additionalProperties": true
|
15
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
3
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/envelope.schema.json",
|
4
|
+
"title": "Alteriom MQTT Base Envelope v1",
|
5
|
+
"type": "object",
|
6
|
+
"required": ["schema_version", "device_id", "device_type", "timestamp", "firmware_version"],
|
7
|
+
"properties": {
|
8
|
+
"schema_version": {"type": "integer", "const": 1},
|
9
|
+
"device_id": {"type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[A-Za-z0-9_-]+$"},
|
10
|
+
"device_type": {"type": "string", "enum": ["sensor", "gateway"]},
|
11
|
+
"timestamp": {"type": "string", "format": "date-time"},
|
12
|
+
"firmware_version": {"type": "string", "minLength": 1, "maxLength": 40},
|
13
|
+
"hardware_version": {"type": "string", "maxLength": 80}
|
14
|
+
},
|
15
|
+
"additionalProperties": true
|
16
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
3
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/firmware_status.schema.json",
|
4
|
+
"title": "Firmware Update Status v1",
|
5
|
+
"allOf": [{"$ref": "envelope.schema.json"}],
|
6
|
+
"type": "object",
|
7
|
+
"required": ["status"],
|
8
|
+
"properties": {
|
9
|
+
"event": {"type": "string"},
|
10
|
+
"from_version": {"type": "string"},
|
11
|
+
"to_version": {"type": "string"},
|
12
|
+
"status": {"type": "string", "enum": ["pending", "downloading", "flashing", "verifying", "rebooting", "completed", "failed"]},
|
13
|
+
"progress_pct": {"type": "number", "minimum": 0, "maximum": 100},
|
14
|
+
"error": {"type": ["string", "null"]}
|
15
|
+
},
|
16
|
+
"additionalProperties": true
|
17
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
3
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/gateway_info.schema.json",
|
4
|
+
"title": "Gateway Info v1",
|
5
|
+
"allOf": [{"$ref": "envelope.schema.json"}],
|
6
|
+
"type": "object",
|
7
|
+
"properties": {
|
8
|
+
"mac_address": {"type": "string", "pattern": "^[0-9A-Fa-f:]{17}$"},
|
9
|
+
"ip_address": {"type": "string", "format": "ipv4"},
|
10
|
+
"capabilities": {
|
11
|
+
"type": "object",
|
12
|
+
"properties": {
|
13
|
+
"max_nodes": {"type": "integer", "minimum": 0},
|
14
|
+
"supports_mesh": {"type": "boolean"},
|
15
|
+
"firmware_update": {"type": "boolean"}
|
16
|
+
},
|
17
|
+
"additionalProperties": true
|
18
|
+
}
|
19
|
+
},
|
20
|
+
"additionalProperties": true
|
21
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
3
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/gateway_metrics.schema.json",
|
4
|
+
"title": "Gateway Metrics v1",
|
5
|
+
"allOf": [{"$ref": "envelope.schema.json"}],
|
6
|
+
"type": "object",
|
7
|
+
"required": ["metrics"],
|
8
|
+
"properties": {
|
9
|
+
"metrics": {
|
10
|
+
"type": "object",
|
11
|
+
"required": ["uptime_s"],
|
12
|
+
"properties": {
|
13
|
+
"uptime_s": {"type": "integer", "minimum": 0},
|
14
|
+
"cpu_usage_pct": {"type": "number", "minimum": 0, "maximum": 100},
|
15
|
+
"memory_usage_pct": {"type": "number", "minimum": 0, "maximum": 100},
|
16
|
+
"temperature_c": {"type": "number"},
|
17
|
+
"connected_devices": {"type": "integer", "minimum": 0},
|
18
|
+
"mesh_nodes": {"type": "integer", "minimum": 0},
|
19
|
+
"packet_loss_pct": {"type": "number", "minimum": 0, "maximum": 100},
|
20
|
+
"data_throughput_kbps": {"type": "number", "minimum": 0}
|
21
|
+
},
|
22
|
+
"additionalProperties": true
|
23
|
+
}
|
24
|
+
},
|
25
|
+
"additionalProperties": true
|
26
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"$comment": "Convenience bundle referencing all v1 schema artifact filenames for tooling discovery.",
|
3
|
+
"version": 1,
|
4
|
+
"schemas": {
|
5
|
+
"envelope": "envelope.schema.json",
|
6
|
+
"sensor_data": "sensor_data.schema.json",
|
7
|
+
"sensor_heartbeat": "sensor_heartbeat.schema.json",
|
8
|
+
"sensor_status": "sensor_status.schema.json",
|
9
|
+
"gateway_info": "gateway_info.schema.json",
|
10
|
+
"gateway_metrics": "gateway_metrics.schema.json",
|
11
|
+
"firmware_status": "firmware_status.schema.json",
|
12
|
+
"control_response": "control_response.schema.json"
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,187 @@
|
|
1
|
+
{
|
2
|
+
"$id": "https://schemas.alteriom.com/ota/ota-manifest.schema.json",
|
3
|
+
"title": "Alteriom OTA Firmware Manifest",
|
4
|
+
"description": "Schema for Alteriom OTA firmware manifests supporting both rich and minimal variants",
|
5
|
+
"type": "object",
|
6
|
+
"oneOf": [
|
7
|
+
{
|
8
|
+
"title": "Rich Manifest",
|
9
|
+
"description": "Rich manifest format with environment, branch, and manifests object",
|
10
|
+
"type": "object",
|
11
|
+
"properties": {
|
12
|
+
"environment": {
|
13
|
+
"type": "string",
|
14
|
+
"description": "Target environment (e.g., universal-sensor)"
|
15
|
+
},
|
16
|
+
"branch": {
|
17
|
+
"type": "string",
|
18
|
+
"description": "Source control branch the build originated from"
|
19
|
+
},
|
20
|
+
"manifests": {
|
21
|
+
"type": "object",
|
22
|
+
"description": "Build variants keyed by type (dev, prod, etc.)",
|
23
|
+
"patternProperties": {
|
24
|
+
"^[a-z][a-z0-9-]*$": {
|
25
|
+
"$ref": "#/$defs/richEntry"
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"additionalProperties": false,
|
29
|
+
"minProperties": 1
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"required": ["environment", "branch", "manifests"],
|
33
|
+
"additionalProperties": true
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"title": "Minimal Environment Map",
|
37
|
+
"description": "Minimal manifest format as environment -> channels mapping",
|
38
|
+
"type": "object",
|
39
|
+
"patternProperties": {
|
40
|
+
"^[a-z][a-z0-9-]*$": {
|
41
|
+
"type": "object",
|
42
|
+
"description": "Environment entry with channel mappings",
|
43
|
+
"patternProperties": {
|
44
|
+
"^[a-z][a-z0-9-]*$": {
|
45
|
+
"$ref": "#/$defs/minimalChannel"
|
46
|
+
}
|
47
|
+
},
|
48
|
+
"additionalProperties": false,
|
49
|
+
"minProperties": 1
|
50
|
+
}
|
51
|
+
},
|
52
|
+
"additionalProperties": false,
|
53
|
+
"minProperties": 1
|
54
|
+
}
|
55
|
+
],
|
56
|
+
"$defs": {
|
57
|
+
"richEntry": {
|
58
|
+
"title": "Rich Build Entry",
|
59
|
+
"description": "Rich manifest build entry (dev or prod)",
|
60
|
+
"type": "object",
|
61
|
+
"properties": {
|
62
|
+
"build_type": {
|
63
|
+
"type": "string",
|
64
|
+
"enum": ["dev", "prod"],
|
65
|
+
"description": "Build type identifier"
|
66
|
+
},
|
67
|
+
"file": {
|
68
|
+
"type": "string",
|
69
|
+
"description": "Firmware binary filename"
|
70
|
+
},
|
71
|
+
"size": {
|
72
|
+
"type": "integer",
|
73
|
+
"minimum": 1,
|
74
|
+
"description": "Total firmware size in bytes"
|
75
|
+
},
|
76
|
+
"sha256": {
|
77
|
+
"type": "string",
|
78
|
+
"pattern": "^[a-f0-9]{64}$",
|
79
|
+
"description": "SHA256 hash of the full firmware binary (lowercase hex)"
|
80
|
+
},
|
81
|
+
"firmware_version": {
|
82
|
+
"type": "string",
|
83
|
+
"description": "Semantic or build version string"
|
84
|
+
},
|
85
|
+
"built": {
|
86
|
+
"type": "string",
|
87
|
+
"format": "date-time",
|
88
|
+
"description": "ISO8601 timestamp when built"
|
89
|
+
},
|
90
|
+
"ota_url": {
|
91
|
+
"type": "string",
|
92
|
+
"format": "uri",
|
93
|
+
"description": "Absolute or relative URL to fetch the firmware"
|
94
|
+
},
|
95
|
+
"chunk_size": {
|
96
|
+
"type": "integer",
|
97
|
+
"minimum": 1,
|
98
|
+
"description": "Size of each chunk except possibly the last"
|
99
|
+
},
|
100
|
+
"chunks": {
|
101
|
+
"description": "Either structured chunk objects or array of SHA256 strings",
|
102
|
+
"oneOf": [
|
103
|
+
{
|
104
|
+
"type": "array",
|
105
|
+
"items": {
|
106
|
+
"$ref": "#/$defs/chunkObject"
|
107
|
+
},
|
108
|
+
"description": "Array of structured chunk objects with metadata"
|
109
|
+
},
|
110
|
+
{
|
111
|
+
"type": "array",
|
112
|
+
"items": {
|
113
|
+
"type": "string",
|
114
|
+
"pattern": "^[a-f0-9]{64}$",
|
115
|
+
"description": "SHA256 hash of chunk (lowercase hex)"
|
116
|
+
},
|
117
|
+
"description": "Array of SHA256 strings for chunks"
|
118
|
+
}
|
119
|
+
]
|
120
|
+
}
|
121
|
+
},
|
122
|
+
"required": ["build_type", "file", "size", "sha256", "firmware_version", "built", "ota_url"],
|
123
|
+
"additionalProperties": true
|
124
|
+
},
|
125
|
+
"chunkObject": {
|
126
|
+
"title": "OTA Chunk Object",
|
127
|
+
"description": "Structured chunk metadata with offset and size",
|
128
|
+
"type": "object",
|
129
|
+
"properties": {
|
130
|
+
"index": {
|
131
|
+
"type": "integer",
|
132
|
+
"minimum": 0,
|
133
|
+
"description": "0-based sequential chunk index"
|
134
|
+
},
|
135
|
+
"offset": {
|
136
|
+
"type": "integer",
|
137
|
+
"minimum": 0,
|
138
|
+
"description": "Byte offset within firmware binary"
|
139
|
+
},
|
140
|
+
"size": {
|
141
|
+
"type": "integer",
|
142
|
+
"minimum": 1,
|
143
|
+
"description": "Chunk size in bytes"
|
144
|
+
},
|
145
|
+
"sha256": {
|
146
|
+
"type": "string",
|
147
|
+
"pattern": "^[a-f0-9]{64}$",
|
148
|
+
"description": "SHA256 hash of the chunk (lowercase hex)"
|
149
|
+
}
|
150
|
+
},
|
151
|
+
"required": ["index", "offset", "size", "sha256"],
|
152
|
+
"additionalProperties": true
|
153
|
+
},
|
154
|
+
"minimalChannel": {
|
155
|
+
"title": "Minimal Channel Entry",
|
156
|
+
"description": "Minimal manifest channel entry",
|
157
|
+
"type": "object",
|
158
|
+
"properties": {
|
159
|
+
"file": {
|
160
|
+
"type": "string",
|
161
|
+
"description": "Firmware binary filename"
|
162
|
+
},
|
163
|
+
"size": {
|
164
|
+
"type": "integer",
|
165
|
+
"minimum": 1,
|
166
|
+
"description": "Total firmware size in bytes"
|
167
|
+
},
|
168
|
+
"sha256": {
|
169
|
+
"type": "string",
|
170
|
+
"pattern": "^[a-f0-9]{64}$",
|
171
|
+
"description": "SHA256 hash of the firmware binary (lowercase hex)"
|
172
|
+
},
|
173
|
+
"version": {
|
174
|
+
"type": "string",
|
175
|
+
"description": "Firmware version string"
|
176
|
+
},
|
177
|
+
"timestamp": {
|
178
|
+
"type": "string",
|
179
|
+
"format": "date-time",
|
180
|
+
"description": "ISO8601 timestamp"
|
181
|
+
}
|
182
|
+
},
|
183
|
+
"required": ["file", "size", "sha256", "version", "timestamp"],
|
184
|
+
"additionalProperties": true
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
3
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/sensor_data.schema.json",
|
4
|
+
"title": "Sensor Data Message v1",
|
5
|
+
"allOf": [{"$ref": "envelope.schema.json"}],
|
6
|
+
"type": "object",
|
7
|
+
"required": ["sensors"],
|
8
|
+
"properties": {
|
9
|
+
"sensors": {
|
10
|
+
"type": "object",
|
11
|
+
"minProperties": 1,
|
12
|
+
"additionalProperties": {
|
13
|
+
"type": "object",
|
14
|
+
"required": ["value"],
|
15
|
+
"properties": {
|
16
|
+
"value": {"type": ["number", "integer"]},
|
17
|
+
"unit": {"type": "string"},
|
18
|
+
"raw_value": {"type": ["number", "integer"]},
|
19
|
+
"calibrated_value": {"type": ["number", "integer"]},
|
20
|
+
"quality_score": {"type": "number", "minimum": 0, "maximum": 1},
|
21
|
+
"name": {"type": "string"},
|
22
|
+
"location": {"type": "string"},
|
23
|
+
"additional_data": {"type": "object"}
|
24
|
+
},
|
25
|
+
"additionalProperties": false
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"battery_level": {"type": "integer", "minimum": 0, "maximum": 100},
|
29
|
+
"signal_strength": {"type": "integer", "minimum": -200, "maximum": 0},
|
30
|
+
"additional": {"type": "object"}
|
31
|
+
},
|
32
|
+
"additionalProperties": true
|
33
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
3
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/sensor_heartbeat.schema.json",
|
4
|
+
"title": "Sensor Heartbeat v1",
|
5
|
+
"type": "object",
|
6
|
+
"required": ["schema_version", "device_id", "device_type", "timestamp"],
|
7
|
+
"properties": {
|
8
|
+
"schema_version": {"type": "integer", "const": 1},
|
9
|
+
"device_id": {"type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[A-Za-z0-9_-]+$"},
|
10
|
+
"device_type": {"type": "string", "enum": ["sensor", "gateway"]},
|
11
|
+
"timestamp": {"type": "string", "format": "date-time"}
|
12
|
+
},
|
13
|
+
"additionalProperties": true
|
14
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
3
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/sensor_status.schema.json",
|
4
|
+
"title": "Sensor Status v1",
|
5
|
+
"allOf": [{"$ref": "envelope.schema.json"}],
|
6
|
+
"type": "object",
|
7
|
+
"required": ["status"],
|
8
|
+
"properties": {
|
9
|
+
"status": {"type": "string", "enum": ["online", "offline", "updating", "error"]},
|
10
|
+
"battery_level": {"type": "integer", "minimum": 0, "maximum": 100},
|
11
|
+
"signal_strength": {"type": "integer", "minimum": -200, "maximum": 0}
|
12
|
+
},
|
13
|
+
"additionalProperties": true
|
14
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Validation Rules (Operational Layer)
|
2
|
+
|
3
|
+
This file captures dynamic / contextual validation that is OUTSIDE pure structural JSON Schema constraints.
|
4
|
+
|
5
|
+
## Timing & Rate
|
6
|
+
- Future timestamp drift: reject if > 5s ahead of server reference.
|
7
|
+
- Heartbeat interval recommended 30s; backend may rate-limit <10s.
|
8
|
+
- Sensor data soft min 30s unless negotiated high-frequency mode.
|
9
|
+
|
10
|
+
## Heartbeat Firmware Version Exception
|
11
|
+
- Only heartbeat topic may omit `firmware_version` when unchanged.
|
12
|
+
- All other topics require the field.
|
13
|
+
|
14
|
+
## Forbidden / Drop Conditions
|
15
|
+
| Reason | Condition |
|
16
|
+
|--------|----------|
|
17
|
+
| missing_fields | Required base fields absent (heartbeat firmware exception applied). |
|
18
|
+
| invalid_timestamp | Non-ISO 8601 / unparseable timestamp. |
|
19
|
+
| deprecated_keys | Usage of forbidden alias keys (f, fw, ver, version, u, up, rssi). |
|
20
|
+
| invalid_sensor_entry | Sensor object missing `value` key. |
|
21
|
+
| future_drift | Timestamp too far in future (>5s). |
|
22
|
+
| out_of_range | battery_level not 0-100 OR quality_score not 0-1. |
|
23
|
+
| invalid_numeric | Non-numeric where numeric expected. |
|
24
|
+
| spec_violation | Generic fallback after failed internal validation. |
|
25
|
+
|
26
|
+
## Sensor Object Semantics
|
27
|
+
- `value` REQUIRED.
|
28
|
+
- `unit`, `raw_value`, `calibrated_value`, `quality_score`, `name`, `location`, `additional_data` OPTIONAL.
|
29
|
+
- If `quality_score` present must be 0.0–1.0 inclusive.
|
30
|
+
|
31
|
+
## Metrics Constraints
|
32
|
+
- Gateway metrics MUST appear under `metrics` (never at top-level).
|
33
|
+
- Required minimal metric: `uptime_s`.
|
34
|
+
|
35
|
+
## Firmware Update Status
|
36
|
+
- `status` must be one of: pending, downloading, flashing, verifying, rebooting, completed, failed.
|
37
|
+
- `progress_pct` (if present) must be 0–100.
|
38
|
+
|
39
|
+
## Extensibility
|
40
|
+
- Unknown top-level keys tolerated (future evolution) except if they collide with any deprecated alias or reserved future keys announced in CHANGELOG.
|
41
|
+
|
42
|
+
## Versioning
|
43
|
+
- Current `schema_version`: 1
|
44
|
+
- Additions remain optional; breaking changes introduce new schema set.
|