diff --git a/lib/extension/bridgeLegacy.js b/lib/extension/bridgeLegacy.js
index d64e93a4a0..15c7be9415 100644
--- a/lib/extension/bridgeLegacy.js
+++ b/lib/extension/bridgeLegacy.js
@@ -63,7 +63,10 @@ class BridgeLegacy extends BaseExtension {
             assert(entity, `Entity '${message}' does not exist`);
             settings.whitelistDevice(entity.ID);
             logger.info(`Whitelisted '${entity.friendlyName}'`);
-            this.mqtt.log('device_whitelisted', {friendly_name: entity.friendlyName});
+            this.mqtt.publish(
+                'bridge/log',
+                JSON.stringify({type: 'device_whitelisted', message: {friendly_name: entity.friendlyName}}),
+            );
         } catch (error) {
             logger.error(`Failed to whitelist '${message}' '${error}'`);
         }
@@ -174,16 +177,18 @@ class BridgeLegacy extends BaseExtension {
         if (topic.split('/').pop() == 'get') {
             this.mqtt.publish(`bridge/config/devices`, JSON.stringify(devices), {});
         } else {
-            this.mqtt.log('devices', devices);
+            this.mqtt.publish('bridge/log', JSON.stringify({type: 'devices', message: devices}));
         }
     }
 
     groups(topic, message) {
-        this.mqtt.log('groups', settings.getGroups().map((g) => {
+        const payload = settings.getGroups().map((g) => {
             const group = {...g};
             delete group.friendlyName;
             return group;
-        }));
+        });
+
+        this.mqtt.publish('bridge/log', JSON.stringify({type: 'groups', message: payload}));
     }
 
     rename(topic, message) {
@@ -223,7 +228,11 @@ class BridgeLegacy extends BaseExtension {
             const entity = this.zigbee.resolveEntity(to);
             const eventData = isGroup ? {group: entity.group} : {device: entity.device};
             this.eventBus.emit(`${isGroup ? 'group' : 'device'}Renamed`, eventData);
-            this.mqtt.log(`${isGroup ? 'group' : 'device'}_renamed`, {from, to});
+
+            this.mqtt.publish(
+                'bridge/log',
+                JSON.stringify({type: `${isGroup ? 'group' : 'device'}_renamed`, message: {from, to}}),
+            );
         } catch (error) {
             logger.error(`Failed to rename - ${from} to ${to}`);
         }
@@ -254,7 +263,7 @@ class BridgeLegacy extends BaseExtension {
 
         const group = settings.addGroup(name, id);
         this.zigbee.createGroup(group.ID);
-        this.mqtt.log('group_added', name);
+        this.mqtt.publish('bridge/log', JSON.stringify({type: `group_added`, message: name}));
         logger.info(`Added group '${name}'`);
     }
 
@@ -264,7 +273,7 @@ class BridgeLegacy extends BaseExtension {
         assert(entity && entity.type === 'group', `Group '${message}' does not exist`);
         settings.removeGroup(message);
         entity.group.removeFromDatabase();
-        this.mqtt.log('group_removed', message);
+        this.mqtt.publish('bridge/log', JSON.stringify({type: `group_removed`, message}));
         logger.info(`Removed group '${name}'`);
     }
 
@@ -291,7 +300,7 @@ class BridgeLegacy extends BaseExtension {
         if (!entity) {
             logger.error(`Cannot ${lookup[action][2]}, device '${message}' does not exist`);
 
-            this.mqtt.log(`device_${lookup[action][0]}_failed`, message);
+            this.mqtt.publish('bridge/log', JSON.stringify({type: `device_${lookup[action][0]}_failed`, message}));
             return;
         }
 
@@ -306,7 +315,7 @@ class BridgeLegacy extends BaseExtension {
             this.state.remove(entity.settings.ID);
 
             logger.info(`Successfully ${lookup[action][0]} ${entity.settings.friendlyName}`);
-            this.mqtt.log(`device_${lookup[action][0]}`, message);
+            this.mqtt.publish('bridge/log', JSON.stringify({type: `device_${lookup[action][0]}`, message}));
         };
 
         try {
@@ -323,7 +332,7 @@ class BridgeLegacy extends BaseExtension {
             // eslint-disable-next-line
             logger.error(`See https://www.zigbee2mqtt.io/information/mqtt_topics_and_message_structure.html#zigbee2mqttbridgeconfigremove for more info`);
 
-            this.mqtt.log(`device_${lookup[action][0]}_failed`, message);
+            this.mqtt.publish('bridge/log', JSON.stringify({type: `device_${lookup[action][0]}_failed`, message}));
         }
 
         if (action === 'ban') {
@@ -374,47 +383,78 @@ class BridgeLegacy extends BaseExtension {
         }
 
         if (type === 'deviceJoined') {
-            this.mqtt.log('device_connected', {friendly_name: resolvedEntity.name});
+            this.mqtt.publish(
+                'bridge/log',
+                JSON.stringify({type: `device_connected`, message: {friendly_name: resolvedEntity.name}}),
+            );
         } else if (type === 'deviceInterview') {
             if (data.status === 'successful') {
                 if (resolvedEntity.definition) {
                     const {vendor, description, model} = resolvedEntity.definition;
                     const log = {friendly_name: resolvedEntity.name, model, vendor, description, supported: true};
-                    this.mqtt.log('pairing', 'interview_successful', log);
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `pairing`, message: 'interview_successful', meta: log}),
+                    );
                 } else {
-                    this.mqtt.log('pairing', 'interview_successful',
-                        {friendly_name: resolvedEntity.name, supported: false});
+                    const meta = {friendly_name: resolvedEntity.name, supported: false};
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `pairing`, message: 'interview_successful', meta}),
+                    );
                 }
             } else if (data.status === 'failed') {
-                this.mqtt.log('pairing', 'interview_failed', {friendly_name: resolvedEntity.name});
+                const meta = {friendly_name: resolvedEntity.name};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `pairing`, message: 'interview_failed', meta}),
+                );
             } else {
                 /* istanbul ignore else */
                 if (data.status === 'started') {
-                    this.mqtt.log('pairing', 'interview_started', {friendly_name: resolvedEntity.name});
+                    const meta = {friendly_name: resolvedEntity.name};
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `pairing`, message: 'interview_started', meta}),
+                    );
                 }
             }
         } else if (type === 'deviceAnnounce') {
-            this.mqtt.log('device_announced', 'announce', {friendly_name: resolvedEntity.name});
+            const meta = {friendly_name: resolvedEntity.name};
+            this.mqtt.publish('bridge/log', JSON.stringify({type: `device_announced`, message: 'announce', meta}));
         } else {
             /* istanbul ignore else */
             if (type === 'deviceLeave') {
                 const name = resolvedEntity ? resolvedEntity.name : data.ieeeAddr;
-                this.mqtt.log('device_removed', 'left_network', {friendly_name: name});
+                const meta = {friendly_name: name};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `device_removed`, message: 'left_network', meta}),
+                );
             }
         }
     }
 
     async touchlinkFactoryReset() {
         logger.info('Starting touchlink factory reset...');
-        this.mqtt.log('touchlink', 'reset_started', {status: 'started'});
+        this.mqtt.publish(
+            'bridge/log',
+            JSON.stringify({type: `touchlink`, message: 'reset_started', meta: {status: 'started'}}),
+        );
         const result = await this.zigbee.touchlinkFactoryReset();
 
         if (result) {
             logger.info('Successfully factory reset device through Touchlink');
-            this.mqtt.log('touchlink', 'reset_success', {status: 'success'});
+            this.mqtt.publish(
+                'bridge/log',
+                JSON.stringify({type: `touchlink`, message: 'reset_success', meta: {status: 'success'}}),
+            );
         } else {
             logger.warn('Failed to factory reset device through Touchlink');
-            this.mqtt.log('touchlink', 'reset_failed', {status: 'failed'});
+            this.mqtt.publish(
+                'bridge/log',
+                JSON.stringify({type: `touchlink`, message: 'reset_failed', meta: {status: 'failed'}}),
+            );
         }
     }
 }
