Commit 912cffc3b1ad1616cee93f824130e70a3a3bb46c

Authored by Andrew Shvayka
Committed by GitHub
2 parents 65e47393 b603f26f

Merge pull request #183 from thingsboard/feature/TB-67

TB-67: Add new filter to rule engine based on device type
Showing 15 changed files with 205 additions and 29 deletions
... ... @@ -19,13 +19,11 @@ import akka.actor.ActorContext;
19 19 import akka.actor.ActorRef;
20 20 import akka.event.LoggingAdapter;
21 21 import org.thingsboard.server.actors.ActorSystemContext;
22   -import org.thingsboard.server.actors.rule.ChainProcessingContext;
23   -import org.thingsboard.server.actors.rule.ChainProcessingMetaData;
24   -import org.thingsboard.server.actors.rule.RuleProcessingMsg;
25   -import org.thingsboard.server.actors.rule.RulesProcessedMsg;
  22 +import org.thingsboard.server.actors.rule.*;
26 23 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
27 24 import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;
28 25 import org.thingsboard.server.common.data.DataConstants;
  26 +import org.thingsboard.server.common.data.Device;
29 27 import org.thingsboard.server.common.data.id.DeviceId;
30 28 import org.thingsboard.server.common.data.id.SessionId;
31 29 import org.thingsboard.server.common.data.kv.AttributeKey;
... ... @@ -42,6 +40,7 @@ import org.thingsboard.server.common.msg.session.ToDeviceMsg;
42 40 import org.thingsboard.server.extensions.api.device.DeviceAttributes;
43 41 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
44 42 import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;
  43 +import org.thingsboard.server.extensions.api.device.DeviceMetaData;
45 44 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
46 45 import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
47 46 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutIntMsg;
... ... @@ -71,6 +70,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
71 70 private final Map<Integer, ToDeviceRpcRequestMetadata> rpcPendingMap;
72 71
73 72 private int rpcSeq = 0;
  73 + private String deviceName;
  74 + private String deviceType;
74 75 private DeviceAttributes deviceAttributes;
75 76
76 77 public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, DeviceId deviceId) {
... ... @@ -84,6 +85,10 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
84 85 }
85 86
86 87 private void initAttributes() {
  88 + //TODO: add invalidation of deviceType cache.
  89 + Device device = systemContext.getDeviceService().findDeviceById(deviceId);
  90 + this.deviceName = device.getName();
  91 + this.deviceType = device.getType();
87 92 this.deviceAttributes = new DeviceAttributes(fetchAttributes(DataConstants.CLIENT_SCOPE),
88 93 fetchAttributes(DataConstants.SERVER_SCOPE), fetchAttributes(DataConstants.SHARED_SCOPE));
89 94 }
... ... @@ -230,7 +235,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
230 235
231 236 void process(ActorContext context, RuleChainDeviceMsg srcMsg) {
232 237 ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(),
233   - srcMsg.getToDeviceActorMsg(), deviceAttributes, context.self());
  238 + srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self());
