Validate settings on startup with JSON schema.

This commit is contained in:
Koen Kanters committed 2019-09-25 12:08:39 +02:00
1 parent 34111159ae
commit cfefaeb73c
8 files changed
+203 -43

No files matched your search

+13 -1
View File
@@ -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,
};
+141
View File
@@ -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,