diff --git a/lib/extension/deviceBind.js b/lib/extension/deviceBind.js
index 6f0d68f482..206351fdbe 100644
--- a/lib/extension/deviceBind.js
+++ b/lib/extension/deviceBind.js
@@ -61,26 +61,44 @@ class DeviceBind extends BaseExtension {
                         `Successfully ${type === 'bind' ? 'bound' : 'unbound'} cluster '${cluster}' from ` +
                         `'${sourceName}' to '${targetName}'`,
                     );
-                    this.mqtt.log(
-                        `device_${type}`,
-                        {from: sourceName, to: targetName, cluster},
-                    );
+
+                    /* istanbul ignore else */
+                    if (settings.get().advanced.legacy_api) {
+                        const message = {from: sourceName, to: targetName, cluster};
+                        this.mqtt.publish(
+                            'bridge/log',
+                            JSON.stringify({type: `device_${type}`, message}),
+                        );
+                    }
                 } catch (error) {
                     logger.error(
                         `Failed to ${type} cluster '${cluster}' from '${sourceName}' to ` +
                         `'${targetName}' (${error})`,
                     );
-                    this.mqtt.log(
-                        `device_${type}_failed`,
-                        {from: sourceName, to: targetName, cluster},
-                    );
+
+                    /* istanbul ignore else */
+                    if (settings.get().advanced.legacy_api) {
+                        const message = {from: sourceName, to: targetName, cluster};
+                        this.mqtt.publish(
+                            'bridge/log',
+                            JSON.stringify({type: `device_${type}_failed`, message}),
+                        );
+                    }
                 }
             }
         }
 
         if (!attemptedToBindSomething) {
             logger.error(`Nothing to ${type} from '${sourceName}' to '${targetName}'`);
-            this.mqtt.log(`device_${type}_failed`, {from: sourceName, to: targetName});
+
+            /* istanbul ignore else */
+            if (settings.get().advanced.legacy_api) {
+                const message = {from: sourceName, to: targetName};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `device_${type}_failed`, message}),
+                );
+            }
         }
     }
 }
