diff --git a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json
index 0158b38fe9..7d5dd46086 100644
--- a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json
+++ b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json
@@ -5,6 +5,38 @@
     "image": null
   },
   "widgetTypes": [
+    {
+      "alias": "attributes_card",
+      "name": "Gateway events",
+      "descriptor": {
+        "type": "latest",
+        "sizeX": 7.5,
+        "sizeY": 8,
+        "resources": [],
+        "templateHtml": "",
+        "templateCss": "#container {\n    overflow: auto;\n}\n\n.tbDatasource-container {\n    margin: 5px;\n    padding: 8px;\n}\n\n.tbDatasource-title {\n    font-size: 1.200rem;\n    font-weight: 500;\n    padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n    width: 100%;\n    box-shadow: 0 0 10px #ccc;\n    border-collapse: collapse;\n    white-space: nowrap;\n    font-size: 1.000rem;\n    color: #757575;\n}\n\n.tbDatasource-table td {\n    position: relative;\n    border-top: 1px solid rgba(0, 0, 0, 0.12);\n    border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n    padding: 0px 18px;\n    box-sizing: border-box;\n}",
+        "controllerScript": "let types;\nlet eventsReg = \"eventsReg\";\n\nself.onInit = function() {\n    \n    self.ctx.datasourceTitleCells = [];\n    self.ctx.valueCells = [];\n    self.ctx.labelCells = [];\n    types = self.ctx.$scope.$injector.get('types');\n    \n    if (self.ctx.datasources.length && self.ctx.datasources[0].type === types.datasourceType.entity) {\n        getDatasourceKeys(self.ctx.datasources[0]);\n    } else {\n        processDatasources(self.ctx.datasources);\n    }\n}\n\nself.onDataUpdated = function() {\n    for (var i = 0; i < self.ctx.valueCells.length; i++) {\n        var cellData = self.ctx.data[i];\n        if (cellData && cellData.data && cellData.data.length > 0) {\n            var tvPair = cellData.data[cellData.data.length -\n                1];\n            var value = tvPair[1];\n            var textValue;\n            //toDo -> + IsNumber\n            \n            if (isNumber(value)) {\n                var decimals = self.ctx.decimals;\n                var units = self.ctx.units;\n                if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n                    decimals = cellData.dataKey.decimals;\n                }\n                if (cellData.dataKey.units) {\n                    units = cellData.dataKey.units;\n                }\n                txtValue = self.ctx.utils.formatValue(value, decimals, units, false);\n            }\n            else {\n                txtValue = value;\n            }\n            self.ctx.valueCells[i].html(txtValue);\n        }\n    }\n    \n    function isNumber(n) {\n        return !isNaN(parseFloat(n)) && isFinite(n);\n    }\n}\n\nself.onResize = function() {\n    var datasourceTitleFontSize = self.ctx.height/8;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        datasourceTitleFontSize = self.ctx.width/12;\n    }\n    datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n    for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n        self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n    }\n    var valueFontSize = self.ctx.height/9;\n    var labelFontSize = self.ctx.height/9;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        valueFontSize = self.ctx.width/15;\n        labelFontSize = self.ctx.width/15;\n    }\n    valueFontSize = Math.min(valueFontSize, 18);\n    labelFontSize = Math.min(labelFontSize, 18);\n\n    for (i = 0; i < self.ctx.valueCells; i++) {\n        self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n        self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n        self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n        self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n        self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n        self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n    }    \n}\n\nfunction processDatasources(datasources) {\n    var i = 0;\n    var tbDatasource = datasources[i];\n    var datasourceId = 'tbDatasource' + i;\n    self.ctx.$container.append(\n        \"
\"\n    );\n\n    var datasourceContainer = $('#' + datasourceId,\n        self.ctx.$container);\n\n    datasourceContainer.append(\n        \"\" +\n        tbDatasource.name + \"
\"\n    );\n    \n    var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n    self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n    \n    var tableId = 'table' + i;\n    datasourceContainer.append(\n        \"\"\n    );\n    var table = $('#' + tableId, self.ctx.$container);\n\n    for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n        var dataKey = tbDatasource.dataKeys[a];\n        var labelCellId = 'labelCell' + a;\n        var cellId = 'cell' + a;\n        table.append(\"| \" + dataKey.label +\n            \" |  | 
\");\n        var labelCell = $('#' + labelCellId, table);\n        self.ctx.labelCells.push(labelCell);\n        var valueCell = $('#' + cellId, table);\n        self.ctx.valueCells.push(valueCell);\n    }\n    self.onResize();\n}\n\nfunction getDatasourceKeys (datasource) {\n    let attributeService = self.ctx.$scope.$injector.get('attributeService');\n    if (datasource.entityId && datasource.entityType) {\n    attributeService.getEntityKeys(datasource.entityType, datasource.entityId, \"\" , types.dataKeyType.timeseries).then(\n        function(data){\n            if (data.length) {\n                subscribeForKeys (datasource, data);\n            }\n        });\n    }\n}\n\nfunction subscribeForKeys (datasource, data) {\n    let eventsRegVals = self.ctx.settings[eventsReg];\n    if (eventsRegVals && eventsRegVals.length > 0) {\n        var dataKeys = [];\n        data.sort();\n        data.forEach(dataValue => {eventsRegVals.forEach(event => {\n                if (dataValue.toLowerCase().includes(event.toLowerCase())) {\n                    var dataKey = {\n                        type: types.dataKeyType.timeseries,\n                        name: dataValue,\n                        label: dataValue,\n                        settings: {},\n                        _hash: Math.random()\n                    };\n                    dataKeys.push(dataKey);\n                }\n        })});\n\n        if (dataKeys.length) {\n            updateSubscription (datasource, dataKeys);\n        }\n    }\n}\n\nfunction updateSubscription (datasource, dataKeys) {\n    var datasources = [\n        {\n            type: types.datasourceType.entity,\n            name: datasource.aliasName,\n            aliasName: datasource.aliasName,\n            entityAliasId: datasource.entityAliasId,\n            dataKeys: dataKeys\n        }\n    ];\n    \n    var subscriptionOptions = {\n        datasources: datasources,\n        useDashboardTimewindow: false,\n        type: types.widgetType.latest.value,\n        callbacks: {\n            onDataUpdated: (subscription) => {\n                self.ctx.data = subscription.data;\n                self.onDataUpdated();\n            }\n        }\n    };\n    \n    processDatasources(datasources);\n    self.ctx.subscriptionApi.createSubscription(subscriptionOptions, true).then(\n        (subscription) => {\n            self.ctx.defaultSubscription = subscription;\n        }\n    );\n}\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\t\n        dataKeysOptional: true\n    };\n}\n\n",
+        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"GatewayEventsForm\",\n        \"properties\": {\n            \"eventsTitle\": {\n                \"title\": \"Gateway events form title\",\n                \"type\": \"string\",\n                \"default\": \"Gateway Events Form\"\n            },\n            \"eventsReg\": {\n                \"title\": \"Events filten.\",\n                \"type\": \"array\",\n                \"items\": {\n                    \"title\": \"Event key contains\",\n                    \"type\": \"string\"\n                }\n            }\n        }\n    },\n    \"form\": [\n        \"eventsTitle\",\n        \"eventsReg\"\n    ]\n}",
+        "dataKeySettingsSchema": "{}\n",
+        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Function Math.round\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.826503672916844,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"eventsTitle\":\"Gateway Events Form\",\"eventsReg\":[]},\"title\":\"Gateway events\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+      }
+    },
+    {
+      "alias": "config_form_latest",
+      "name": "Gateway configuration (Single device)",
+      "descriptor": {
+        "type": "latest",
+        "sizeX": 7.5,
+        "sizeY": 9,
+        "resources": [],
+        "templateHtml": "\n",
+        "templateCss": "#container {\n    overflow: auto;\n}\n\n.tbDatasource-container {\n    margin: 5px;\n    padding: 8px;\n}\n\n.tbDatasource-title {\n    font-size: 1.200rem;\n    font-weight: 500;\n    padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n    width: 100%;\n    box-shadow: 0 0 10px #ccc;\n    border-collapse: collapse;\n    white-space: nowrap;\n    font-size: 1.000rem;\n    color: #757575;\n}\n\n.tbDatasource-table td {\n    position: relative;\n    border-top: 1px solid rgba(0, 0, 0, 0.12);\n    border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n    padding: 0px 18px;\n    box-sizing: border-box;\n}",
+        "controllerScript": "self.onInit = function() {\n    var scope = self.ctx.$scope;\n    var id = self.ctx.$scope.$injector.get('utils').guid();\n    scope.formId = \"form-\"+id;\n    scope.ctx = self.ctx;\n    scope.isStateForm = true;\n}\n\nself.onResize = function() {\n    self.ctx.$scope.$broadcast('gateway-form-resize', self.ctx.$scope.formId);\n}\n\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\t\t\t\n        dataKeysOptional: true\t\t\n    };\n}\n\n",
+        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"GatewayConfigForm\",\n        \"properties\": {\n            \"gatewayTitle\": {\n                \"title\": \"Gateway form\",\n                \"type\": \"string\",\n                \"default\": \"Gateway configuration (Single device)\"\n            },\n            \"readOnly\": {\n                \"title\": \"Read Only\",\n                \"type\": \"boolean\",\n                \"default\": false\n            }\n        }\n    },\n    \"form\": [\n        \"gatewayTitle\",\n        \"readOnly\"\n    ]\n}\n",
+        "dataKeySettingsSchema": "{}\n",
+        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Gateway configuration (Single device)\"}"
+      }
+    },
     {
       "alias": "extension_configuration_widget",
       "name": "Extensions table",
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index aff3821573..8285430c56 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -606,13 +606,29 @@ export default angular.module('thingsboard.types', [])
                     value: "modbus",
                     name: "Modbus"
                 },
-                opc_ua:  {
+                opcua:  {
                     value: "opcua",
                     name: "OPC-UA"
                 },
                 ble:  {
                     value: "ble",
                     name: "BLE"
+                },
+                request:  {
+                    value: "request",
+                    name: "Request"
+                },
+                can:  {
+                    value: "can",
+                    name: "CAN"
+                },
+                bacnet: {
+                    value: "bacnet",
+                    name: "BACnet"
+                },
+                custom:  {
+                    value: "custom",
+                    name: "Custom"
                 }
             },
             gatewayLogLevel: {
diff --git a/ui/src/app/components/gateway/gateway-config.directive.js b/ui/src/app/components/gateway/gateway-config.directive.js
index 120a6d3e15..19e7f554b9 100644
--- a/ui/src/app/components/gateway/gateway-config.directive.js
+++ b/ui/src/app/components/gateway/gateway-config.directive.js
@@ -37,7 +37,8 @@ function GatewayConfig() {
             disabled: '=ngDisabled',
             gatewayConfig: '=',
             changeAlignment: '=',
-            theForm: '='
+            theForm: '=',
+            isReadOnly: '='
         },
         controller: GatewayConfigController,
         controllerAs: 'vm',
@@ -83,6 +84,10 @@ function GatewayConfigController($scope, $document, $mdDialog, $mdUtil, $window,
             multiple: true,
         }).then(function (config) {
             if (config && index > -1) {
+                console.log(config); //eslint-disable-line
+                if (!angular.equals(vm.gatewayConfig[index].config, config)) {
+                    $scope.gatewayConfiguration.$setDirty();
+                }
                 vm.gatewayConfig[index].config = config;
             }
         });
diff --git a/ui/src/app/components/gateway/gateway-config.tpl.html b/ui/src/app/components/gateway/gateway-config.tpl.html
index eef7c737e4..e4525b2bf2 100644
--- a/ui/src/app/components/gateway/gateway-config.tpl.html
+++ b/ui/src/app/components/gateway/gateway-config.tpl.html
@@ -18,7 +18,7 @@