@alteriom/mqtt-schema 0.2.0
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 +174 -0
- package/dist/cjs/generated/types.d.ts +101 -0
- package/dist/cjs/generated/types.js +104 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +23 -0
- package/dist/cjs/schema_data.d.ts +319 -0
- package/dist/cjs/schema_data.js +402 -0
- package/dist/cjs/schemas/control_response.schema.json +15 -0
- package/dist/cjs/schemas/envelope.schema.json +16 -0
- package/dist/cjs/schemas/firmware_status.schema.json +17 -0
- package/dist/cjs/schemas/gateway_info.schema.json +21 -0
- package/dist/cjs/schemas/gateway_metrics.schema.json +26 -0
- package/dist/cjs/schemas/mqtt_v1_bundle.json +14 -0
- package/dist/cjs/schemas/sensor_data.schema.json +33 -0
- package/dist/cjs/schemas/sensor_heartbeat.schema.json +14 -0
- package/dist/cjs/schemas/sensor_status.schema.json +14 -0
- package/dist/cjs/schemas/validation_rules.md +44 -0
- package/dist/cjs/types.d.ts +1 -0
- package/dist/cjs/types.js +23 -0
- package/dist/cjs/validators.d.ts +23 -0
- package/dist/cjs/validators.js +84 -0
- package/dist/esm/generated/types.js +92 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/schema_data.js +399 -0
- package/dist/esm/schemas/control_response.schema.json +15 -0
- package/dist/esm/schemas/envelope.schema.json +16 -0
- package/dist/esm/schemas/firmware_status.schema.json +17 -0
- package/dist/esm/schemas/gateway_info.schema.json +21 -0
- package/dist/esm/schemas/gateway_metrics.schema.json +26 -0
- package/dist/esm/schemas/mqtt_v1_bundle.json +14 -0
- package/dist/esm/schemas/sensor_data.schema.json +33 -0
- package/dist/esm/schemas/sensor_heartbeat.schema.json +14 -0
- package/dist/esm/schemas/sensor_status.schema.json +14 -0
- package/dist/esm/schemas/validation_rules.md +44 -0
- package/dist/esm/types.js +7 -0
- package/dist/esm/validators.js +76 -0
- package/package.json +91 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
export interface CompileOptions {
|
2
|
+
allErrors?: boolean;
|
3
|
+
strict?: boolean;
|
4
|
+
}
|
5
|
+
export interface ValidationResult {
|
6
|
+
valid: boolean;
|
7
|
+
errors?: string[];
|
8
|
+
}
|
9
|
+
export declare const validators: {
|
10
|
+
sensorData: (d: unknown) => ValidationResult;
|
11
|
+
sensorHeartbeat: (d: unknown) => ValidationResult;
|
12
|
+
sensorStatus: (d: unknown) => ValidationResult;
|
13
|
+
gatewayInfo: (d: unknown) => ValidationResult;
|
14
|
+
gatewayMetrics: (d: unknown) => ValidationResult;
|
15
|
+
firmwareStatus: (d: unknown) => ValidationResult;
|
16
|
+
controlResponse: (d: unknown) => ValidationResult;
|
17
|
+
};
|
18
|
+
export type ValidatorName = keyof typeof validators;
|
19
|
+
export declare function validateMessage(kind: ValidatorName, data: unknown): ValidationResult;
|
20
|
+
export declare function classifyAndValidate(data: any): {
|
21
|
+
kind?: ValidatorName;
|
22
|
+
result: ValidationResult;
|
23
|
+
};
|
@@ -0,0 +1,84 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.validators = void 0;
|
7
|
+
exports.validateMessage = validateMessage;
|
8
|
+
exports.classifyAndValidate = classifyAndValidate;
|
9
|
+
const _2020_js_1 = __importDefault(require("ajv/dist/2020.js"));
|
10
|
+
const ajv_formats_1 = __importDefault(require("ajv-formats"));
|
11
|
+
// Schemas embedded via generated schema_data.ts (copy-schemas.cjs) to avoid filesystem dependency
|
12
|
+
const schema_data_js_1 = require("./schema_data.js");
|
13
|
+
// Load JSON schemas via createRequire so it works in both CJS and ESM builds without import assertions.
|
14
|
+
// Bind embedded schema objects for Ajv consumption
|
15
|
+
const envelope = schema_data_js_1.envelope_schema;
|
16
|
+
const sensorData = schema_data_js_1.sensor_data_schema;
|
17
|
+
const sensorHeartbeat = schema_data_js_1.sensor_heartbeat_schema;
|
18
|
+
const sensorStatus = schema_data_js_1.sensor_status_schema;
|
19
|
+
const gatewayInfo = schema_data_js_1.gateway_info_schema;
|
20
|
+
const gatewayMetrics = schema_data_js_1.gateway_metrics_schema;
|
21
|
+
const firmwareStatus = schema_data_js_1.firmware_status_schema;
|
22
|
+
const controlResponse = schema_data_js_1.control_response_schema;
|
23
|
+
// Lazy singleton Ajv instance so consumers can optionally supply their own if needed.
|
24
|
+
let _ajv = null;
|
25
|
+
function getAjv(opts) {
|
26
|
+
if (_ajv)
|
27
|
+
return _ajv;
|
28
|
+
_ajv = new _2020_js_1.default({
|
29
|
+
strict: false,
|
30
|
+
allErrors: true,
|
31
|
+
allowUnionTypes: true,
|
32
|
+
...opts
|
33
|
+
});
|
34
|
+
(0, ajv_formats_1.default)(_ajv);
|
35
|
+
// Add base schema so $ref works for those referencing envelope
|
36
|
+
_ajv.addSchema(envelope, 'envelope.schema.json');
|
37
|
+
return _ajv;
|
38
|
+
}
|
39
|
+
function toResult(v, data) {
|
40
|
+
const valid = v(data);
|
41
|
+
if (valid)
|
42
|
+
return { valid: true };
|
43
|
+
return { valid: false, errors: (v.errors || []).map((e) => `${e.instancePath || '/'} ${e.message || ''}`.trim()) };
|
44
|
+
}
|
45
|
+
// Pre-compile validators (they are small; compilation cost negligible for typical web usage)
|
46
|
+
const ajv = getAjv();
|
47
|
+
const sensorDataValidate = ajv.compile(sensorData);
|
48
|
+
const sensorHeartbeatValidate = ajv.compile(sensorHeartbeat);
|
49
|
+
const sensorStatusValidate = ajv.compile(sensorStatus);
|
50
|
+
const gatewayInfoValidate = ajv.compile(gatewayInfo);
|
51
|
+
const gatewayMetricsValidate = ajv.compile(gatewayMetrics);
|
52
|
+
const firmwareStatusValidate = ajv.compile(firmwareStatus);
|
53
|
+
const controlResponseValidate = ajv.compile(controlResponse);
|
54
|
+
exports.validators = {
|
55
|
+
sensorData: (d) => toResult(sensorDataValidate, d),
|
56
|
+
sensorHeartbeat: (d) => toResult(sensorHeartbeatValidate, d),
|
57
|
+
sensorStatus: (d) => toResult(sensorStatusValidate, d),
|
58
|
+
gatewayInfo: (d) => toResult(gatewayInfoValidate, d),
|
59
|
+
gatewayMetrics: (d) => toResult(gatewayMetricsValidate, d),
|
60
|
+
firmwareStatus: (d) => toResult(firmwareStatusValidate, d),
|
61
|
+
controlResponse: (d) => toResult(controlResponseValidate, d)
|
62
|
+
};
|
63
|
+
function validateMessage(kind, data) {
|
64
|
+
return exports.validators[kind](data);
|
65
|
+
}
|
66
|
+
// Classifier using lightweight heuristics to pick a schema validator.
|
67
|
+
function classifyAndValidate(data) {
|
68
|
+
if (!data || typeof data !== 'object')
|
69
|
+
return { result: { valid: false, errors: ['Not an object'] } };
|
70
|
+
if (data.metrics)
|
71
|
+
return { kind: 'gatewayMetrics', result: exports.validators.gatewayMetrics(data) };
|
72
|
+
if (data.sensors)
|
73
|
+
return { kind: 'sensorData', result: exports.validators.sensorData(data) };
|
74
|
+
if (data.progress_pct !== undefined || (data.status && ['pending', 'downloading', 'flashing', 'verifying', 'rebooting', 'completed', 'failed'].includes(data.status)))
|
75
|
+
return { kind: 'firmwareStatus', result: exports.validators.firmwareStatus(data) };
|
76
|
+
if (data.status && ['online', 'offline', 'updating', 'error'].includes(data.status) && data.device_type === 'sensor')
|
77
|
+
return { kind: 'sensorStatus', result: exports.validators.sensorStatus(data) };
|
78
|
+
if (data.status && ['ok', 'error'].includes(data.status))
|
79
|
+
return { kind: 'controlResponse', result: exports.validators.controlResponse(data) };
|
80
|
+
if (data.device_type === 'gateway')
|
81
|
+
return { kind: 'gatewayInfo', result: exports.validators.gatewayInfo(data) };
|
82
|
+
// Fallback treat as heartbeat attempt
|
83
|
+
return { kind: 'sensorHeartbeat', result: exports.validators.sensorHeartbeat(data) };
|
84
|
+
}
|
@@ -0,0 +1,92 @@
|
|
1
|
+
/**
|
2
|
+
* Auto-generated TypeScript types for Alteriom MQTT Schema v1
|
3
|
+
* Source: docs/mqtt_schema/*.schema.json
|
4
|
+
* Generation Date: 2025-09-20
|
5
|
+
* NOTE: This file is maintained in firmware repo for UI alignment. Changes require coordinated review.
|
6
|
+
*/
|
7
|
+
// Type Guards ------------------------------------------------
|
8
|
+
export function isSensorDataMessage(msg) {
|
9
|
+
return msg && msg.schema_version === 1 && msg.device_type === 'sensor' && typeof msg.sensors === 'object';
|
10
|
+
}
|
11
|
+
export function isSensorHeartbeatMessage(msg) {
|
12
|
+
return msg && msg.schema_version === 1 && !!msg.device_type && !!msg.timestamp && !('sensors' in msg) && !('metrics' in msg) && !('status' in msg || (msg.status && ['online', 'offline', 'updating', 'error'].includes(msg.status)));
|
13
|
+
}
|
14
|
+
export function isSensorStatusMessage(msg) {
|
15
|
+
return msg && msg.schema_version === 1 && msg.device_type === 'sensor' && typeof msg.status === 'string' && ['online', 'offline', 'updating', 'error'].includes(msg.status);
|
16
|
+
}
|
17
|
+
export function isGatewayInfoMessage(msg) {
|
18
|
+
return msg && msg.schema_version === 1 && msg.device_type === 'gateway' && (!msg.metrics) && (!msg.status) && (!msg.progress_pct);
|
19
|
+
}
|
20
|
+
export function isGatewayMetricsMessage(msg) {
|
21
|
+
return msg && msg.schema_version === 1 && msg.device_type === 'gateway' && typeof msg.metrics === 'object';
|
22
|
+
}
|
23
|
+
export function isFirmwareStatusMessage(msg) {
|
24
|
+
return msg && msg.schema_version === 1 && typeof msg.status === 'string' && ['pending', 'downloading', 'flashing', 'verifying', 'rebooting', 'completed', 'failed'].includes(msg.status) && (msg.progress_pct === undefined || (typeof msg.progress_pct === 'number' && msg.progress_pct >= 0 && msg.progress_pct <= 100));
|
25
|
+
}
|
26
|
+
export function isControlResponseMessage(msg) {
|
27
|
+
return msg && msg.schema_version === 1 && (msg.status === 'ok' || msg.status === 'error') && 'timestamp' in msg;
|
28
|
+
}
|
29
|
+
export function classifyMessage(msg) {
|
30
|
+
if (isSensorDataMessage(msg))
|
31
|
+
return msg;
|
32
|
+
if (isGatewayMetricsMessage(msg))
|
33
|
+
return msg;
|
34
|
+
if (isSensorStatusMessage(msg))
|
35
|
+
return msg;
|
36
|
+
if (isGatewayInfoMessage(msg))
|
37
|
+
return msg;
|
38
|
+
if (isFirmwareStatusMessage(msg))
|
39
|
+
return msg;
|
40
|
+
if (isControlResponseMessage(msg))
|
41
|
+
return msg;
|
42
|
+
if (isSensorHeartbeatMessage(msg))
|
43
|
+
return msg;
|
44
|
+
return null;
|
45
|
+
}
|
46
|
+
export function basicValidate(msg) {
|
47
|
+
const issues = [];
|
48
|
+
if (msg.schema_version !== 1)
|
49
|
+
issues.push({ reason: 'unsupported_schema' });
|
50
|
+
if (!msg.device_id)
|
51
|
+
issues.push({ field: 'device_id', reason: 'missing' });
|
52
|
+
if (!msg.device_type)
|
53
|
+
issues.push({ field: 'device_type', reason: 'missing' });
|
54
|
+
if (!msg.timestamp)
|
55
|
+
issues.push({ field: 'timestamp', reason: 'missing' });
|
56
|
+
if ('firmware_version' in msg) {
|
57
|
+
if (msg.firmware_version === '')
|
58
|
+
issues.push({ field: 'firmware_version', reason: 'empty' });
|
59
|
+
}
|
60
|
+
else if (!isSensorHeartbeatMessage(msg)) {
|
61
|
+
issues.push({ field: 'firmware_version', reason: 'missing' });
|
62
|
+
}
|
63
|
+
if (isSensorDataMessage(msg)) {
|
64
|
+
if (!Object.keys(msg.sensors).length)
|
65
|
+
issues.push({ field: 'sensors', reason: 'empty' });
|
66
|
+
for (const [k, v] of Object.entries(msg.sensors)) {
|
67
|
+
if (typeof v.value !== 'number')
|
68
|
+
issues.push({ field: `sensors.${k}.value`, reason: 'invalid' });
|
69
|
+
if (v.quality_score !== undefined && (v.quality_score < 0 || v.quality_score > 1))
|
70
|
+
issues.push({ field: `sensors.${k}.quality_score`, reason: 'out_of_range' });
|
71
|
+
}
|
72
|
+
}
|
73
|
+
if (isGatewayMetricsMessage(msg)) {
|
74
|
+
if (typeof msg.metrics.uptime_s !== 'number')
|
75
|
+
issues.push({ field: 'metrics.uptime_s', reason: 'missing' });
|
76
|
+
}
|
77
|
+
if (isFirmwareStatusMessage(msg)) {
|
78
|
+
if (msg.progress_pct !== undefined && (msg.progress_pct < 0 || msg.progress_pct > 100))
|
79
|
+
issues.push({ field: 'progress_pct', reason: 'out_of_range' });
|
80
|
+
}
|
81
|
+
return issues;
|
82
|
+
}
|
83
|
+
// Example parse wrapper
|
84
|
+
export function parseMessage(json) {
|
85
|
+
try {
|
86
|
+
const obj = JSON.parse(json);
|
87
|
+
return classifyMessage(obj);
|
88
|
+
}
|
89
|
+
catch {
|
90
|
+
return null;
|
91
|
+
}
|
92
|
+
}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
// Public export surface for @alteriom/mqtt-schema
|
2
|
+
// NOTE: This initial iteration re-exports pre-generated types produced in the firmware repo.
|
3
|
+
// In future, generation could move into this package build, but we intentionally depend on
|
4
|
+
// the firmware repo as the single source of truth to avoid divergence.
|
5
|
+
// Use explicit .js extensions so ESM build (with dist/esm/package.json type=module) treats them as ESM.
|
6
|
+
export * from './validators.js';
|
7
|
+
export * from './types.js';
|
@@ -0,0 +1,399 @@
|
|
1
|
+
// AUTO-GENERATED by copy-schemas.cjs. Do not edit manually.
|
2
|
+
/* eslint-disable */
|
3
|
+
export const envelope_schema = {
|
4
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
5
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/envelope.schema.json",
|
6
|
+
"title": "Alteriom MQTT Base Envelope v1",
|
7
|
+
"type": "object",
|
8
|
+
"required": [
|
9
|
+
"schema_version",
|
10
|
+
"device_id",
|
11
|
+
"device_type",
|
12
|
+
"timestamp",
|
13
|
+
"firmware_version"
|
14
|
+
],
|
15
|
+
"properties": {
|
16
|
+
"schema_version": {
|
17
|
+
"type": "integer",
|
18
|
+
"const": 1
|
19
|
+
},
|
20
|
+
"device_id": {
|
21
|
+
"type": "string",
|
22
|
+
"minLength": 1,
|
23
|
+
"maxLength": 64,
|
24
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
25
|
+
},
|
26
|
+
"device_type": {
|
27
|
+
"type": "string",
|
28
|
+
"enum": [
|
29
|
+
"sensor",
|
30
|
+
"gateway"
|
31
|
+
]
|
32
|
+
},
|
33
|
+
"timestamp": {
|
34
|
+
"type": "string",
|
35
|
+
"format": "date-time"
|
36
|
+
},
|
37
|
+
"firmware_version": {
|
38
|
+
"type": "string",
|
39
|
+
"minLength": 1,
|
40
|
+
"maxLength": 40
|
41
|
+
},
|
42
|
+
"hardware_version": {
|
43
|
+
"type": "string",
|
44
|
+
"maxLength": 80
|
45
|
+
}
|
46
|
+
},
|
47
|
+
"additionalProperties": true
|
48
|
+
};
|
49
|
+
export const sensor_data_schema = {
|
50
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
51
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/sensor_data.schema.json",
|
52
|
+
"title": "Sensor Data Message v1",
|
53
|
+
"allOf": [
|
54
|
+
{
|
55
|
+
"$ref": "envelope.schema.json"
|
56
|
+
}
|
57
|
+
],
|
58
|
+
"type": "object",
|
59
|
+
"required": [
|
60
|
+
"sensors"
|
61
|
+
],
|
62
|
+
"properties": {
|
63
|
+
"sensors": {
|
64
|
+
"type": "object",
|
65
|
+
"minProperties": 1,
|
66
|
+
"additionalProperties": {
|
67
|
+
"type": "object",
|
68
|
+
"required": [
|
69
|
+
"value"
|
70
|
+
],
|
71
|
+
"properties": {
|
72
|
+
"value": {
|
73
|
+
"type": [
|
74
|
+
"number",
|
75
|
+
"integer"
|
76
|
+
]
|
77
|
+
},
|
78
|
+
"unit": {
|
79
|
+
"type": "string"
|
80
|
+
},
|
81
|
+
"raw_value": {
|
82
|
+
"type": [
|
83
|
+
"number",
|
84
|
+
"integer"
|
85
|
+
]
|
86
|
+
},
|
87
|
+
"calibrated_value": {
|
88
|
+
"type": [
|
89
|
+
"number",
|
90
|
+
"integer"
|
91
|
+
]
|
92
|
+
},
|
93
|
+
"quality_score": {
|
94
|
+
"type": "number",
|
95
|
+
"minimum": 0,
|
96
|
+
"maximum": 1
|
97
|
+
},
|
98
|
+
"name": {
|
99
|
+
"type": "string"
|
100
|
+
},
|
101
|
+
"location": {
|
102
|
+
"type": "string"
|
103
|
+
},
|
104
|
+
"additional_data": {
|
105
|
+
"type": "object"
|
106
|
+
}
|
107
|
+
},
|
108
|
+
"additionalProperties": false
|
109
|
+
}
|
110
|
+
},
|
111
|
+
"battery_level": {
|
112
|
+
"type": "integer",
|
113
|
+
"minimum": 0,
|
114
|
+
"maximum": 100
|
115
|
+
},
|
116
|
+
"signal_strength": {
|
117
|
+
"type": "integer",
|
118
|
+
"minimum": -200,
|
119
|
+
"maximum": 0
|
120
|
+
},
|
121
|
+
"additional": {
|
122
|
+
"type": "object"
|
123
|
+
}
|
124
|
+
},
|
125
|
+
"additionalProperties": true
|
126
|
+
};
|
127
|
+
export const sensor_heartbeat_schema = {
|
128
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
129
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/sensor_heartbeat.schema.json",
|
130
|
+
"title": "Sensor Heartbeat v1",
|
131
|
+
"type": "object",
|
132
|
+
"required": [
|
133
|
+
"schema_version",
|
134
|
+
"device_id",
|
135
|
+
"device_type",
|
136
|
+
"timestamp"
|
137
|
+
],
|
138
|
+
"properties": {
|
139
|
+
"schema_version": {
|
140
|
+
"type": "integer",
|
141
|
+
"const": 1
|
142
|
+
},
|
143
|
+
"device_id": {
|
144
|
+
"type": "string",
|
145
|
+
"minLength": 1,
|
146
|
+
"maxLength": 64,
|
147
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
148
|
+
},
|
149
|
+
"device_type": {
|
150
|
+
"type": "string",
|
151
|
+
"enum": [
|
152
|
+
"sensor",
|
153
|
+
"gateway"
|
154
|
+
]
|
155
|
+
},
|
156
|
+
"timestamp": {
|
157
|
+
"type": "string",
|
158
|
+
"format": "date-time"
|
159
|
+
}
|
160
|
+
},
|
161
|
+
"additionalProperties": true
|
162
|
+
};
|
163
|
+
export const sensor_status_schema = {
|
164
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
165
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/sensor_status.schema.json",
|
166
|
+
"title": "Sensor Status v1",
|
167
|
+
"allOf": [
|
168
|
+
{
|
169
|
+
"$ref": "envelope.schema.json"
|
170
|
+
}
|
171
|
+
],
|
172
|
+
"type": "object",
|
173
|
+
"required": [
|
174
|
+
"status"
|
175
|
+
],
|
176
|
+
"properties": {
|
177
|
+
"status": {
|
178
|
+
"type": "string",
|
179
|
+
"enum": [
|
180
|
+
"online",
|
181
|
+
"offline",
|
182
|
+
"updating",
|
183
|
+
"error"
|
184
|
+
]
|
185
|
+
},
|
186
|
+
"battery_level": {
|
187
|
+
"type": "integer",
|
188
|
+
"minimum": 0,
|
189
|
+
"maximum": 100
|
190
|
+
},
|
191
|
+
"signal_strength": {
|
192
|
+
"type": "integer",
|
193
|
+
"minimum": -200,
|
194
|
+
"maximum": 0
|
195
|
+
}
|
196
|
+
},
|
197
|
+
"additionalProperties": true
|
198
|
+
};
|
199
|
+
export const gateway_info_schema = {
|
200
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
201
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/gateway_info.schema.json",
|
202
|
+
"title": "Gateway Info v1",
|
203
|
+
"allOf": [
|
204
|
+
{
|
205
|
+
"$ref": "envelope.schema.json"
|
206
|
+
}
|
207
|
+
],
|
208
|
+
"type": "object",
|
209
|
+
"properties": {
|
210
|
+
"mac_address": {
|
211
|
+
"type": "string",
|
212
|
+
"pattern": "^[0-9A-Fa-f:]{17}$"
|
213
|
+
},
|
214
|
+
"ip_address": {
|
215
|
+
"type": "string",
|
216
|
+
"format": "ipv4"
|
217
|
+
},
|
218
|
+
"capabilities": {
|
219
|
+
"type": "object",
|
220
|
+
"properties": {
|
221
|
+
"max_nodes": {
|
222
|
+
"type": "integer",
|
223
|
+
"minimum": 0
|
224
|
+
},
|
225
|
+
"supports_mesh": {
|
226
|
+
"type": "boolean"
|
227
|
+
},
|
228
|
+
"firmware_update": {
|
229
|
+
"type": "boolean"
|
230
|
+
}
|
231
|
+
},
|
232
|
+
"additionalProperties": true
|
233
|
+
}
|
234
|
+
},
|
235
|
+
"additionalProperties": true
|
236
|
+
};
|
237
|
+
export const gateway_metrics_schema = {
|
238
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
239
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/gateway_metrics.schema.json",
|
240
|
+
"title": "Gateway Metrics v1",
|
241
|
+
"allOf": [
|
242
|
+
{
|
243
|
+
"$ref": "envelope.schema.json"
|
244
|
+
}
|
245
|
+
],
|
246
|
+
"type": "object",
|
247
|
+
"required": [
|
248
|
+
"metrics"
|
249
|
+
],
|
250
|
+
"properties": {
|
251
|
+
"metrics": {
|
252
|
+
"type": "object",
|
253
|
+
"required": [
|
254
|
+
"uptime_s"
|
255
|
+
],
|
256
|
+
"properties": {
|
257
|
+
"uptime_s": {
|
258
|
+
"type": "integer",
|
259
|
+
"minimum": 0
|
260
|
+
},
|
261
|
+
"cpu_usage_pct": {
|
262
|
+
"type": "number",
|
263
|
+
"minimum": 0,
|
264
|
+
"maximum": 100
|
265
|
+
},
|
266
|
+
"memory_usage_pct": {
|
267
|
+
"type": "number",
|
268
|
+
"minimum": 0,
|
269
|
+
"maximum": 100
|
270
|
+
},
|
271
|
+
"temperature_c": {
|
272
|
+
"type": "number"
|
273
|
+
},
|
274
|
+
"connected_devices": {
|
275
|
+
"type": "integer",
|
276
|
+
"minimum": 0
|
277
|
+
},
|
278
|
+
"mesh_nodes": {
|
279
|
+
"type": "integer",
|
280
|
+
"minimum": 0
|
281
|
+
},
|
282
|
+
"packet_loss_pct": {
|
283
|
+
"type": "number",
|
284
|
+
"minimum": 0,
|
285
|
+
"maximum": 100
|
286
|
+
},
|
287
|
+
"data_throughput_kbps": {
|
288
|
+
"type": "number",
|
289
|
+
"minimum": 0
|
290
|
+
}
|
291
|
+
},
|
292
|
+
"additionalProperties": true
|
293
|
+
}
|
294
|
+
},
|
295
|
+
"additionalProperties": true
|
296
|
+
};
|
297
|
+
export const firmware_status_schema = {
|
298
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
299
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/firmware_status.schema.json",
|
300
|
+
"title": "Firmware Update Status v1",
|
301
|
+
"allOf": [
|
302
|
+
{
|
303
|
+
"$ref": "envelope.schema.json"
|
304
|
+
}
|
305
|
+
],
|
306
|
+
"type": "object",
|
307
|
+
"required": [
|
308
|
+
"status"
|
309
|
+
],
|
310
|
+
"properties": {
|
311
|
+
"event": {
|
312
|
+
"type": "string"
|
313
|
+
},
|
314
|
+
"from_version": {
|
315
|
+
"type": "string"
|
316
|
+
},
|
317
|
+
"to_version": {
|
318
|
+
"type": "string"
|
319
|
+
},
|
320
|
+
"status": {
|
321
|
+
"type": "string",
|
322
|
+
"enum": [
|
323
|
+
"pending",
|
324
|
+
"downloading",
|
325
|
+
"flashing",
|
326
|
+
"verifying",
|
327
|
+
"rebooting",
|
328
|
+
"completed",
|
329
|
+
"failed"
|
330
|
+
]
|
331
|
+
},
|
332
|
+
"progress_pct": {
|
333
|
+
"type": "number",
|
334
|
+
"minimum": 0,
|
335
|
+
"maximum": 100
|
336
|
+
},
|
337
|
+
"error": {
|
338
|
+
"type": [
|
339
|
+
"string",
|
340
|
+
"null"
|
341
|
+
]
|
342
|
+
}
|
343
|
+
},
|
344
|
+
"additionalProperties": true
|
345
|
+
};
|
346
|
+
export const control_response_schema = {
|
347
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
348
|
+
"$id": "https://schemas.alteriom.io/mqtt/v1/control_response.schema.json",
|
349
|
+
"title": "Control / Command Response v1",
|
350
|
+
"allOf": [
|
351
|
+
{
|
352
|
+
"$ref": "envelope.schema.json"
|
353
|
+
}
|
354
|
+
],
|
355
|
+
"type": "object",
|
356
|
+
"required": [
|
357
|
+
"status"
|
358
|
+
],
|
359
|
+
"properties": {
|
360
|
+
"command": {
|
361
|
+
"type": "string"
|
362
|
+
},
|
363
|
+
"status": {
|
364
|
+
"type": "string",
|
365
|
+
"enum": [
|
366
|
+
"ok",
|
367
|
+
"error"
|
368
|
+
]
|
369
|
+
},
|
370
|
+
"message": {
|
371
|
+
"type": "string"
|
372
|
+
},
|
373
|
+
"result": {
|
374
|
+
"type": [
|
375
|
+
"object",
|
376
|
+
"array",
|
377
|
+
"string",
|
378
|
+
"number",
|
379
|
+
"boolean",
|
380
|
+
"null"
|
381
|
+
]
|
382
|
+
}
|
383
|
+
},
|
384
|
+
"additionalProperties": true
|
385
|
+
};
|
386
|
+
export const mqtt_v1_bundle_json = {
|
387
|
+
"$comment": "Convenience bundle referencing all v1 schema artifact filenames for tooling discovery.",
|
388
|
+
"version": 1,
|
389
|
+
"schemas": {
|
390
|
+
"envelope": "envelope.schema.json",
|
391
|
+
"sensor_data": "sensor_data.schema.json",
|
392
|
+
"sensor_heartbeat": "sensor_heartbeat.schema.json",
|
393
|
+
"sensor_status": "sensor_status.schema.json",
|
394
|
+
"gateway_info": "gateway_info.schema.json",
|
395
|
+
"gateway_metrics": "gateway_metrics.schema.json",
|
396
|
+
"firmware_status": "firmware_status.schema.json",
|
397
|
+
"control_response": "control_response.schema.json"
|
398
|
+
}
|
399
|
+
};
|
@@ -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
|
+
}
|