diff --git a/lib/extension/entityPublish.js b/lib/extension/entityPublish.js
index c0ec011a49..3e2968a567 100644
--- a/lib/extension/entityPublish.js
+++ b/lib/extension/entityPublish.js
@@ -59,7 +59,15 @@ class EntityPublish extends BaseExtension {
         const entity = this.zigbee.resolveEntity(entityName);
 
         if (!entity) {
-            this.mqtt.log('entity_not_found', {friendly_name: entityName});
+            /* istanbul ignore else */
+            if (settings.get().advanced.legacy_api) {
+                const message = {friendly_name: entityName};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `entity_not_found`, message}),
+                );
+            }
+
             logger.error(`Entity '${entityName}' is unknown`);
             return;
         }
@@ -211,7 +219,15 @@ class EntityPublish extends BaseExtension {
                     `Publish '${topic.type}' '${key}' to '${entity.name}' failed: '${error}'`;
                 logger.error(message);
                 logger.debug(error.stack);
-                this.mqtt.log('zigbee_publish_error', message, {friendly_name: entity.name});
+
+                /* istanbul ignore else */
+                if (settings.get().advanced.legacy_api) {
+                    const meta = {friendly_name: entity.name};
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `zigbee_publish_error`, message, meta}),
+                    );
+                }
             }
 
             usedConverters.push(converter);
diff --git a/lib/extension/groups.js b/lib/extension/groups.js
index fb8032f4c1..7f46856a7b 100644
--- a/lib/extension/groups.js
+++ b/lib/extension/groups.js
@@ -144,9 +144,16 @@ class Groups extends BaseExtension {
 
             if (!group || group.type !== 'group') {
                 logger.error(`Group '${topicMatch[1]}' does not exist`);
-                this.mqtt.log(
-                    `device_group_${type}_failed`,
-                    {friendly_name: message, group: topicMatch[1], error: 'group doesn\'t exists'});
+
+                /* istanbul ignore else */
+                if (settings.get().advanced.legacy_api) {
+                    const payload = {friendly_name: message, group: topicMatch[1], error: 'group doesn\'t exists'};
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `device_group_${type}_failed`, message: payload}),
+                    );
+                }
+
                 return;
             }
         } else if (topic.match(topicRegexRemoveAll)) {
@@ -158,9 +165,16 @@ class Groups extends BaseExtension {
         const entity = this.zigbee.resolveEntity(message);
         if (!entity || !entity.type === 'device') {
             logger.error(`Device '${message}' does not exist`);
-            this.mqtt.log(
-                `device_group_${type}_failed`,
-                {friendly_name: message, group: topicMatch[1], error: 'entity doesn\'t exists'});
+
+            /* istanbul ignore else */
+            if (settings.get().advanced.legacy_api) {
+                const payload = {friendly_name: message, group: topicMatch[1], error: 'entity doesn\'t exists'};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `device_group_${type}_failed`, message: payload}),
+                );
+            }
+
             return;
         }
 
