Skip to content

Commit

Permalink
Restructure settings (#10437)
Browse files Browse the repository at this point in the history
* -

* deep copy schema

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -
  • Loading branch information
Koenkk committed Jan 9, 2022
1 parent ee6b035 commit 30177b0
Show file tree
Hide file tree
Showing 24 changed files with 753 additions and 612 deletions.
12 changes: 6 additions & 6 deletions lib/controller.ts
Expand Up @@ -222,11 +222,11 @@ class Controller {
}

const options: MQTTOptions = {
retain: utils.getObjectProperty(entity.settings, 'retain', false) as boolean,
qos: utils.getObjectProperty(entity.settings, 'qos', 0) as 0 | 1 | 2,
retain: utils.getObjectProperty(entity.options, 'retain', false) as boolean,
qos: utils.getObjectProperty(entity.options, 'qos', 0) as 0 | 1 | 2,
};

const retention = utils.getObjectProperty(entity.settings, 'retention', false);
const retention = utils.getObjectProperty(entity.options, 'retention', false);
if (retention !== false) {
options.properties = {messageExpiryInterval: retention as number};
}
Expand Down Expand Up @@ -259,12 +259,12 @@ class Controller {
}

// filter mqtt message attributes
if (entity.settings.filtered_attributes) {
entity.settings.filtered_attributes.forEach((a) => delete message[a]);
if (entity.options.filtered_attributes) {
entity.options.filtered_attributes.forEach((a) => delete message[a]);
}

if (Object.entries(message).length) {
const output = settings.get().experimental.output;
const output = settings.get().advanced.output;
if (output === 'attribute_and_json' || output === 'json') {
await this.mqtt.publish(entity.name, stringify(message), options);
}
Expand Down
16 changes: 7 additions & 9 deletions lib/extension/availability.ts
Expand Up @@ -13,17 +13,13 @@ export default class Availability extends Extension {
private pingQueueExecuting = false;

private getTimeout(device: Device): number {
if (typeof device.settings.availability === 'object' && device.settings.availability?.timeout != null) {
return utils.minutes(device.settings.availability.timeout);
if (typeof device.options.availability === 'object' && device.options.availability?.timeout != null) {
return utils.minutes(device.options.availability.timeout);
}

const key = this.isActiveDevice(device) ? 'active' : 'passive';
const availabilitySettings = settings.get().availability;
if (typeof availabilitySettings === 'object' && availabilitySettings[key]?.timeout != null) {
return utils.minutes(availabilitySettings[key]?.timeout);
}

return key === 'active' ? utils.minutes(10) : utils.hours(25);
const value = settings.get().availability[key]?.timeout;
return key === 'active' ? utils.minutes(value) : utils.hours(value);
}

private isActiveDevice(device: Device): boolean {
Expand Down Expand Up @@ -96,7 +92,9 @@ export default class Availability extends Extension {

override async start(): Promise<void> {
this.eventBus.onEntityRenamed(this, (data) =>
data.entity.isDevice() && this.publishAvailability(data.entity, false, true));
data.entity.isDevice() &&
utils.isAvailabilityEnabledForDevice(data.entity, settings.get()) &&
this.publishAvailability(data.entity, false, true));
this.eventBus.onDeviceRemoved(this, (data) => clearTimeout(this.timers[data.ieeeAddr]));
this.eventBus.onDeviceLeave(this, (data) => clearTimeout(this.timers[data.ieeeAddr]));
this.eventBus.onDeviceAnnounce(this, (data) => this.retrieveState(data.device));
Expand Down
8 changes: 4 additions & 4 deletions lib/extension/bridge.ts
Expand Up @@ -392,9 +392,9 @@ export default class Bridge extends Extension {

const ID = message.id;
const entity = this.getEntity(entityType, ID);
const oldOptions = objectAssignDeep({}, cleanup(entity.settings));
const oldOptions = objectAssignDeep({}, cleanup(entity.options));
settings.changeEntityOptions(ID, message.options);
const newOptions = cleanup(entity.settings);
const newOptions = cleanup(entity.options);
await this.publishInfo();

logger.info(`Changed config for ${entityType} ${ID}`);
Expand Down Expand Up @@ -448,7 +448,7 @@ export default class Bridge extends Extension {
const homeAssisantRename = message.hasOwnProperty('homeassistant_rename') ?
message.homeassistant_rename : false;
const entity = this.getEntity(entityType, from);
const oldFriendlyName = entity.settings.friendly_name;
const oldFriendlyName = entity.options.friendly_name;

settings.changeFriendlyName(from, to);

Expand Down Expand Up @@ -683,7 +683,7 @@ export default class Bridge extends Extension {

getDefinitionPayload(device: Device): DefinitionPayload {
if (!device.definition) return null;
let icon = device.settings.icon ? device.settings.icon : device.definition.icon;
let icon = device.options.icon ? device.options.icon : device.definition.icon;
if (icon) {
icon = icon.replace('${zigbeeModel}', utils.sanitizeImageParameter(device.zh.modelID));
icon = icon.replace('${model}', utils.sanitizeImageParameter(device.definition.model));
Expand Down
6 changes: 3 additions & 3 deletions lib/extension/frontend.ts
Expand Up @@ -17,9 +17,9 @@ import bind from 'bind-decorator';
*/
export default class Frontend extends Extension {
private mqttBaseTopic = settings.get().mqtt.base_topic;
private host = settings.get().frontend.host || '0.0.0.0';
private port = settings.get().frontend.port || 8080;
private authToken = settings.get().frontend.auth_token || false;
private host = settings.get().frontend.host;
private port = settings.get().frontend.port;
private authToken = settings.get().frontend.auth_token;
private retainedMessages = new Map();
private server: http.Server;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
2 changes: 1 addition & 1 deletion lib/extension/groups.ts
Expand Up @@ -123,7 +123,7 @@ export default class Groups extends Extension {
if (Object.keys(payload).length) {
const entity = data.entity;
const groups = this.zigbee.groups().filter((g) => {
return g.settings && (!g.settings.hasOwnProperty('optimistic') || g.settings.optimistic);
return g.options && (!g.options.hasOwnProperty('optimistic') || g.options.optimistic);
});

if (entity instanceof Device) {
Expand Down
38 changes: 19 additions & 19 deletions lib/extension/homeassistant.ts
Expand Up @@ -65,16 +65,16 @@ export default class HomeAssistant extends Extension {
private discovered: {[s: string]:
{topics: Set<string>, mockProperties: Set<MockProperty>, objectIDs: Set<string>}} = {};
private discoveredTriggers : {[s: string]: Set<string>}= {};
private discoveryTopic = settings.get().advanced.homeassistant_discovery_topic;
private statusTopic = settings.get().advanced.homeassistant_status_topic;
private entityAttributes = settings.get().advanced.homeassistant_legacy_entity_attributes;
private discoveryTopic = settings.get().homeassistant.discovery_topic;
private statusTopic = settings.get().homeassistant.status_topic;
private entityAttributes = settings.get().homeassistant.legacy_entity_attributes;
private zigbee2MQTTVersion: string;

constructor(zigbee: Zigbee, mqtt: MQTT, state: State, publishEntityState: PublishEntityState,
eventBus: EventBus, enableDisableExtension: (enable: boolean, name: string) => Promise<void>,
restartCallback: () => void, addExtension: (extension: Extension) => Promise<void>) {
super(zigbee, mqtt, state, publishEntityState, eventBus, enableDisableExtension, restartCallback, addExtension);
if (settings.get().experimental.output === 'attribute') {
if (settings.get().advanced.output === 'attribute') {
throw new Error('Home Assistant integration is not possible with attribute output!');
}
}
Expand Down Expand Up @@ -835,7 +835,7 @@ export default class HomeAssistant extends Extension {
* can use Home Assistant entities in automations.
* https://github.com/Koenkk/zigbee2mqtt/issues/959#issuecomment-480341347
*/
if (settings.get().advanced.homeassistant_legacy_triggers) {
if (settings.get().homeassistant.legacy_triggers) {
const keys = ['action', 'click'].filter((k) => data.message[k]);
for (const key of keys) {
this.publishEntityState(data.entity, {[key]: ''});
Expand Down Expand Up @@ -978,19 +978,19 @@ export default class HomeAssistant extends Extension {
configs.push(updateAvailableSensor);
}

if (isDevice && entity.settings.hasOwnProperty('legacy') && !entity.settings.legacy) {
if (isDevice && entity.options.hasOwnProperty('legacy') && !entity.options.legacy) {
configs = configs.filter((c) => c !== sensorClick);
}

if (!settings.get().advanced.homeassistant_legacy_triggers) {
if (!settings.get().homeassistant.legacy_triggers) {
configs = configs.filter((c) => c.object_id !== 'action' && c.object_id !== 'click');
}

// deep clone of the config objects
configs = JSON.parse(JSON.stringify(configs));

if (entity.settings.homeassistant) {
const s = entity.settings.homeassistant;
if (entity.options.homeassistant) {
const s = entity.options.homeassistant;
configs = configs.filter((config) => !s.hasOwnProperty(config.object_id) || s[config.object_id] != null);
configs.forEach((config) => {
const configOverride = s[config.object_id];
Expand All @@ -1016,7 +1016,7 @@ export default class HomeAssistant extends Extension {
if (entity.isGroup()) {
if (!discover || entity.zh.members.length === 0) return;
} else if (!discover || !entity.definition || entity.zh.interviewing ||
(entity.settings.hasOwnProperty('homeassistant') && !entity.settings.homeassistant)) {
(entity.options.hasOwnProperty('homeassistant') && !entity.options.homeassistant)) {
return;
}

Expand Down Expand Up @@ -1061,7 +1061,7 @@ export default class HomeAssistant extends Extension {
}

// Set unique_id
payload.unique_id = `${entity.settings.ID}_${config.object_id}_${settings.get().mqtt.base_topic}`;
payload.unique_id = `${entity.options.ID}_${config.object_id}_${settings.get().mqtt.base_topic}`;

// Attributes for device registry
payload.device = this.getDevicePayload(entity);
Expand Down Expand Up @@ -1179,7 +1179,7 @@ export default class HomeAssistant extends Extension {
}

// Override configuration with user settings.
if (entity.settings.hasOwnProperty('homeassistant')) {
if (entity.options.hasOwnProperty('homeassistant')) {
const add = (obj: KeyValue): void => {
Object.keys(obj).forEach((key) => {
if (['type', 'object_id'].includes(key)) {
Expand All @@ -1197,10 +1197,10 @@ export default class HomeAssistant extends Extension {
});
};

add(entity.settings.homeassistant);
add(entity.options.homeassistant);

if (entity.settings.homeassistant.hasOwnProperty(config.object_id)) {
add(entity.settings.homeassistant[config.object_id]);
if (entity.options.homeassistant.hasOwnProperty(config.object_id)) {
add(entity.options.homeassistant[config.object_id]);
}
}

Expand Down Expand Up @@ -1260,7 +1260,7 @@ export default class HomeAssistant extends Extension {
`${this.discoveryTopic}/${this.getDiscoveryTopic(c, entity)}` === data.topic);
}
// Device was flagged to be excluded from homeassistant discovery
clear = clear || (entity.settings.hasOwnProperty('homeassistant') && !entity.settings.homeassistant);
clear = clear || (entity.options.hasOwnProperty('homeassistant') && !entity.options.homeassistant);

if (clear) {
logger.debug(`Clearing Home Assistant config '${data.topic}'`);
Expand Down Expand Up @@ -1290,7 +1290,7 @@ export default class HomeAssistant extends Extension {
const identifierPostfix = entity.isGroup() ?
`zigbee2mqtt_${this.getEncodedBaseTopic()}` : 'zigbee2mqtt';
const payload: KeyValue = {
identifiers: [`${identifierPostfix}_${entity.settings.ID}`],
identifiers: [`${identifierPostfix}_${entity.options.ID}`],
name: entity.name,
sw_version: `Zigbee2MQTT ${this.zigbee2MQTTVersion}`,
};
Expand Down Expand Up @@ -1339,8 +1339,8 @@ export default class HomeAssistant extends Extension {
}

private async publishDeviceTriggerDiscover(device: Device, key: string, value: string, force=false): Promise<void> {
const haConfig = device.settings.homeassistant;
if (device.settings.hasOwnProperty('homeassistant') && (haConfig == null ||
const haConfig = device.options.homeassistant;
if (device.options.hasOwnProperty('homeassistant') && (haConfig == null ||
(haConfig.hasOwnProperty('device_automation') && typeof haConfig === 'object' &&
haConfig.device_automation == null))) {
return;
Expand Down
4 changes: 2 additions & 2 deletions lib/extension/legacy/bridgeLegacy.ts
Expand Up @@ -51,7 +51,7 @@ export default class BridgeLegacy extends Extension {
try {
const entity = settings.getDevice(message);
assert(entity, `Entity '${message}' does not exist`);
settings.whitelistDevice(entity.ID.toString());
settings.addDeviceToPasslist(entity.ID.toString());
logger.info(`Whitelisted '${entity.friendly_name}'`);
this.mqtt.publish(
'bridge/log',
Expand Down Expand Up @@ -336,7 +336,7 @@ export default class BridgeLegacy extends Extension {
}

if (action === 'ban') {
settings.banDevice(ieeeAddr);
settings.blockDevice(ieeeAddr);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/extension/onEvent.ts
Expand Up @@ -43,7 +43,7 @@ export default class OnEvent extends Extension {
zhc.onEvent(type, data, device.zh);

if (device.definition?.onEvent) {
await device.definition.onEvent(type, data, device.zh, device.settings);
await device.definition.onEvent(type, data, device.zh, device.options);
}
}
}
2 changes: 1 addition & 1 deletion lib/extension/otaUpdate.ts
Expand Up @@ -39,7 +39,7 @@ export default class OTAUpdate extends Extension {
override async start(): Promise<void> {
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
this.eventBus.onDeviceMessage(this, this.onZigbeeEvent);
if (settings.get().advanced.ikea_ota_use_test_url) {
if (settings.get().ota.ikea_ota_use_test_url) {
tradfriOTA.useTestURL();
}

Expand Down
4 changes: 2 additions & 2 deletions lib/extension/publish.ts
Expand Up @@ -91,7 +91,7 @@ export default class Publish extends Extension {
// Only do this when the retrieve_state option is enabled for this device.
// retrieve_state == decprecated
if (re instanceof Device && result && result.hasOwnProperty('readAfterWriteTime') &&
re.settings.retrieve_state
re.options.retrieve_state
) {
setTimeout(() => converter.convertGet(target, key, meta), result.readAfterWriteTime);
}
Expand Down Expand Up @@ -134,7 +134,7 @@ export default class Publish extends Extension {
return;
}
const device = re instanceof Device ? re.zh : null;
const entitySettings = re.settings;
const entitySettings = re.options;
const entityState = this.state.get(re) || {};
const membersState = re instanceof Group ?
Object.fromEntries(re.zh.members.map((e) => [e.getDevice().ieeeAddr,
Expand Down
8 changes: 4 additions & 4 deletions lib/extension/receive.ts
Expand Up @@ -128,9 +128,9 @@ export default class Receive extends Extension {
}

// Check if we have to debounce
if (data.device.settings.debounce) {
this.publishDebounce(data.device, payload, data.device.settings.debounce,
data.device.settings.debounce_ignore);
if (data.device.options.debounce) {
this.publishDebounce(data.device, payload, data.device.options.debounce,
data.device.options.debounce_ignore);
} else {
this.publishEntityState(data.device, payload);
}
Expand All @@ -141,7 +141,7 @@ export default class Receive extends Extension {
for (const converter of converters) {
try {
const converted = await converter.convert(
data.device.definition, data, publish, data.device.settings, meta);
data.device.definition, data, publish, data.device.options, meta);
if (converted) {
payload = {...payload, ...converted};
}
Expand Down
6 changes: 3 additions & 3 deletions lib/model/device.ts
Expand Up @@ -8,9 +8,9 @@ export default class Device {

get ieeeAddr(): string {return this.zh.ieeeAddr;}
get ID(): string {return this.zh.ieeeAddr;}
get settings(): DeviceSettings {return {...settings.get().device_options, ...settings.getDevice(this.ieeeAddr)};}
get options(): DeviceOptions {return {...settings.get().device_options, ...settings.getDevice(this.ieeeAddr)};}
get name(): string {
return this.zh.type === 'Coordinator' ? 'Coordinator' : this.settings?.friendly_name || this.ieeeAddr;
return this.zh.type === 'Coordinator' ? 'Coordinator' : this.options?.friendly_name || this.ieeeAddr;
}
get definition(): zhc.Definition {
if (!this._definition && !this.zh.interviewing) {
Expand All @@ -26,7 +26,7 @@ export default class Device {
exposes(): zhc.DefinitionExpose[] {
/* istanbul ignore if */
if (typeof this.definition.exposes == 'function') {
return this.definition.exposes(this.zh, this.settings);
return this.definition.exposes(this.zh, this.options);
} else {
return this.definition.exposes;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/model/group.ts
Expand Up @@ -6,8 +6,8 @@ export default class Group {
public zh: zh.Group;

get ID(): number {return this.zh.groupID;}
get settings(): GroupSettings {return settings.getGroup(this.ID);}
get name(): string {return this.settings?.friendly_name || this.ID.toString();}
get options(): GroupOptions {return settings.getGroup(this.ID);}
get name(): string {return this.options?.friendly_name || this.ID.toString();}

constructor(group: zh.Group) {
this.zh = group;
Expand Down

1 comment on commit 30177b0

@timoline
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, still need some work though.

keep up the good work!

Please sign in to comment.