234 239 ChainProcessingContext ctx = new ChainProcessingContext(md);
235 240 if (ctx.getChainLength() > 0) {
236 241 RuleProcessingMsg msg = new RuleProcessingMsg(ctx);
... ...
... ... @@ -21,6 +21,7 @@ import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
21 21 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
22 22 import org.thingsboard.server.common.msg.session.ToDeviceMsg;
23 23 import org.thingsboard.server.extensions.api.device.DeviceAttributes;
  24 +import org.thingsboard.server.extensions.api.device.DeviceMetaData;
24 25
25 26 public class ChainProcessingContext {
26 27
... ... @@ -85,8 +86,20 @@ public class ChainProcessingContext {
85 86 return md.inMsg;
86 87 }
87 88
  89 + public DeviceMetaData getDeviceMetaData() {
  90 + return md.deviceMetaData;
  91 + }
  92 +
  93 + public String getDeviceName() {
  94 + return md.deviceMetaData.getDeviceName();
  95 + }
  96 +
  97 + public String getDeviceType() {
  98 + return md.deviceMetaData.getDeviceType();
  99 + }
  100 +
88 101 public DeviceAttributes getAttributes() {
89   - return md.deviceAttributes;
  102 + return md.deviceMetaData.getDeviceAttributes();
90 103 }
91 104
92 105 public ToDeviceMsg getResponse() {
... ...
... ... @@ -15,10 +15,9 @@
15 15 */
16 16 package org.thingsboard.server.actors.rule;
17 17
18   -import org.thingsboard.server.extensions.api.device.DeviceAttributes;
19   -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
20   -
21 18 import akka.actor.ActorRef;
  19 +import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
  20 +import org.thingsboard.server.extensions.api.device.DeviceMetaData;
22 21
23 22 /**
24 23 * Immutable part of chain processing data;
... ... @@ -30,13 +29,13 @@ public final class ChainProcessingMetaData {
30 29 final RuleActorChain chain;
31 30 final ToDeviceActorMsg inMsg;
32 31 final ActorRef originator;
33   - final DeviceAttributes deviceAttributes;
  32 + final DeviceMetaData deviceMetaData;
34 33
35   - public ChainProcessingMetaData(RuleActorChain chain, ToDeviceActorMsg inMsg, DeviceAttributes deviceAttributes, ActorRef originator) {
  34 + public ChainProcessingMetaData(RuleActorChain chain, ToDeviceActorMsg inMsg, DeviceMetaData deviceMetaData, ActorRef originator) {
36 35 super();
37 36 this.chain = chain;
38 37 this.inMsg = inMsg;
39 38 this.originator = originator;
40   - this.deviceAttributes = deviceAttributes;
  39 + this.deviceMetaData = deviceMetaData;
41 40 }
42 41 }
... ...
... ... @@ -144,7 +144,7 @@ class RuleActorMessageProcessor extends ComponentMsgProcessor<RuleId> {
144 144 ChainProcessingContext chainCtx = msg.getCtx();
145 145 ToDeviceActorMsg inMsg = chainCtx.getInMsg();
146 146
147   - ruleCtx.update(inMsg, chainCtx.getAttributes());
  147 + ruleCtx.update(inMsg, chainCtx.getDeviceMetaData());
148 148
149 149 logger.debug("[{}] Going to filter in msg: {}", entityId, inMsg);
150 150 for (RuleFilter filter : filters) {
... ...
... ... @@ -22,6 +22,7 @@ import org.thingsboard.server.dao.event.EventService;
22 22 import org.thingsboard.server.dao.timeseries.TimeseriesService;
23 23 import org.thingsboard.server.extensions.api.device.DeviceAttributes;
24 24 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
  25 +import org.thingsboard.server.extensions.api.device.DeviceMetaData;
25 26 import org.thingsboard.server.extensions.api.rules.RuleContext;
26 27
27 28 import java.util.Optional;
... ... @@ -34,7 +35,7 @@ public class RuleProcessingContext implements RuleContext {
34 35 private TenantId tenantId;
35 36 private CustomerId customerId;
36 37 private DeviceId deviceId;
37   - private DeviceAttributes deviceAttributes;
  38 + private DeviceMetaData deviceMetaData;
38 39
39 40 RuleProcessingContext(ActorSystemContext systemContext, RuleId ruleId) {
40 41 this.tsService = systemContext.getTsService();
... ... @@ -42,11 +43,11 @@ public class RuleProcessingContext implements RuleContext {
42 43 this.ruleId = ruleId;
43 44 }
44 45
45   - void update(ToDeviceActorMsg toDeviceActorMsg, DeviceAttributes attributes) {
  46 + void update(ToDeviceActorMsg toDeviceActorMsg, DeviceMetaData deviceMetaData) {
46 47 this.tenantId = toDeviceActorMsg.getTenantId();
47 48 this.customerId = toDeviceActorMsg.getCustomerId();
48 49 this.deviceId = toDeviceActorMsg.getDeviceId();
49   - this.deviceAttributes = attributes;
  50 + this.deviceMetaData = deviceMetaData;
50 51 }
51 52
52 53 @Override
... ... @@ -55,8 +56,8 @@ public class RuleProcessingContext implements RuleContext {
55 56 }
56 57
57 58 @Override
58   - public DeviceAttributes getDeviceAttributes() {
59   - return deviceAttributes;
  59 + public DeviceMetaData getDeviceMetaData() {
  60 + return deviceMetaData;
60 61 }
61 62
62 63 @Override
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.extensions.api.device;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.DeviceId;
  20 +
  21 +/**
  22 + * Contains basic device metadata;
  23 + *
  24 + * @author ashvayka
  25 + */
  26 +@Data
  27 +public final class DeviceMetaData {
  28 +
  29 + final DeviceId deviceId;
  30 + final String deviceName;
  31 + final String deviceType;
  32 + final DeviceAttributes deviceAttributes;
  33 +
  34 +}
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.extensions.api.rules;
18 18 import org.thingsboard.server.common.data.Event;
19 19 import org.thingsboard.server.common.data.id.RuleId;
20 20 import org.thingsboard.server.extensions.api.device.DeviceAttributes;
  21 +import org.thingsboard.server.extensions.api.device.DeviceMetaData;
21 22
22 23 import java.util.Optional;
23 24
... ... @@ -25,7 +26,7 @@ public interface RuleContext {
25 26
26 27 RuleId getRuleId();
27 28
28   - DeviceAttributes getDeviceAttributes();
  29 + DeviceMetaData getDeviceMetaData();
29 30
30 31 Event save(Event event);
31 32
... ...
... ... @@ -71,8 +71,8 @@ public abstract class AbstractTemplatePluginAction<T extends TemplateActionConfi
71 71 }
72 72
73 73 protected String getMsgBody(RuleContext ctx, ToDeviceActorMsg msg) {
74   - log.trace("Creating context for: {} and payload {}", ctx.getDeviceAttributes(), msg.getPayload());
75   - VelocityContext context = VelocityUtils.createContext(ctx.getDeviceAttributes(), msg.getPayload());
  74 + log.trace("Creating context for: {} and payload {}", ctx.getDeviceMetaData(), msg.getPayload());
  75 + VelocityContext context = VelocityUtils.createContext(ctx.getDeviceMetaData(), msg.getPayload());
76 76 return VelocityUtils.merge(template, context);
77 77 }
78 78
... ...
... ... @@ -46,7 +46,7 @@ public class DeviceAttributesFilter extends BasicJsFilter {
46 46
47 47 @Override
48 48 protected boolean doFilter(RuleContext ctx, ToDeviceActorMsg msg) throws ScriptException {
49   - return evaluator.execute(toBindings(ctx.getDeviceAttributes(), msg != null ? msg.getPayload() : null));
  49 + return evaluator.execute(toBindings(ctx.getDeviceMetaData().getDeviceAttributes(), msg != null ? msg.getPayload() : null));
50 50 }
51 51
52 52 private Bindings toBindings(DeviceAttributes attributes, FromDeviceMsg msg) {
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.extensions.core.filter;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
  20 +import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
  21 +import org.thingsboard.server.extensions.api.component.Filter;
  22 +import org.thingsboard.server.extensions.api.rules.RuleContext;
  23 +import org.thingsboard.server.extensions.api.rules.RuleFilter;
  24 +import org.thingsboard.server.extensions.api.rules.SimpleRuleLifecycleComponent;
  25 +
  26 +import java.util.Arrays;
  27 +import java.util.Set;
  28 +import java.util.stream.Collectors;
  29 +
  30 +import static org.thingsboard.server.common.msg.session.MsgType.TO_SERVER_RPC_REQUEST;
  31 +
  32 +/**
  33 + * @author Andrew Shvayka
  34 + */
  35 +@Filter(name = "Device Type Filter", descriptor = "DeviceTypeFilterDescriptor.json", configuration = DeviceTypeFilterConfiguration.class)
  36 +@Slf4j
  37 +public class DeviceTypeFilter extends SimpleRuleLifecycleComponent implements RuleFilter<DeviceTypeFilterConfiguration> {
  38 +
  39 + private Set<String> deviceTypes;
  40 +
  41 + @Override
  42 + public void init(DeviceTypeFilterConfiguration configuration) {
  43 + deviceTypes = Arrays.stream(configuration.getDeviceTypes())
  44 + .map(m -> m.getName())
  45 + .collect(Collectors.toSet());
  46 + }
  47 +
  48 + @Override
  49 + public boolean filter(RuleContext ctx, ToDeviceActorMsg msg) {
  50 + return deviceTypes.contains(ctx.getDeviceMetaData().getDeviceType());
  51 + }
  52 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.extensions.core.filter;
  17 +
  18 +import lombok.Data;
  19 +
  20 +/**
  21 + * @author Andrew Shvayka
  22 + */
  23 +@Data
  24 +public class DeviceTypeFilterConfiguration {
  25 +
  26 + private DeviceTypeName[] deviceTypes;
  27 +
  28 + @Data
  29 + public static class DeviceTypeName {
  30 + private String name;
  31 + }
  32 +
  33 +}
... ...
... ... @@ -59,7 +59,7 @@ public class AlarmDeduplicationProcessor extends SimpleRuleLifecycleComponent
59 59 @Override
60 60 public RuleProcessingMetaData process(RuleContext ctx, ToDeviceActorMsg msg) throws RuleException {
61 61 RuleProcessingMetaData md = new RuleProcessingMetaData();
62   - VelocityContext context = VelocityUtils.createContext(ctx.getDeviceAttributes(), msg.getPayload());
  62 + VelocityContext context = VelocityUtils.createContext(ctx.getDeviceMetaData(), msg.getPayload());
63 63 String alarmId = VelocityUtils.merge(alarmIdTemplate, context);
64 64 String alarmBody = VelocityUtils.merge(alarmBodyTemplate, context);
65 65 Optional<Event> existingEvent = ctx.findEvent(DataConstants.ALARM, alarmId);
... ...
... ... @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
27 27 import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
28 28 import org.thingsboard.server.common.msg.session.FromDeviceMsg;
29 29 import org.thingsboard.server.extensions.api.device.DeviceAttributes;
  30 +import org.thingsboard.server.extensions.api.device.DeviceMetaData;
30 31 import org.thingsboard.server.extensions.api.rules.RuleProcessingMetaData;
31 32 import org.thingsboard.server.extensions.core.filter.DeviceAttributesFilter;
32 33
... ... @@ -64,9 +65,11 @@ public class VelocityUtils {
64 65 return context;
65 66 }
66 67
67   - public static VelocityContext createContext(DeviceAttributes deviceAttributes, FromDeviceMsg payload) {
  68 + public static VelocityContext createContext(DeviceMetaData deviceMetaData, FromDeviceMsg payload) {
68 69 VelocityContext context = new VelocityContext();
69 70 context.put("date", new DateTool());
  71 + DeviceAttributes deviceAttributes = deviceMetaData.getDeviceAttributes();
  72 +
70 73 pushAttributes(context, deviceAttributes.getClientSideAttributes(), DeviceAttributesFilter.CLIENT_SIDE);
71 74 pushAttributes(context, deviceAttributes.getServerSideAttributes(), DeviceAttributesFilter.SERVER_SIDE);
72 75 pushAttributes(context, deviceAttributes.getServerSidePublicAttributes(), DeviceAttributesFilter.SHARED);
... ... @@ -77,6 +80,10 @@ public class VelocityUtils {
77 80 break;
78 81 }
79 82
  83 + context.put("deviceId", deviceMetaData.getDeviceId().getId().toString());
  84 + context.put("deviceName", deviceMetaData.getDeviceName());
  85 + context.put("deviceType", deviceMetaData.getDeviceType());
  86 +
80 87 return context;
81 88 }
82 89
... ...
  1 +{
  2 + "schema": {
  3 + "title": "Device Type Filter Configuration",
  4 + "type": "object",
  5 + "properties": {
  6 + "deviceTypes": {
  7 + "title": "Device types",
  8 + "type": "array",
  9 + "minItems" : 1,
  10 + "items": {
  11 + "type": "object",
  12 + "title": "Device Type",
  13 + "properties": {
  14 + "name": {
  15 + "title": "Device Type",
  16 + "type": "string"
  17 + }
  18 + }
  19 + },
  20 + "uniqueItems": true
  21 + }
  22 + },
  23 + "required": ["deviceTypes"]
  24 + },
  25 + "form": [
  26 + "deviceTypes"
  27 + ]
  28 +}
\ No newline at end of file
... ...
... ... @@ -15,11 +15,13 @@
15 15 */
16 16 package org.thingsboard.server.extensions.core.filter;
17 17
  18 +import org.thingsboard.server.common.data.id.DeviceId;
18 19 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
19 20 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
20 21 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
21 22 import org.thingsboard.server.common.data.kv.DoubleDataEntry;
22 23 import org.thingsboard.server.extensions.api.device.DeviceAttributes;
  24 +import org.thingsboard.server.extensions.api.device.DeviceMetaData;
23 25 import org.thingsboard.server.extensions.api.rules.RuleContext;
24 26 import org.junit.Assert;
25 27 import org.junit.Test;
... ... @@ -30,6 +32,7 @@ import org.mockito.runners.MockitoJUnitRunner;
30 32
31 33 import java.util.ArrayList;
32 34 import java.util.List;
  35 +import java.util.UUID;
33 36
34 37 /**
35 38 * @author Andrew Shvayka
... ... @@ -52,7 +55,7 @@ public class DeviceAttributesFilterTest {
52 55 clientAttributes.add(new BaseAttributeKvEntry(new BooleanDataEntry("booleanValue", false), 42));
53 56 DeviceAttributes attributes = new DeviceAttributes(clientAttributes, new ArrayList<>(), new ArrayList<>());
54 57
55   - Mockito.when(ruleCtx.getDeviceAttributes()).thenReturn(attributes);
  58 + Mockito.when(ruleCtx.getDeviceMetaData()).thenReturn(new DeviceMetaData(new DeviceId(UUID.randomUUID()), "A", "A", attributes));
56 59 Assert.assertTrue(filter.filter(ruleCtx, null));
57 60 filter.stop();
58 61 }
... ... @@ -66,7 +69,7 @@ public class DeviceAttributesFilterTest {
66 69 clientAttributes.add(new BaseAttributeKvEntry(new BooleanDataEntry("booleanValue", false), 42));
67 70 DeviceAttributes attributes = new DeviceAttributes(clientAttributes, new ArrayList<>(), new ArrayList<>());
68 71
69   - Mockito.when(ruleCtx.getDeviceAttributes()).thenReturn(attributes);
  72 + Mockito.when(ruleCtx.getDeviceMetaData()).thenReturn(new DeviceMetaData(new DeviceId(UUID.randomUUID()), "A", "A", attributes));
70 73 Assert.assertTrue(filter.filter(ruleCtx, null));
71 74 filter.stop();
72 75 }
... ... @@ -81,7 +84,7 @@ public class DeviceAttributesFilterTest {
81 84 clientAttributes.add(new BaseAttributeKvEntry(new BooleanDataEntry("booleanValue", false), 42));
82 85 DeviceAttributes attributes = new DeviceAttributes(clientAttributes, new ArrayList<>(), new ArrayList<>());
83 86
84   - Mockito.when(ruleCtx.getDeviceAttributes()).thenReturn(attributes);
  87 + Mockito.when(ruleCtx.getDeviceMetaData()).thenReturn(new DeviceMetaData(new DeviceId(UUID.randomUUID()), "A", "A", attributes));
85 88
86 89 for (int i = 0; i < 10000; i++) {
87 90 Assert.assertTrue(filter.filter(ruleCtx, null));
... ... @@ -99,7 +102,7 @@ public class DeviceAttributesFilterTest {
99 102 serverAttributes.add(new BaseAttributeKvEntry(new BooleanDataEntry("booleanValue", false), 42));
100 103 DeviceAttributes attributes = new DeviceAttributes(new ArrayList<>(), serverAttributes, new ArrayList<>());
101 104
102   - Mockito.when(ruleCtx.getDeviceAttributes()).thenReturn(attributes);
  105 + Mockito.when(ruleCtx.getDeviceMetaData()).thenReturn(new DeviceMetaData(new DeviceId(UUID.randomUUID()), "A", "A", attributes));
103 106 Assert.assertTrue(filter.filter(ruleCtx, null));
104 107 filter.stop();
105 108 }
... ... @@ -118,7 +121,7 @@ public class DeviceAttributesFilterTest {
118 121 serverAttributes.add(new BaseAttributeKvEntry(new BooleanDataEntry("booleanValue", false), 42));
119 122 DeviceAttributes attributes = new DeviceAttributes(clientAttributes, serverAttributes, new ArrayList<>());
120 123
121   - Mockito.when(ruleCtx.getDeviceAttributes()).thenReturn(attributes);
  124 + Mockito.when(ruleCtx.getDeviceMetaData()).thenReturn(new DeviceMetaData(new DeviceId(UUID.randomUUID()), "A", "A", attributes));
122 125 Assert.assertTrue(filter.filter(ruleCtx, null));
123 126 filter.stop();
124 127 }
... ...