@@ -188,18 +202,42 @@ class Groups extends BaseExtension {
             logger.info(`Adding '${entity.name}' to '${group.name}'`);
             await entity.endpoint.addToGroup(group.group);
             settings.addDeviceToGroup(group.settings.ID, keys);
-            this.mqtt.log('device_group_add', {friendly_name: entity.name, group: group.name});
+
+            /* istanbul ignore else */
+            if (settings.get().advanced.legacy_api) {
+                const payload = {friendly_name: entity.name, group: group.name};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `device_group_add`, message: payload}),
+                );
+            }
         } else if (type === 'remove') {
             logger.info(`Removing '${entity.name}' from '${group.name}'`);
             await entity.endpoint.removeFromGroup(group.group);
             settings.removeDeviceFromGroup(group.settings.ID, keys);
-            this.mqtt.log('device_group_remove', {friendly_name: entity.name, group: group.name});
+
+            /* istanbul ignore else */
+            if (settings.get().advanced.legacy_api) {
+                const payload = {friendly_name: entity.name, group: group.name};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `device_group_remove`, message: payload}),
+                );
+            }
         } else { // remove_all
             logger.info(`Removing '${entity.name}' from all groups`);
             await entity.endpoint.removeFromAllGroups();
             for (const settingsGroup of settings.getGroups()) {
                 settings.removeDeviceFromGroup(settingsGroup.ID, keys);
-                this.mqtt.log('device_group_remove_all', {friendly_name: entity.name});
+
+                /* istanbul ignore else */
+                if (settings.get().advanced.legacy_api) {
+                    const payload = {friendly_name: entity.name};
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `device_group_remove_all`, message: payload}),
+                    );
+                }
             }
         }
     }
diff --git a/lib/extension/otaUpdate.js b/lib/extension/otaUpdate.js
index 57e01b0fd9..e285337d0b 100644
--- a/lib/extension/otaUpdate.js
+++ b/lib/extension/otaUpdate.js
@@ -36,10 +36,15 @@ class OTAUpdate extends BaseExtension {
             if (available) {
                 const message = `Update available for '${resolvedEntity.settings.friendly_name}'`;
                 logger.info(message);
-                this.mqtt.log(
-                    'ota_update', message,
-                    {status: 'available', device: resolvedEntity.settings.friendly_name},
-                );
+
+                /* istanbul ignore else */
+                if (settings.get().advanced.legacy_api) {
+                    const meta = {status: 'available', device: resolvedEntity.settings.friendly_name};
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `ota_update`, message, meta}),
+                    );
+                }
             }
         }
 
@@ -77,7 +82,16 @@ class OTAUpdate extends BaseExtension {
         if (!device.definition || !device.definition.ota) {
             const message = `Device '${device.name}' does not support OTA updates`;
             logger.error(message);
-            this.mqtt.log('ota_update', message, {status: `not_supported`, device: device.name});
+
+            /* istanbul ignore else */
+            if (settings.get().advanced.legacy_api) {
+                const meta = {status: `not_supported`, device: device.name};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `ota_update`, message, meta}),
+                );
+            }
+
             return;
         }
 
