mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-10-22 14:01:20 +00:00
Validate settings on startup with JSON schema.
This commit is contained in:
1 parent
34111159ae
commit
cfefaeb73c
8 files changed
+203
-43
No files matched your search
+13
-1
@@ -70,6 +70,18 @@ class Controller {
|
||||
}
|
||||
|
||||
async start() {
|
||||
const settingsErrors = settings.validate();
|
||||
if (settingsErrors) {
|
||||
logger.error(`Refusing to start, configuration.yaml is not valid, found the following errors:`);
|
||||
for (const error of settingsErrors) {
|
||||
logger.error(`\t - ${error}`);
|
||||
}
|
||||
logger.error(
|
||||
`If you don't know how to solve this, read https://www.zigbee2mqtt.io/configuration/configuration.html`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
logger.info(`Logging to directory: '${logger.directory}'`);
|
||||
logger.cleanup();
|
||||
this.state.start();
|
||||
@@ -242,7 +254,7 @@ class Controller {
|
||||
}
|
||||
|
||||
const options = {
|
||||
retain: entity.settings.hasOwnProperty('retain') ? entity.settings.retain : false,
|
||||
retain: entity.settings.retain,
|
||||
qos: entity.settings.hasOwnProperty('qos') ? entity.settings.qos : 0,
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ const file = data.joinPath('configuration.yaml');
|
||||
const objectAssignDeep = require(`object-assign-deep`);
|
||||
const path = require('path');
|
||||
const yaml = require('./yaml');
|
||||
const Ajv = require('ajv');
|
||||
const ajv = new Ajv({allErrors: true});
|
||||
|
||||
const defaults = {
|
||||
whitelist: [],
|
||||
@@ -100,6 +102,138 @@ const defaults = {
|
||||
},
|
||||
};
|
||||
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
homeassistant: {type: 'boolean'},
|
||||
permit_join: {type: 'boolean'},
|
||||
mqtt: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
base_topic: {type: 'string'},
|
||||
server: {type: 'string'},
|
||||
ca: {type: 'string'},
|
||||
key: {type: 'string'},
|
||||
cert: {type: 'string'},
|
||||
user: {type: 'string'},
|
||||
password: {type: 'string'},
|
||||
client_id: {type: 'string'},
|
||||
reject_unauthorized: {type: 'boolean'},
|
||||
include_device_information: {type: 'boolean'},
|
||||
},
|
||||
required: ['base_topic', 'server'],
|
||||
},
|
||||
serial: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
port: {type: 'string'},
|
||||
disable_led: {type: 'boolean'},
|
||||
},
|
||||
required: ['port'],
|
||||
},
|
||||
ban: {type: 'array', items: {type: 'string'}},
|
||||
whitelist: {type: 'array', items: {type: 'string'}},
|
||||
advanced: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
pan_id: {type: 'number'},
|
||||
ext_pan_id: {type: 'array', items: {type: 'number'}},
|
||||
channel: {type: 'number', minimum: 11, maximum: 26},
|
||||
cache_state: {type: 'boolean'},
|
||||
log_level: {type: 'string', enum: ['info', 'warn', 'error', 'debug']},
|
||||
log_directory: {type: 'string'},
|
||||
baudrate: {type: 'number'},
|
||||
rtscts: {type: 'boolean'},
|
||||
soft_reset_timeout: {type: 'number', minimum: 0},
|
||||
network_key: {type: 'array', items: {type: 'number'}},
|
||||
last_seen: {type: 'string', enum: ['disable', 'ISO_8601', 'ISO_8601_local', 'epoch']},
|
||||
elapsed: {type: 'boolean'},
|
||||
availability_timeout: {type: 'number', minimum: 0},
|
||||
availability_blacklist: {type: 'array', items: {type: 'string'}},
|
||||
report: {type: 'boolean'},
|
||||
homeassistant_discovery_topic: {type: 'string'},
|
||||
homeassistant_status_topic: {type: 'string'},
|
||||
},
|
||||
},
|
||||
map_options: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
graphviz: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
colors: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
fill: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
enddevice: {type: 'string'},
|
||||
coordinator: {type: 'string'},
|
||||
router: {type: 'string'},
|
||||
},
|
||||
},
|
||||
font: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
enddevice: {type: 'string'},
|
||||
coordinator: {type: 'string'},
|
||||
router: {type: 'string'},
|
||||
},
|
||||
},
|
||||
line: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
active: {type: 'string'},
|
||||
inactive: {type: 'string'},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
devices: {
|
||||
type: 'object',
|
||||
propertyNames: {
|
||||
pattern: '^0x[\\d\\w]{16}$',
|
||||
},
|
||||
patternProperties: {
|
||||
'^.*$': {
|
||||
type: 'object',
|
||||
properties: {
|
||||
friendly_name: {type: 'string'},
|
||||
retain: {type: 'boolean'},
|
||||
qos: {type: 'number'},
|
||||
},
|
||||
required: ['friendly_name', 'retain'],
|
||||
},
|
||||
},
|
||||
},
|
||||
groups: {
|
||||
type: 'object',
|
||||
propertyNames: {
|
||||
pattern: '^[\\w].*$',
|
||||
},
|
||||
patternProperties: {
|
||||
'^.*$': {
|
||||
type: 'object',
|
||||
properties: {
|
||||
friendly_name: {type: 'string'},
|
||||
retain: {type: 'boolean'},
|
||||
devices: {type: 'array', items: {type: 'string'}},
|
||||
optimistic: {type: 'boolean'},
|
||||
qos: {type: 'number'},
|
||||
},
|
||||
required: ['friendly_name', 'retain'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['homeassistant', 'permit_join', 'mqtt', 'serial'],
|
||||
};
|
||||
|
||||
|
||||
let _settings;
|
||||
let _settingsWithDefaults;
|
||||
|
||||
@@ -125,6 +259,12 @@ function write() {
|
||||
_settingsWithDefaults = objectAssignDeep.noMutate(defaults, get());
|
||||
}
|
||||
|
||||
function validate() {
|
||||
const validate = ajv.compile(schema);
|
||||
const valid = validate(_settings);
|
||||
return !valid ? validate.errors.map((v) => `${v.dataPath.substring(1)} ${v.message}`) : null;
|
||||
}
|
||||
|
||||
function read() {
|
||||
const s = yaml.read(file);
|
||||
|
||||
@@ -391,6 +531,7 @@ function changeFriendlyName(IDorName, newName) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validate,
|
||||
get: getWithDefaults,
|
||||
set,
|
||||
getDevice,
|
||||
|
||||
Reference in new issue
Block a user