@@ -91,25 +105,59 @@ class OTAUpdate extends BaseExtension {
         if (type === 'check') {
             const message = `Checking if update available for '${device.name}'`;
             logger.info(message);
-            this.mqtt.log('ota_update', message, {status: `checking_if_available`, device: device.name});
+
+            /* istanbul ignore else */
+            if (settings.get().advanced.legacy_api) {
+                const meta = {status: `checking_if_available`, device: device.name};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `ota_update`, message, meta}),
+                );
+            }
+
             try {
                 const available = await device.definition.ota.isUpdateAvailable(device.device, logger);
                 const message=(available ?
                     `Update available for '${device.name}'` : `No update available for '${device.name}'`);
                 logger.info(message);
-                const meta = {status: available ? 'available' : 'not_available', device: device.name};
-                this.mqtt.log('ota_update', message, meta);
+
+                /* istanbul ignore else */
+                if (settings.get().advanced.legacy_api) {
+                    const meta = {status: available ? 'available' : 'not_available', device: device.name};
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `ota_update`, message, meta}),
+                    );
+                }
+
                 this.publishEntityState(device.device.ieeeAddr, {update_available: available});
                 this.lastChecked[device.device.ieeeAddr] = Date.now();
             } catch (error) {
                 const message = `Failed to check if update available for '${device.name}' (${error.message})`;
                 logger.error(message);
-                this.mqtt.log('ota_update', message, {status: `check_failed`, device: device.name});
+
+                /* istanbul ignore else */
+                if (settings.get().advanced.legacy_api) {
+                    const meta = {status: `check_failed`, device: device.name};
+                    this.mqtt.publish(
+                        'bridge/log',
+                        JSON.stringify({type: `ota_update`, message, meta}),
+                    );
+                }
             }
         } else { // type === 'update'
             const message = `Updating '${device.name}' to latest firmware`;
             logger.info(message);
-            this.mqtt.log('ota_update', message, {status: `update_in_progress`, device: device.name});
+
+            /* istanbul ignore else */
+            if (settings.get().advanced.legacy_api) {
+                const meta = {status: `update_in_progress`, device: device.name};
+                this.mqtt.publish(
+                    'bridge/log',
+                    JSON.stringify({type: `ota_update`, message, meta}),
+                );
+            }
+
             try {
                 const onProgress = (progress, remaining) => {
                     let message = `Update of '${device.name}' at ${progress}%`;
@@ -118,7 +166,12 @@ class OTAUpdate extends BaseExtension {
                     }
 
                     logger.info(message);
-                    this.mqtt.log('ota_update', message, {status: `update_progress`, device: device.name, progress});
+
+                    /* istanbul ignore else */
+                    if (settings.get().advanced.legacy_api) {
+                        const meta = {status: `update_progress`, device: device.name, progress};
+                        this.mqtt.publish('bridge/log', JSON.stringify({type: `ota_update`, message, meta}));
+                    }
                 };
 
                 const from_ = await this.readSoftwareBuildIDAndDateCode(device.device, false);
@@ -127,13 +180,22 @@ class OTAUpdate extends BaseExtension {
                 const [fromS, toS] = [JSON.stringify(from_), JSON.stringify(to)];
                 const message = `Finished update of '${device.name}'` + (to ? `, from '${fromS}' to '${toS}'` : ``);
                 logger.info(message);
-                const meta = {status: `update_succeeded`, device: device.name, from: from_, to};
-                this.mqtt.log('ota_update', message, meta);
                 this.publishEntityState(device.device.ieeeAddr, {update_available: false});
+
+                /* istanbul ignore else */
+                if (settings.get().advanced.legacy_api) {
+                    const meta = {status: `update_succeeded`, device: device.name, from: from_, to};
+                    this.mqtt.publish('bridge/log', JSON.stringify({type: `ota_update`, message, meta}));
+                }
             } catch (error) {
                 const message = `Update of '${device.name}' failed (${error.message})`;
                 logger.error(message);
-                this.mqtt.log('ota_update', message, {status: `update_failed`, device: device.name});
+
+                /* istanbul ignore else */
+                if (settings.get().advanced.legacy_api) {
+                    const meta = {status: `update_failed`, device: device.name};
+                    this.mqtt.publish('bridge/log', JSON.stringify({type: `ota_update`, message, meta}));
+                }
             }
         }
 
diff --git a/lib/mqtt.js b/lib/mqtt.js
index b1171b89ae..3650cbffe2 100644
--- a/lib/mqtt.js
+++ b/lib/mqtt.js
@@ -111,16 +111,6 @@ class MQTT extends events.EventEmitter {
             this.client.publish(topic, payload, options, () => resolve());
         });
     }
-
-    log(type, message, meta=null) {
-        const payload = {type, message};
-
-        if (meta) {
-            payload.meta = meta;
-        }
-
-        this.publish('bridge/log', JSON.stringify(payload), {retain: false});
-    }
 }
 
 module.exports = MQTT;